| OLD | NEW |
| 1 <h1 id="add-images"> | 1 <h1 id="add-images"> |
| 2 <span class="h1-step">Step 5:</span> | 2 <span class="h1-step">Step 5:</span> |
| 3 Add Images From the Web | 3 Add Images From the Web |
| 4 </h1> | 4 </h1> |
| 5 | 5 |
| 6 <p class="note"> | 6 <p class="note"> |
| 7 <strong>Want to start fresh from here?</strong> | 7 <strong>Want to start fresh from here?</strong> |
| 8 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>. | 8 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>. |
| 9 </p> | 9 </p> |
| 10 | 10 |
| 11 <p>In this step, you will learn:</p> | 11 <p>In this step, you will learn:</p> |
| 12 | 12 |
| 13 <ul> | 13 <ul> |
| 14 <li>How to load resources from outside your app and add them to the DOM throug
h XHR and ObjectURLs.</li> | 14 <li>How to load resources from outside your app and add them to the DOM throug
h XHR and ObjectURLs.</li> |
| 15 </ul> | 15 </ul> |
| 16 | 16 |
| 17 <p> | 17 <p> |
| 18 <em>Estimated time to complete this step: 20 minutes.</em> | 18 <em>Estimated time to complete this step: 20 minutes.</em> |
| 19 <br> | 19 <br> |
| 20 To preview what you will complete in this step, <a href="#launch">jump down to
the bottom of this page ↓</a>. | 20 To preview what you will complete in this step, <a href="#launch">jump down to
the bottom of this page ↓</a>. |
| 21 </p> | 21 </p> |
| 22 | 22 |
| 23 <h2 id="csp-compliance">Learn how CSP affects the use of external web resources<
/h2> | 23 <h2 id="csp-compliance">How CSP affects the use of external resources</h2> |
| 24 | 24 |
| 25 <p>The Chrome Apps platform forces your app to be fully compliant with Content | 25 <p>The Chrome Apps platform forces your app to be fully compliant with Content |
| 26 Security Policies (CSP). This includes not being able to directly load DOM | 26 Security Policies (CSP). You can't directly load DOM |
| 27 resources like images, fonts, and CSS from outside of your packaged app.</p> | 27 resources like images, fonts, and CSS from outside of your Chrome App package.</
p> |
| 28 | 28 |
| 29 <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">XMLHt
tpRequest</a>, | 29 <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">XMLHt
tpRequest</a>, |
| 30 transform it into a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Bl
ob">Blob</a>, and create an <a href="https://developer.mozilla.org/en-US/docs/We
b/API/URL.createObjectURL">ObjectURL</a>. This ObjectURL can then be | 30 transform it into a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Bl
ob">Blob</a>, and create an <a href="https://developer.mozilla.org/en-US/docs/We
b/API/URL.createObjectURL">ObjectURL</a>. This <code>ObjectURL</code> can be add
ed to the DOM |
| 31 added to the DOM because it refers to an in-memory item in the context of the | 31 because it refers to an in-memory item in the context of the app.</p> |
| 32 app.</p> | |
| 33 | 32 |
| 34 <h2 id="show-images">Show thumbnail images for todo items</h2> | 33 <h2 id="show-images">Show thumbnail images for todo items</h2> |
| 35 | 34 |
| 36 <p>Let's change our app to look for image URLs in a todo item. If the URL | 35 <p>Let's change our app to look for image URLs in a todo item. |
| 37 looks like an image (e.g. ends with .png, .jpg, .svg, or .gif), we will apply th
e | 36 If the URL looks like an image (for example, ends with .png, .jpg, .svg, or .gif
), |
| 38 process mentioned above in order to show an image thumbnail next to the URL.</p> | 37 apply the process mentioned above in order to show an image thumbnail next to th
e URL.</p> |
| 39 | 38 |
| 40 <h3 id="update-permissions">Update permissions</h3> | 39 <h3 id="update-permissions">Update permissions</h3> |
| 41 | 40 |
| 42 <p>In a Chrome App you can make XMLHttpRequest calls to any URL as long as you | 41 <p>In a Chrome App, you can make XMLHttpRequest calls to any URL as long as you |
| 43 whitelist its domain in the manifest. Since we won't know | 42 whitelist its domain in the manifest. Since you won't know |
| 44 beforehand what image URL the user will type, | 43 beforehand what image URL the user will type, |
| 45 we will ask permission to make requests to <code>"<all_urls>"</code>.</p> | 44 ask permission to make requests to <code>"<all_urls>"</code>.</p> |
| 46 | 45 |
| 47 <p>In <strong><em>manifest.json</em></strong>, request the "<all_urls>" pe
rmission:</p> | 46 <p>In <strong><em>manifest.json</em></strong>, request the "<all_urls>" pe
rmission:</p> |
| 48 | 47 |
| 49 <pre data-filename="manifest.json"> | 48 <pre data-filename="manifest.json"> |
| 50 "permissions": ["storage", "alarms", "notifications", | 49 "permissions": ["storage", "alarms", "notifications", |
| 51 "webview"<b>, "<all_urls>"</b>], | 50 "webview"<b>, "<all_urls>"</b>], |
| 52 </pre> | 51 </pre> |
| 53 | 52 |
| 54 <h3 id="object-urls">Create and clear ObjectURLs</h3> | 53 <h3 id="object-urls">Create and clear ObjectURLs</h3> |
| 55 | 54 |
| 56 In <strong><em>controller.js</em></strong>, add a <code>_createObjectURL()</code
> method to create ObjectURLs from a Blob: | 55 In <strong><em>controller.js</em></strong>, add a <code>_createObjectURL()</code
> method to create ObjectURLs from a Blob: |
| 57 | 56 |
| 58 <pre data-filename="controller.js"> | 57 <pre data-filename="controller.js"> |
| 59 Controller.prototype._createObjectURL = function(blob) { | 58 Controller.prototype._createObjectURL = function(blob) { |
| 60 var objURL = URL.createObjectURL(blob); | 59 var objURL = URL.createObjectURL(blob); |
| 61 this.objectURLs = this.objectURLs || []; | 60 this.objectURLs = this.objectURLs || []; |
| 62 this.objectURLs.push(objURL); | 61 this.objectURLs.push(objURL); |
| 63 return objURL; | 62 return objURL; |
| 64 }; | 63 }; |
| 65 </pre> | 64 </pre> |
| 66 | 65 |
| 67 <p>ObjectURLs hold memory so, when you no longer need the ObjectURL, you | 66 <p>ObjectURLs hold memory, so when you no longer need the ObjectURL, you |
| 68 should revoke them. Add this | 67 should revoke them. Add this |
| 69 <code>_clearObjectURL()</code> method to <em>controller.js</em> to handle that:<
/p> | 68 <code>_clearObjectURL()</code> method to <em>controller.js</em> to handle that:<
/p> |
| 70 | 69 |
| 71 <pre data-filename="controller.js"> | 70 <pre data-filename="controller.js"> |
| 72 Controller.prototype._clearObjectURL = function() { | 71 Controller.prototype._clearObjectURL = function() { |
| 73 if (this.objectURLs) { | 72 if (this.objectURLs) { |
| 74 this.objectURLs.forEach(function(objURL) { | 73 this.objectURLs.forEach(function(objURL) { |
| 75 URL.revokeObjectURL(objURL); | 74 URL.revokeObjectURL(objURL); |
| 76 }); | 75 }); |
| 77 this.objectURLs = null; | 76 this.objectURLs = null; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 94 img.setAttribute('data-src', imageUrl); | 93 img.setAttribute('data-src', imageUrl); |
| 95 img.className = 'icon'; | 94 img.className = 'icon'; |
| 96 var objURL = this._createObjectURL(xhr.response); | 95 var objURL = this._createObjectURL(xhr.response); |
| 97 img.setAttribute('src', objURL); | 96 img.setAttribute('src', objURL); |
| 98 element.appendChild(img); | 97 element.appendChild(img); |
| 99 }.bind(this); | 98 }.bind(this); |
| 100 xhr.send(); | 99 xhr.send(); |
| 101 }; | 100 }; |
| 102 </pre> | 101 </pre> |
| 103 | 102 |
| 104 <p>On XHR load, this method will create an ObjectURL from the XHR's response, | 103 <p>On XHR load, this method creates an <code>ObjectURL</code> from the XHR's res
ponse, |
| 105 and add an <code><img></code> element with this ObjectURL to the DOM.</p> | 104 and adds an <code><img></code> element with this <code>ObjectURL</code> to
the DOM.</p> |
| 106 | 105 |
| 107 <h3 id="parse-urls">Parse for image URLs in todo items</h3> | 106 <h3 id="parse-urls">Parse for image URLs in todo items</h3> |
| 108 | 107 |
| 109 <p>Now add a <code>_parseForImageURLs()</code> method that finds all links not y
et processed and checks them | 108 <p>Now add a <code>_parseForImageURLs()</code> method that finds all links not y
et processed and checks them |
| 110 for images. For each URL that looks like an image, execute <code>_requestRemoteI
mageAndAppend()</code>: | 109 for images. For each URL that looks like an image, execute <code>_requestRemoteI
mageAndAppend()</code>: |
| 111 | 110 |
| 112 <pre data-filename="controller.js"> | 111 <pre data-filename="controller.js"> |
| 113 Controller.prototype._parseForImageURLs = function () { | 112 Controller.prototype._parseForImageURLs = function () { |
| 114 // remove old blobs to avoid memory leak: | 113 // remove old blobs to avoid memory leak: |
| 115 this._clearObjectURL(); | 114 this._clearObjectURL(); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 ... | 173 ... |
| 175 label.innerHTML = this._parseForURLs(value); | 174 label.innerHTML = this._parseForURLs(value); |
| 176 <b>this._parseForImageURLs();</b> | 175 <b>this._parseForImageURLs();</b> |
| 177 } else if (value.length === 0) { | 176 } else if (value.length === 0) { |
| 178 ... | 177 ... |
| 179 } | 178 } |
| 180 </pre> | 179 </pre> |
| 181 | 180 |
| 182 <h3 id="css">Constrain the displayed image dimensions</h3> | 181 <h3 id="css">Constrain the displayed image dimensions</h3> |
| 183 | 182 |
| 184 <p>Finally, in <strong></em>bower_components/todomvc-common/base.css</strong></e
m>, add a CSS rule to limit | 183 <p>Finally, in <strong><em>bower_components/todomvc-common/base.css</strong></em
>, |
| 185 the size of the image:</p> | 184 add a CSS rule to limit the size of the image:</p> |
| 186 | 185 |
| 187 <pre data-filename="base.css"> | 186 <pre data-filename="base.css"> |
| 188 .thumbnail img[data-src] { | 187 .thumbnail img[data-src] { |
| 189 max-width: 100px; | 188 max-width: 100px; |
| 190 max-height: 28px; | 189 max-height: 28px; |
| 191 } | 190 } |
| 192 </pre> | 191 </pre> |
| 193 | 192 |
| 194 <h2 id="launch">Launch your finished Todo app</h2> | 193 <h2 id="launch">Launch your finished Todo app</h2> |
| 195 | 194 |
| 196 <p>You are done Step 5! Reload your app and add a todo item with a URL | 195 <p>You are done Step 5! Reload your app and add a todo item with a URL |
| 197 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> | 196 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> |
| 198 | 197 |
| 199 <p>The result should be something like this:</p> | 198 <p>The result should be something like this:</p> |
| 200 | 199 |
| 201 <figure> | 200 <figure> |
| 202 <img src="{{static}}/images/app_codelab/step5-completed.gif" alt="The Todo app
with image thumbnails"/> | 201 <img src="{{static}}/images/app_codelab/step5-completed.gif" alt="The Todo app
with image thumbnails"/> |
| 203 </figure> | 202 </figure> |
| 204 | 203 |
| 205 <p class="note"><strong>Tip</strong>: For real-world situations, when you need t
o control | 204 <p class="note"><strong>Tip</strong>: For real-world situations, when you need t
o control |
| 206 offline cache and dozens of simultaneous resource downloads, we have created | 205 offline cache and dozens of simultaneous resource downloads, we have created |
| 207 <a href="https://github.com/GoogleChrome/apps-resource-loader#readme">a helper l
ibrary</a> | 206 <a href="https://github.com/GoogleChrome/apps-resource-loader#readme">a helper l
ibrary</a> |
| 208 to handle some common use cases.</p> | 207 to handle some common use cases.</p> |
| 209 | 208 |
| 210 <h2 id="recap">Recap APIs referenced in this step</h2> | 209 <h2 id="recap">For more information</h2> |
| 211 | 210 |
| 212 <p>For more detailed information about some of the APIs introduced in this step,
refer to:</p> | 211 <p>For more detailed information about some of the APIs introduced in this step,
refer to:</p> |
| 213 | 212 |
| 214 <ul> | 213 <ul> |
| 215 <li> | 214 <li> |
| 216 <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy'
in the Chrome developer docs">Content Security Policy</a> | 215 <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy'
in the Chrome developer docs">Content Security Policy</a> |
| 217 <a href="#csp-compliance" class="anchor-link-icon" title="This feature menti
oned in 'Learn how CSP affects the use of external web resources'">↑</a> | 216 <a href="#csp-compliance" class="anchor-link-icon" title="This feature menti
oned in 'Learn how CSP affects the use of external web resources'">↑</a> |
| 218 </li> | 217 </li> |
| 219 <li> | 218 <li> |
| 220 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the
Chrome developer docs">Declare Permissions</a> | 219 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the
Chrome developer docs">Declare Permissions</a> |
| 221 <a href="#update-permissions" class="anchor-link-icon" title="This feature m
entioned in 'Update permissions'">↑</a> | 220 <a href="#update-permissions" class="anchor-link-icon" title="This feature m
entioned in 'Update permissions'">↑</a> |
| 222 </li> | 221 </li> |
| 223 </ul> | 222 </ul> |
| 224 | 223 |
| 225 <p>Ready to continue onto the next step? Go to <a href="app_codelab_filesystem.h
tml">Step 6 - Export todos to the filesystem »</a></p> | 224 <p>Ready to continue onto the next step? Go to <a href="app_codelab_filesystem.h
tml">Step 6 - Export todos to the filesystem »</a></p> |
| OLD | NEW |