/*jshint sub:true, evil:true */ (function () { "use strict"; var view = '', // data-view listFeeds = '', // data-list-feeds filter = '', // data-filter order = '', // data-order autoreadItem = '', // data-autoread-item autoreadPage = '', // data-autoread-page autohide = '', // data-autohide byPage = -1, // data-by-page shaarli = '', // data-shaarli redirector = '', // data-redirector currentHash = '', // data-current-hash currentPage = 1, // data-current-page currentNbItems = 0, // data-nb-items autoupdate = false, // data-autoupdate autofocus = false, // data-autofocus addFavicon = false, // data-add-favicon preload = false, // data-preload stars = false, // data-stars isLogged = false, // data-is-logged blank = false, // data-blank status = '', listUpdateFeeds = [], listItemsHash = [], currentItemHash = '', currentUnread = 0, title = '', cache = {}, intlTop = 'top', intlShare = 'share', intlRead = 'read', intlUnread = 'unread', intlStar = 'star', intlUnstar = 'unstar', intlFrom = 'from'; /** * trim function * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim */ if(!String.prototype.trim) { String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g,''); }; } /** * http://javascript.info/tutorial/bubbling-and-capturing */ function stopBubbling(event) { if(event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } } /** * JSON Object * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON */ if (!window.JSON) { window.JSON = { parse: function (sJSON) { return eval("(" + sJSON + ")"); }, stringify: function (vContent) { if (vContent instanceof Object) { var sOutput = ""; if (vContent.constructor === Array) { for (var nId = 0; nId < vContent.length; sOutput += this.stringify(vContent[nId]) + ",", nId++); return "[" + sOutput.substr(0, sOutput.length - 1) + "]"; } if (vContent.toString !== Object.prototype.toString) { return "\"" + vContent.toString().replace(/"/g, "\\$&") + "\""; } for (var sProp in vContent) { sOutput += "\"" + sProp.replace(/"/g, "\\$&") + "\":" + this.stringify(vContent[sProp]) + ","; } return "{" + sOutput.substr(0, sOutput.length - 1) + "}"; } return typeof vContent === "string" ? "\"" + vContent.replace(/"/g, "\\$&") + "\"" : String(vContent); } }; } /** * https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started */ function getXHR() { var httpRequest = false; if (window.XMLHttpRequest) { // Mozilla, Safari, ... httpRequest = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE try { httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e2) {} } } return httpRequest; } /** * http://www.sitepoint.com/xhrrequest-and-javascript-closures/ */ // Constructor for generic HTTP client function HTTPClient() {} HTTPClient.prototype = { url: null, xhr: null, callinprogress: false, userhandler: null, init: function(url, obj) { this.url = url; this.obj = obj; this.xhr = new getXHR(); }, asyncGET: function (handler) { // Prevent multiple calls if (this.callinprogress) { throw "Call in progress"; } this.callinprogress = true; this.userhandler = handler; // Open an async request - third argument makes it async this.xhr.open('GET', this.url, true); var self = this; // Assign a closure to the onreadystatechange callback this.xhr.onreadystatechange = function() { self.stateChangeCallback(self); }; this.xhr.send(null); }, stateChangeCallback: function(client) { switch (client.xhr.readyState) { // Request not yet made case 1: try { client.userhandler.onInit(); } catch (e) { /* Handler method not defined */ } break; // Contact established with server but nothing downloaded yet case 2: try { // Check for HTTP status 200 if ( client.xhr.status != 200 ) { client.userhandler.onError( client.xhr.status, client.xhr.statusText ); // Abort the request client.xhr.abort(); // Call no longer in progress client.callinprogress = false; } } catch (e) { /* Handler method not defined */ } break; // Called multiple while downloading in progress case 3: // Notify user handler of download progress try { // Get the total content length // -useful to work out how much has been downloaded var contentLength; try { contentLength = client.xhr.getResponseHeader("Content-Length"); } catch (e) { contentLength = NaN; } // Call the progress handler with what we've got client.userhandler.onProgress( client.xhr.responseText, contentLength ); } catch (e) { /* Handler method not defined */ } break; // Download complete case 4: try { client.userhandler.onSuccess(client.xhr.responseText, client.obj); } catch (e) { /* Handler method not defined */ } finally { client.callinprogress = false; } break; } } }; /** * Handler */ var ajaxHandler = { onInit: function() {}, onError: function(status, statusText) {}, onProgress: function(responseText, length) {}, onSuccess: function(responseText, noFocus) { var result = JSON.parse(responseText); if (result['logout'] && isLogged) { alert('You have been disconnected'); } if (result['item']) { cache['item-' + result['item']['itemHash']] = result['item']; loadDivItem(result['item']['itemHash'], noFocus); } if (result['page']) { updateListItems(result['page']); setCurrentItem(); if (preload) { preloadItems(); } } if (result['read']) { markAsRead(result['read']); } if (result['unread']) { markAsUnread(result['unread']); } if (result['update']) { updateNewItems(result['update']); } } }; /** * http://stackoverflow.com/questions/4652734/return-html-from-a-user-selection/4652824#4652824 */ function getSelectionHtml() { var html = ''; if (typeof window.getSelection != 'undefined') { var sel = window.getSelection(); if (sel.rangeCount) { var container = document.createElement('div'); for (var i = 0, len = sel.rangeCount; i < len; ++i) { container.appendChild(sel.getRangeAt(i).cloneContents()); } html = container.innerHTML; } } else if (typeof document.selection != 'undefined') { if (document.selection.type == 'Text') { html = document.selection.createRange().htmlText; } } return html; } /** * Some javascript snippets */ function removeChildren(elt) { while (elt.hasChildNodes()) { elt.removeChild(elt.firstChild); } } function removeElement(elt) { if (elt && elt.parentNode) { elt.parentNode.removeChild(elt); } } function addClass(elt, cls) { if (elt) { elt.className = (elt.className + ' ' + cls).trim(); } } function removeClass(elt, cls) { if (elt) { elt.className = (' ' + elt.className + ' ').replace(cls, ' ').trim(); } } function hasClass(elt, cls) { if (elt && (' ' + elt.className + ' ').indexOf(' ' + cls + ' ') > -1) { return true; } return false; } /** * Add redirector to link */ function anonymize(elt) { if (redirector !== '') { var domain, a_to_anon = elt.getElementsByTagName("a"); for (var i = 0; i < a_to_anon.length; i++) { domain = a_to_anon[i].href.replace('http://','').replace('https://','').split(/[/?#]/)[0]; if (domain !== window.location.host) { if (redirector !== 'noreferrer') { a_to_anon[i].href = redirector+a_to_anon[i].href; } else { a_to_anon[i].setAttribute('rel', 'noreferrer'); } } } } } function initAnonyme() { if (redirector !== '') { var i = 0, elements = document.getElementById('list-items'); elements = elements.getElementsByTagName('div'); for (i = 0; i < elements.length; i += 1) { if (hasClass(elements[i], 'item-content')) { anonymize(elements[i]); } } } } /** * Replace collapse bootstrap function */ function collapseElement(element) { if (element !== null) { var targetElement = document.getElementById( element.getAttribute('data-target').substring(1) ); if (hasClass(targetElement, 'in')) { removeClass(targetElement, 'in'); targetElement.style.height = 0; } else { addClass(targetElement, 'in'); targetElement.style.height = 'auto'; } } } function collapseClick(event) { event = event || window.event; stopBubbling(event); collapseElement(this); } function initCollapse(list) { var i = 0; for (i = 0; i < list.length; i += 1) { if (list[i].hasAttribute('data-toggle') && list[i].hasAttribute('data-target')) { addEvent(list[i], 'click', collapseClick); } } } /** * Shaarli functions */ function htmlspecialchars_decode(string) { return string .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/&/g, '&') .replace(/*39;/g, "'") .replace(/ /g, " "); } function shaarliItem(itemHash) { var domainUrl, url, domainVia, via, title, sel, element; element = document.getElementById('item-div-'+itemHash); if (element.childNodes.length > 1) { title = getTitleItem(itemHash); url = getUrlItem(itemHash); via = getViaItem(itemHash); if (redirector != 'noreferrer') { url = url.replace(redirector,''); via = via.replace(redirector,''); } domainUrl = url.replace('http://','').replace('https://','').split(/[/?#]/)[0]; domainVia = via.replace('http://','').replace('https://','').split(/[/?#]/)[0]; if (domainUrl !== domainVia) { via = 'via ' + via; } else { via = ''; } sel = getSelectionHtml(); if (sel !== '') { sel = '«' + sel + '»'; } if (shaarli !== '') { window.open( shaarli .replace('${url}', encodeURIComponent(htmlspecialchars_decode(url))) .replace('${title}', encodeURIComponent(htmlspecialchars_decode(title))) .replace('${via}', encodeURIComponent(htmlspecialchars_decode(via))) .replace('${sel}', encodeURIComponent(htmlspecialchars_decode(sel))), '_blank', 'height=390, width=600, menubar=no, toolbar=no, scrollbars=no, status=no, dialog=1' ); } else { alert('Please configure your share link first'); } } else { loadDivItem(itemHash); alert('Sorry ! This item is not loaded, try again !'); } } function shaarliCurrentItem() { shaarliItem(currentItemHash); } function shaarliClickItem(event) { event = event || window.event; stopBubbling(event); shaarliItem(getItemHash(this)); return false; } /** * Folder functions */ function getFolder(element) { var folder = null; while (folder === null && element !== null) { if (element.tagName === 'LI' && element.id.indexOf('folder-') === 0) { folder = element; } element = element.parentNode; } return folder; } function getLiParentByClassName(element, classname) { var li = null; while (li === null && element !== null) { if (element.tagName === 'LI' && hasClass(element, classname)) { li = element; } element = element.parentNode; } if (classname === 'folder' && li.id === 'all-subscriptions') { li = null; } return li; } function getFolderHash(element) { var folder = getFolder(element); if (folder !== null) { return folder.id.replace('folder-',''); } return null; } function toggleFolder(folderHash) { var i, listElements, url, client; listElements = document.getElementById('folder-' + folderHash); listElements = listElements.getElementsByTagName('h5'); if (listElements.length > 0) { listElements = listElements[0].getElementsByTagName('span'); for (i = 0; i < listElements.length; i += 1) { if (hasClass(listElements[i], 'folder-toggle-open')) { removeClass(listElements[i], 'folder-toggle-open'); addClass(listElements[i], 'folder-toggle-close'); } else if (hasClass(listElements[i], 'folder-toggle-close')) { removeClass(listElements[i], 'folder-toggle-close'); addClass(listElements[i], 'folder-toggle-open'); } } } url = '?toggleFolder=' + folderHash + '&ajax'; client = new HTTPClient(); client.init(url); try { client.asyncGET(ajaxHandler); } catch (e) { alert(e); } } function toggleClickFolder(event) { event = event || window.event; stopBubbling(event); toggleFolder(getFolderHash(this)); return false; } function initLinkFolders(listFolders) { var i = 0; for (i = 0; i < listFolders.length; i += 1) { if (listFolders[i].hasAttribute('data-toggle') && listFolders[i].hasAttribute('data-target')) { listFolders[i].onclick = toggleClickFolder; } } } function getListLinkFolders() { var i = 0, listFolders = [], listElements = document.getElementById('list-feeds'); if (listElements) { listElements = listElements.getElementsByTagName('a'); for (i = 0; i < listElements.length; i += 1) { listFolders.push(listElements[i]); } } return listFolders; } /** * MarkAs functions */ function toggleMarkAsLinkItem(itemHash) { var i, item = getItem(itemHash), listLinks; if (item !== null) { listLinks = item.getElementsByTagName('a'); for (i = 0; i < listLinks.length; i += 1) { if (hasClass(listLinks[i], 'item-mark-as')) { if (listLinks[i].href.indexOf('unread=') > -1) { listLinks[i].href = listLinks[i].href.replace('unread=','read='); listLinks[i].firstChild.innerHTML = intlRead; } else { listLinks[i].href = listLinks[i].href.replace('read=','unread='); listLinks[i].firstChild.innerHTML = intlUnread; } } } } } function getUnreadLabelItems(itemHash) { var i, listLinks, regex = new RegExp('read=' + itemHash.substr(0,6)), items = []; listLinks = getListLinkFolders(); for (i = 0; i < listLinks.length; i += 1) { if (regex.test(listLinks[i].href)) { items.push(listLinks[i].children[0]); } } return items; } function addToUnreadLabel(unreadLabelItem, value) { var unreadLabel = -1; if (unreadLabelItem !== null) { unreadLabel = parseInt(unreadLabelItem.innerHTML, 10) + value; unreadLabelItem.innerHTML = unreadLabel; } return unreadLabel; } function getUnreadLabel(folder) { var element = null; if (folder !== null) { element = folder.getElementsByClassName('label')[0]; } return element; } function markAsItem(itemHash) { var item, url, client, indexItem, i, unreadLabelItems, nb, feed, folder, addRead = 1; item = getItem(itemHash); if (item !== null) { unreadLabelItems = getUnreadLabelItems(itemHash); if (!hasClass(item, 'read')) { addRead = -1; } for (i = 0; i < unreadLabelItems.length; i += 1) { nb = addToUnreadLabel(unreadLabelItems[i], addRead); if (nb === 0) { feed = getLiParentByClassName(unreadLabelItems[i], 'feed'); removeClass(feed, 'has-unread'); if (autohide) { addClass(feed, 'autohide-feed'); } } folder = getLiParentByClassName(unreadLabelItems[i], 'folder'); nb = addToUnreadLabel(getUnreadLabel(folder), addRead); if (nb === 0 && autohide) { addClass(folder, 'autohide-folder'); } } addToUnreadLabel(getUnreadLabel(document.getElementById('all-subscriptions')), addRead); if (hasClass(item, 'read')) { url = '?unread=' + itemHash; removeClass(item, 'read'); toggleMarkAsLinkItem(itemHash); } else { url = '?read=' + itemHash; addClass(item, 'read'); toggleMarkAsLinkItem(itemHash); if (filter === 'unread') { url += '¤tHash=' + currentHash + '&page=' + currentPage + '&last=' + listItemsHash[listItemsHash.length - 1]; removeElement(item); indexItem = listItemsHash.indexOf(itemHash); listItemsHash.splice(listItemsHash.indexOf(itemHash), 1); if (listItemsHash.length <= byPage) { appendItem(listItemsHash[listItemsHash.length - 1]); } setCurrentItem(listItemsHash[indexItem]); } } } else { url = '?currentHash=' + currentHash + '&page=' + currentPage; } client = new HTTPClient(); client.init(url + '&ajax'); try { client.asyncGET(ajaxHandler); } catch (e) { alert(e); } } function markAsCurrentItem() { markAsItem(currentItemHash); } function markAsClickItem(event) { event = event || window.event; stopBubbling(event); markAsItem(getItemHash(this)); return false; } function toggleMarkAsStarredLinkItem(itemHash) { var i, item = getItem(itemHash), listLinks, url = ''; if (item !== null) { listLinks = item.getElementsByTagName('a'); for (i = 0; i < listLinks.length; i += 1) { if (hasClass(listLinks[i], 'item-starred')) { url = listLinks[i].href; if (listLinks[i].href.indexOf('unstar=') > -1) { listLinks[i].href = listLinks[i].href.replace('unstar=','star='); listLinks[i].firstChild.innerHTML = intlStar; } else { listLinks[i].href = listLinks[i].href.replace('star=','unstar='); listLinks[i].firstChild.innerHTML = intlUnstar; } } } } return url; } function markAsStarredCurrentItem() { markAsStarredItem(currentItemHash); } function markAsStarredItem(itemHash) { var url, client, indexItem; url = toggleMarkAsStarredLinkItem(itemHash); if (url.indexOf('unstar=') > -1 && stars) { removeElement(getItem(itemHash)); indexItem = listItemsHash.indexOf(itemHash); listItemsHash.splice(listItemsHash.indexOf(itemHash), 1); if (listItemsHash.length <= byPage) { appendItem(listItemsHash[listItemsHash.length - 1]); } setCurrentItem(listItemsHash[indexItem]); url += '&page=' + currentPage; } if (url !== '') { client = new HTTPClient(); client.init(url + '&ajax'); try { client.asyncGET(ajaxHandler); } catch (e) { alert(e); } } } function markAsStarredClickItem(event) { event = event || window.event; stopBubbling(event); markAsStarredItem(getItemHash(this)); return false; } function markAsRead(itemHash) { setNbUnread(currentUnread - 1); } function markAsUnread(itemHash) { setNbUnread(currentUnread + 1); } /** * Div item functions */ function loadDivItem(itemHash, noFocus) { var element, url, client, cacheItem; element = document.getElementById('item-div-'+itemHash); if (element.childNodes.length <= 1) { cacheItem = getCacheItem(itemHash); if (cacheItem !== null) { setDivItem(element, cacheItem); if(!noFocus) { setItemFocus(element); } removeCacheItem(itemHash); } else { url = '?'+(stars?'stars&':'')+'currentHash=' + currentHash + '¤t=' + itemHash + '&ajax'; client = new HTTPClient(); client.init(url, noFocus); try { client.asyncGET(ajaxHandler); } catch (e) { alert(e); } } } } function toggleItem(itemHash) { var i, listElements, element, targetElement; if (view === 'expanded') { return; } if (currentItemHash != itemHash) { closeCurrentItem(); } // looking for ico + or - listElements = document.getElementById('item-toggle-' + itemHash); listElements = listElements.getElementsByTagName('span'); for (i = 0; i < listElements.length; i += 1) { if (hasClass(listElements[i], 'item-toggle-open')) { removeClass(listElements[i], 'item-toggle-open'); addClass(listElements[i], 'item-toggle-close'); } else if (hasClass(listElements[i], 'item-toggle-close')) { removeClass(listElements[i], 'item-toggle-close'); addClass(listElements[i], 'item-toggle-open'); } } element = document.getElementById('item-toggle-'+itemHash); targetElement = document.getElementById( element.getAttribute('data-target').substring(1) ); if (element.href.indexOf('&open') > -1) { element.href = element.href.replace('&open',''); addClass(targetElement, 'well'); setCurrentItem(itemHash); loadDivItem(itemHash); } else { element.href = element.href + '&open'; removeClass(targetElement, 'well'); } } function toggleCurrentItem() { toggleItem(currentItemHash); collapseElement(document.getElementById('item-toggle-' + currentItemHash)); } function toggleClickItem(event) { event = event || window.event; stopBubbling(event); toggleItem(getItemHash(this)); return false; } /** * Item management functions */ function getItem(itemHash) { return document.getElementById('item-' + itemHash); } function getTitleItem(itemHash) { var i = 0, element = document.getElementById('item-div-'+itemHash), listElements = element.getElementsByTagName('a'), title = ''; for (i = 0; title === '' && i < listElements.length; i += 1) { if (hasClass(listElements[i], 'item-link')) { title = listElements[i].innerHTML; } } return title; } function getUrlItem(itemHash) { var i = 0, element = document.getElementById('item-'+itemHash), listElements = element.getElementsByTagName('a'), url = ''; for (i = 0; url === '' && i < listElements.length; i += 1) { if (hasClass(listElements[i], 'item-link')) { url = listElements[i].href; } } return url; } function getViaItem(itemHash) { var i = 0, element = document.getElementById('item-div-'+itemHash), listElements = element.getElementsByTagName('a'), via = ''; for (i = 0; via === '' && i < listElements.length; i += 1) { if (hasClass(listElements[i], 'item-via')) { via = listElements[i].href; } } return via; } function getLiItem(element) { var item = null; while (item === null && element !== null) { if (element.tagName === 'LI' && element.id.indexOf('item-') === 0) { item = element; } element = element.parentNode; } return item; } function getItemHash(element) { var item = getLiItem(element); if (item !== null) { return item.id.replace('item-',''); } return null; } function getCacheItem(itemHash) { if (typeof cache['item-' + itemHash] !== 'undefined') { return cache['item-' + itemHash]; } return null; } function removeCacheItem(itemHash) { if (typeof cache['item-' + itemHash] !== 'undefined') { delete cache['item-' + itemHash]; } } function isCurrentUnread() { var item = getItem(currentItemHash); if (hasClass(item, 'read')) { return false; } return true; } function setDivItem(div, item) { var markAs = intlRead, starred = intlStar, target = ' target="_blank"', linkMarkAs = 'read', linkStarred = 'star'; if (item['read'] == 1) { markAs = intlUnread; linkMarkAs = 'unread'; } if (item['starred'] == 1) { starred = intlUnstar; linkStarred = 'unstar'; } if (!blank) { target = ''; } div.innerHTML = '