| Index: chrome/common/extensions/docs/templates/articles/app_codelab_images.html
|
| diff --git a/chrome/common/extensions/docs/templates/articles/app_codelab_images.html b/chrome/common/extensions/docs/templates/articles/app_codelab_images.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..df161d8cb4bb093d68703c9b54c1bd64fcbfeb60
|
| --- /dev/null
|
| +++ b/chrome/common/extensions/docs/templates/articles/app_codelab_images.html
|
| @@ -0,0 +1,225 @@
|
| +<h1 id="add-images">
|
| + <span class="h1-step">Step 5:</span>
|
| + Add Images From the Web
|
| +</h1>
|
| +
|
| +<p class="note">
|
| + <strong>Want to start fresh from here?</strong>
|
| + Find the previous step's code in the <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step4</strong></em>.
|
| +</p>
|
| +
|
| +<p>In this step, you will learn:</p>
|
| +
|
| +<ul>
|
| + <li>How to load resources from outside your app and add them to the DOM through XHR and ObjectURLs.</li>
|
| +</ul>
|
| +
|
| +<p>
|
| + <em>Estimated time to complete this step: 20 minutes.</em>
|
| + <br>
|
| + To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page ↓</a>.
|
| +</p>
|
| +
|
| +<h2 id="csp-compliance">Learn how CSP affects the use of external web resources</h2>
|
| +
|
| +<p>The Chrome Apps platform forces your app to be fully compliant with Content
|
| +Security Policies (CSP). This includes not being able to directly load DOM
|
| +resources like images, fonts, and CSS from outside of your packaged app.</p>
|
| +
|
| +<p>If you want to show an external image in your app, you need to request it via <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequest</a>,
|
| +transform it into a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a>, and create an <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL">ObjectURL</a>. This ObjectURL can then be
|
| +added to the DOM because it refers to an in-memory item in the context of the
|
| +app.</p>
|
| +
|
| +<h2 id="show-images">Show thumbnail images for todo items</h2>
|
| +
|
| +<p>Let's change our app to look for image URLs in a todo item. If the URL
|
| +looks like an image (e.g. ends with .png, .jpg, .svg, or .gif), we will apply the
|
| +process mentioned above in order to show an image thumbnail next to the URL.</p>
|
| +
|
| +<h3 id="update-permissions">Update permissions</h3>
|
| +
|
| +<p>In a Chrome App you can make XMLHttpRequest calls to any URL as long as you
|
| +whitelist its domain in the manifest. Since we won't know
|
| +beforehand what image URL the user will type,
|
| +we will ask permission to make requests to <code>"<all_urls>"</code>.</p>
|
| +
|
| +<p>In <strong><em>manifest.json</em></strong>, request the "<all_urls>" permission:</p>
|
| +
|
| +<pre data-filename="manifest.json">
|
| +"permissions": ["storage", "alarms", "notifications",
|
| + "webview"<b>, "<all_urls>"</b>],
|
| +</pre>
|
| +
|
| +<h3 id="object-urls">Create and clear ObjectURLs</h3>
|
| +
|
| +In <strong><em>controller.js</em></strong>, add a <code>_createObjectURL()</code> method to create ObjectURLs from a Blob:
|
| +
|
| +<pre data-filename="controller.js">
|
| +Controller.prototype._createObjectURL = function(blob) {
|
| + var objURL = URL.createObjectURL(blob);
|
| + this.objectURLs = this.objectURLs || [];
|
| + this.objectURLs.push(objURL);
|
| + return objURL;
|
| +};
|
| +</pre>
|
| +
|
| +<p>ObjectURLs hold memory so, when you no longer need the ObjectURL, you
|
| +should revoke them. Add this
|
| +<code>_clearObjectURL()</code> method to <em>controller.js</em> to handle that:</p>
|
| +
|
| +<pre data-filename="controller.js">
|
| +Controller.prototype._clearObjectURL = function() {
|
| + if (this.objectURLs) {
|
| + this.objectURLs.forEach(function(objURL) {
|
| + URL.revokeObjectURL(objURL);
|
| + });
|
| + this.objectURLs = null;
|
| + }
|
| +};
|
| +</pre>
|
| +
|
| +<h3 id="xhr">Make a XHR request</h3>
|
| +
|
| +<p>Add a <code>_requestRemoteImageAndAppend()</code> method to execute a XMLHttpRequest
|
| +on a given image URL:</p>
|
| +
|
| +<pre data-filename="controller.js">
|
| +Controller.prototype._requestRemoteImageAndAppend = function(imageUrl, element) {
|
| + var xhr = new XMLHttpRequest();
|
| + xhr.open('GET', imageUrl);
|
| + xhr.responseType = 'blob';
|
| + xhr.onload = function() {
|
| + var img = document.createElement('img');
|
| + img.setAttribute('data-src', imageUrl);
|
| + img.className = 'icon';
|
| + var objURL = this._createObjectURL(xhr.response);
|
| + img.setAttribute('src', objURL);
|
| + element.appendChild(img);
|
| + }.bind(this);
|
| + xhr.send();
|
| +};
|
| +</pre>
|
| +
|
| +<p>On XHR load, this method will create an ObjectURL from the XHR's response,
|
| +and add an <code><img></code> element with this ObjectURL to the DOM.</p>
|
| +
|
| +<h3 id="parse-urls">Parse for image URLs in todo items</h3>
|
| +
|
| +<p>Now add a <code>_parseForImageURLs()</code> method that finds all links not yet processed and checks them
|
| +for images. For each URL that looks like an image, execute <code>_requestRemoteImageAndAppend()</code>:
|
| +
|
| +<pre data-filename="controller.js">
|
| +Controller.prototype._parseForImageURLs = function () {
|
| + // remove old blobs to avoid memory leak:
|
| + this._clearObjectURL();
|
| + var links = this.$todoList.querySelectorAll('a[data-src]:not(.thumbnail)');
|
| + var re = /\.(png|jpg|jpeg|svg|gif)$/;
|
| + for (var i = 0; i<links.length; i++) {
|
| + var url = links[i].getAttribute('data-src');
|
| + if (re.test(url)) {
|
| + links[i].classList.add('thumbnail');
|
| + this._requestRemoteImageAndAppend(url, links[i]);
|
| + }
|
| + }
|
| +};
|
| +</pre>
|
| +
|
| +<h3 id="render-thumbnails">Render thumbnails in the todo list</h3>
|
| +
|
| +<p>Now call <code>_parseForImageURLs()</code> from <code>showAll()</code>, <code>showActive()</code>, and
|
| +<code>showCompleted()</code>:</p>
|
| +
|
| +<pre data-filename="controller.js">
|
| +/**
|
| + * An event to fire on load. Will get all items and display them in the
|
| + * todo-list
|
| + */
|
| +Controller.prototype.showAll = function () {
|
| + this.model.read(function (data) {
|
| + this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));
|
| + <b>this._parseForImageURLs();</b>
|
| + }.bind(this));
|
| +};
|
| +
|
| +/**
|
| + * Renders all active tasks
|
| + */
|
| +Controller.prototype.showActive = function () {
|
| + this.model.read({ completed: 0 }, function (data) {
|
| + this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));
|
| + <b>this._parseForImageURLs();</b>
|
| + }.bind(this));
|
| +};
|
| +
|
| +/**
|
| + * Renders all completed tasks
|
| + */
|
| +Controller.prototype.showCompleted = function () {
|
| + this.model.read({ completed: 1 }, function (data) {
|
| + this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));
|
| + <b>this._parseForImageURLs();</b>
|
| + }.bind(this));
|
| +};
|
| +</pre>
|
| +
|
| +<p>Do the same for <code>editItem()</code>:</p>
|
| +
|
| +<pre data-filename="controller.js">
|
| +Controller.prototype.editItem = function (id, label) {
|
| + ...
|
| + var onSaveHandler = function () {
|
| + ...
|
| + if (value.length && !discarding) {
|
| + ...
|
| + label.innerHTML = this._parseForURLs(value);
|
| + <b>this._parseForImageURLs();</b>
|
| + } else if (value.length === 0) {
|
| + ...
|
| +}
|
| +</pre>
|
| +
|
| +<h3 id="css">Constrain the displayed image dimensions</h3>
|
| +
|
| +<p>Finally, in <strong></em>bower_components/todomvc-common/base.css</strong></em>, add a CSS rule to limit
|
| + the size of the image:</p>
|
| +
|
| +<pre data-filename="base.css">
|
| +.thumbnail img[data-src] {
|
| + max-width: 100px;
|
| + max-height: 28px;
|
| +}
|
| +</pre>
|
| +
|
| +<h2 id="launch">Launch your finished Todo app</h2>
|
| +
|
| +<p>You are done Step 5! Reload your app and add a todo item with a URL
|
| +to an image hosted online. Some URLs you could use: <strong>http://goo.gl/nqHMF#.jpg</strong> or <strong>http://goo.gl/HPBGR#.png</strong>.</p>
|
| +
|
| +<p>The result should be something like this:</p>
|
| +
|
| +<figure>
|
| + <img src="{{static}}/images/app_codelab/step5-completed.gif" alt="The Todo app with image thumbnails"/>
|
| +</figure>
|
| +
|
| +<p class="note"><strong>Tip</strong>: For real-world situations, when you need to control
|
| +offline cache and dozens of simultaneous resource downloads, we have created
|
| +<a href="https://github.com/GoogleChrome/apps-resource-loader#readme">a helper library</a>
|
| +to handle some common use cases.</p>
|
| +
|
| +<h2 id="recap">Recap APIs referenced in this step</h2>
|
| +
|
| +<p>For more detailed information about some of the APIs introduced in this step, refer to:</p>
|
| +
|
| +<ul>
|
| + <li>
|
| + <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy' in the Chrome developer docs">Content Security Policy</a>
|
| + <a href="#csp-compliance" class="anchor-link-icon" title="This feature mentioned in 'Learn how CSP affects the use of external web resources'">↑</a>
|
| + </li>
|
| + <li>
|
| + <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a>
|
| + <a href="#update-permissions" class="anchor-link-icon" title="This feature mentioned in 'Update permissions'">↑</a>
|
| + </li>
|
| +</ul>
|
| +
|
| +<p>Ready to continue onto the next step? Go to <a href="app_codelab_filesystem.html">Step 6 - Export todos to the filesystem »</a></p>
|
|
|