make google news samples functional (#855)

* update old google news samples

Co-authored-by: Joe Medley <jmedley@google.com>
This commit is contained in:
patrick kettner 2023-03-29 12:59:36 -04:00 коммит произвёл GitHub
Родитель 4baa57bd47
Коммит 7b5d882159
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 349 добавлений и 331 удалений

Просмотреть файл

@ -0,0 +1,7 @@
# News
This sample demonstrates a popup panel that access a remote URL. In this case, we are the Google News RSS feed, and building a UI to display that information.
## Implementation Notes
Note that this is a _very_ old example, and is kept here purely for archival purposes. We do not recommend using this as a base of a new extension.

Просмотреть файл

@ -209,8 +209,7 @@ function buildPreview(doc) {
// The story body is created as an iframe with a data: URL in order to
// isolate it from this page and protect against XSS. As a data URL, it
// has limited privileges and must communicate back using postMessage().
desc.src = 'data:text/html;charset=utf-8,' + iframe_src + itemDesc +
'</body></html>';
desc.src = 'data:text/html;charset=utf-8,' + btoa(unescape(encodeURIComponent(iframe_src + itemDesc + '</body></html>')));
}
if (moreStoriesUrl && entries.length != 0) {
var more = document.createElement('a');

Просмотреть файл

@ -0,0 +1,7 @@
# News (accessibility)
This sample demonstrates a popup panel that access a remote URL. In this case, we are the Google News RSS feed, and building a UI to display that information. The UI here was built in a way to be particularly mindful of accessibility, and is fully navigable by a keyboard.
## Implementation Notes
Note that this is a _very_ old example, and is kept here purely for archival purposes. We do not recommend using this as a base of a new extension.

Просмотреть файл

@ -3,7 +3,7 @@
// found in the LICENSE file.
// Feed
var feedUrl = 'http://news.google.com/?output=rss';
var feedUrl = 'https://news.google.com/?output=rss';
// The XMLHttpRequest object that tries to load and parse the feed.
var req;
@ -79,11 +79,11 @@ function buildPreview(doc) {
}
// Construct the iframe's HTML.
var iframe_src = '<!doctype html><html><head><script ' +
'src="chrome-extension://ldglnfnokeifbcaeppacaejckagballg/' +
'feed_iframe.js"><' + '/script><link href="chrome-extension://ldglnf' +
'nokeifbcaeppacaejckagballg/feed_iframe.css" rel="stylesheet" ' +
'type="text/css"></head><body>';
var iframe_src = `<!doctype html><html><head><title>f</title>
<script src="${chrome.runtime.getURL('/feed_iframe.js')}"></script>
<link href="${chrome.runtime.getURL('/feed_iframe.css')} rel="stylesheet" type="text/css">
</head><body>`;
var feed = document.getElementById('feed');
// Set ARIA role indicating the feed element has a tree structure
@ -159,11 +159,10 @@ function buildPreview(doc) {
// Disable keyboard focus on elements in iFrames that have not been
// displayed yet
desc.tabIndex = -1;
// The story body is created as an iframe with a data: URL in order to
// isolate it from this page and protect against XSS. As a data URL, it
// has limited privileges and must communicate back using postMessage().
desc.src='data:text/html,' + iframe_src + itemDesc + '</body></html>';
desc.src=`data:text/html,${iframe_src}${itemDesc.replace(/#/g,"")}</body></html>`;
item.appendChild(desc);
feed.appendChild(item);

Просмотреть файл

@ -10,8 +10,8 @@
},
"permissions": [
"tabs",
"http://news.google.com/*"
"https://news.google.com/*"
],
"manifest_version": 2,
"content_security_policy": "img-src 'self' http://* https://*; script-src 'self'; connect-src http://news.google.com; frame-src data:"
"content_security_policy": "default-src; img-src 'self' http://* https://*; script-src 'self'; connect-src http://news.google.com; frame-src data:"
}

Просмотреть файл

@ -0,0 +1,7 @@
# News (i18n)
This sample demonstrates a popup panel that access a remote URL. In this case, we are the Google News RSS feed, and building a UI to display that information. Additionally, this sample was built for multiple languages, to demonstrate how to support multiple languages within one extension.
## Implementation Notes
Note that this is a _very_ old example, and is kept here purely for archival purposes. We do not recommend using this as a base of a new extension.

Просмотреть файл

@ -56,7 +56,7 @@
}
},
"newsUrl": {
"message": "http://news.google.com",
"message": "https://news.google.com",
"description": "Url to Google News."
}
}

Просмотреть файл

@ -1,11 +1,11 @@
{
"name": {
"message": "Lector de noticias",
"description": "Nombre de la extensión en el manifiesto."
"description": "Nombre de la extensi\xC3\xB3n en el manifiesto."
},
"description": {
"message": "Muestra los primeros 5 eventos de '$Google$ noticias - destacados' RSS feed en una ventana.",
"description": "Descripción de la extensión en el manifiesto.",
"description": "Descripci\xC3\xB3n de la extensi\xC3\xB3n en el manifiesto.",
"placeholders": {
"google": {
"content": "Google",
@ -15,7 +15,7 @@
},
"default_title": {
"message": "$Google$ noticias",
"description": "Texto de la accion de menú de la extension en el manifiesto.",
"description": "Texto de la accion de men\xC3\xBA de la extension en el manifiesto.",
"placeholders": {
"google": {
"content": "Google",
@ -24,12 +24,12 @@
}
},
"unknown_title": {
"message": "Título desconocido",
"description": "Noticia con título desconocido."
"message": "T\xC3\xADtulo desconocido",
"description": "Noticia con t\xC3\xADtulo desconocido."
},
"error": {
"message": "Error: $error$",
"description": "Plantilla de error genérico. Hace falta pasar un parámetro de error.",
"description": "Plantilla de error gen\xC3\xA9rico. Hace falta pasar un par\xC3\xA1metro de error.",
"placeholders": {
"error": {
"content": "$1",
@ -42,7 +42,7 @@
"description": "Mensaje de error visible para el usuario."
},
"not_a_valid_feed": {
"message": "Feed no válido.",
"message": "Feed no v\xC3\xA1lido.",
"description": "Mensaje de error visible para el usuario."
},
"more_stories": {
@ -56,8 +56,8 @@
}
},
"newsUrl": {
"message": "http://news.google.es",
"description": "Dirección de Google News."
"message": "https://news.google.es",
"description": "Direcci\xC3\xB3n de Google News."
}
}

Просмотреть файл

@ -0,0 +1,69 @@
body {
font-family: helvetica, arial, sans-serif;
font-size: 12px;
overflow: hidden;
}
a {
color:#0000CC;
text-decoration: underline;
cursor: pointer;
}
.open_box {
display: block;
overflow: hidden;
margin-right: 4px;
margin-top: 2px;
height: 12px;
width: 12px;
float: left;
clear: left;
background-image: url(sprite_arrows.gif);
background-position: 0px -24px;
cursor: pointer;
}
.opened .open_box {
background-position:-12px -24px;
}
.item {
padding: 2px 0px;
}
.item_title {
display: block;
min-width: 300px;
padding-left: 15px;
cursor: pointer;
}
.item_desc {
min-width: 500px;
height: 0px;
display: block;
border: none;
padding: 0px;
margin: 0px;
transition: height 0.2s ease-out;
}
#title {
display: block;
margin-left: auto;
}
.error {
white-space: nowrap;
color: red;
}
.more {
display: block;
text-align: right;
padding-top: 20px;
padding-right: 10px;
color: #88C;
}

Просмотреть файл

@ -1,309 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: helvetica, arial, sans-serif;
font-size: 12px;
overflow: hidden;
}
a {
color:#0000CC;
text-decoration: underline;
cursor: pointer;
}
.open_box {
display: block;
overflow: hidden;
margin-right: 4px;
margin-top: 2px;
height: 12px;
width: 12px;
float: left;
clear: left;
background-image: url(sprite_arrows.gif);
background-position: 0px -24px;
cursor: pointer;
}
.opened .open_box {
background-position:-12px -24px;
}
.item {
padding: 2px 0px;
}
.item_title {
display: block;
min-width: 300px;
padding-left: 15px;
cursor: pointer;
}
.item_desc {
min-width: 500px;
height: 0px;
display: block;
border: none;
padding: 0px;
margin: 0px;
transition: height 0.2s ease-out;
}
#title {
display: block;
margin-left: auto;
}
.error {
white-space: nowrap;
color: red;
}
.more {
display: block;
text-align: right;
padding-top: 20px;
padding-right: 10px;
color: #88C;
}
</style>
<script id="iframe_script">
function reportHeight() {
var msg = JSON.stringify({type:"size", size:document.body.offsetHeight});
parent.postMessage(msg, "*");
}
function frameLoaded() {
var links = document.getElementsByTagName("A");
for (i = 0; i < links.length; i++) {
var class = links[i].className;
if (class != "item_title" && class != "open_box") {
links[i].addEventListener("click", showStory);
}
}
window.addEventListener("message", messageHandler);
}
function showStory(event) {
var href = event.currentTarget.href;
parent.postMessage(JSON.stringify({type:"show", url:href}), "*");
event.preventDefault();
}
function messageHandler(event) {
reportHeight();
}
</script>
<script>
// Feed URL.
var feedUrl = chrome.i18n.getMessage('newsUrl') + '/?output=rss';
// The XMLHttpRequest object that tries to load and parse the feed.
var req;
function main() {
req = new XMLHttpRequest();
req.onload = handleResponse;
req.onerror = handleError;
req.open("GET", feedUrl, true);
req.send(null);
}
// Handles feed parsing errors.
function handleFeedParsingFailed(error) {
var feed = document.getElementById("feed");
feed.className = "error";
feed.innerText = chrome.i18n.getMessage("error", error);
}
// Handles errors during the XMLHttpRequest.
function handleError() {
handleFeedParsingFailed(chrome.i18n.getMessage('failed_to_fetch_rss'));
}
// Handles parsing the feed data we got back from XMLHttpRequest.
function handleResponse() {
var doc = req.responseXML;
if (!doc) {
handleFeedParsingFailed(chrome.i18n.getMessage('not_a_valid_feed'));
return;
}
buildPreview(doc);
}
// The maximum number of feed items to show in the preview.
var maxFeedItems = 5;
// Where the more stories link should navigate to.
var moreStoriesUrl;
function buildPreview(doc) {
// Get the link to the feed source.
var link = doc.getElementsByTagName("link");
var parentTag = link[0].parentNode.tagName;
if (parentTag != "item" && parentTag != "entry") {
moreStoriesUrl = link[0].textContent;
}
// Setup the title image.
var images = doc.getElementsByTagName("image");
var titleImg;
if (images.length != 0) {
var urls = images[0].getElementsByTagName("url");
if (urls.length != 0) {
titleImg = urls[0].textContent;
}
}
var img = document.getElementById("title");
if (titleImg) {
img.src = titleImg;
if (moreStoriesUrl) {
document.getElementById("title_a").addEventListener("click", moreStories);
}
} else {
img.style.display = "none";
}
// Construct the iframe's HTML.
var iframe_src = "<!doctype html><html><head><script>" +
document.getElementById("iframe_script").textContent + "<" +
"/script></head><body onload='frameLoaded();' " +
"style='padding:0px;margin:0px;'>";
var feed = document.getElementById("feed");
var entries = doc.getElementsByTagName('entry');
if (entries.length == 0) {
entries = doc.getElementsByTagName('item');
}
var count = Math.min(entries.length, maxFeedItems);
for (var i = 0; i < count; i++) {
item = entries.item(i);
// Grab the title for the feed item.
var itemTitle = item.getElementsByTagName('title')[0];
if (itemTitle) {
itemTitle = itemTitle.textContent;
} else {
itemTitle = chrome.i18n.getMessage("unknown_title");
}
// Grab the description.
var itemDesc = item.getElementsByTagName('description')[0];
if (!itemDesc) {
itemDesc = item.getElementsByTagName('summary')[0];
if (!itemDesc) {
itemDesc = item.getElementsByTagName('content')[0];
}
}
if (itemDesc) {
itemDesc = itemDesc.childNodes[0].nodeValue;
} else {
itemDesc = '';
}
var item = document.createElement("div");
item.className = "item";
var box = document.createElement("div");
box.className = "open_box";
box.addEventListener("click", showDesc);
item.appendChild(box);
var title = document.createElement("a");
title.className = "item_title";
title.innerText = itemTitle;
title.addEventListener("click", showDesc);
item.appendChild(title);
var desc = document.createElement("iframe");
desc.scrolling = "no";
desc.className = "item_desc";
item.appendChild(desc);
feed.appendChild(item);
// The story body is created as an iframe with a data: URL in order to
// isolate it from this page and protect against XSS. As a data URL, it
// has limited privileges and must communicate back using postMessage().
desc.src="data:text/html," + iframe_src + itemDesc + "</body></html>";
}
if (moreStoriesUrl) {
var more = document.createElement("a");
more.className = "more";
more.innerText = chrome.i18n.getMessage("more_stories");
more.addEventListener("click", moreStories);
feed.appendChild(more);
}
}
// Show |url| in a new tab.
function showUrl(url) {
// Only allow http and https URLs.
if (url.indexOf("http:") != 0 && url.indexOf("https:") != 0) {
return;
}
chrome.tabs.create({url: url});
}
function moreStories(event) {
showUrl(moreStoriesUrl);
}
function showDesc(event) {
var item = event.currentTarget.parentNode;
var items = document.getElementsByClassName("item");
for (var i = 0; i < items.length; i++) {
var iframe = items[i].getElementsByClassName("item_desc")[0];
if (items[i] == item && items[i].className == "item") {
items[i].className = "item opened";
iframe.contentWindow.postMessage("reportHeight", "*");
} else {
items[i].className = "item";
iframe.style.height = "0px";
}
}
}
function iframeMessageHandler(e) {
// Only listen to messages from one of our own iframes.
var iframes = document.getElementsByTagName("IFRAME");
for (var i = 0; i < iframes.length; i++) {
if (iframes[i].contentWindow == e.source) {
var msg = JSON.parse(e.data);
if (msg) {
if (msg.type == "size") {
iframes[i].style.height = msg.size + "px";
} else if (msg.type == "show") {
var url = msg.url;
if (url.indexOf(chrome.i18n.getMessage('newsUrl')) == 0) {
// If the URL is a redirect URL, strip of the destination and go to
// that directly. This is necessary because the Google news
// redirector blocks use of the redirects in this case.
var index = url.indexOf("&url=");
if (index >= 0) {
url = url.substring(index + 5);
index = url.indexOf("&");
if (index >= 0)
url = url.substring(0, index);
}
}
showUrl(url);
}
}
return;
}
}
}
window.addEventListener("message", iframeMessageHandler);
</script>
<head>
<link rel="stylesheet" href="./feed.css" />
<script id="iframe_script" src="./feed.js"></script>
</head>
<body onload="main();">
<body>
<a id="title_a"><img id='title'></a>
<div id="feed"></div>
</body>

Просмотреть файл

@ -0,0 +1,227 @@
function reportHeight() {
var msg = JSON.stringify({type:"size", size:document.body.offsetHeight});
parent.postMessage(msg, "*");
}
function frameLoaded() {
var links = document.getElementsByTagName("A");
for (i = 0; i < links.length; i++) {
var klass = links[i].klassName;
if (klass != "item_title" && klass != "open_box") {
links[i].addEventListener("click", showStory);
}
}
window.addEventListener("message", messageHandler);
}
function showStory(event) {
var href = event.currentTarget.href;
parent.postMessage(JSON.stringify({type:"show", url:href}), "*");
event.preventDefault();
}
function messageHandler(event) {
reportHeight();
}
// Feed URL.
var feedUrl = chrome.i18n.getMessage('newsUrl') + '/?output=rss';
// The XMLHttpRequest object that tries to load and parse the feed.
var req;
function main() {
req = new XMLHttpRequest();
req.onload = handleResponse;
req.onerror = handleError;
req.open("GET", feedUrl, true);
req.send(null);
}
// Handles feed parsing errors.
function handleFeedParsingFailed(error) {
var feed = document.getElementById("feed");
feed.klassName = "error";
feed.innerText = chrome.i18n.getMessage("error", error);
}
// Handles errors during the XMLHttpRequest.
function handleError() {
handleFeedParsingFailed(chrome.i18n.getMessage('failed_to_fetch_rss'));
}
// Handles parsing the feed data we got back from XMLHttpRequest.
function handleResponse() {
var doc = req.responseXML;
if (!doc) {
handleFeedParsingFailed(chrome.i18n.getMessage('not_a_valid_feed'));
return;
}
buildPreview(doc);
}
// The maximum number of feed items to show in the preview.
var maxFeedItems = 5;
// Where the more stories link should navigate to.
var moreStoriesUrl;
function buildPreview(doc) {
// Get the link to the feed source.
var link = doc.getElementsByTagName("link");
var parentTag = link[0].parentNode.tagName;
if (parentTag != "item" && parentTag != "entry") {
moreStoriesUrl = link[0].textContent;
}
// Setup the title image.
var images = doc.getElementsByTagName("image");
var titleImg;
if (images.length != 0) {
var urls = images[0].getElementsByTagName("url");
if (urls.length != 0) {
titleImg = urls[0].textContent;
}
}
var img = document.getElementById("title");
if (titleImg) {
img.src = titleImg;
if (moreStoriesUrl) {
document.getElementById("title_a").addEventListener("click", moreStories);
}
} else {
img.style.display = "none";
}
// Construct the iframe's HTML.
var iframe_src = "<!doctype html><html><head><script>" +
document.getElementById("iframe_script").textContent + "<" +
"/script></head><body onload='frameLoaded();' " +
"style='padding:0px;margin:0px;'>";
var feed = document.getElementById("feed");
var entries = doc.getElementsByTagName('entry');
if (entries.length == 0) {
entries = doc.getElementsByTagName('item');
}
var count = Math.min(entries.length, maxFeedItems);
for (var i = 0; i < count; i++) {
item = entries.item(i);
// Grab the title for the feed item.
var itemTitle = item.getElementsByTagName('title')[0];
if (itemTitle) {
itemTitle = itemTitle.textContent;
} else {
itemTitle = chrome.i18n.getMessage("unknown_title");
}
// Grab the description.
var itemDesc = item.getElementsByTagName('description')[0];
if (!itemDesc) {
itemDesc = item.getElementsByTagName('summary')[0];
if (!itemDesc) {
itemDesc = item.getElementsByTagName('content')[0];
}
}
if (itemDesc) {
itemDesc = itemDesc.childNodes[0].nodeValue;
} else {
itemDesc = '';
}
var item = document.createElement("div");
item.klassName = "item";
var box = document.createElement("div");
box.klassName = "open_box";
box.addEventListener("click", showDesc);
item.appendChild(box);
var title = document.createElement("a");
title.klassName = "item_title";
title.innerText = itemTitle;
title.addEventListener("click", showDesc);
item.appendChild(title);
var desc = document.createElement("iframe");
desc.scrolling = "no";
desc.klassName = "item_desc";
item.appendChild(desc);
feed.appendChild(item);
// The story body is created as an iframe with a data: URL in order to
// isolate it from this page and protect against XSS. As a data URL, it
// has limited privileges and must communicate back using postMessage().
desc.src="data:text/html," + iframe_src + itemDesc + "</body></html>";
}
if (moreStoriesUrl) {
var more = document.createElement("a");
more.klassName = "more";
more.innerText = chrome.i18n.getMessage("more_stories");
more.addEventListener("click", moreStories);
feed.appendChild(more);
}
}
// Show |url| in a new tab.
function showUrl(url) {
// Only allow http and https URLs.
if (url.indexOf("http:") != 0 && url.indexOf("https:") != 0) {
return;
}
chrome.tabs.create({url: url});
}
function moreStories(event) {
showUrl(moreStoriesUrl);
}
function showDesc(event) {
var item = event.currentTarget.parentNode;
var items = document.getElementsByClassName("item");
for (var i = 0; i < items.length; i++) {
var iframe = items[i].getElementsByClassName("item_desc")[0];
if (items[i] == item && items[i].klassName == "item") {
items[i].klassName = "item opened";
iframe.contentWindow.postMessage("reportHeight", "*");
} else {
items[i].klassName = "item";
iframe.style.height = "0px";
}
}
}
function iframeMessageHandler(e) {
// Only listen to messages from one of our own iframes.
var iframes = document.getElementsByTagName("IFRAME");
for (var i = 0; i < iframes.length; i++) {
if (iframes[i].contentWindow == e.source) {
var msg = JSON.parse(e.data);
if (msg) {
if (msg.type == "size") {
iframes[i].style.height = msg.size + "px";
} else if (msg.type == "show") {
var url = msg.url;
if (url.indexOf(chrome.i18n.getMessage('newsUrl')) == 0) {
// If the URL is a redirect URL, strip of the destination and go to
// that directly. This is necessary because the Google news
// redirector blocks use of the redirects in this case.
var index = url.indexOf("&url=");
if (index >= 0) {
url = url.substring(index + 5);
index = url.indexOf("&");
if (index >= 0)
url = url.substring(0, index);
}
}
showUrl(url);
}
}
return;
}
}
}
window.addEventListener("message", iframeMessageHandler);
window.addEventListener("DOMContentLoaded", main);

Просмотреть файл

@ -1,17 +1,19 @@
{
"name": "__MSG_name__",
"name": "News Reader",
"version": "1.1",
"description": "__MSG_description__",
"description": "Displays the first 5 items from the 'Google News - top news' RSS feed in a popup.",
"icons": { "128": "news_icon.png" },
"browser_action": {
"default_title": "__MSG_default_title__",
"default_title": "Google News",
"default_icon": "news_action.png",
"default_popup": "feed.html"
},
"default_locale": "en",
"permissions": [
"tabs",
"http://news.google.com/*",
"http://news.google.es/*"
"https://news.google.es/*",
"https://news.google.com/*"
],
"default_locale": "en"
"manifest_version": 2,
"content_security_policy": "default-src; img-src 'self' http://* https://*; script-src 'self'; connect-src https://news.google.com; frame-src data:"
}