summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorkurrik@chromium.org <kurrik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-18 18:24:50 +0000
committerkurrik@chromium.org <kurrik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-18 18:24:50 +0000
commit16f03c44d162be8278afda756ded6bbd0d370a7d (patch)
treed76d2b0cbba5e52fa3e22f07e959da881858d896 /chrome
parentcb84dc2b98f8132e71994f08e13d0e9c30ac1c1e (diff)
downloadchromium_src-16f03c44d162be8278afda756ded6bbd0d370a7d.zip
chromium_src-16f03c44d162be8278afda756ded6bbd0d370a7d.tar.gz
chromium_src-16f03c44d162be8278afda756ded6bbd0d370a7d.tar.bz2
Adding Rachel Shearer's updated feed.html with keyboard nav and ARIA attributes.
BUG=None TEST=None Review URL: http://codereview.chromium.org/2711005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50262 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/common/extensions/docs/examples/extensions/news_a11y/feed.html381
-rw-r--r--chrome/common/extensions/docs/examples/extensions/news_a11y/manifest.json15
-rw-r--r--chrome/common/extensions/docs/examples/extensions/news_a11y/news_action.pngbin0 -> 1109 bytes
-rw-r--r--chrome/common/extensions/docs/examples/extensions/news_a11y/news_icon.pngbin0 -> 10782 bytes
-rw-r--r--chrome/common/extensions/docs/examples/extensions/news_a11y/sprite_arrows.gifbin0 -> 1328 bytes
5 files changed, 396 insertions, 0 deletions
diff --git a/chrome/common/extensions/docs/examples/extensions/news_a11y/feed.html b/chrome/common/extensions/docs/examples/extensions/news_a11y/feed.html
new file mode 100644
index 0000000..1c0121f
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/extensions/news_a11y/feed.html
@@ -0,0 +1,381 @@
+<!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;
+ -webkit-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 = 'http://news.google.com/?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 = "Error: " + error;
+}
+
+// Handles errors during the XMLHttpRequest.
+function handleError() {
+ handleFeedParsingFailed('Failed to fetch RSS feed.');
+}
+
+// Handles parsing the feed data we got back from XMLHttpRequest.
+function handleResponse() {
+ var doc = req.responseXML;
+ if (!doc) {
+ handleFeedParsingFailed("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");
+ // Listen for mouse and key events
+ if (titleImg) {
+ img.src = titleImg;
+ if (moreStoriesUrl) {
+ document.getElementById("title_a").addEventListener("click", moreStories);
+ document.getElementById("title_a").addEventListener("keydown",
+ function(event) {
+ if (event.keyCode == 13) {
+ moreStories(event);
+ }});
+ }
+ } 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");
+ // Set ARIA role indicating the feed element has a tree structure
+ feed.setAttribute("role", "tree");
+
+ 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 = "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);
+ // Disable focusing on box image separately from rest of tree item
+ box.tabIndex = -1;
+ item.appendChild(box);
+
+ var title = document.createElement("a");
+ title.className = "item_title";
+ // Give title an ID for use with ARIA
+ title.id = "item" + i;
+ title.innerText = itemTitle;
+ title.addEventListener("click", showDesc);
+ title.addEventListener("keydown", keyHandlerShowDesc);
+ // Update aria-activedescendant property in response to focus change
+ // within the tree
+ title.addEventListener("focus", function(event) {
+ feed.setAttribute(
+ "aria-activedescendant", this.id);
+ });
+ // Enable keyboard focus on the item title element
+ title.tabIndex = 0;
+ // Set ARIA role role indicating that the title element is a node in the
+ // tree structure
+ title.setAttribute("role", "treeitem");
+ // Set the ARIA state indicating this tree item is currently collapsed.
+ title.setAttribute("aria-expanded", "false");
+ // Set ARIA property indicating that all items are at the same hierarchical
+ // level (no nesting)
+ title.setAttribute("aria-level", "1");
+ item.appendChild(title);
+
+ var desc = document.createElement("iframe");
+ desc.scrolling = "no";
+ desc.className = "item_desc";
+ // Disable keyboard focus on elements in iFrames that have not been
+ // displayed yet
+ desc.tabIndex = -1;
+
+ 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 = "More stories \u00BB";
+ more.tabIndex = 0;
+ more.addEventListener("click", moreStories);
+ more.addEventListener("keydown", function(event) {
+ if (event.keyCode == 13) {
+ moreStories(event);
+ }});
+ 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 keyHandlerShowDesc(event) {
+// Display content under heading when spacebar or right-arrow pressed
+// Hide content when spacebar pressed again or left-arrow pressed
+// Move to next heading when down-arrow pressed
+// Move to previous heading when up-arrow pressed
+ if (event.keyCode == 32) {
+ showDesc(event);
+ } else if ((this.parentNode.className == "item opened") &&
+ (event.keyCode == 37)) {
+ showDesc(event);
+ } else if ((this.parentNode.className == "item") && (event.keyCode == 39)) {
+ showDesc(event);
+ } else if (event.keyCode == 40) {
+ if (this.parentNode.nextSibling) {
+ this.parentNode.nextSibling.children[1].focus();
+ }
+ } else if (event.keyCode == 38) {
+ if (this.parentNode.previousSibling) {
+ this.parentNode.previousSibling.children[1].focus();
+ }
+ }
+}
+
+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", "*");
+ // Set the ARIA state indicating the tree item is currently expanded.
+ items[i].getElementsByClassName("item_title")[0].
+ setAttribute("aria-expanded", "true");
+ iframe.tabIndex = 0;
+ } else {
+ items[i].className = "item";
+ iframe.style.height = "0px";
+ // Set the ARIA state indicating the tree item is currently collapsed.
+ items[i].getElementsByClassName("item_title")[0].
+ setAttribute("aria-expanded", "false");
+ iframe.tabIndex = -1;
+ }
+ }
+}
+
+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("http://news.google.com") == 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>
+<body onload="main();">
+<a id="title_a" tabIndex="0"><img id='title' alt="Google News logo"></a>
+<div id="feed"></div>
+</body>
+</html>
+
diff --git a/chrome/common/extensions/docs/examples/extensions/news_a11y/manifest.json b/chrome/common/extensions/docs/examples/extensions/news_a11y/manifest.json
new file mode 100644
index 0000000..f0c7e0e
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/extensions/news_a11y/manifest.json
@@ -0,0 +1,15 @@
+{
+ "name": "News Reader",
+ "version": "1.1",
+ "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": "Google News",
+ "default_icon": "news_action.png",
+ "popup": "feed.html"
+ },
+ "permissions": [
+ "tabs",
+ "http://news.google.com/*"
+ ]
+}
diff --git a/chrome/common/extensions/docs/examples/extensions/news_a11y/news_action.png b/chrome/common/extensions/docs/examples/extensions/news_a11y/news_action.png
new file mode 100644
index 0000000..24b0ca7
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/extensions/news_a11y/news_action.png
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/extensions/news_a11y/news_icon.png b/chrome/common/extensions/docs/examples/extensions/news_a11y/news_icon.png
new file mode 100644
index 0000000..42da406
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/extensions/news_a11y/news_icon.png
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/extensions/news_a11y/sprite_arrows.gif b/chrome/common/extensions/docs/examples/extensions/news_a11y/sprite_arrows.gif
new file mode 100644
index 0000000..4560faf
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/extensions/news_a11y/sprite_arrows.gif
Binary files differ