Home | History | Annotate | Download | only in javascript
      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.
     14 var moreStoriesLocale = chrome.i18n.getMessage('more_stories') + ' \u00BB ';
     15 var directionLocale = chrome.i18n.getMessage('direction');
     16 
     17 // Feed URL.
     18 var feedUrl = DEFAULT_NEWS_URL;
     19 
     20 //The XMLHttpRequest object that tries to load and parse the feed.
     21 var req;
     22 
     23 /**
     24  * Sends request to Google News server
     25  */
     26 function 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  */
     38 function 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  */
     48 function handleError() {
     49   handleFeedParsingFailed(chrome.i18n.getMessage('fetchError'));
     50   $('topics').style.display = 'none';
     51 }
     52 
     53 /**
     54  * Parses the feed response.
     55  */
     56 function 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.
     72 var maxFeedItems = (window.localStorage.getItem('count')) ?
     73     window.localStorage.getItem('count') : 5;
     74 
     75 // Where the more stories link should navigate to.
     76 var 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  */
     82 function 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  */
    238 function 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  */
    250 function 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  */
    258 function 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  */
    277 function 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  */
    310 function 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  */
    319 function 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  */
    354 function 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 
    388 window.addEventListener('message', iframeMessageHandler);
    389