aboutsummaryrefslogtreecommitdiff
path: root/juick-www/src
diff options
context:
space:
mode:
Diffstat (limited to 'juick-www/src')
-rw-r--r--juick-www/src/main/js/killy/index.js192
-rw-r--r--juick-www/src/main/js/killy/package.json6
-rw-r--r--juick-www/src/main/static/scripts.js3
3 files changed, 201 insertions, 0 deletions
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();
});