Mercurial > projects > booket
diff booket.js @ 7:a1a934adff8d version-2
Add support for favicons
author | Guido Berhoerster <guido+booket@berhoerster.name> |
---|---|
date | Sun, 14 Sep 2014 23:12:37 +0200 |
parents | e9ad4c625b7a |
children | 20902b548d9f |
line wrap: on
line diff
--- a/booket.js Wed Sep 10 19:45:23 2014 +0200 +++ b/booket.js Sun Sep 14 23:12:37 2014 +0200 @@ -25,15 +25,73 @@ 'use strict'; var BOOKMARKLET_URI = - 'javascript:(function () {' + - '\'use strict\';' + + 'javascript:(function() {' + + '\'use strict\';' + + '' + + 'function displayBookmarkData(bookmarkData) {' + + 'window.alert(\'Copy the following data and paste it into \' +' + + '\'Booket:\\n\\n\' + JSON.stringify(bookmarkData));' + + '}' + + '' + + 'var bookmarkData = {' + + '\'url\': document.URL,' + + '\'title\': document.title,' + + '\'favicon\': undefined' + + '};' + + 'var faviconLinkElement;' + + 'var faviconUrls = [];' + + 'var aElement;' + + 'var canvasElement;' + + 'var canvasCtx;' + + 'var imgElement;' + + '' + + 'aElement = document.createElement(\'a\');' + + 'aElement.href = document.URL;' + + '' + + 'faviconUrls.push(aElement.protocol + \'//\' + aElement.host + ' + + '\'/favicon.ico\');' + + '' + + 'faviconLinkElement = document.querySelector(' + + '\'link[rel~=\\\'icon\\\']\');' + + 'if (faviconLinkElement !== null) {' + + 'faviconUrls.push(faviconLinkElement.href);' + + '}' + '' + - 'window.alert(\'Copy the following data and paste it into \' +' + - '\'Booket:\\n\\n\' + JSON.stringify({' + - '\'url\': document.URL,' + - '\'title\': document.title' + - '}));' + - '}) ();'; + 'canvasElement = document.createElement(\'canvas\');' + + 'canvasCtx = canvasElement.getContext(\'2d\');' + + '' + + 'imgElement = new Image();' + + 'imgElement.addEventListener(\'load\', function(e) {' + + 'var faviconUrl;' + + '' + + 'canvasElement.width = 16;' + + 'canvasElement.height = 16;' + + 'canvasCtx.clearRect(0, 0, 16, 16);' + + 'try {' + + 'canvasCtx.drawImage(this, 0, 0, 16, 16);' + + 'bookmarkData.favicon = canvasElement.toDataURL();' + + '} catch (exception) {' + + 'faviconUrl = faviconUrls.pop();' + + '}' + + 'if (bookmarkData.favicon !== undefined || ' + + 'faviconUrl === undefined) {' + + 'displayBookmarkData(bookmarkData);' + + '} else {' + + 'imgElement.src = faviconUrl;' + + '}' + + '});' + + 'imgElement.addEventListener(\'error\', function(e) {' + + 'var faviconUrl;' + + '' + + 'faviconUrl = faviconUrls.pop();' + + 'if (faviconUrl !== undefined) {' + + 'imgElement.src = faviconUrl;' + + '} else {' + + 'displayBookmarkData(bookmarkData);' + + '}' + + '});' + + 'imgElement.src = faviconUrls.pop();' + + '})();'; /* @@ -348,7 +406,7 @@ * model */ -var Bookmark = function (url, title, tags, ctime, mtime) { +var Bookmark = function (url, title, favicon, tags, ctime, mtime) { var parsedTime; if (!isString(url)) { @@ -358,6 +416,12 @@ this.title = (isString(title) && title !== '') ? title : url; + if (isString(favicon) && favicon.match(/^data:image\/png;base64,/)) { + this.favicon = favicon; + } else { + this.favicon = undefined; + } + if (Array.isArray(tags)) { // remove duplicates, non-string or empty tags and tags containing // commas @@ -647,7 +711,8 @@ parsedData.bookmarks.forEach(function (bookmark) { if (isString(bookmark.url) && bookmark.url !== '') { bookmarks.push(new Bookmark(bookmark.url, bookmark.title, - bookmark.tags, bookmark.ctime, bookmark.mtime)); + bookmark.favicon, bookmark.tags, bookmark.ctime, + bookmark.mtime)); } }, this); @@ -822,7 +887,6 @@ var saveFormElement; var loadFormElement; var newNode; - var editorFormElement; ObservableMixin.call(this); @@ -839,17 +903,24 @@ newNode = document.importNode( document.querySelector('#bookmark-editor-template').content, true); - editorFormElement = newNode.querySelector('form.bookmark-editor-form'); - editorFormElement.querySelector('legend').textContent = 'Add Bookmark'; - editorFormElement.querySelector('input:not([type="hidden"])').accessKey = - 'a'; - editorFormElement.addEventListener('input', this); - editorFormElement.addEventListener('click', this); - editorFormElement.addEventListener('submit', this); - editorFormElement.addEventListener('reset', this); + this.editorFormElement = newNode.querySelector('form.bookmark-editor-form'); + this.editorFormElement.querySelector('legend').textContent = 'Add Bookmark'; + this.editorFormElement.querySelector( + 'input:not([type="hidden"])').accessKey = 'a'; + this.editorFormElement.addEventListener('input', this); + this.editorFormElement.addEventListener('click', this); + this.editorFormElement.addEventListener('submit', this); + this.editorFormElement.addEventListener('reset', this); + + this.faviconImageElement = + this.editorFormElement.querySelector('img.bookmark-favicon'); + this.faviconImageElement.addEventListener('load', this); + this.faviconImageElement.addEventListener('error', this); + + this.missingFaviconUri = this.faviconImageElement.src; this.editTagListElement = - editorFormElement.querySelector('ul.tag-input-list'); + this.editorFormElement.querySelector('ul.tag-input-list'); this.editTagListElement.appendChild(this.createTagInputElement('')); saveFormElement.parentNode.insertBefore(newNode, @@ -876,6 +947,19 @@ var i; switch (e.type) { + case 'error': + if (e.target.classList.contains('bookmark-favicon')) { + if (e.target.src !== this.missingFaviconUri) { + e.target.src = this.missingFaviconUri; + } + } + break; + case 'load': + if (e.target.classList.contains('bookmark-favicon')) { + this.editorFormElement.favicon.value = + (e.target.src !== this.missingFaviconUri) ? e.target.src : ''; + } + break; case 'input': if (e.target.name === 'bookmarklet-import') { // get rid of any preceding text @@ -893,6 +977,10 @@ if (isString(parsedData.title) && parsedData.title !== '') { e.target.form.elements.title.value = parsedData.title; } + if (isString(parsedData.favicon) && + parsedData.favicon.match(/^data:image\/png;base64,/)) { + this.faviconImageElement.src = parsedData.favicon; + } } break; case 'click': @@ -928,7 +1016,7 @@ } this.notify('save-bookmark', e.target.url.value, - e.target.title.value, tags); + e.target.title.value, e.target.favicon.value, tags); e.target.reset(); } @@ -937,6 +1025,9 @@ if (e.target.classList.contains('bookmark-editor-form')) { e.target.blur(); + e.target.querySelector('img.bookmark-favicon').src = + this.missingFaviconUri; + // remove all but one tag input element while (this.editTagListElement.firstChild !== null) { this.editTagListElement.removeChild( @@ -979,6 +1070,7 @@ this.tagInputTemplate = document.querySelector('#tag-input-template'); this.bookmarkListElement = document.querySelector('ul#bookmark-list'); + this.bookmarkListElement.addEventListener('input', this); this.bookmarkListElement.addEventListener('click', this); this.bookmarkListElement.addEventListener('submit', this); this.bookmarkListElement.addEventListener('reset', this); @@ -991,17 +1083,65 @@ this.bookmarkMessageElement = document.querySelector('#bookmark-message'); + this.missingFaviconUri = ''; + this.updateBookmarkMessage(); }; extend(BookmarkView, ObservableMixin); BookmarkView.prototype.handleEvent = function (e) { + var bookmarkletData; + var parsedData; var i; var tags = []; var node; switch (e.type) { + case 'error': + if (e.target.classList.contains('bookmark-favicon')) { + if (e.target.src !== this.missingFaviconUri) { + e.target.src = this.missingFaviconUri; + } + } + break; + case 'load': + if (e.target.classList.contains('bookmark-favicon')) { + node = e.target; + while ((node = node.parentNode) !== null) { + if (node.classList.contains('bookmark-editor-form')) { + node.favicon.value = + (e.target.src !== this.missingFaviconUri) ? + e.target.src : ''; + break; + } + } + } + break; + case 'input': + if (e.target.name === 'bookmarklet-import') { + // get rid of any preceding text + bookmarkletData = e.target.value.replace(/^[^{]*/, ''); + + try { + parsedData = JSON.parse(bookmarkletData); + } catch (exception) { + return; + } + + if (isString(parsedData.url) && parsedData.url !== '') { + e.target.form.elements.url.value = parsedData.url; + } + if (isString(parsedData.title) && parsedData.title !== '') { + e.target.form.elements.title.value = parsedData.title; + } + if (isString(parsedData.favicon) && + parsedData.favicon.match(/^data:image\/png;base64,/)) { + e.target.form.querySelector('img.bookmark-favicon').src = + parsedData.favicon; + } + } + break; case 'click': switch (e.target.name) { case 'edit-bookmark': @@ -1040,7 +1180,8 @@ } this.notify('save-bookmark', e.target.url.value, - e.target.title.value, tags, e.target['original-url'].value); + e.target.title.value, e.target.favicon.value, tags, + e.target['original-url'].value); } else if (e.target.id === 'search-form') { // search e.preventDefault(); @@ -1083,6 +1224,7 @@ BookmarkView.prototype.onBookmarkAdded = function (bookmark) { var newNode; var bookmarkElement; + var faviconElement; var linkElement; var hostnameElement; var urlElement; @@ -1095,6 +1237,11 @@ bookmarkElement = newNode.querySelector('li'); bookmarkElement.dataset.bookmarkUrl = bookmark.url; + faviconElement = bookmarkElement.querySelector('img.bookmark-favicon'); + faviconElement.src = (bookmark.favicon) ? bookmark.favicon : + this.missingFaviconUri; + faviconElement.alt = ''; + linkElement = bookmarkElement.querySelector('a.bookmark-link'); linkElement.textContent = linkElement.title = bookmark.title; linkElement.href = bookmark.url; @@ -1211,6 +1358,7 @@ var bookmarkElement; var newNode; var formElement; + var faviconImageElement; var editTagListElement; bookmarkElement = @@ -1231,6 +1379,14 @@ formElement.url.value = bookmark.url; formElement.title.value = bookmark.title; + faviconImageElement = formElement.querySelector('img.bookmark-favicon'); + faviconImageElement.addEventListener('load', this); + faviconImageElement.addEventListener('error', this); + this.missingFaviconUri = faviconImageElement.src; + if (bookmark.favicon) { + faviconImageElement.src = bookmark.favicon; + } + editTagListElement = formElement.querySelector('ul.tag-input-list'); bookmark.tags.forEach(function (tag) { editTagListElement.appendChild(this.createTagInputElement(tag)); @@ -1379,8 +1535,8 @@ this.bookmarkModel.get(bookmarkUrl)); }; -BooketController.prototype.onSaveBookmark = function (url, title, tags, - originalUrl) { +BooketController.prototype.onSaveBookmark = function (url, title, + favicon, tags, originalUrl) { var ctime; if (originalUrl === undefined) { @@ -1403,7 +1559,7 @@ this.bookmarkModel.delete(originalUrl); } - this.bookmarkModel.add(new Bookmark(url, title, tags, ctime)); + this.bookmarkModel.add(new Bookmark(url, title, favicon, tags, ctime)); }; BooketController.prototype.onDeleteBookmark = function (bookmarkUrl) {