From db1003e7a5194d2aa34a36272291923818661f8a Mon Sep 17 00:00:00 2001 From: makc Date: Thu, 18 May 2017 22:38:44 +0200 Subject: embed image links --- juick-www/package.json | 6 +- juick-www/src/main/js/killy/index.js | 192 +++++++++++++++++++++++++++++++ juick-www/src/main/js/killy/package.json | 6 + juick-www/src/main/static/scripts.js | 3 + juick-www/webpack.config.js | 1 + 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 juick-www/src/main/js/killy/index.js create mode 100644 juick-www/src/main/js/killy/package.json (limited to 'juick-www') diff --git a/juick-www/package.json b/juick-www/package.json index 2eda35cd..6ef22c44 100644 --- a/juick-www/package.json +++ b/juick-www/package.json @@ -12,8 +12,11 @@ }, "license": "AGPLv3", "devDependencies": { + "babel-core": "^6.0.20", + "babel-loader": "^6.0.1", + "babel-preset-es2015": "^6.24.1", "css-loader": "^0.23.1", - "csslint": "^1.0.2", + "csslint": "^1.0.5", "csslint-loader": "^1.0.0", "eslint": "^3.1.1", "eslint-loader": "^1.4.1", @@ -31,6 +34,7 @@ "classlist.js": "^1.1.20150312", "element-closest": "^2.0.1", "evil-icons": "^1.8.0", + "killy": "file:src/main/js/killy", "whatwg-fetch": "^1.0.0" } } diff --git a/juick-www/src/main/js/killy/index.js b/juick-www/src/main/js/killy/index.js new file mode 100644 index 00000000..f7eb6e5a --- /dev/null +++ b/juick-www/src/main/js/killy/index.js @@ -0,0 +1,192 @@ +function intersect(a, b) { + if (b.length > a.length) { [a, b] = [b, a]; } // loop over shorter array + return a.filter(item => (b.indexOf(item) !== -1)); +} + +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +function moveAll(fromNode, toNode) { + for (let c; (c = fromNode.firstChild) != null; ) { toNode.appendChild(c); } +} + +function removeAllFrom(fromNode) { + for (let c; (c = fromNode.lastChild) != null; ) { fromNode.removeChild(c); } +} + +function turnIntoCts(node, makeNodeCallback) { + node.onclick = function(e){ + e.preventDefault(); + let newNode = makeNodeCallback(); + if(newNode !== node) { + removeAllFrom(node); + moveAll(newNode, node); + node.className = newNode.className; + } else { + node.onclick = ''; + node.classList.remove('cts'); + } + }; +} + +function makeCts(makeNodeCallback, title) { + let ctsNode = document.createElement('div'); + let placeholder = document.createElement('div'); + placeholder.className = 'placeholder'; + placeholder.innerHTML = title; + ctsNode.className = 'cts'; + ctsNode.appendChild(placeholder); + turnIntoCts(ctsNode, makeNodeCallback); + return ctsNode; +} + +function naiveEllipsis(str, len, ellStr='...') { + let ellLen = ellStr.length; + if (str.length <= len) { return str; } + let half = Math.floor((len - ellLen) / 2); + let left = str.substring(0, half); + let right = str.substring(str.length - (len - half - ellLen)); + return '' + left + ellStr + right; +} + +function wrapIntoTag(node, tagName, className) { + let tag = document.createElement(tagName); + if (className !== undefined) { tag.className = className; } + tag.appendChild(node); + return tag; +} + +function getEmbedableLinkTypes() { + return [ + { + name: 'Jpeg and png images', + id: 'embed_jpeg_and_png_images', + ctsDefault: false, + re: /\.(jpeg|jpg|png|svg)(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i, + makeNode: function(aNode) { + let aNode2 = document.createElement('a'); + let imgNode = document.createElement('img'); + imgNode.src = aNode.href; + aNode2.href = aNode.href; + aNode2.appendChild(imgNode); + return wrapIntoTag(aNode2, 'div'); + } + }, + { + name: 'Gif images', + id: 'embed_gif_images', + ctsDefault: true, + re: /\.gif(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i, + makeNode: function(aNode) { + let aNode2 = document.createElement('a'); + let imgNode = document.createElement('img'); + imgNode.src = aNode.href; + aNode2.href = aNode.href; + aNode2.appendChild(imgNode); + return wrapIntoTag(aNode2, 'div'); + } + } + ] +} + +function embedLink(aNode, linkTypes, container, alwaysCts, afterNode) { + let anyEmbed = false; + let linkId = (aNode.href.replace(/^https?:/i, '').replace(/\'/i,'')); + let sameEmbed = container.querySelector('*[data-linkid=\'' + linkId + '\']'); // do not embed the same thing twice + if (sameEmbed === null) { + anyEmbed = [].some.call(linkTypes, function(linkType) { + /* if (GM_getValue(linkType.id, true)) { TODO user embed particular link type setting */ + let reResult = linkType.re.exec(aNode.href); + if (reResult !== null) { + if ((linkType.match !== undefined) && (linkType.match(aNode, reResult) === false)) { return false; } + let newNode; + let isCts = alwaysCts /* || TODO user click-to-show setting */; + if (isCts) { + let linkTitle = (linkType.makeTitle !== undefined) ? linkType.makeTitle(aNode, reResult) : naiveEllipsis(aNode.href, 55); + newNode = makeCts(() => linkType.makeNode(aNode, reResult, newNode), 'Click to show: ' + linkTitle); + } else { + newNode = linkType.makeNode(aNode, reResult); + } + if (!newNode) { return false; } + aNode.classList.add('embedLink'); + /* + if (GM_getValue('enable_link_text_update', true) && (linkType.linkTextUpdate !== undefined)) { + linkType.linkTextUpdate(aNode, reResult); + } + */ + newNode.setAttribute('data-linkid', linkId); + if (afterNode !== undefined) { + insertAfter(newNode, afterNode); + } else { + container.appendChild(newNode); + } + //setHighlightOnHover(aNode, newNode); + return true; + } + /* } */ + }); + } + return anyEmbed; +} + +function embedLinks(aNodes, container, alwaysCts, afterNode) { + let anyEmbed = false; + let embedableLinkTypes = getEmbedableLinkTypes(); + Array.from(aNodes).forEach(aNode => { + let isEmbedded = embedLink(aNode, embedableLinkTypes, container, alwaysCts, afterNode); + anyEmbed = anyEmbed || isEmbedded; + }); + return anyEmbed; +} + +function articleInfo(article) { + let userId = article.querySelector('div.msg-avatar > a > img').alt; + let tagNodes = article.querySelectorAll('.msg-tags > *'); + let tags = Array.from(tagNodes).map(d => d.textContent.toLowerCase()); + return { userId: userId, tags: tags }; +} + +function isFilteredX(x, filteredUsers, filteredTags) { + let {userId, tags} = articleInfo(x); + return (filteredUsers !== undefined && filteredUsers.indexOf(userId.toLowerCase()) !== -1) + || (intersect(tags, filteredTags).length > 0); +} + +function embedLinksToX(x, beforeNodeSelector, allLinksSelector, ctsUsers, ctsTags) { + let isCtsPost = isFilteredX(x, ctsUsers, ctsTags); + let allLinks = x.querySelectorAll(allLinksSelector); + let embedContainer = document.createElement('div'); + embedContainer.className = 'embedContainer'; + let anyEmbed = embedLinks(allLinks, embedContainer, isCtsPost); + if (anyEmbed) { + let beforeNode = x.querySelector(beforeNodeSelector); + x.insertBefore(embedContainer, beforeNode); + } +} + +function embedLinksToArticles() { + let ctsUsers = [], ctsTags = [] // TODO click-to-show users and tags + let beforeNodeSelector = 'nav.l'; + let allLinksSelector = 'p:not(.ir) a, pre a'; + Array.from(document.querySelectorAll('#content > article')).forEach(article => { + embedLinksToX(article, beforeNodeSelector, allLinksSelector, ctsUsers, ctsTags); + }); +} + +function embedLinksToPost() { + let ctsUsers = [], ctsTags = [] // TODO click-to-show users and tags + let beforeNodeSelector = '.msg-txt + *'; + let allLinksSelector = '.msg-txt a'; + Array.from(document.querySelectorAll('#content .msg-cont')).forEach(msg => { + embedLinksToX(msg, beforeNodeSelector, allLinksSelector, ctsUsers, ctsTags); + }); +} + +exports.embed = function() { + if (document.querySelectorAll('#content > article').length) { + embedLinksToArticles(); + } else { + embedLinksToPost(); + } +} diff --git a/juick-www/src/main/js/killy/package.json b/juick-www/src/main/js/killy/package.json new file mode 100644 index 00000000..1360826d --- /dev/null +++ b/juick-www/src/main/js/killy/package.json @@ -0,0 +1,6 @@ +{ + "main": "../../src/main/js/killy/index.js", + "name": "killy", + "version": "0.0.1", + "private": true +} \ No newline at end of file diff --git a/juick-www/src/main/static/scripts.js b/juick-www/src/main/static/scripts.js index d48844f7..57661caa 100644 --- a/juick-www/src/main/static/scripts.js +++ b/juick-www/src/main/static/scripts.js @@ -2,6 +2,7 @@ var autosize = require('autosize'); require('whatwg-fetch'); require('element-closest'); require('classlist.js'); +var killy = require('killy'); if (!('remove' in Element.prototype)) { // Firefox <23 Element.prototype.remove = function() { if (this.parentNode) { @@ -768,4 +769,6 @@ ready(function () { window.addEventListener('hashchange', unfoldReply); window.addEventListener('pagehide', wsShutdown); + + killy.embed(); }); diff --git a/juick-www/webpack.config.js b/juick-www/webpack.config.js index 27e4a28a..0b833b04 100644 --- a/juick-www/webpack.config.js +++ b/juick-www/webpack.config.js @@ -20,6 +20,7 @@ module.exports = { { test: /\.css$/, loader: 'csslint?failOnWarning=false', exclude: /node_modules/ } ], loaders: [ + { test: /killy.index\.js$/, loader: 'babel-loader', query: { presets: ['es2015'] }}, { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }, { test: /\.png$/, loader: "url-loader?limit=10000000000" }, { test: /\.svg$/, loader: "url-loader?limit=10000000000" } -- cgit v1.2.3