1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
|
---
title: Tu segunda extensión
slug: Mozilla/Add-ons/WebExtensions/Tutorial
tags:
- WebExtensions
translation_of: Mozilla/Add-ons/WebExtensions/Your_second_WebExtension
---
<div>{{AddonSidebar}}</div>
<p>Si ya ha visto el artículo <a href="https://developer.mozilla.org/es/Add-ons/WebExtensions/Tu_primera_WebExtension">tu primer extensión</a>, ya posee una idea de como escribir una extensión. En este artículo se escribirá una extensión ligeramente más compleja para demostrar un par de cosas más de las APIs.</p>
<p>La extensión añade un nuevo botón a la barra de herramientas de Firefox. Cuando el usuario da clic sobre el botón, mostraremos una ventana emergente que permite escoger un animal. Una vez que un animal sea escogido, reemplazaremos todas las imágenes en la página actual con la imagen del animal seleccionado.</p>
<p>Para implementar esto, haremos lo siguiente:</p>
<ul>
<li><strong>Definir una <a href="/es/Add-ons/WebExtensions/user_interface/Browser_action">acción del navegador</a>, que será el botón añadido a la barra de herramientas de Firefox</strong>.<br>
Para el botón vamos a proporcionar:
<ul>
<li>un icono, llamado "beasts-32.png"</li>
<li>una ventana emergente para abrir cuando el botón sea presionado. La ventana emergente incluye HTML, CSS y JavaScript.</li>
</ul>
</li>
<li><strong>Define un ícono para la extensión,</strong> llamado "beasts-48.png". Este será mostrado en el Administrador de Complementos.</li>
<li><strong>Escribe un script de contenido, "beastify.js" que será inyectado dentro de las páginas web.</strong><br>
Este es el código que modificará las páginas web.</li>
<li><strong>Empaqueta algunas imágenes de animales, para reemplazar las imágnes de la página web</strong>.<br>
Nosotros haremos las imágenes "recursos web accesibles" para que la página web pueda referenciarlos.</li>
</ul>
<p>Tu puedes notar que la estructura general de la extensión luce como esto:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/13671/Untitled-1.png" style="display: block; height: 1200px; margin-left: auto; margin-right: auto; width: 860px;"></p>
<p>Esta es una extensión simple, pero muestra muchos de los principales conceptos de la API WebExtensions:</p>
<ul>
<li>Adicionando un botón a la barra de herramientas</li>
<li>Definiendo un panel emergente usando HTML, CSS y JavaScript</li>
<li>Inyectando scripts de contenido dentro de las páginas web</li>
<li>Comunicándonos entre los scripts de contenido y el resto de la extensión</li>
<li>Empaquetando recursos con tu extensión que pueden ser usados por las páginas web</li>
</ul>
<p>Tu puedes encontrar el <a href="https://github.com/mdn/webextensions-examples/tree/master/beastify">código fuente completo de la extensión en GitHub</a>.</p>
<p>Para escribir una extensión, necesitará de Firefox 45 o más reciente.</p>
<h2 id="Escribiendo_la_extensión">Escribiendo la extensión</h2>
<p>Crea una carpeta nueva y navega hacia ella:</p>
<pre class="brush: bash">mkdir beastify
cd beastify</pre>
<h3 id="manifest.json">manifest.json</h3>
<p>Ahora crea un archivo llamado "manifest.json", y agrega el siguiente contenido:</p>
<pre class="brush: json">{
"manifest_version": 2,
"name": "Beastify",
"version": "1.0",
"description": "Agrega un icono de acción navegación a la barra de herramientas. Haga clic en el botón para elegir una bestia. El contenido del cuerpo de la pestaña activa se sustituye por una imagen de la bestia elegida. Consulte https://developer.mozilla.org/es/Add-ons/WebExtensions/Examples#beastify",
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
"icons": {
"48": "icons/beasts-48.png"
},
"permissions": [
"activeTab"
],
"browser_action": {
"default_icon": "icons/beasts-32.png",
"default_title": "Bestificar",
"default_popup": "popup/choose_beast.html"
},
"web_accessible_resources": [
"beasts/frog.jpg",
"beasts/turtle.jpg",
"beasts/snake.jpg"
]
}
</pre>
<ul>
<li>Las tres primeras llaves: <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/manifest_version">manifest_version</a></code> , <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/name">name</a></code> , y <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/version">version</a></code> , son obligatorias y contienen los metadatos básicos para la extensión.</li>
<li><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/description">description</a></code> y <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/homepage_url">homepage_url</a></code> son opcionales, pero recomendadas: proporcionan información útil acerca de la extensión.</li>
<li><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/icons">icons</a></code> es opcional, pero recomedada: permite la especificación de un ícono para la extensión, que será mostrada en el Administrador de Complementos.</li>
<li><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions">permissions</a></code> lista los permisos que la extensión necesita. Aquí solo se pide el permiso de <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/permissions#activeTab_permission"><code>activeTab</code> permission</a>.</li>
<li><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action">browser_action</a></code> especifica el botón de la barra de herramientas. Nosotros proveemos tres piezas de información aquí:
<ul>
<li><code>default_icon</code> es obligatorio y enlaza al icono para el botón</li>
<li><code>default_title</code> es opcional y será mostrado como descripción</li>
<li><code>default_popup</code> es usado su tu quieres una ventana emergente que será mostrada cuando el usuario de clic en el botón. Lo hacemos y hemos incluido esta llave que apunta a un archivo HTML de la extensión.</li>
</ul>
</li>
<li><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/web_accessible_resources">web_accessible_resources</a></code> lista los archivos que queremos hacer accesibles a las páginas web. Como la extensión reemplaza imágenes en una página con imágenes que hemos empaquetado, necesitamos hacer estas imágenes accesibles a la página.</li>
</ul>
<p>Nota que todas las rutas dadas son relativas a manifest.json.</p>
<h3 id="El_ícono">El ícono</h3>
<p>La extensión debería tener un íncono. Éste, será mostrado junto a la lista de complementos en el Administrador de Complementos (Pude abrirlo introduciendo en la URL "about:addons"). El manifest.json promete que se posee un ícono para la barra de herramientas en "icons/beasts-48.png".</p>
<p>Cree el directorio "icons" y guarde un ícono ahí y nómbrelo como "beasts-48.png". Puede utilizar <a href="https://github.com/mdn/webextensions-examples/blob/master/beastify/icons/beasts-48.png">uno de nuestro ejemplo</a>, el cual ha sido tomado del <a href="https://www.iconfinder.com/iconsets/free-retina-icon-set">conjuto de íconos Aha-Soft’s Free Retina</a>, que es utilizado bajo el término de su propia <a href="http://www.aha-soft.com/free-icons/free-retina-icon-set/">licencia</a>.</p>
<p>Si elige proporcionar su propio ícono, debería ser de 48x48 pixeles. También puede proporcionar un ícono de 96x96 pixeles, para pantallas de altas resoluciones y, si usted hace esto, deberá especificarlo como la propiedad <code>96</code> del objeto <code>icons</code> en el manifest.json:</p>
<pre class="brush: json line-numbers language-json"><code class="language-json"><span class="key token">"icons":</span> <span class="punctuation token">{</span>
<span class="key token">"48":</span> <span class="string token">"icons/beasts-48.png"</span><span class="punctuation token">,</span>
<span class="key token">"96":</span> <span class="string token">"icons/beasts-96.png"</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="El_botón_de_la_barra_de_herramientas">El botón de la barra de herramientas</h3>
<p>El botón de la barra de herramientas necesita un ícono, y nuestro manifest.json promete eso y nos gustaría tener un ícono para la barra de herramientas en "icons/beasts-32.png".</p>
<p>Guarde un ícono llamado "beasts-32.png" en el directorio "icons". Tu podrías usar <a href="https://github.com/mdn/webextensions-examples/blob/master/beastify/icons/beasts-32.png">uno de nuestros ejemplos</a>, los cuales son tomados desde el sitio <a href="http://www.iconbeast.com/free">IconBeast Lite icon set</a> y empleados bajo sus términos de <a href="http://www.iconbeast.com/faq/">licencia</a>.</p>
<p>Si tu no provees una ventana emergente, entonces el evento clic es disparado hacia tu extensión cuando el usuario de clic sobre el botón. Si provees una ventana emergente entonces el evento clic no se disparará, pero en cambio, se muestra la ventana emergente. Nosotros queremos una ventana emergente, así que vamos a crearla.</p>
<h3 id="La_ventana_emergente">La ventana emergente</h3>
<p>La función de la ventana emergente es habilitada si el usuario escoge una de los tres animales.</p>
<p>Crea una nueva carpeta llamada "popup" bajo la carpeta raíz de la extensión . Esta será donde pondremos el código para la ventana emergente. La carpeta "popup" contendrá estos tres archivos:</p>
<ul>
<li><strong><code>choose_beast.html</code></strong> define el contenido del panel</li>
<li><strong><code>choose_beast.css</code></strong> los estilos CSS para el contenido</li>
<li><strong><code>choose_beast.js</code></strong> maneja las opciones del usuario ejecutando un script de contenido en la pestaña activa</li>
</ul>
<h4 id="choose_beast.html">choose_beast.html</h4>
<p>El archivo HTML luce así:</p>
<pre class="brush: html line-numbers language-html"><code class="language-html"><span class="doctype token"><!DOCTYPE html></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>html</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>head</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>meta</span> <span class="attr-name token">charset</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>utf-8<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>link</span> <span class="attr-name token">rel</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>stylesheet<span class="punctuation token">"</span></span> <span class="attr-name token">href</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>choose_beast.css<span class="punctuation token">"</span></span><span class="punctuation token">/></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>head</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>body</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>div</span> <span class="attr-name token">id</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>popup-content<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>div</span> <span class="attr-name token">class</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>button beast<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>Frog<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>div</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>div</span> <span class="attr-name token">class</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>button beast<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>Turtle<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>div</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>div</span> <span class="attr-name token">class</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>button beast<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>Snake<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>div</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>div</span> <span class="attr-name token">class</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>button reset<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>Reset<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>div</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>div</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>div</span> <span class="attr-name token">id</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>error-content<span class="punctuation token">"</span></span> <span class="attr-name token">class</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>hidden<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>p</span><span class="punctuation token">></span></span>Can't beastify this web page.<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>p</span><span class="punctuation token">></span></span><span class="tag token"><span class="tag token"><span class="punctuation token"><</span>p</span><span class="punctuation token">></span></span>Try a different page.<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>p</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>div</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>script</span> <span class="attr-name token">src</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>choose_beast.js<span class="punctuation token">"</span></span><span class="punctuation token">></span></span><span class="tag token"><span class="tag token"><span class="punctuation token"></</span>script</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>body</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>html</span><span class="punctuation token">></span></span></code></pre>
<p>Tenemos un elemento <code><div></code> con un ID <code>"popup-content"</code> que contiene un elemento para cada elección de animal. Además, tenemos otro <code><div></code> con un ID <code>"error-content"</code> y una clase <code>"hidden"</code>, que usaremos en el case de que surja algún problema al inicializar la ventana emergente.</p>
<p>Note que referenciamos los archivos CSS y JS en el HTML como lo haríamos si se tratase de una página web.</p>
<h4 id="choose_beast.css">choose_beast.css</h4>
<p>El CSS ajusta el tamaño de la ventana emergente, se asegura que las tres posibles opciones llenen el espacio y les da un poco de estilo básico. Además oculta los elementos con <code>class="hidden"</code>: esto significa que nuestro <code>"error-content"</code> <code><div></code> estará oculto por defecto.</p>
<pre class="brush: css line-numbers language-css"><code class="language-css"><span class="selector token">html, body</span> <span class="punctuation token">{</span>
<span class="property token">width</span><span class="punctuation token">:</span> <span class="number token">100</span><span class="token unit">px</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="selector token"><span class="class token">.hidden</span></span> <span class="punctuation token">{</span>
<span class="property token">display</span><span class="punctuation token">:</span> none<span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="selector token"><span class="class token">.button</span></span> <span class="punctuation token">{</span>
<span class="property token">margin</span><span class="punctuation token">:</span> <span class="number token">3</span><span class="token unit">%</span> auto<span class="punctuation token">;</span>
<span class="property token">padding</span><span class="punctuation token">:</span> <span class="number token">4</span><span class="token unit">px</span><span class="punctuation token">;</span>
<span class="property token">text-align</span><span class="punctuation token">:</span> center<span class="punctuation token">;</span>
<span class="property token">font-size</span><span class="punctuation token">:</span> <span class="number token">1.5</span><span class="token unit">em</span><span class="punctuation token">;</span>
<span class="property token">cursor</span><span class="punctuation token">:</span> pointer<span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="selector token"><span class="class token">.beast</span><span class="pseudo-class token">:hover</span></span> <span class="punctuation token">{</span>
<span class="property token">background-color</span><span class="punctuation token">:</span> <span class="hexcode token">#CFF2F2</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="selector token"><span class="class token">.beast</span></span> <span class="punctuation token">{</span>
<span class="property token">background-color</span><span class="punctuation token">:</span> <span class="hexcode token">#E5F2F2</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="selector token"><span class="class token">.reset</span></span> <span class="punctuation token">{</span>
<span class="property token">background-color</span><span class="punctuation token">:</span> <span class="hexcode token">#FBFBC9</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="selector token"><span class="class token">.reset</span><span class="pseudo-class token">:hover</span></span> <span class="punctuation token">{</span>
<span class="property token">background-color</span><span class="punctuation token">:</span> <span class="hexcode token">#EAEA9D</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h4 id="choose_beast.js">choose_beast.js</h4>
<p>Aquí está el JavaScript para la ventana emergente:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">/**
* CSS para ocultar toda la página
* Excepto los elementos que pertenecen a la clase "beastify-image".
*/</span>
<span class="keyword token">const</span> hidePage <span class="operator token">=</span> <span class="template-string token"><span class="string token">`body > :not(.beastify-image) {
display: none;
}`</span></span><span class="punctuation token">;</span>
<span class="comment token">/**
* Esucha los clicks en los botones y envía el mensaje apropiado
* al script de contenido de la página.
*/</span>
<span class="keyword token">function</span> <span class="function token">listenForClicks</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
document<span class="punctuation token">.</span><span class="function token">addEventListener</span><span class="punctuation token">(</span><span class="string token">"click"</span><span class="punctuation token">,</span> <span class="punctuation token">(</span><span class="parameter token">e</span><span class="punctuation token">)</span> <span class="operator token">=></span> <span class="punctuation token">{</span>
<span class="comment token">/**
* Recibe el nombre de una bestia y obtiene la URL de la imagen correspondiente.
*/</span>
<span class="keyword token">function</span> <span class="function token">beastNameToURL</span><span class="punctuation token">(</span><span class="parameter token">beastName</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">switch</span> <span class="punctuation token">(</span>beastName<span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">case</span> <span class="string token">"Frog"</span><span class="punctuation token">:</span>
<span class="keyword token">return</span> browser<span class="punctuation token">.</span>extension<span class="punctuation token">.</span><span class="function token">getURL</span><span class="punctuation token">(</span><span class="string token">"beasts/frog.jpg"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">case</span> <span class="string token">"Snake"</span><span class="punctuation token">:</span>
<span class="keyword token">return</span> browser<span class="punctuation token">.</span>extension<span class="punctuation token">.</span><span class="function token">getURL</span><span class="punctuation token">(</span><span class="string token">"beasts/snake.jpg"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">case</span> <span class="string token">"Turtle"</span><span class="punctuation token">:</span>
<span class="keyword token">return</span> browser<span class="punctuation token">.</span>extension<span class="punctuation token">.</span><span class="function token">getURL</span><span class="punctuation token">(</span><span class="string token">"beasts/turtle.jpg"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Inserta dentro de la pestaña activa el CSS que oculta la página
* luego toma la URL de la imagen y
* envía un mensaje "beastify" al script de contenido de la pestaña activa.
*/</span>
<span class="keyword token">function</span> <span class="function token">beastify</span><span class="punctuation token">(</span><span class="parameter token">tabs</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">insertCSS</span><span class="punctuation token">(</span><span class="punctuation token">{</span>code<span class="punctuation token">:</span> hidePage<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="operator token">=></span> <span class="punctuation token">{</span>
<span class="keyword token">let</span> url <span class="operator token">=</span> <span class="function token">beastNameToURL</span><span class="punctuation token">(</span>e<span class="punctuation token">.</span>target<span class="punctuation token">.</span>textContent<span class="punctuation token">)</span><span class="punctuation token">;</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">sendMessage</span><span class="punctuation token">(</span>tabs<span class="punctuation token">[</span><span class="number token">0</span><span class="punctuation token">]</span><span class="punctuation token">.</span>id<span class="punctuation token">,</span> <span class="punctuation token">{</span>
command<span class="punctuation token">:</span> <span class="string token">"beastify"</span><span class="punctuation token">,</span>
beastURL<span class="punctuation token">:</span> url
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Remueve el CSS que oculta la página y
* envía un mensaje de "reset" al script de contenido de la pestaña activa.
*/</span>
<span class="keyword token">function</span> <span class="function token">reset</span><span class="punctuation token">(</span><span class="parameter token">tabs</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">removeCSS</span><span class="punctuation token">(</span><span class="punctuation token">{</span>code<span class="punctuation token">:</span> hidePage<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="operator token">=></span> <span class="punctuation token">{</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">sendMessage</span><span class="punctuation token">(</span>tabs<span class="punctuation token">[</span><span class="number token">0</span><span class="punctuation token">]</span><span class="punctuation token">.</span>id<span class="punctuation token">,</span> <span class="punctuation token">{</span>
command<span class="punctuation token">:</span> <span class="string token">"reset"</span><span class="punctuation token">,</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Imprime el error en consola.
*/</span>
<span class="keyword token">function</span> <span class="function token">reportError</span><span class="punctuation token">(</span><span class="parameter token">error</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
console<span class="punctuation token">.</span><span class="function token">error</span><span class="punctuation token">(</span><span class="template-string token"><span class="string token">`Could not beastify: </span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span>error<span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">`</span></span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Toma la pestaña activa y
* llama a "beastify()" o "reset()" según corresponda.
*/</span>
<span class="keyword token">if</span> <span class="punctuation token">(</span>e<span class="punctuation token">.</span>target<span class="punctuation token">.</span>classList<span class="punctuation token">.</span><span class="function token">contains</span><span class="punctuation token">(</span><span class="string token">"beast"</span><span class="punctuation token">)</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">query</span><span class="punctuation token">(</span><span class="punctuation token">{</span>active<span class="punctuation token">:</span> <span class="boolean token">true</span><span class="punctuation token">,</span> currentWindow<span class="punctuation token">:</span> <span class="boolean token">true</span><span class="punctuation token">}</span><span class="punctuation token">)</span>
<span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span>beastify<span class="punctuation token">)</span>
<span class="punctuation token">.</span><span class="function token">catch</span><span class="punctuation token">(</span>reportError<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="keyword token">else</span> <span class="keyword token">if</span> <span class="punctuation token">(</span>e<span class="punctuation token">.</span>target<span class="punctuation token">.</span>classList<span class="punctuation token">.</span><span class="function token">contains</span><span class="punctuation token">(</span><span class="string token">"reset"</span><span class="punctuation token">)</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">query</span><span class="punctuation token">(</span><span class="punctuation token">{</span>active<span class="punctuation token">:</span> <span class="boolean token">true</span><span class="punctuation token">,</span> currentWindow<span class="punctuation token">:</span> <span class="boolean token">true</span><span class="punctuation token">}</span><span class="punctuation token">)</span>
<span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span>reset<span class="punctuation token">)</span>
<span class="punctuation token">.</span><span class="function token">catch</span><span class="punctuation token">(</span>reportError<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Si hubo algún error al ejecutar el script,
* Despliega un popup con el mensaje de error y oculta la UI normal.
*/</span>
<span class="keyword token">function</span> <span class="function token">reportExecuteScriptError</span><span class="punctuation token">(</span><span class="parameter token">error</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
document<span class="punctuation token">.</span><span class="function token">querySelector</span><span class="punctuation token">(</span><span class="string token">"#popup-content"</span><span class="punctuation token">)</span><span class="punctuation token">.</span>classList<span class="punctuation token">.</span><span class="function token">add</span><span class="punctuation token">(</span><span class="string token">"hidden"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
document<span class="punctuation token">.</span><span class="function token">querySelector</span><span class="punctuation token">(</span><span class="string token">"#error-content"</span><span class="punctuation token">)</span><span class="punctuation token">.</span>classList<span class="punctuation token">.</span><span class="function token">remove</span><span class="punctuation token">(</span><span class="string token">"hidden"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
console<span class="punctuation token">.</span><span class="function token">error</span><span class="punctuation token">(</span><span class="template-string token"><span class="string token">`Failed to execute beastify content script: </span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span>error<span class="punctuation token">.</span>message<span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">`</span></span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Cuando se carga la ventana emergente, inyecta el script de contenido en la pestaña activa,
* y agrega un manejador de eventos.
* Si no es posible inyectar el script, se ocupa de manejar el error.
*/</span>
browser<span class="punctuation token">.</span>tabs<span class="punctuation token">.</span><span class="function token">executeScript</span><span class="punctuation token">(</span><span class="punctuation token">{</span>file<span class="punctuation token">:</span> <span class="string token">"/content_scripts/beastify.js"</span><span class="punctuation token">}</span><span class="punctuation token">)</span>
<span class="punctuation token">.</span><span class="function token">then</span><span class="punctuation token">(</span>listenForClicks<span class="punctuation token">)</span>
<span class="punctuation token">.</span><span class="function token">catch</span><span class="punctuation token">(</span>reportExecuteScriptError<span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Empecemos por la linea 96. La ventana emergente ejecuta un script de contenido en la pestaña activa tan pronto como se termina de cargar, usando la API <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript">browser.tabs.executeScript()</a></code>. Si la ejecución del script de contenido es exitosa, este quedará cargado en la página hasta que sea cerrada la pestaña o hasta que el usuario navegue hacia una página distinta.</p>
<div class="blockIndicator note">
<p>Un motivo común por el cual el llamado a <code>browser.tabs.executeScript()</code> puede fallar, es porque no es posible ejecutar scripts de contenido en todas las páginas, por ejemplo, en páginas de navegador privilegiadas como about:debugging, o páginas del dominio <a href="https://addons.mozilla.org/">addons.mozilla.org</a>, no es posible hacerlo.</p>
</div>
<p>Si la ejecución falla, <code>reportExecuteScriptError()</code> ocultará el <code><div></code> <code>"popup-content"</code>, mostrará el <code><div></code> <code>"error-content"</code>, y reportará el error en la consola.</p>
<p>Si la ejecución del script de contenido es exitosa, se llamará a <code>listenForClicks()</code>, que atiende los eventos que se generan al cliquear en la ventana emergente.</p>
<ul>
<li>Si el clic se produjo en el botón con <code>class="beast"</code>, se llama a <code>beastify()</code>.</li>
<li>Si el clic se produjo en el botón con <code>class="reset"</code>, se llama a <code>reset()</code>.</li>
</ul>
<p>La función <code>beastify()</code> hace tres cosas:</p>
<ul>
<li>map the button clicked to a URL pointing to an image of a particular beast</li>
<li>Oculta todo el contenido de la página al insertar CSS con la API <code><a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/insertCSS">browser.tabs.insertCSS()</a></code></li>
<li>Envía un mensaje "beastify" al script de contenido usando la API <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/sendMessage">browser.tabs.sendMessage()</a></code>, solicitándole "bestificar" la página, y enviándole la URL de la imagen.</li>
</ul>
<p>La función <code>reset()</code> deshace lo hecho por <code>beastify()</code>:</p>
<ul>
<li>Remueve el CSS que agregó, usando la API <code><a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/removeCSS">browser.tabs.removeCSS()</a></code></li>
<li>Envía un mensaje de "reset" al script de contenido, solicitándole que resetee la página</li>
</ul>
<h3 id="El_script_de_contenido">El script de contenido</h3>
<p>Crea una carpeta nueva bajo la raíz del complemento llamada "content_scripts" y crea un nuevo archivo en ella llamado "beastify.js", con el contenido siguiente:</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="punctuation token">(</span><span class="keyword token">function</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="comment token">/**
* Revisa e inicializa una variable de guardia.
* Si este script de contenido es insertado más de una vez
* en la misma página, dejará de ejecutarse.
*/</span>
<span class="keyword token">if</span> <span class="punctuation token">(</span>window<span class="punctuation token">.</span>hasRun<span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">return</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
window<span class="punctuation token">.</span>hasRun <span class="operator token">=</span> <span class="boolean token">true</span><span class="punctuation token">;</span>
<span class="comment token">/**
* Recibe la URL de la imagen, remueve las que se hayan agregado antes,
* crea un nodo IMG que apunt hacia la imagen
* e inserta ese nodo en el documento.
*/</span>
<span class="keyword token">function</span> <span class="function token">insertBeast</span><span class="punctuation token">(</span><span class="parameter token">beastURL</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="function token">removeExistingBeasts</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">let</span> beastImage <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">createElement</span><span class="punctuation token">(</span><span class="string token">"img"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
beastImage<span class="punctuation token">.</span><span class="function token">setAttribute</span><span class="punctuation token">(</span><span class="string token">"src"</span><span class="punctuation token">,</span> beastURL<span class="punctuation token">)</span><span class="punctuation token">;</span>
beastImage<span class="punctuation token">.</span>style<span class="punctuation token">.</span>height <span class="operator token">=</span> <span class="string token">"100vh"</span><span class="punctuation token">;</span>
beastImage<span class="punctuation token">.</span>className <span class="operator token">=</span> <span class="string token">"beastify-image"</span><span class="punctuation token">;</span>
document<span class="punctuation token">.</span>body<span class="punctuation token">.</span><span class="function token">appendChild</span><span class="punctuation token">(</span>beastImage<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Remueve todas las bestias de la página.
*/</span>
<span class="keyword token">function</span> <span class="function token">removeExistingBeasts</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">let</span> existingBeasts <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">querySelectorAll</span><span class="punctuation token">(</span><span class="string token">".beastify-image"</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">for</span> <span class="punctuation token">(</span><span class="keyword token">let</span> beast <span class="keyword token">of</span> existingBeasts<span class="punctuation token">)</span> <span class="punctuation token">{</span>
beast<span class="punctuation token">.</span><span class="function token">remove</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span>
<span class="comment token">/**
* Atiende mensajes del script de segundo plano.
* Llama a "beastify()" o "reset()".
*/</span>
browser<span class="punctuation token">.</span>runtime<span class="punctuation token">.</span>onMessage<span class="punctuation token">.</span><span class="function token">addListener</span><span class="punctuation token">(</span><span class="punctuation token">(</span><span class="parameter token">message</span><span class="punctuation token">)</span> <span class="operator token">=></span> <span class="punctuation token">{</span>
<span class="keyword token">if</span> <span class="punctuation token">(</span>message<span class="punctuation token">.</span>command <span class="operator token">===</span> <span class="string token">"beastify"</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="function token">insertBeast</span><span class="punctuation token">(</span>message<span class="punctuation token">.</span>beastURL<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span> <span class="keyword token">else</span> <span class="keyword token">if</span> <span class="punctuation token">(</span>message<span class="punctuation token">.</span>command <span class="operator token">===</span> <span class="string token">"reset"</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="function token">removeExistingBeasts</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
<p>Lo primero que hace el script de contenido es revisar la variable global <code>window.hasRun</code>: si está inicializada termina su ejecución, sino, la inicializa y continúa. La razón por la que hacemos esto es porque cada vez que el usuario abre la ventana emergente, se vuelve a ejecutar el script de contenido en la pestaña activa, por lo que podríamos tener múltiples instancias del script ejecutandose en una sola pestaña. Si esto sucede, necesitamos asegurarnos de que sea sólo la primera instancia la que vaya a realizar cambios.</p>
<p>Luego, en la linea 40, donde el script de contenido atiende mensajes provenientes de la ventana emergente (usando la API <code><a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage">browser.runtime.onMessage</a></code>), vemos que ésta puede enviar dos mensajes diferentes: "beastify" y "reset".</p>
<ul>
<li>si el mensaje es "beastify", esperamos que contenga la URL de la imagen. Removemos el contenido que ha sido agregado por el anterior llamado a "beastify", y luego construimos y añadimos un elemento <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img"><img></a></code> cuyo atributo <code>src</code> contiene la URL de la imagen.</li>
<li>si el mensaje es "reset", simplemente removemos cualquier imagen que haya sido agregada antes.</li>
</ul>
<h3 id="Las_bestias">Las bestias</h3>
<p>Finalmente, necesitamos incluir las imágenes de los animales.</p>
<p>Crea una carpeta nueva llamada "beasts", y adiciona tres imágenes en ella, con su nombre apropiado. Tu puedes obtener estas imágenes desde el <a href="https://github.com/mdn/webextensions-examples/tree/master/beastify/beasts">repositorio en GitHub</a>, o desde aquí:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/11459/frog.jpg" style="display: inline-block; height: 200px; margin: 20px; width: 200px;"><img alt="" src="https://mdn.mozillademos.org/files/11461/snake.jpg" style="display: inline-block; height: 200px; margin: 20px; width: 200px;"><img alt="" src="https://mdn.mozillademos.org/files/11463/turtle.jpg" style="display: inline-block; height: 200px; margin: 20px; width: 200px;"></p>
<h2 id="Probándolo">Probándolo</h2>
<p>Primero, comprueba nuevamente que tienes todos los archivos necesarios en el lugar adecuado:</p>
<pre class="line-numbers language-html"><code class="language-html">beastify/
beasts/
frog.jpg
snake.jpg
turtle.jpg
content_scripts/
beastify.js
icons/
beasts-32.png
beasts-48.png
popup/
choose_beast.css
choose_beast.html
choose_beast.js
manifest.json</code></pre>
<p>Comenzando con Firefox 45, pueden instalar temporalmente una extensión desde el disco.</p>
<p>Abre "about:debugging" en Firefox, de clic en "Cargar complemento temporalmente", y seleccione el archivo manifest.json. Entonces, debería de ver el ícono de la extensión aparecer en la barra de herramientas de Firefox:</p>
<p>{{EmbedYouTube("sAM78GU4P34")}}</p>
<p>Abra una página web, luego haga clic sobre el ícono, seleccione una bestia, y vea cómo cambia la página web:</p>
<p>{{EmbedYouTube("YMQXyAQSiE8")}}</p>
<h2 id="Desarrollo_desde_la_línea_de_comandos">Desarrollo desde la línea de comandos</h2>
<div id="gt-res-content">
<div class="trans-verified-button-small" dir="ltr" id="gt-res-dir-ctr">
<div id="tts_button"> </div>
<p><span id="result_box" lang="es"><span>Puede automatizar el paso de instalación temporal mediante la herramienta <a href="https://developer.mozilla.org/es/Add-ons/WebExtensions/Getting_started_with_web-ext">web-ext</a>.</span> <span>Pruebe esto</span></span>:</p>
</div>
</div>
<pre class="brush: bash line-numbers language-bash"><code class="language-bash">cd beastify
web-ext run</code></pre>
<p> </p>
|