Commit 093cfa5a authored by rob's avatar rob Committed by Commit bot

New Getting started example for extensions

Replaces the outdated Kittens tutorial with a new getting started tutorial that uses the Google Image search API.

BUG=383385
NOTRY=true
R=kalman@chromium.org,mkearney@chromium.org

Review URL: https://codereview.chromium.org/732943002

Cr-Commit-Position: refs/heads/master@{#311485}
parent 5d4299f0
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "One-click Kittens", "name": "Getting started example",
"description": "This extension demonstrates a 'browser action' with kittens.", "description": "This extension shows a Google Image search result for the current page",
"version": "1.0", "version": "1.0",
"browser_action": { "browser_action": {
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
"default_popup": "popup.html" "default_popup": "popup.html"
}, },
"permissions": [ "permissions": [
"https://secure.flickr.com/" "activeTab",
"https://ajax.googleapis.com/"
] ]
} }
<!doctype html> <!doctype html>
<!--
This page is shown when the extension button is clicked, because the
"browser_action" field in manifest.json contains the "default_popup" key with
value "popup.html".
-->
<html> <html>
<head> <head>
<title>Getting Started Extension's Popup</title> <title>Getting Started Extension's Popup</title>
<style> <style>
body { body {
min-width: 357px; font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;
overflow-x: hidden; font-size: 100%;
} }
#status {
img { /* avoid an excessively wide status text */
margin: 5px; white-space: pre;
border: 2px solid black; text-overflow: ellipsis;
vertical-align: middle; overflow: hidden;
width: 75px; max-width: 400px;
height: 75px;
} }
</style> </style>
...@@ -21,11 +25,13 @@ ...@@ -21,11 +25,13 @@
- JavaScript and HTML must be in separate files: see our Content Security - JavaScript and HTML must be in separate files: see our Content Security
- Policy documentation[1] for details and explanation. - Policy documentation[1] for details and explanation.
- -
- [1]: http://developer.chrome.com/extensions/contentSecurityPolicy.html - [1]: https://developer.chrome.com/extensions/contentSecurityPolicy
--> -->
<script src="popup.js"></script> <script src="popup.js"></script>
</head> </head>
<body> <body>
<div id="status"></div>
<img id="image-result" hidden>
</body> </body>
</html> </html>
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
/** /**
* Global variable containing the query we'd like to pass to Flickr. In this * Get the current URL.
* case, kittens!
* *
* @type {string} * @param {function(string)} callback - called when the URL of the current tab
*/ * is found.
var QUERY = 'kittens'; **/
function getCurrentTabUrl(callback) {
// Query filter to be passed to chrome.tabs.query - see
// https://developer.chrome.com/extensions/tabs#method-query
var queryInfo = {
active: true,
currentWindow: true
};
var kittenGenerator = { chrome.tabs.query(queryInfo, function(tabs) {
/** // chrome.tabs.query invokes the callback with a list of tabs that match the
* Flickr URL that will give us lots and lots of whatever we're looking for. // query. When the popup is opened, there is certainly a window and at least
* // one tab, so we can safely assume that |tabs| is a non-empty array.
* See http://www.flickr.com/services/api/flickr.photos.search.html for // A window can only have one active tab at a time, so the array consists of
* details about the construction of this URL. // exactly one tab.
* var tab = tabs[0];
* @type {string}
* @private
*/
searchOnFlickr_: 'https://secure.flickr.com/services/rest/?' +
'method=flickr.photos.search&' +
'api_key=90485e931f687a9b9c2a66bf58a3861a&' +
'text=' + encodeURIComponent(QUERY) + '&' +
'safe_search=1&' +
'content_type=1&' +
'sort=interestingness-desc&' +
'per_page=20',
/** // A tab is a plain object that provides information about the tab.
* Sends an XHR GET request to grab photos of lots and lots of kittens. The // See https://developer.chrome.com/extensions/tabs#type-Tab
* XHR's 'onload' event is hooks up to the 'showPhotos_' method. var url = tab.url;
*
* @public
*/
requestKittens: function() {
var req = new XMLHttpRequest();
req.open("GET", this.searchOnFlickr_, true);
req.onload = this.showPhotos_.bind(this);
req.send(null);
},
/** // tab.url is only available if the "activeTab" permission is declared.
* Handle the 'onload' event of our kitten XHR request, generated in // If you want to see the URL of other tabs (e.g. after removing active:true
* 'requestKittens', by generating 'img' elements, and stuffing them into // from |queryInfo|), then the "tabs" permission is required to see their
* the document for display. // "url" properties.
* console.assert(typeof url == 'string', 'tab.url should be a string');
* @param {ProgressEvent} e The XHR ProgressEvent.
* @private
*/
showPhotos_: function (e) {
var kittens = e.target.responseXML.querySelectorAll('photo');
for (var i = 0; i < kittens.length; i++) {
var img = document.createElement('img');
img.src = this.constructKittenURL_(kittens[i]);
img.setAttribute('alt', kittens[i].getAttribute('title'));
document.body.appendChild(img);
}
},
/** callback(url);
* Given a photo, construct a URL using the method outlined at });
* http://www.flickr.com/services/api/misc.urlKittenl
* // Most methods of the Chrome extension APIs are asynchronous. This means that
* @param {DOMElement} A kitten. // you CANNOT do something like this:
* @return {string} The kitten's URL. //
* @private // var url;
// chrome.tabs.query(queryInfo, function(tabs) {
// url = tabs[0].url;
// });
// alert(url); // Shows "undefined", because chrome.tabs.query is async.
}
/**
* @param {string} searchTerm - Search term for Google Image search.
* @param {function(string,number,number)} callback - Called when an image has
* been found. The callback gets the URL, width and height of the image.
* @param {function(string)} errorCallback - Called when the image is not found.
* The callback gets a string that describes the failure reason.
*/ */
constructKittenURL_: function (photo) { function getImageUrl(searchTerm, callback, errorCallback) {
return "http://farm" + photo.getAttribute("farm") + // Google image search - 100 searches per day.
".static.flickr.com/" + photo.getAttribute("server") + // https://developers.google.com/image-search/
"/" + photo.getAttribute("id") + var searchUrl = 'https://ajax.googleapis.com/ajax/services/search/images' +
"_" + photo.getAttribute("secret") + '?v=1.0&q=' + encodeURIComponent(searchTerm);
"_s.jpg"; var x = new XMLHttpRequest();
x.open('GET', searchUrl);
// The Google image search API responds with JSON, so let Chrome parse it.
x.responseType = 'json';
x.onload = function() {
// Parse and process the response from Google Image Search.
var response = x.response;
if (!response || !response.responseData || !response.responseData.results ||
response.responseData.results.length === 0) {
errorCallback('No response from Google Image search!');
return;
} }
}; var firstResult = response.responseData.results[0];
// Take the thumbnail instead of the full image to get an approximately
// consistent image size.
var imageUrl = firstResult.tbUrl;
var width = parseInt(firstResult.tbWidth);
var height = parseInt(firstResult.tbHeight);
console.assert(
typeof imageUrl == 'string' && !isNaN(width) && !isNaN(height),
'Unexpected respose from the Google Image Search API!');
callback(imageUrl, width, height);
};
x.onerror = function() {
errorCallback('Network error.');
};
x.send();
}
function renderStatus(statusText) {
document.getElementById('status').textContent = statusText;
}
document.addEventListener('DOMContentLoaded', function() {
getCurrentTabUrl(function(url) {
// Put the image URL in Google search.
renderStatus('Performing Google Image search for ' + url);
getImageUrl(url, function(imageUrl, width, height) {
renderStatus('Search term: ' + url + '\n' +
'Google image search result: ' + imageUrl);
var imageResult = document.getElementById('image-result');
// Explicitly set the width/height to minimize the number of reflows. For
// a single image, this does not matter, but if you're going to embed
// multiple external images in your page, then the absence of width/height
// attributes causes the popup to resize multiple times.
imageResult.width = width;
imageResult.height = height;
imageResult.src = imageUrl;
imageResult.hidden = false;
// Run our kitten generation script as soon as the document's DOM is ready. }, function(errorMessage) {
document.addEventListener('DOMContentLoaded', function () { renderStatus('Cannot display image. ' + errorMessage);
kittenGenerator.requestKittens(); });
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment