SEO Hero extension for Google Chrome
Opera extension
At the moment I'm developing an extension SEO Hero for the Opera browser.
License
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
The application developed for Opera browsers, Chrome is licensed by GNU GENERAL PUBLIC LICENSE (Version 3, 29 June 2007) - https://www.gnu.org/licenses/gpl.html
Privacy policy
Extension transparent in how handle user data (e.g., information provided by a user or collected about a user or a user’s use of the Product or Opera Browser), including by disclosing the collection, use, and sharing of the data. This policy establishes the Opera Web Store’s minimum user data privacy requirements. Product comply with applicable laws.
SEO Hero extension does not handle sensitive or personal data.
Google Chrome extension
Updated: I publish extension. You can install by link https://chrome.google.com/webstore/detail/seo-hero-httpsseoheronews/hcdhoafhkcckclknfekmpbnpgcgfeplj
At the moment I'm developing an extension SEO Hero for the Google Chrome browser. Every day takes a lot of energy, attention and effort.
I spend every day development. Extension of SEO Hero for Google Chrome and Canary browser is created and run in the browser. I pay the fee.
However, to publish the extension has not obtained and I work for publish.
At first I thought the first error:
No manifest found in package. Please make sure to put manifest.json at the root directory of the zip package.
The error has been corrected. Today met a new second error:
An error occurred: Failed to process your item.
the manifest.json: 31: 2: unexpected char.
I re-read documentation for correct the error:
- https://developer.chrome.com/extensions/manifest/icons
- https://developer.chrome.com/apps/about_apps
- https://developer.chrome.com/webstore
The error has been corrected.
Finally, I finished writing a code of extension and published SEO Hero for New York and World on the Google Chrome Webstore site.
Google Chrome SEO Hero extension video
Google Chrome SEO Hero extension screenshots
Code
(function () { $.ajaxSetup({cache: false}); $.ajaxSetup({async: true}); window.SeoHeroNews = { options: { restUrl: "https://seoheronews.com", site: "https://seoheronews.com", newsUrl: "https://seoheronews.com", offset: 0, template: null }, defaultOptions: { 'newsOnPage': 5, 'newsLimit': 15, 'updateInterval': 5, 'lastSyncTime': 0 }, updateList: function () { SeoHeroRepository.getParam("lastSyncTime", 0, function (startFrom) { debug("SeoHeroNews.Synchronizing from date: ", startFrom); $.ajax({ type: "GET", dataType: "json", url: this.options.restUrl.replace("{date}", startFrom), success: function (data, textStatus, jqXHR) { SeoHeroNews.handleNewData(data); }.bind(this), error: function (jqXHR, textStatus, errorThrown) { if (textStatus == "parsererror") { console.error("Error parsing JSON message:\n" + (jqXHR.responseText || jqXHR.responseXML)); } else { console.error("Error: " + textStatus); } }.bind(this) }); }.bind(this)); }, handleNewData: function (list) { debug("SeoHeroNews.NEW DATA", list.length); SeoHeroRepository.getCachedNews(function (cachedNewsList) { SeoHeroRepository.getParam("newsLimit", 10, function (newsLimit) { SeoHeroRepository.getAlreadyReadNews(function (readNews) { var curTime = (new Date()).getTime(), newUrlList; if (!list) { return; } debug("SeoHeroNews.Cached news loaded", cachedNewsList.length); newUrlList = _.pluck(list, 'uri'); _.each(cachedNewsList, function (val, key) { if (_.indexOf(newUrlList, val.uri) !== -1) { delete cachedNewsList[key]; } }); debug("SeoHeroNews.Already cached", (list.length - newUrlList.length)); list = list.concat(_.compact(cachedNewsList)).slice(0, newsLimit); var oldestAcceptableDate = (new Date().getTime()) - 7 * 24 * 60 * 60 * 1000; list = _.filter(list, function (news) { return news.date > oldestAcceptableDate; }); newUrlList = _.pluck(list, 'uri'); debug("SeoHeroNews.Read news loaded: " + readNews.length); _.each(readNews, function (val, url) { if (_.indexOf(newUrlList, url) === -1) { delete readNews[url]; } }); debug("SeoHeroNews.Set lastSyncTime: " + curTime); SeoHeroRepository.setParams({ lastSyncTime: curTime, alreadyReadNews: JSON.stringify(readNews), newsList: JSON.stringify(list) }); SeoHeroRepository.updateUnreadNewsCount(function () { debug("SeoHeroNews.Synchronization done"); }); }); }); }); }, getNewsHtml: function (callback) { SeoHeroRepository.getCachedNews(function (news) { SeoHeroRepository.getAlreadyReadNews(function (readedNewsUrls) { SeoHeroRepository.getPageSize(function (pageSize) { try { var html = "", readedNewsList = [], unreadedNewsList = []; debug("SeoHeroNews.Render news ", news); for (var i = 0; i < news.length; i++) { if (!news[i]) { continue; } if (readedNewsUrls[news[i].uri]) { news[i].readed = 1; readedNewsList.push(news[i]); } else { news[i].readed = 0; unreadedNewsList.push(news[i]); } if (news[i].image) { news[i].image = this.options.site + news[i].image; } news[i].uri = this.options.site + news[i].uri; } news = unreadedNewsList.concat(readedNewsList); for (var i = this.options.offset, count = 0; i < news.length && count < pageSize; i++, count++) { if (!news[i]) { continue; } var data = news[i]; html += this.options.template(data); } debug("SeoHeroNews.Render html ", html); callback(html); } catch (e) { console.log(e) } }.bind(this)); }.bind(this)); }.bind(this)); }, getTimeInterval: function (time) { var interval = parseInt(new Date().getTime() - time) / 1000, // seconds secondsInWeek = 604800, secondsInDay = 86400, secondsInHour = 3600; if (interval > secondsInWeek) { return parseInt(interval / secondsInWeek) + " weeks ago" } else if (interval > secondsInDay) { return parseInt(interval / secondsInDay) + " days ago" } else if (interval > secondsInHour) { return parseInt(interval / secondsInHour) + " hours ago" } else if (interval > 60) { return parseInt(interval / 60) + " minutes ago" } else { return "less than minute ago" } }, startListeningReadingUrls: function () { chrome.tabs.onUpdated.addListener(_.bind(function (tabId, changeInfo, tab) { var tabUrl = tab.url, newsUrl = tabUrl.substring(SeoHeroNews.options.site.length); if (tabUrl.indexOf(this.options.newsUrl) !== -1 && changeInfo.status == "complete") { debug("Url is opened: " + tabUrl, newsUrl); SeoHeroRepository.addAlreadyReadNews(tabUrl.substring(SeoHeroNews.options.site.length)); } }, this)); }, setDefaults: function () { SeoHeroRepository.setParamsIfEmpty(this.defaultOptions); } }; SeoHeroNews.setDefaults();})();