1/** 2 * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this 3 * source code is governed by a BSD-style license that can be found in the 4 * LICENSE file. 5 */ 6 7/** 8 * @fileoverview This file retrieves news feed and shows news in pop-up 9 * page according to country, topics and no. of stories selected in the 10 * option page. 11 */ 12 13// Store value retrieved from locale. 14var moreStoriesLocale = chrome.i18n.getMessage('more_stories') + ' \u00BB '; 15var directionLocale = chrome.i18n.getMessage('direction'); 16 17// Feed URL. 18var feedUrl = DEFAULT_NEWS_URL; 19 20//The XMLHttpRequest object that tries to load and parse the feed. 21var req; 22 23/** 24 * Sends request to Google News server 25 */ 26function main() { 27 req = new XMLHttpRequest(); 28 req.onload = handleResponse; 29 req.onerror = handleError; 30 req.open('GET', feedUrl, true); 31 req.send(null); 32} 33 34/** 35 * Handles feed parsing errors. 36 * @param {String} error The localized error message. 37 */ 38function handleFeedParsingFailed(error) { 39 var feed = $('feed'); 40 $('noStories').style.display = 'none'; 41 feed.className = 'error'; 42 feed.innerText = error; 43} 44 45/** 46 * Handles errors during the XMLHttpRequest. 47 */ 48function handleError() { 49 handleFeedParsingFailed(chrome.i18n.getMessage('fetchError')); 50 $('topics').style.display = 'none'; 51} 52 53/** 54 * Parses the feed response. 55 */ 56function handleResponse() { 57 var doc = req.responseXML; 58 if (!doc) { 59 handleFeedParsingFailed(chrome.i18n.getMessage('wrongTopic')); 60 var img = $('title'); 61 if(!img.src) { 62 img.src = "/images/news.gif"; 63 } 64 65 document.querySelector('body').style.minHeight = 0; 66 return; 67 } 68 buildPreview(doc); 69} 70 71// Stores no. of stories selected in options page. 72var maxFeedItems = (window.localStorage.getItem('count')) ? 73 window.localStorage.getItem('count') : 5; 74 75// Where the more stories link should navigate to. 76var moreStoriesUrl; 77 78/** 79 * Generates news iframe in pop-up page by parsing retrieved feed. 80 * @param {HTMLDocument} doc HTML Document received in feed. 81 */ 82function buildPreview(doc) { 83 // Get the link to the feed source. 84 var link = doc.querySelector('link'); 85 var parentTag = link.parentNode.tagName; 86 if (parentTag != 'item' && parentTag != 'entry') { 87 moreStoriesUrl = link.textContent; 88 } 89 90 // Setup the title image. 91 var image = doc.querySelector('image'); 92 var titleImg; 93 94 // Stores whether language script is Right to Left or not for setting style 95 // of share buttons(Facebook, Twitter and Google Buzz) in iframe. 96 var isRtl = 'lTR'; 97 98 if (image) { 99 var url = image.querySelector('url'); 100 if (url) { 101 titleImg = url.textContent; 102 103 // Stores URL of title image to be shown on pop-up page. 104 var titleImgUrl = titleImg; 105 var pattern = /ar_/gi; 106 var result = titleImgUrl.match(pattern); 107 if (result != null || titleImgUrl == ISRAEL_IMAGE_URL) { 108 isRtl = 'rTL'; 109 } 110 } 111 } 112 113 var img = $('title'); 114 if (titleImg) { 115 img.src = titleImg; 116 if (moreStoriesUrl) { 117 $('title_a').addEventListener('click', moreStories); 118 } 119 } else { 120 img.style.display = 'none'; 121 } 122 123 // Construct the iframe's HTML. 124 var iframe_src = '<!doctype html><html><head><script>' + 125 $('iframe_script').textContent + '<' + 126 '/script><style> ' + 127 '.rTL {margin-right: 102px; text-align: right;} ' + 128 '.lTR {margin-left: 102px; text-align: left;} ' + 129 '</style></head><body onload="frameLoaded();" ' + 130 'style="padding:0px;margin:0px;">'; 131 132 var feed = $('feed'); 133 feed.className = ''; 134 var entries = doc.getElementsByTagName('entry'); 135 if (entries.length == 0) { 136 entries = doc.getElementsByTagName('item'); 137 } 138 var count = Math.min(entries.length, maxFeedItems); 139 140 // Stores required height by pop-up page. 141 var minHeight = 19; 142 minHeight = (minHeight * (count - 1)) + 100; 143 document.querySelector('body').style.minHeight = minHeight + 'px'; 144 $('feed').innerHTML = ''; 145 146 for (var i = 0; i < count; i++) { 147 item = entries.item(i); 148 149 // Grab the title for the feed item. 150 var itemTitle = item.querySelector('title'); 151 if (itemTitle) { 152 itemTitle = itemTitle.textContent; 153 } else { 154 itemTitle = 'Unknown title'; 155 } 156 157 // Grab the description. 158 var itemDesc = item.querySelector('description'); 159 if (!itemDesc) { 160 itemDesc = item.querySelector('summary'); 161 if (!itemDesc) { 162 itemDesc = item.querySelector('content'); 163 } 164 } 165 if (itemDesc) { 166 itemDesc = itemDesc.childNodes[0].nodeValue; 167 168 } else { 169 itemDesc = ''; 170 } 171 var itemLink = item.querySelector('link'); 172 if (itemLink) { 173 itemLink = itemLink.textContent; 174 } else { 175 itemLink = 'Unknown itemLink'; 176 } 177 var item = document.createElement('div'); 178 item.className = 'item'; 179 var box = document.createElement('div'); 180 box.className = 'open_box'; 181 box.addEventListener('click', showDesc); 182 item.appendChild(box); 183 184 var title = document.createElement('a'); 185 title.className = 'item_title'; 186 title.innerText = itemTitle; 187 title.addEventListener('click', showDesc); 188 item.appendChild(title); 189 190 var desc = document.createElement('iframe'); 191 desc.scrolling = 'no'; 192 desc.className = 'item_desc'; 193 item.appendChild(desc); 194 feed.appendChild(item); 195 196 // Adds share buttons images(Facebook, Twitter and Google Buzz). 197 itemDesc += "<div class = '" + isRtl + "'>"; 198 itemDesc += "<a style='cursor: pointer' id='fb' " + 199 "onclick='openNewsShareWindow(this.id,\"" + itemLink + "\")'>" + 200 "<img src='" + chrome.extension.getURL('/images/fb.png') + "'/></a>"; 201 itemDesc += " <a style='cursor: pointer' id='twitter' " + 202 "onclick='openNewsShareWindow(this.id,\"" + itemLink + "\")'>" + 203 "<img src='" + chrome.extension.getURL('/images/twitter.png') + "'/></a>"; 204 itemDesc += " <a style='cursor: pointer' id='buzz' " + 205 "onclick='openNewsShareWindow(this.id,\"" + itemLink + "\")'>" + 206 "<img src='" + chrome.extension.getURL('/images/buzz.png') + "'/></a>"; 207 itemDesc += '</div>'; 208 209 // The story body is created as an iframe with a data: URL in order to 210 // isolate it from this page and protect against XSS. As a data URL, it 211 // has limited privileges and must communicate back using postMessage(). 212 desc.src = 'data:text/html;charset=utf-8,' + iframe_src + itemDesc + 213 '</body></html>'; 214 } 215 if (moreStoriesUrl && entries.length != 0) { 216 var more = document.createElement('a'); 217 more.className = 'more'; 218 more.innerText = moreStoriesLocale; 219 more.addEventListener('click', moreStories); 220 feed.appendChild(more); 221 } 222 setStyleByLang(titleImgUrl); 223 224 // Checks whether feed retrieved has news story or not. If not, then shows 225 // error message accordingly. 226 if (entries.length == 0) { 227 $('noStories').innerText = chrome.i18n.getMessage('noStory'); 228 $('noStories').style.display = 'block'; 229 } else { 230 $('noStories').style.display = 'none'; 231 } 232} 233 234/** 235 * Show |url| in a new tab. 236 * @param {String} url The news URL. 237 */ 238function showUrl(url) { 239 // Only allow http and https URLs. 240 if (url.indexOf('http:') != 0 && url.indexOf('https:') != 0) { 241 return; 242 } 243 chrome.tabs.create({url: url}); 244} 245 246/** 247 * Redirects to Google news site for more stories. 248 * @param {Object} event Onclick event. 249 */ 250function moreStories(event) { 251 showUrl(moreStoriesUrl); 252} 253 254/** 255 * Shows description of the news when users clicks on news title. 256 * @param {Object} event Onclick event. 257 */ 258function showDesc(event) { 259 var item_ = event.currentTarget.parentNode; 260 var items = document.getElementsByClassName('item'); 261 for (var i = 0, item; item = items[i]; i++) { 262 var iframe = item.querySelector('.item_desc'); 263 if (item == item_ && item.className == 'item') { 264 item.className = 'item opened'; 265 iframe.contentWindow.postMessage('reportHeight', '*'); 266 } else { 267 item.className = 'item'; 268 iframe.style.height = '0px'; 269 } 270 } 271} 272 273/** 274 * Handles messages between different iframes and sets the display of iframe. 275 * @param {Object} e Onmessage event. 276 */ 277function iframeMessageHandler(e) { 278 var iframes = document.getElementsByTagName('IFRAME'); 279 for (var i = 0, iframe; iframe = iframes[i]; i++) { 280 if (iframe.contentWindow == e.source) { 281 var msg = JSON.parse(e.data); 282 if (msg) { 283 if (msg.type == 'size') { 284 iframe.style.height = msg.size + 'px'; 285 } else if (msg.type == 'show') { 286 var url = msg.url; 287 if (url.indexOf('http://news.google.com') == 0) { 288 // If the URL is a redirect URL, strip of the destination and go to 289 // that directly. This is necessary because the Google news 290 // redirector blocks use of the redirects in this case. 291 var index = url.indexOf('&url='); 292 if (index >= 0) { 293 url = url.substring(index + 5); 294 index = url.indexOf('&'); 295 if (index >= 0) 296 url = url.substring(0, index); 297 } 298 } 299 showUrl(url); 300 } 301 } 302 return; 303 } 304 } 305} 306 307/** 308 * Saves last viewed topic by user in local storage on unload of pop-up page. 309 */ 310function saveLastTopic() { 311 var topicVal = $('topics').value; 312 window.localStorage.setItem('lastTopic', topicVal); 313} 314 315/** 316 * Sets the URL according to selected topic(or default topic), then retrieves 317 * feed and sets pop-up page. 318 */ 319function getNewsByTitle() { 320 var country = window.localStorage.getItem('country'); 321 country = (country == 'noCountry' || !country) ? '' : country; 322 323 // Sets direction of topics showed under dropdown in pop-up page according 324 // to set language in browser. 325 $('topics').className = (directionLocale == 'rtl') ? 'topicsRTL' : 326 'topicsLTR'; 327 328 var topicVal = $('topics').value; 329 330 // Sets Feed URL in case of custom topic selected. 331 var keywords = JSON.parse(window.localStorage.getItem('keywords')); 332 var isFound = false; 333 if (keywords) { 334 for (i = 0; i < keywords.length; i++) { 335 if (topicVal == keywords[i]) { 336 isFound = true; 337 feedUrl = DEFAULT_NEWS_URL + '&cf=all&ned=' + country + '&q=' + topicVal + 338 '&hl=' + country; 339 break; 340 } 341 } 342 } 343 if (!isFound) { 344 feedUrl = DEFAULT_NEWS_URL + '&cf=all&ned=' + country + 345 '&topic=' + topicVal; 346 } 347 main(); 348} 349 350/** 351 * Shows topic list retrieved from local storage(if any),else shows 352 * default topics list. 353 */ 354function getTopics() { 355 var topics = JSON.parse(window.localStorage.getItem('topics')); 356 var keywords = JSON.parse(window.localStorage.getItem('keywords')); 357 var element = $('topics'); 358 359 // Sets all topics as default list if no list is found from local storage. 360 if (!topics && !keywords) { 361 topics = [' ','n','w','b','t','e','s','m','po']; 362 } 363 364 if (topics) { 365 for (var i = 0; i < (topics.length); i++) { 366 var val = (topics[i] == ' ') ? '1' : topics[i]; 367 element.options[element.options.length] = new Option( 368 chrome.i18n.getMessage(val), topics[i]); 369 } 370 } 371 372 // Shows custom topics in list(if any). 373 if (keywords) { 374 for (i = 0; i < (keywords.length); i++) { 375 element.options[element.options.length] = new Option(keywords[i], 376 keywords[i]); 377 } 378 } 379 380 $('option_link').innerText = chrome.i18n.getMessage('options'); 381 382 var topicVal = window.localStorage.getItem('lastTopic'); 383 if (topicVal) { 384 $('topics').value = topicVal; 385 } 386} 387 388window.addEventListener('message', iframeMessageHandler); 389