From 7fbf1e87774c203f010a5be5bcb33c345f8f912e Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 13:34:06 +0100 Subject: [PATCH 01/11] Added support for locale --- app/_locales/en/messages.json | 39 +++++++++++++++++++++++- app/scripts/content.js | 56 +++++++++++++++++------------------ 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 9946061..c21c106 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1,10 +1,47 @@ { "appName": { - "message": "fullyfeedly", + "message": "FullyFeedly", "description": "The name of the application" }, "appDescription": { "message": "Display the full content of articles in Feedly", "description": "The description of the application" + }, + "articleNotLoaded": { + "message": "Article Not Loaded", + "description": "NNotification message displayed when the article is not loaded correctly" + }, + "articleNotFound": { + "message": "Article Not Found", + "description": "Notification message displayed when the article is not found" + }, + "loading": { + "message": "Loading", + "description": "Notification message displayed while loading the article" + }, + "done": { + "message": "Done", + "description": "Notification message displayed when the operation is completed with success" + }, + "APIOverQuota": { + "message": "API over quota", + "description": "Notification message displayed when API exceeded free usage quota" + }, + "APIBadRequest": { + "message": "API bad request", + "description": "Notification message displayed when the API request format is not valid" + }, + "APIAuthorizationRequired": { + "message": "API authorization required", + "description": "Notification message displayed when API requires authorization" + }, + "APIUnknownError": { + "message": "API unknown error", + "description": "Notification message displayed when the an unknown error occurred" + }, + "APIMissingKey": { + "message": "API missing key", + "description": "Notification message displayed when the key for API is missing" } + } diff --git a/app/scripts/content.js b/app/scripts/content.js index 4bcced9..910a926 100644 --- a/app/scripts/content.js +++ b/app/scripts/content.js @@ -49,7 +49,7 @@ function loadingOverlay() { document.body.appendChild(target); var spinner = new Spinner(spinOpts).spin(target); var overlay = iosOverlay({ - text: 'Loading', + text: chrome.i18n.getMessage('loading'), spinner: spinner }); @@ -57,18 +57,16 @@ function loadingOverlay() { } function genericOverlay(message, icon, duration, overlay) { - + var settings = { + text: message, + icon: icon + }; + if (overlay === undefined || overlay === null) { - overlay = iosOverlay({ - text: message, - icon: icon - }); + overlay = iosOverlay(settings); } else { - overlay.update({ - text: message, - icon: icon - }); + overlay.update(settings); } window.setTimeout(function() { overlay.hide(); @@ -77,11 +75,15 @@ function genericOverlay(message, icon, duration, overlay) { } function successOverlay(message, overlay) { - genericOverlay(message, chrome.extension.getURL('images/check.png'), 1e3, overlay); + genericOverlay(chrome.i18n.getMessage(message), + chrome.extension.getURL('images/check.png'), + 1e3, overlay); } function failOverlay(message, overlay) { - genericOverlay(message, chrome.extension.getURL('images/cross.png'), 2e3, overlay); + genericOverlay(chrome.i18n.getMessage(message), + chrome.extension.getURL('images/cross.png'), + 2e3, overlay); } @@ -97,7 +99,7 @@ function onBoilerpipeArticleExtracted(data, overlay) { // Check if the API failed to extract the text if (data.status === null || data.status !== 'success') { console.log('[FullyFeedly] API failed to extract the content'); - failOverlay('Article not loaded', overlay); + failOverlay(chrome.i18n.getMessage('articleNotLoaded'), overlay); return; } @@ -108,7 +110,7 @@ function onBoilerpipeArticleExtracted(data, overlay) { var contentElement = document.querySelector('.content'); if (contentElement === null) { console.log('[FullyFeedly] There is something wrong: no content element found'); - failOverlay('No content', overlay); + failOverlay('contentNotFound', overlay); return; } @@ -126,7 +128,7 @@ function onBoilerpipeArticleExtracted(data, overlay) { contentElement.insertBefore(articleImage, contentElement.firstChild); } - successOverlay('Done', overlay); + successOverlay('done', overlay); } function boilerpipeRequest(xhr, overlay) { @@ -138,10 +140,10 @@ function boilerpipeRequest(xhr, overlay) { onBoilerpipeArticleExtracted(data, overlay); } else if (xhr.status === 503) { console.log('[FullyFeedly] Boilerpipe API exceeded quota'); - failOverlay('API over quota', overlay); + failOverlay('APIOverQuota', overlay); } else { console.log('[FullyFeedly] Failed to load the content of the page'); - failOverlay('Article not found', overlay); + failOverlay(chrome.i18n.getMessage('articleNotFound'), overlay); } } }; @@ -156,7 +158,7 @@ function onReadabilityArticleExtracted(data, overlay) { // Check if the API failed to extract the text if (data.content === null) { console.log('[FullyFeedly] API failed to extract the content'); - failOverlay('Article not loaded', overlay); + failOverlay('articleNotFound', overlay); return; } @@ -167,7 +169,7 @@ function onReadabilityArticleExtracted(data, overlay) { var contentElement = document.querySelector('.content'); if (contentElement === null) { console.log('[FullyFeedly] There is something wrong: no content element found'); - failOverlay('No content', overlay); + failOverlay('contentNotFound', overlay); return; } @@ -179,7 +181,7 @@ function onReadabilityArticleExtracted(data, overlay) { // Replace the preview of the article with the full text contentElement.innerHTML = articleContent; - successOverlay('Done', overlay); + successOverlay('done', overlay); // Put the image back at the beginning of the article if (articleImage !== null && contentElement.querySelector('img') === null) { @@ -198,14 +200,14 @@ function readabilityRequest(xhr, overlay) { console.log('[FullyFeedly] Readability API Bad request: ' + 'The server could not understand your request. ' + 'Verify that request parameters (and content, if any) are valid.'); - failOverlay('API bad request', overlay); + failOverlay('APIBadRequest', overlay); } else if (xhr.status === 400) { console.log('[FullyFeedly] Readability API Authorization Required: ' + 'Authentication failed or was not provided.'); - failOverlay('API authorization required', overlay); + failOverlay('APIAuthorizationRequired', overlay); } else { console.log('[FullyFeedly] Readability API Unknown error'); - failOverlay('API unknown error', overlay); + failOverlay('APIUnknownError', overlay); } } }; @@ -214,10 +216,6 @@ function readabilityRequest(xhr, overlay) { /** * Performs an XMLHttpRequest to boilerpipe to get the content of the artile. - * - * @param callback Function If the response from fetching url has a - * HTTP status of 200, this function is called with a JSON decoded - * response. Otherwise, this function is called with null. */ function fetchPageContent() { @@ -225,7 +223,7 @@ function fetchPageContent() { var linkElement = document.querySelector('.websiteCallForAction'); if (linkElement === null) { console.log('[FullyFeedly] Link to article not found'); - failOverlay('Article not found'); + failOverlay('articleNotFound'); return; } @@ -252,7 +250,7 @@ function fetchPageContent() { else if (options.extractionAPI === 'Readability') { // Check if the key is set if (options.readabilityAPIKey === '') { - failOverlay('Missing API Key', overlay); + failOverlay('APIMissingKey', overlay); return; } From 8a656d59bef0b03db49a31a74c1d5dad6ad0b9f0 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 13:55:39 +0100 Subject: [PATCH 02/11] Added Italian translation Fix #1 --- app/_locales/it/messages.json | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 app/_locales/it/messages.json diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json new file mode 100644 index 0000000..ca20ca4 --- /dev/null +++ b/app/_locales/it/messages.json @@ -0,0 +1,46 @@ +{ + "appName": { + "message": "FullyFeedly", + "description": "The name of the application" + }, + "appDescription": { + "message": "Mostra il contenuto completo degli articoli in Feedly", + "description": "The description of the application" + }, + "articleNotLoaded": { + "message": "Articolo Non Caricato", + "description": "NNotification message displayed when the article is not loaded correctly" + }, + "articleNotFound": { + "message": "Articolo Non Trovato", + "description": "Notification message displayed when the article is not found" + }, + "loading": { + "message": "Caricamento", + "description": "Notification message displayed while loading the article" + }, + "done": { + "message": "Fatto", + "description": "Notification message displayed when the operation is completed with success" + }, + "APIOverQuota": { + "message": "API Over Quota", + "description": "Notification message displayed when API exceeded free usage quota" + }, + "APIBadRequest": { + "message": "API Bad Request", + "description": "Notification message displayed when the API request format is not valid" + }, + "APIAuthorizationRequired": { + "message": "API Autorizzazione Richiesta", + "description": "Notification message displayed when API requires authorization" + }, + "APIUnknownError": { + "message": "API Errore Sconosciuto", + "description": "Notification message displayed when the an unknown error occurred" + }, + "APIMissingKey": { + "message": "Chiave API Mancante", + "description": "Notification message displayed when the key for API is missing" + } +} From 00391734e1124b8afeba2a699a1acf1e6f757d15 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 13:56:12 +0100 Subject: [PATCH 03/11] Improved visualization of the notifications --- app/libs/iosOverlay/iosOverlay.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/libs/iosOverlay/iosOverlay.css b/app/libs/iosOverlay/iosOverlay.css index 30c75fe..c8a38b4 100755 --- a/app/libs/iosOverlay/iosOverlay.css +++ b/app/libs/iosOverlay/iosOverlay.css @@ -3,7 +3,7 @@ position: fixed; top: 50%; left: 50%; - width: 200px; + width: 250px; height: 200px; margin-left: -100px; margin-top: -100px; @@ -18,7 +18,7 @@ font-weight: bold; text-align: center; display: block; - font-size: 22px; + font-size: 20px; position: absolute; bottom: 30px; left: 0; From 2dd850619e7ba9fb3a3b17d286b8b804b3906a80 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 13:56:24 +0100 Subject: [PATCH 04/11] Fixed capital letters --- app/_locales/en/messages.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c21c106..0ad62d6 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -4,7 +4,7 @@ "description": "The name of the application" }, "appDescription": { - "message": "Display the full content of articles in Feedly", + "message": "Show the full content of articles in Feedly", "description": "The description of the application" }, "articleNotLoaded": { @@ -24,24 +24,23 @@ "description": "Notification message displayed when the operation is completed with success" }, "APIOverQuota": { - "message": "API over quota", + "message": "API Over Quota", "description": "Notification message displayed when API exceeded free usage quota" }, "APIBadRequest": { - "message": "API bad request", + "message": "API Bad Request", "description": "Notification message displayed when the API request format is not valid" }, "APIAuthorizationRequired": { - "message": "API authorization required", + "message": "API Authorization Required", "description": "Notification message displayed when API requires authorization" }, "APIUnknownError": { - "message": "API unknown error", + "message": "API Unknown Error", "description": "Notification message displayed when the an unknown error occurred" }, "APIMissingKey": { - "message": "API missing key", + "message": "API Missing Key", "description": "Notification message displayed when the key for API is missing" } - } From ed93816fae484a8a5264d8c9ae46d87ff4ed516f Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 14:01:04 +0100 Subject: [PATCH 05/11] Readability now uses https Fix #15 --- app/manifest.json | 2 +- app/scripts/content.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/manifest.json b/app/manifest.json index 226b6f6..91b7159 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -46,7 +46,7 @@ "activeTab", "storage", "http://boilerpipe-web.appspot.com/*", - "http://www.readability.com/api/*" + "https://www.readability.com/api/*" ], "options_page": "options.html" } diff --git a/app/scripts/content.js b/app/scripts/content.js index 910a926..b397115 100644 --- a/app/scripts/content.js +++ b/app/scripts/content.js @@ -255,7 +255,7 @@ function fetchPageContent() { } // Prepare the request to Readability - url = 'http://www.readability.com/api/content/v1/parser?url=' + + url = 'https://www.readability.com/api/content/v1/parser?url=' + encodedPageUrl + '&token=' + options.readabilityAPIKey; xhr.onreadystatechange = readabilityRequest(xhr, overlay); From 392c99ccb87cab1c502827bbc3fe3fb65e4c4055 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 17:32:35 +0100 Subject: [PATCH 06/11] Experiment with DOM Observer --- app/scripts/content.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/scripts/content.js b/app/scripts/content.js index b397115..59e8b2d 100644 --- a/app/scripts/content.js +++ b/app/scripts/content.js @@ -275,4 +275,32 @@ chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { } }); +/* ================ DOM Observer =============== */ +var observeDOM = (function() { + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; + var eventListenerSupported = window.addEventListener; + + return function(obj, callback) { + if (MutationObserver) { + // define a new observer + var obs = new MutationObserver(function(mutations, observer) { + console.log(observer); + if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) { + callback(mutations); + } + }); + // have the observer observe foo for changes in children + obs.observe(obj, { childList:true, subtree:true }); + } + else if(eventListenerSupported) { + obj.addEventListener('DOMNodeInserted', callback, false); + obj.addEventListener('DOMNodeRemoved', callback, false); + } + }; +})(); +// Observe a specific DOM element: +observeDOM(document.getElementById('box'), function(mutations) { + console.log('[FullyFeedly] DOM changed'); + console.log('[FullyFeedly] ', mutations); +}); From 6dd3f2f6ef2128b2aec62406f793549e91dd8a25 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Sun, 7 Sep 2014 19:15:12 +0100 Subject: [PATCH 07/11] Added button to load content in the page. The code is still very confuse and need refactoring --- app/manifest.json | 3 ++- app/scripts/content.js | 44 +++++++++++++++++++++++------------------- app/styles/content.css | 29 ++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 app/styles/content.css diff --git a/app/manifest.json b/app/manifest.json index 91b7159..3b15811 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -33,7 +33,8 @@ "scripts/content.js" ], "css": [ - "libs/iosOverlay/iosOverlay.css" + "libs/iosOverlay/iosOverlay.css", + "styles/content.css" ] } ], diff --git a/app/scripts/content.js b/app/scripts/content.js index 59e8b2d..6687bde 100644 --- a/app/scripts/content.js +++ b/app/scripts/content.js @@ -227,7 +227,6 @@ function fetchPageContent() { return; } - // Get the link and convert it for usage as parameter in the GET request var pageUrl = linkElement.getAttribute('href'); var encodedPageUrl = encodeURIComponent(pageUrl); @@ -278,29 +277,34 @@ chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { /* ================ DOM Observer =============== */ var observeDOM = (function() { var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; - var eventListenerSupported = window.addEventListener; return function(obj, callback) { - if (MutationObserver) { - // define a new observer - var obs = new MutationObserver(function(mutations, observer) { - console.log(observer); - if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) { - callback(mutations); - } - }); - // have the observer observe foo for changes in children - obs.observe(obj, { childList:true, subtree:true }); - } - else if(eventListenerSupported) { - obj.addEventListener('DOMNodeInserted', callback, false); - obj.addEventListener('DOMNodeRemoved', callback, false); - } + // define a new observer + var obs = new MutationObserver(function(mutations) { + if (mutations[0].addedNodes.length) { + callback(mutations); + } + }); + // have the observer observe foo for changes in children + obs.observe(obj, { childList:true, subtree:true }); }; })(); -// Observe a specific DOM element: -observeDOM(document.getElementById('box'), function(mutations) { +observeDOM(document.getElementById('box'), function() { console.log('[FullyFeedly] DOM changed'); - console.log('[FullyFeedly] ', mutations); + if (document.querySelector('.loadContent') !== null) { + return; + } + var goButton = document.querySelector('.websiteCallForAction'); + if (goButton !== null) { + console.log('[FullyFeedly] Button found: ', goButton); + var loadButton = goButton.cloneNode(); + loadButton.className = 'loadContent'; + loadButton.innerText = 'Load Full Article'; + loadButton.removeAttribute('href'); + loadButton.onclick = fetchPageContent; + var entry = document.querySelector('.u100entry'); + entry.insertBefore(loadButton, goButton); + return; + } }); diff --git a/app/styles/content.css b/app/styles/content.css new file mode 100644 index 0000000..8c06583 --- /dev/null +++ b/app/styles/content.css @@ -0,0 +1,29 @@ +.loadContent { + padding: 14px 20px 14px 20px; + font-size: 14px; + line-height: 17px; + font-weight: normal; + text-decoration: none; + border-radius: 3px; + color: #777; + -webkit-appearance: none; + -moz-appearance: none; + -o-appearance: none; + appearance: none; + cursor: pointer; + z-index: 1000; + -webkit-font-smoothing: antialiased; + cursor: pointer; + border: 1px solid #DCDCDC; + background: #FFF; + box-shadow: none; + display: block; + margin-top: 40px; + clear: both; + text-align: center; + transition: background-color 0.1s linear; +} + +.websiteCallForAction { + margin-top: 10px !important; +} \ No newline at end of file From c88641013bb3f9dad1950c691f1b65571634a30f Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Tue, 9 Sep 2014 21:15:08 +0100 Subject: [PATCH 08/11] Improved CSS --- app/styles/content.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/styles/content.css b/app/styles/content.css index 8c06583..f591e43 100644 --- a/app/styles/content.css +++ b/app/styles/content.css @@ -1,4 +1,4 @@ -.loadContent { +.loadArticleBtn { padding: 14px 20px 14px 20px; font-size: 14px; line-height: 17px; @@ -24,6 +24,13 @@ transition: background-color 0.1s linear; } +.loadArticleBtn:hover { + opacity: 1; + background-color: #F0F0F0; + border: 1px solid #D0D0D0; + text-decoration: none; +} + .websiteCallForAction { margin-top: 10px !important; } \ No newline at end of file From 7cc79d879b49ebba01546691e6f017a8b58ae42d Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Tue, 9 Sep 2014 21:15:29 +0100 Subject: [PATCH 09/11] Added message for invalid API --- app/_locales/en/messages.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 0ad62d6..2a92be5 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -42,5 +42,9 @@ "APIMissingKey": { "message": "API Missing Key", "description": "Notification message displayed when the key for API is missing" + }, + "InvalidAPI": { + "message": "Invalid API", + "description": "Notification message displayed when API is not valid" } } From bae6a0d69391f353f903e0dea02b19a143c4c672 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Tue, 9 Sep 2014 21:15:49 +0100 Subject: [PATCH 10/11] Code refactoring --- app/scripts/content.js | 48 ++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/app/scripts/content.js b/app/scripts/content.js index 6687bde..3a52524 100644 --- a/app/scripts/content.js +++ b/app/scripts/content.js @@ -238,6 +238,7 @@ function fetchPageContent() { var xhr = new XMLHttpRequest(); var url = ''; + // Select the API to use to extract the article if (options.extractionAPI === 'Boilerpipe') { // Prepare the request to Boilerpipe url = 'http://boilerpipe-web.appspot.com/extract?url=' + @@ -259,12 +260,16 @@ function fetchPageContent() { xhr.onreadystatechange = readabilityRequest(xhr, overlay); } + else { + failOverlay('InvalidAPI', overlay); + return; + } xhr.open('GET', url, true); xhr.send(); } -/* Listen for requests */ +// Listen for requests chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { // If the received message has the expected format... if (msg.text && (msg.text === 'extract_article')) { @@ -275,36 +280,47 @@ chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { }); /* ================ DOM Observer =============== */ + +// Define a generic DOM Observer var observeDOM = (function() { - var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; return function(obj, callback) { - // define a new observer + // Define a new observer var obs = new MutationObserver(function(mutations) { if (mutations[0].addedNodes.length) { callback(mutations); } }); - // have the observer observe foo for changes in children + // Have the observer observe foo for changes in children obs.observe(obj, { childList:true, subtree:true }); }; })(); +// This is used to add the button to the article when its +// preview is opened in Feedly observeDOM(document.getElementById('box'), function() { - console.log('[FullyFeedly] DOM changed'); - if (document.querySelector('.loadContent') !== null) { + // Check if the button is already there + if (document.querySelector('.loadArticleBtn') !== null) { return; } - var goButton = document.querySelector('.websiteCallForAction'); - if (goButton !== null) { - console.log('[FullyFeedly] Button found: ', goButton); - var loadButton = goButton.cloneNode(); - loadButton.className = 'loadContent'; - loadButton.innerText = 'Load Full Article'; - loadButton.removeAttribute('href'); - loadButton.onclick = fetchPageContent; - var entry = document.querySelector('.u100entry'); - entry.insertBefore(loadButton, goButton); + + // Search the button to open the website and the container element + var openWebsiteBtn = document.querySelector('.websiteCallForAction'); + var entryElement = document.querySelector('.u100entry'); + + if (openWebsiteBtn !== null && entryElement !== null) { + // Create a new button used to load the article + var loadArticleBtn = openWebsiteBtn.cloneNode(); + loadArticleBtn.className = 'loadArticleBtn'; + loadArticleBtn.innerText = 'Load Full Article'; + + // Remove the link and assign a different action + loadArticleBtn.removeAttribute('href'); + loadArticleBtn.onclick = fetchPageContent; + + // Add the new button to the page + entryElement.insertBefore(loadArticleBtn, openWebsiteBtn); return; } }); From 94da2fb27c3a230bc22e8f99d77f4f36ae4e2312 Mon Sep 17 00:00:00 2001 From: Andrea Grandi Date: Tue, 9 Sep 2014 21:20:03 +0100 Subject: [PATCH 11/11] Updated version number --- app/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/manifest.json b/app/manifest.json index 3b15811..df326a7 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,6 +1,6 @@ { "name": "__MSG_appName__", - "version": "0.2.0", + "version": "0.3.0", "manifest_version": 2, "description": "__MSG_appDescription__", "icons": {