Everytime the extension refreshes it duplicates the entire feed.json. How do I prevent this and only add new items from feed.json (when that is updated) on top of the old ones? Also how do I only set the badgeText when new items have been added?
Here's what I got so far:
background.html
<html>
<head>
<script type="text/javascript">
var fetchFreq = 30000; // how often we fetch new items (30s)
var req; // request object
var unreadCount = 0; // how many unread items we have
var items; // all currently fetched items
getItems();
setInterval(getItems, fetchFreq);
function getItems() {
req = new XMLHttpRequest();
req.open('GET', 'http://siteurl.com/feed.json');
req.onload = processItems;
req.send();
}
function processItems() {
var res = JSON.parse(req.responseText);
unreadCount += res.length;
if (unreadCount > 0) {
chrome.browserAction.setBadgeBackgroundColor({
color: [255, 0, 0, 255]
});
chrome.browserAction.setBadgeText({text: '' + unreadCount});
}
items = res.concat(items);
}
</script>
</head>
</html>
popup.html
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script src="util.js"></script>
<script>
var bg; // background page
// timeline attributes
var timeline;
var template;
var link;
var image;
var author;
var content;
onload = setTimeout(init, 0); // workaround for http://crbug.com/24467
// initialize timeline template
function init() {
chrome.browserAction.setBadgeText({text: ''});
bg = chrome.extension.getBackgroundPage();
bg.unreadCount = 0;
timeline = document.getElementById('timeline');
template = xpath('//ol[#id="template"]/li', document);
link = xpath('//div[#class="thumbnail"]/a', template);
image = xpath('img', link);
author = xpath('//div[#class="text"]/a', template);
content = xpath('//div[#class="text"]/span', template);
update();
}
// update display
function update() {
var user;
var item;
for (var i in bg.items) {
user = bg.items[i];
// thumbnail
link.title = user.name;
link.href = openInNewTab(profileurl);
image.src = user.thumbnail;
image.alt = user.name;
// text
author.href = openInNewTab(profileurl);
author.innerHTML = user.name;
content.innerHTML = linkify(bg.items[i].profileurl);
// copy node and update
item = template.cloneNode(true);
timeline.appendChild(item);
}
}
</script>
</head>
<body>
<div id="body">
<div id="title">
<h2>Chrome Extension</h2>
</div>
<ol id="timeline" />
</div>
<ol id="template">
<li>
<div class="thumbnail">
<a>
<img />
</a>
</div>
<div class="text">
<a></a>
<span></span>
</div>
<div class="clear"></div>
</li>
</ol>
</body>
</html>
feed.json
{
"name":"John Doe",
"about":"I am me",
"thumbnail":"http://thumbnail.jpg",
"profileurl":"http://siteurl.com/profileurl.php"
}
Thanks in advance. The extension's purpose is to fetch new items from feed.json and show in the popup.html. Think of it like a feed reader for new twitter tweets.
I know it seems fine now, but having a single global req object is probably not as safe as having one for each request.
You don't seem to ever be clearing the items. Your popup and background should communicate. The background should hold the new and old feeds separately and when the popup shows them, should release them.
Consider, for example, holding the old items in an array, and the new ones in a separate array. And then when the popup shows the new feeds to the user it can call bg.clearItems() which will copy the new ones into the array of the old ones, and then on the next request the new items are put into the now-empty new items array.
Related
I am currently attempting to learn Javascript and starting with a very basic incremental game where you click a button and it increases. I implemented a save button that properly saves to local storage, however upon refresh, it gets reset.
This is the local storage after hitting save, however, it gets reset to 0 when the page is refreshed.
I wanted to implement save functionality in the following way (see loadGame() and saveGame()):
// JavaScript Document
var saveState = {
food: 0
};
var GlobalFood = 0;
window.onunload = saveGame();
window.onload = loadGame();
function loadGame(){
var loadSaveStateString = localStorage.getItem('saveState');
saveState = JSON.parse(loadSaveStateString);
}
function saveGame(){
var saveStateString = JSON.stringify(saveState);
localStorage.setItem('saveState', saveStateString);
}
function tutorialFoodBtnFunc() {
var fVal = document.getElementById("GlobalFood");
GlobalFood++;
fVal.innerHTML = GlobalFood;
saveState['food'] = GlobalFood;
}
function tutorialFoodBtnFunc2() {
var fVal = document.getElementById("GlobalFood");
if(GlobalFood < 1)
{
var foodError = document.getElementById("foodErrorText");
foodError.classList.add("fade-in");
setTimeout(function (){
foodError.classList.remove("fade-in");
}, 2000)
}
else
{
GlobalFood--;
fVal.innerHTML = GlobalFood;
saveState.food = GlobalFood;
}
}
Here's my HTML as well if it's relevant:
<html>
<head>
<meta charset="utf-8">
<title>Wilderness</title>
<link rel="stylesheet" href="styling/tutorial.css">
<script src="js/main.js"></script>
</head>
<div id="page-wrapper">
<body>
<div id="nav">
<p class="GlobalVals">Foodstuffs: </p><span class="GlobalVals" id = "GlobalFood">0</span>
</div>
<div id="tutorialBody">
<button id="tutorialFoodBtn" onclick="tutorialFoodBtnFunc()">Gather Berries</button>
<button id="tutorialFoodBtn2" onclick="tutorialFoodBtnFunc2()">Eat Berries</button>
<p id="foodErrorText">You have no berries!!!</p>
<button id="tutorialFoodBtn2" onclick="saveGame()">Save!!</button>
</div>
</body>
</div>
</html>
First of all, onunload and onload should reference a function. (What you did is execute the function)
window.onunload = saveGame;
window.onload = loadGame;
Then, your code is properly loading the saveState, but you're not assigning it to your GlobalFood variable.
function loadGame(){
var loadSaveStateString = localStorage.getItem('saveState');
saveState = JSON.parse(loadSaveStateString);
GlobalFood = saveState.food;
var fVal = document.getElementById("GlobalFood");
fVal.innerHTML = GlobalFood;
}
I am trying to build a simple dashboard where if user access their dashboard it will show data from the google spreadsheet. But There is one problem I don't know why only my first div is getting target via JavaScript not the second one..
any Help and solution would apricated..
Why My Second Line Targeting div id="load1" is not executing ????
Code.gs
`function loadDetails(id){
var ss = SpreadsheetApp.openById("14RM_170AefKbQq82NXAdSOemQLyAGcv_BTwYWd1-qtM")
var ws = ss.getSheetByName("Data")
var data = ws.getDataRange().getValues()
for(var i=0; i<data.length; i++){
var row = data[i]
if(row[0]== id){
password = {
psd: row[1],
id: row[0]
}
}
}
if(row[0]!= id){
password = "not found"
}
return password;
}`
Dashboard.HTML
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1>Hey this is other Employee Dashboard</h1>
<p>Your Employee Id: = </p>
<p>Your Password : = </P>
<div id="load">
<div>
<div id="load1">
<div>
<script>
window.onload = function(){
var id = "MN00018"
google.script.run.withSuccessHandler(function(output){
document.getElementById("load").innerHTML = `passing objects are ${output.psd} & ${output.id}`
document.getElementById("load1").innerHTML = `passing objects are ${output.id}`
}).loadDetails(id)
}
</script>
</body>
</html>
If you are actually testing your showing script, I think that there are 2 modification points.
HTML & Javascript side:
The tag of div is not enclosed. How about modifying as follows?
From:
<div id="load">
<div>
<div id="load1">
<div>
To:
<div id="load">
</div>
<div id="load1">
</div>
I think that this is the reason of your issue of Why My Second Line Targeting div id="load1" is not executing ????.
Google Apps Script side:
At Google Apps Script side, by if(row[0]!= id){password = "not found"}, even when id is found from the column "A", "not found" is returned. How about modifying as follows?
Modified script:
function loadDetails(id) {
var ss = SpreadsheetApp.openById("###");
var ws = ss.getSheetByName("Data")
var data = ws.getDataRange().getValues()
var password = {psd: "", id: ""};
for (var i = 0; i < data.length; i++) {
var row = data[i]
if (row[0] == id) {
password = {
psd: row[1],
id: row[0]
}
break;
}
}
return password;
}
I'm trying to make a todo list and store it in local storage so it gets saved.
I run the get() and list() function on startup to pull it out of localStorage and list it. Problem is that the for loop won't run in the list() function. Once I put in a new item and run the newItem() function it pulls out of localStorage and lists it all fine. Any ideas?
get();
list();
function Todo(name){
this.name = name;
this.completed = false;
}
function newItem(){
var t = new Todo(document.getElementById("newItem").value)
items.push(t)
save();
console.log(items)
}
function save(){
var save = JSON.stringify(items)
localStorage.setItem("localsave", save)
list();
}
function list(name){
var html = "";
console.log(items)
for(var i in items){
var todo = items[i];
var name = todo.name
var completed = todo.completed;
html += "<li>"+name+""+completed+"</li>"
}
$("#ul").html(html);
}
function get(){
var temp = localStorage.getItem("localsave")
items = JSON.parse(temp)
}
HTML document looks like this if anyone is interested in that
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<script
src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script src="todo.js"></script>
<form method="post" action="javascript:newItem()">
<input type="text" id="newItem" name="newItem" placeholder="New item">
</form>
<ul id="ul">
</ul>
your code runs the java script code first then it renders the HTMl elements.
this line had been executed first , before the control with "ul" id was rendered , so it has fetched the data from storage already but can't view them in the not rendered "ui".
$("#ul").html(html);
so your code should call todo.js after rendering the HTML elements like that:
<html>
<body>
<script
src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<form method="post" action="javascript:newItem()">
<input type="text" id="newItem" name="newItem" placeholder="New item">
</form>
<ul id="ul">
</ul>
<script src="todo.js"></script>
</body>
</html>
As #Jonas Wilms mentioned you need to handle the null value from store. Your get function needs to be something like below.
get() {
var temp = localStorage.getItem("localsave")
if (temp) {
items = JSON.parse(temp)
}
else {
items = [];
}
}
I think this is you want.
list();
function Todo(name){
this.name = name;
this.completed = false;
}
function newItem(){
var items = get();
var t = new Todo(document.getElementById("newItem").value)
items.push(t);
save(items);
console.log('saving items', items);
}
function save(items){
var save = JSON.stringify(items)
localStorage.setItem("localsave", save)
list();
}
function list(name){
var html = "";
var items = get();
if(items.length > 0){
for(var i in items){
var todo = items[i];
var name = todo.name;
var completed = todo.completed;
html += "<li>"+name+""+completed+"</li>"
}
$("#ul").html(html);
}
console.log('listing items', items);
}
function get(){
var temp = localStorage.getItem("localsave");
if(temp){
return JSON.parse(temp);
}else{
return [];
}
}
I have a simple widget, and want to load it on different sites. I've read this article and tried to make a solution with AJAX - setting response type to document .
Everything seems ok on first look - slider items loaded, styling is ok. But slider does not start to roll. Seems javascript does not start to run. Pervious/Next buttons also not working.
Below code describes what I've tried so far.
Widget - I used twig for templating, however it is not important. And that produces a carousel/slider like this.
<div class="slider-items-holder">
<div class="control-btns">
<ul><li><strong>Slider</strong></li>
<li></li>
<li></li>
</ul>
</div>
<div class="content-wrapper">
<ul>
{% for job in jobs %}
<li>
<div class="job-spotlight">
<h4>{{ job.title }}<span class="job-type part-time">{{ job.type }}</span></h4>
<span><a target="_blank" href="{{ job.loc_statecode }}">
<i class="marker"></i> {{ job.loc_state }}</a>
</span>
<span><i class="user"></i>{{ job.company }}</span>
<p>{{ job.detail|striptags }} ... </p>
Apply For This Job
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<link rel="stylesheet" type="text/css" href="css/styles.css">
<script src="js/script.js"></script>
JS
function fetch_jobs_widget(selector) {
var url = "http://localhost:9874";
var t = "/static";
var js_template = '<script type="text/javascript" src="'+url+'/js/script.js"></script>';
var css_template = '<link rel="stylesheet" type="text/css" href="'+url+'/css/styles.css">'
var xhr = new XMLHttpRequest();
xhr.open("GET", url+t, true);
xhr.responseType = 'document';
xhr.onload = function(e) {
container(selector).innerHTML = "";
var doc = e.target.response;
var widgetFragment = document.createDocumentFragment();
widgetFragment.appendChild(doc.querySelector(".my-slider"));
container(selector).appendChild(widgetFragment);
container(selector).innerHTML += css_template;
container(selector).innerHTML += js_template;
}
xhr.send();
}
HTML - And I want to use that widget on that page.
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div class="textwidget jobs-plugin-carousel">
</div>
<script type="text/javascript" src="widgets.js"></script>
<script type="text/javascript">fetch_jobs_widget('.textwidget');</script>
</body>
</html>
I have also tried to load HTML/CSS/JS at once but I've came up with the same result.
UPDATE
JS code for slider
//script.js
(function () {
var mySlider, cWrapper, ul, li;
var current = 0;
var timeOut = 3000;
var intervalHandler;
var automaticStart = true;
init();
function init() {
mySlider = document.querySelector(".my-slider");
cWrapper = document.querySelector(".my-slider > .content-wrapper");
ul = document.getElementById("slider-items-holder");
li = ul.querySelectorAll("li");
if (!mySlider || !cWrapper || !ul || !li.length)
return;
ul.style.width = cWrapper.offsetWidth * li.length;
ul.style.height = cWrapper.offsetHeight;
bindEvents();
startSlider();
}
function startSlider() {
if (automaticStart)
intervalHandler = setInterval(next, timeOut);
}
function next() {
if (current + 1 >= li.length) {
current = -1;
}
ul.style.marginLeft = '-' + (li[0].offsetWidth * ++current);
}
function prev() {
if (current - 1 < 0) {
current = li.length;
}
ul.style.marginLeft = '-' + (li[0].offsetWidth * --current);
}
function bindEvents() {
mySlider.addEventListener('mouseover', function () {
clearInterval(intervalHandler);
});
mySlider.addEventListener('mouseout', startSlider);
var prevBtn = document.querySelector(".my-slider > .control-btns a.prev");
var nextBtn = document.querySelector(".my-slider > .control-btns a.next");
if (prevBtn)
prevBtn.addEventListener('click', prev);
if (nextBtn)
nextBtn.addEventListener('click', next);
}
})();
As I see your slider closely I found that you have this line
ul = document.getElementById("slider-items-holder");
which try to find an element with ID "slider-items-holder" but in your slider template you don't have this id for your ul element. So add this id to your second ul element.
Edit
The way you add your JavaScript file dynamically is wrong. You need to add your script to head tag to your html page like this
var myScript= document.createElement('script');
myScript.type = 'text/javascript';
myScript.src = 'http://localhost:9874/js/script.js';
(document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(myScript);
I am using a script to load news from different sources, using Google AJAX feed API. How can I get the description of an entry? Below is an hello world program:
<html>
<head>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("feeds", "1");
function initialize() {
var feed = new google.feeds.Feed("http://news.google.com/?output=rss");
feed.load(function(result) {
if (!result.error) {
var container = document.getElementById("feed");
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
var div = document.createElement("div");
div.appendChild(document.createTextNode(entry.title));
container.appendChild(div);
}
}
});
}
google.setOnLoadCallback(initialize);
</script>
</head>
<body>
<div id="feed"></div>
</body>
</html>
How can I get the description using the entry object??? I am using the google URL - http://news.google.com/?output=rss for RSS feeds in XML format. I want the "Description" part. How can I get that
You can get the description, but you can't use the JSON format and the entry object to do it. If you read the feed parameters at https://developers.google.com/feed/v1/devguide carefully, you'll see that description is not a field it returns at the entry level - just at the feed level.
To do it, you need to request the feed in XML format, and then load the individual nodes, including description. Here's the relevant snippet I've used to do it - change the formatting etc. as you need.
function initialize() {
var feed = new google.feeds.Feed("http://myblog.com/blog/feed/");
feed.setResultFormat(google.feeds.Feed.XML_FORMAT);
feed.load(function(result) {
if (!result.error) {
var items = result.xmlDocument.getElementsByTagName('item');
item = items[0];
//build each element
var title = document.createElement("h4");
title.innerHTML = item.getElementsByTagName('title')[0].firstChild.nodeValue;
var content = document.createElement("p");
content.innerHTML = item.getElementsByTagName('description')[0].firstChild.nodeValue;
href = item.getElementsByTagName('link')[0].firstChild.nodeValue;
}
The HTML description can be retrieved by using the content variable.
Thus you should have:
div.appendChild(document.createTextNode(entry.content));
Be aware that this will retrieve HTML data format.
After much digging, I found that the Google API uses "contentSnippet" instead of description. No XML formatting needed.
function initialize() {
var feed = new google.feeds.Feed("http://myblog.com/blog/feed/");
feed.setNumEntries(10);
feed.load(function(result) {
if (!result.error) {
$(document).ready(function(){
$('#feed-pull').append('<ul></ul>');
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
var desc = entry.contentSnippet;
Change entry.title in:
div.appendChild(document.createTextNode(entry.title));
to entry.description.