Browse code

Verificado con JSHint, pero aún falta revisar que todo esté bien

NikaZhenya authored on 28/09/2018 11:33:54
Showing 2 changed files
... ...
@@ -1,4 +1,4 @@
1
-var type  = 'story',
1
+var type  = 'arcade',
2 2
     lang  = 'es',
3 3
     debug = true,
4 4
     timer
... ...
@@ -1,1660 +1,2041 @@
1 1
 // Objeto que incluye toda la lógica de Foo
2 2
 var foo = (function() {
3
-
4
-    // Lenguaje
5
-    var lang_loading = 'Loading…',
6
-        lang_load_error = 'Data not found.'
7
-
8
-    // Todo lo relativo al juego
9
-    var game_opt
10
-
11
-    // Variables que van cambiando
12
-    var touch_old = null,
13
-        touch_timer,
14
-        game_timer,
15
-        bar_timer
16
-
17
-    // Obtiene una muestra aleatoria del conjunto
18
-    Array.prototype.sample = function () {
19
-        return this[Math.floor(Math.random() * this.length)]
3
+  'use strict';
4
+
5
+  // Todo lo relativo al juego
6
+  var game_opt;
7
+
8
+  // Lenguaje
9
+  var lang_loading = 'Loading…',
10
+      lang_load_error = 'Data not found.';
11
+
12
+  // Variables que van cambiando
13
+  var touch_old = null,
14
+      touch_timer,
15
+      game_timer,
16
+      bar_timer;
17
+
18
+  // Obtiene una muestra aleatoria del conjunto
19
+  Object.defineProperty(Array.prototype, 'sample', {
20
+    enumerable: false,
21
+    value: function () {
22
+      return this[Math.floor(Math.random() * this.length)];
20 23
     }
24
+  });
21 25
 
22
-    // Limpia un conjunto
23
-    Array.prototype.clear = function () {
24
-        return this.filter(function (e) {return e})
26
+  // Limpia un conjunto
27
+  Object.defineProperty(Array.prototype, 'clear', {
28
+    enumerable: false,
29
+    value: function () {
30
+      return this.filter(function (e) {return e;});
25 31
     }
32
+  });
26 33
 
27
-    // Revuelve el conjunto; viene de: http://alturl.com/33yti
28
-    Array.prototype.shuffle = function () {
29
-        var x = this.length, j, temp
34
+  // Revuelve el conjunto; viene de: http://alturl.com/33yti
35
+  Object.defineProperty(Array.prototype, 'shuffle', {
36
+    enumerable: false,
37
+    value: function () {
38
+      var x = this.length, j, temp;
30 39
 
31
-        if (x == 0) return this
40
+      if (x === 0) {return this;}
32 41
 
33
-        while (--x) {
34
-            j       = Math.floor(Math.random() * (x + 1))
35
-            temp    = this[x]
36
-            this[x] = this[j]
37
-            this[j] = temp
38
-        }
42
+      while (--x) {
43
+        j       = Math.floor(Math.random() * (x + 1));
44
+        temp    = this[x];
45
+        this[x] = this[j];
46
+        this[j] = temp;
47
+      }
39 48
 
40
-        return this
49
+      return this;
41 50
     }
42
-
43
-    // Capitaliza la línea de texto
44
-    String.prototype.capitalize = function () {
45
-        return this.charAt(0).toUpperCase() + this.toLowerCase().slice(1)
51
+  });
52
+
53
+  // Capitaliza la línea de texto
54
+  Object.defineProperty(String.prototype, 'capitalize', {
55
+    enumerable: false,
56
+    value: function () {
57
+      return this.charAt(0).toUpperCase() +
58
+             this.toLowerCase().slice(1);
46 59
     }
47
-
48
-    // Elimina tildes a las letras; basado en: http://alturl.com/37gbp
49
-    String.prototype.transliterate = function() {
50
-        var translate_re = /[ÁÉÍÓÚÜÑáéíóúüñ]/g,
51
-            translate = {
52
-                'Á':'A', 'É':'E', 'Í':'I', 
53
-                'Ó':'O', 'Ú':'U', 'Ü':'U', 
54
-                'Ñ':'N', 'á':'a', 'é':'e', 
55
-                'í':'i', 'ó':'o', 'ú':'u', 
56
-                'ü':'u', 'ñ':'n'
57
-            }
58
-
59
-        return (this.replace(translate_re, function(match){
60
-            return translate[match]
61
-        }))
60
+  });
61
+
62
+  // Elimina tildes a las letras; basado en: http://alturl.com/37gbp
63
+  Object.defineProperty(String.prototype, 'transliterate', {
64
+    enumerable: false,
65
+    value: function() {
66
+      var translate_re = /[ÁÉÍÓÚÜÑáéíóúüñ]/g,
67
+          translate = {
68
+            'Á':'A', 'É':'E', 'Í':'I',
69
+            'Ó':'O', 'Ú':'U', 'Ü':'U',
70
+            'Ñ':'N', 'á':'a', 'é':'e',
71
+            'í':'i', 'ó':'o', 'ú':'u',
72
+            'ü':'u', 'ñ':'n'
73
+          };
74
+
75
+      return (this.replace(translate_re, function(match){
76
+        return translate[match];
77
+      }));
62 78
     }
63
-
64
-    // Mueve el juego; viene de: http://alturl.com/musku
65
-    function scrollTo (element, to, duration) {
66
-        if (duration <= 0) return
67
-
68
-        var difference = to - element.scrollTop,
69
-            perTick    = difference / duration * 10
70
-
71
-        setTimeout(function() {
72
-            element.scrollTop = element.scrollTop + perTick
73
-
74
-            if (element.scrollTop === to) return
75
-
76
-            scrollTo(element, to, duration - 10)
77
-        }, 10)
79
+  });
80
+
81
+  // Mueve el juego; viene de: http://alturl.com/musku
82
+  function scrollTo (element, to, duration) {
83
+    if (duration <= 0) {return;}
84
+
85
+    var difference = to - element.scrollTop,
86
+        perTick  = difference / duration * 10;
87
+
88
+    setTimeout(function() {
89
+      element.scrollTop = element.scrollTop + perTick;
90
+
91
+      if (element.scrollTop === to) {return;}
92
+
93
+      scrollTo(element, to, duration - 10);
94
+    }, 10);
95
+  }
96
+
97
+  // Incrementa la barra de progreso
98
+  function resize_bar (increment = 0) {
99
+    var div_pbar    = document.getElementById('foo-bar'),
100
+        total_width   = document.body.clientWidth,
101
+        current_width = div_pbar.clientWidth,
102
+        total_lines   = game_opt.data.content.length -
103
+                        (game_opt.size[1] - 1),
104
+        line_width    = total_width / total_lines,
105
+        new_width;
106
+
107
+    // El intervalo se cambia con una animación
108
+    function move () {
109
+      if (current_width >= new_width) {
110
+        clearInterval(bar_timer);
111
+        bar_timer = 0;
112
+      } else {
113
+        if (increment) {current_width += 10;}
114
+        else           {current_width++;}
115
+        div_pbar.style.width = current_width + 'px';
116
+      }
78 117
     }
79 118
 
80
-    // Incrementa la barra de progreso
81
-    function resize_bar (increment = 0) {
82
-        var div_pbar      = document.getElementById('foo-bar'),
83
-            total_width   = document.body.clientWidth,
84
-            current_width = div_pbar.clientWidth,
85
-            total_lines   = game_opt.data.content.length - (game_opt.size[1] - 1),
86
-            line_width    = total_width / total_lines,
87
-            new_width
88
-
89
-        // El intervalo se cambia con una animación
90
-        function move () {
91
-            if (current_width >= new_width) {
92
-                clearInterval(bar_timer)
93
-                bar_timer = 0
94
-            } else {
95
-                if (increment)  current_width += 10
96
-                else            current_width++
97
-                div_pbar.style.width = current_width + 'px'
98
-            }
99
-        }
100
-
101
-        // Quiere decir que se tiene que calcular su posición inicial o desde el último guardado
102
-        if (increment == 0) {
103
-            var lines   = document.getElementsByClassName('foo-line'),
104
-                last_lt = lines[lines.length - 1].getElementsByClassName('foo-letter')[0].getAttribute('data-status'),
105
-                total   = 0
106
-
107
-            // Obtiene cuántas líneas se han avanzado
108
-            for (i = 0; i < lines.length; i++)
109
-                if (lines[i].getAttribute('data-visible') == 'true')
110
-                    total += 1
111
-
112
-            // Según si todas las líneas han sido respondidas o no
113
-            if (total_lines == total - (game_opt.size[1] - 1) && (last_lt == 'correct' || last_lt == 'incorrect'))
114
-                increment = (total - (game_opt.size[1] - 1))
115
-            else
116
-                increment = (total - (game_opt.size[1] - 1)) - 1
119
+    // Cálculo de su posición inicial o desde el último guardado
120
+    if (increment == 0) {
121
+      var lines   = document.getElementsByClassName('foo-line'),
122
+          last_lt = lines[lines.length - 1]
123
+                    .getElementsByClassName('foo-letter')[0]
124
+                    .getAttribute('data-status'),
125
+          total   = 0;
126
+
127
+      // Obtiene cuántas líneas se han avanzado
128
+      for (var i = 0; i < lines.length; i++) {
129
+        if (lines[i].getAttribute('data-visible') == 'true') {
130
+          total += 1;
117 131
         }
132
+      }
133
+
134
+      // Según si todas las líneas han sido respondidas o no
135
+      if (total_lines == total - (game_opt.size[1] - 1) &&
136
+          (last_lt == 'correct' || last_lt == 'incorrect')) {
137
+        increment = (total - (game_opt.size[1] - 1));
138
+      } else {
139
+        increment = (total - (game_opt.size[1] - 1)) - 1;
140
+      }
141
+    }
118 142
 
119
-        // Cambia el tamaño
120
-        new_width = current_width + (line_width * increment)
143
+    // Cambia el tamaño
144
+    new_width = current_width + (line_width * increment);
121 145
 
122
-        // Cambia el tamaño si no se está moviendo
123
-        if (typeof bar_timer === 'undefined' || bar_timer == 0)
124
-            bar_timer = setInterval(move, 1)
146
+    // Cambia el tamaño si no se está moviendo
147
+    if (typeof bar_timer === 'undefined' || bar_timer == 0) {
148
+      bar_timer = setInterval(move, 1);
125 149
     }
150
+  }
126 151
 
127
-    // Valida conjuntos
128
-    function validate_array (array, min_length, min_quantity, label) {
129
-
130
-        // No procede si no tiene la extensión necesaria
131
-        if (array.length != min_length)
132
-            throw new Error(label + " array requires only " + min_length + " elements.")
152
+  // Valida conjuntos
153
+  function validate_array (array, min_length, min_quantity, label) {
133 154
 
134
-        // No procede si el tipo o el tamaño mínimo son erróneos
135
-        for (i = 0; i < array.length; i++)
136
-            if (typeof array[i] != 'number' || array[i] % 1 !== 0 || array[i] < min_quantity)
137
-                throw new Error(label + " array's elements have to be integers equal or greater than " + min_quantity + ".")
155
+    // No procede si no tiene la extensión necesaria
156
+    if (array.length != min_length) {
157
+      throw new Error(label + " array requires only " +
158
+                      min_length + " elements.");
138 159
     }
139 160
 
140
-    // Ejecuta la función si se estipuló alguna
141
-    function exec_fn (e) {
142
-        // Si 'e' es texto, se supone que es el nombre de una función
143
-        if (typeof e === 'string' && typeof window[e] === 'function')
144
-            window[e]()
145
-        else if (typeof e === 'function')
146
-            e()
161
+    // No procede si el tipo o el tamaño mínimo son erróneos
162
+    for (var i = 0; i < array.length; i++) {
163
+      if (typeof array[i] != 'number' ||
164
+          array[i] % 1 !== 0          ||
165
+          array[i] < min_quantity) {
166
+        throw new Error(label + " array's elements have to be" +
167
+                                " integers equal or greater" +
168
+                                " than " + min_quantity + ".");
169
+      }
170
+    }
171
+  }
172
+
173
+  // Ejecuta la función si se estipuló alguna
174
+  function exec_fn (e) {
175
+    // Si 'e' es texto, se supone que es el nombre de una función
176
+    if (typeof e === 'string' &&
177
+        typeof window[e] === 'function') {
178
+      window[e]();
179
+    }
180
+    else if (typeof e === 'function') {
181
+      e();
182
+    }
183
+  }
184
+
185
+  // Cuando pasa cada segundo del juego
186
+  function new_time (extra = 0) {
187
+    var div_timer   = document.getElementById('foo-timer-total'),
188
+        qnt_timer   = parseInt(div_timer.innerHTML);
189
+
190
+    // Agrega un extra, si lo hay
191
+    if (extra != 0) {
192
+      qnt_timer = qnt_timer + extra;
193
+    // Agrega o quita lo regular
194
+    } else {
195
+      if (game_opt.type == 'story') {qnt_timer = qnt_timer + 1;}
196
+      else {qnt_timer = qnt_timer - 1;}
147 197
     }
148 198
 
149
-    // Cuando pasa cada segundo del juego
150
-    function new_time (extra = 0) {
151
-        var div_timer   = document.getElementById('foo-timer-total'),
152
-            qnt_timer   = parseInt(div_timer.innerHTML)
153
-
154
-        // Agrega un extra, si lo hay
155
-        if (extra != 0)
156
-            qnt_timer = qnt_timer + extra
157
-        // Agrega o quita lo regular
158
-        else {
159
-            if (game_opt.type == 'story')
160
-                qnt_timer = qnt_timer + 1
161
-            else
162
-                qnt_timer = qnt_timer - 1
163
-        }
164
-
165
-        // Se fija variable
166
-        game_opt.time = qnt_timer
167
-        foo.data.time = game_opt.time
168
-
169
-        // Imprime el resultado
170
-        div_timer.innerHTML = qnt_timer
199
+    // Se fija variable
200
+    game_opt.time = qnt_timer;
201
+    foo.data.time = game_opt.time;
171 202
 
172
-        // Corre función cuando se llegan a los últimos 10 segundos del arcade
173
-        if (qnt_timer == 10 && game_opt.type == 'arcade')
174
-            exec_fn(game_opt.ar_on_ten_seconds)
203
+    // Imprime el resultado
204
+    div_timer.innerHTML = qnt_timer;
175 205
 
176
-        // Si se llega a cero y es arcade, se deshabilita
177
-        if (qnt_timer <= 0 && game_opt.type == 'arcade')
178
-            lose()
206
+    // Corre cuando se llegan a los últimos 10 segundos del arcade
207
+    if (qnt_timer == 10 && game_opt.type == 'arcade') {
208
+      exec_fn(game_opt.ar_on_ten_seconds);
209
+    }
179 210
 
180
-        // Corre función cuando se cambia el temporizador
181
-        exec_fn(game_opt.on_new_time)
211
+    // Si se llega a cero y es arcade, se deshabilita
212
+    if (qnt_timer <= 0 && game_opt.type == 'arcade') {
213
+      lose();
182 214
     }
183 215
 
184
-    // Cuando hay un cambio en el puntaje
185
-    function new_score (int) {
186
-        var div_score = document.getElementById('foo-score-total'),
187
-            qnt_score = parseInt(div_score.innerHTML)
216
+    // Corre función cuando se cambia el temporizador
217
+    exec_fn(game_opt.on_new_time);
218
+  }
188 219
 
189
-        // Se fija variable
190
-        game_opt.past_score = qnt_score
191
-        foo.data.past_score = game_opt.past_score
220
+  // Cuando hay un cambio en el puntaje
221
+  function new_score (int) {
222
+    var div_score = document.getElementById('foo-score-total'),
223
+        qnt_score = parseInt(div_score.innerHTML);
192 224
 
193
-        qnt_score = qnt_score + int
225
+    // Se fija variable
226
+    game_opt.past_score = qnt_score;
227
+    foo.data.past_score = game_opt.past_score;
194 228
 
195
-        // Se fija variable
196
-        game_opt.score = qnt_score
197
-        foo.data.score = game_opt.score
229
+    qnt_score = qnt_score + int;
198 230
 
199
-        // Imprime el resultado
200
-        div_score.innerHTML = qnt_score
231
+    // Se fija variable
232
+    game_opt.score = qnt_score;
233
+    foo.data.score = game_opt.score;
201 234
 
202
-        // Si la puntuación es menor a cero, se deshabilita 
203
-        if (qnt_score < 0)
204
-            lose()
235
+    // Imprime el resultado
236
+    div_score.innerHTML = qnt_score;
205 237
 
206
-        // Corre función cuando se cambia el puntaje
207
-        exec_fn(game_opt.on_new_score)
238
+    // Si la puntuación es menor a cero, se deshabilita
239
+    if (qnt_score < 0) {
240
+      lose();
208 241
     }
209 242
 
210
-    // Cuando empieza el touch
211
-    function on_touchstart (e) {
212
-        if (!game_opt.win) {
213
-            var element         = e.currentTarget,
214
-                p               = element.getElementsByTagName('p')[0],
215
-                type            = element.getAttribute('data-type'),
216
-                status          = element.getAttribute('data-status'),
217
-                correct         = element.getAttribute('data-correct'),
218
-                picked          = element.getAttribute('data-picked'),
219
-                picked_i        = element.getAttribute('data-picked_i'),
220
-                letters         = element.getAttribute('data-letters').split('¤'),
221
-                chances         = element.getAttribute('data-chances'),
222
-                loops           = element.getAttribute('data-loops'),
223
-                on_first_change = element.getAttribute('data-on_first_change'),
224
-                on_change       = element.getAttribute('data-on_change'),
225
-                on_loop         = element.getAttribute('data-on_loop'),
226
-                on_bomb         = element.getAttribute('data-on_bomb'),
227
-                on_bonus        = element.getAttribute('data-on_bonus')
228
-
229
-            game_opt.currentTarget = element
230
-
231
-            // Se fijan variables
232
-            game_opt.letter = element
233
-            foo.data.letter = game_opt.letter
234
-
235
-            // Dibuja la letra actual para mayor visibilización
236
-            function draw_label (l) {
237
-                var d_add = document.getElementById('foo-adds'),
238
-                    p_add = document.createElement('p')
239
-
240
-                // Elimina el token con la letra presionada
241
-                if (document.getElementById('foo-token'))
242
-                    document.getElementById('foo-token').parentElement.removeChild(document.getElementById('foo-token'))
243
-
244
-                // Muestra los anuncios
245
-                d_add.style.display = 'inherit'
246
-
247
-                // Agrega la etiqueta y sus estilos
248
-                p_add.id            = 'foo-token'
249
-                p_add.style.cssText = 'width:' + d_add.clientHeight + 'px;height:' + d_add.clientHeight + 'px;font-size:' + ((parseInt(game_opt.game_fontSize) * 2) + game_opt.game_fontSize.replace(/\d+/, '') ) + ';background:' + game_opt.lt_bkcolor_enable + ';border-radius:' + (d_add.clientHeight / 5) + 'px;margin:0 auto;display:flex;align-items:center;justify-content:center;border:1px solid #ccc;-webkit-box-shadow: 0 0 25px 0 rgba(0,0,0,0.25);-moz-box-shadow: 0 0 25px 0 rgba(0,0,0,0.25);box-shadow: 0 0 25px 0 rgba(0,0,0,0.25);'
250
-                p_add.innerHTML     = l
251
-                d_add.appendChild(p_add)
252
-            }
243
+    // Corre función cuando se cambia el puntaje
244
+    exec_fn(game_opt.on_new_score);
245
+  }
246
+
247
+  // Cuando empieza el touch
248
+  function on_touchstart (e) {
249
+    // Dibuja la letra actual para mayor visibilización
250
+    function draw_label (l) {
251
+      var d_add = document.getElementById('foo-adds'),
252
+          p_add = document.createElement('p');
253
+
254
+      // Elimina el token con la letra presionada
255
+      if (document.getElementById('foo-token')) {
256
+        document.getElementById('foo-token').parentElement
257
+                .removeChild(document.getElementById('foo-token'));
258
+      }
259
+
260
+      // Muestra los anuncios
261
+      d_add.style.display = 'inherit';
262
+
263
+      // Agrega la etiqueta y sus estilos
264
+      p_add.id        = 'foo-token';
265
+      p_add.style
266
+        .cssText      = 'width:' + d_add.clientHeight               +
267
+                        'px;height:'                                +
268
+                        d_add.clientHeight                          +
269
+                        'px;font-size:'                             +
270
+                        ((parseInt(game_opt.game_fontSize) * 2)     +
271
+                        game_opt.game_fontSize.replace(/\d+/, '') ) +
272
+                        ';background:'                              +
273
+                        game_opt.lt_bkcolor_enable                  +
274
+                        ';border-radius:'                           +
275
+                        (d_add.clientHeight / 5)                    +
276
+                        'px;margin:0 auto;display:flex;'            +
277
+                        'align-items:center;justify-content:center;'+
278
+                        'border:1px solid #ccc;'                    +
279
+                        '-webkit-box-shadow: '                      +
280
+                                    '0 0 25px 0 rgba(0,0,0,0.25);'  +
281
+                        '-moz-box-shadow: '                         +
282
+                                    '0 0 25px 0 rgba(0,0,0,0.25);'  +
283
+                        'box-shadow: 0 0 25px 0 rgba(0,0,0,0.25);';
284
+      p_add.innerHTML = l;
285
+      d_add.appendChild(p_add);
286
+    }
253 287
 
254
-            // Analiza si ejecutar las funciones de la bomba o el bono
255
-            function trigger_bomb_bonus (el, i) {
256
-                var bomb_bonus   = game_opt.type + '_bombs_bonus_' + game_opt.lang,
257
-                    picked       = el.getAttribute('data-picked'),
258
-                    fn           = picked == 0 ? el.getAttribute('data-on_bomb') : el.getAttribute('data-on_bonus'),
259
-                    line_index   = get_index(el.parentElement.previousSibling, 0),
260
-                    letter_index = get_index(el.previousSibling, 0),
261
-                    position     = line_index + '-' + letter_index + '-' + i,
262
-                    valid        = true,
263
-                    final_pos    = []
264
-
265
-                // Obtiene el índice
266
-                function get_index (e, i) {
267
-                    while(e != null ) {
268
-                        e = e.previousSibling
269
-                        i++
270
-                    }
271
-
272
-                    return i
273
-                }
288
+    // Analiza si ejecutar las funciones de la bomba o el bono
289
+    function trigger_bomb_bonus (el, i) {
290
+      var bomb_bonus  = game_opt.type + '_bombs_bonus_' +
291
+                        game_opt.lang,
292
+          picked        = el.getAttribute('data-picked'),
293
+          fn            = picked == 0 ?
294
+                            el.getAttribute('data-on_bomb') :
295
+                            el.getAttribute('data-on_bonus'),
296
+          line_index    = get_index(el.parentElement
297
+                                      .previousSibling, 0),
298
+          letter_index  = get_index(el.previousSibling, 0),
299
+          position      = line_index + '-' + letter_index + '-' + i,
300
+          valid         = true,
301
+          final_pos     = [];
302
+
303
+      // Obtiene el índice
304
+      function get_index (e, i) {
305
+        while(e != null ) {
306
+          e = e.previousSibling;
307
+          i++;
308
+        }
274 309
 
275
-                // Si no hay datos guardados, se guarda
276
-                if (localStorage.getItem(bomb_bonus) == null) {
277
-                    final_pos.push(position)
278
-                // Si hay datos, se analiza e incorporan
279
-                } else {
280
-                    var bomb_bonus_array = JSON.parse(localStorage[bomb_bonus])
310
+        return i;
311
+      }
312
+
313
+      // Si no hay datos guardados, se guarda
314
+      if (localStorage.getItem(bomb_bonus) == null) {
315
+        final_pos.push(position);
316
+      // Si hay datos, se analiza e incorporan
317
+      } else {
318
+        var bomb_bonus_array = JSON.parse(localStorage[bomb_bonus]);
319
+
320
+        // Si ya se activó no será válido
321
+        for (var j = 0; j < bomb_bonus_array.length; j++) {
322
+          if (bomb_bonus_array[j] == position) {
323
+            valid = false;
324
+          }
325
+        }
281 326
 
282
-                    // Si ya se activó no será válido
283
-                    for (j = 0; j < bomb_bonus_array.length; j++) {
284
-                        if (bomb_bonus_array[j] == position) {
285
-                            valid = false
286
-                        }
287
-                    }
327
+        bomb_bonus_array.push(position);
328
+
329
+        final_pos = bomb_bonus_array.slice();
330
+      }
331
+
332
+      /* Si es válido, se ejecuta, cambia la puntuación y guarda
333
+         el registro de las bombas o los bonos activados */
334
+      if (valid) {
335
+        // Corre función cuando hay bomba o bono
336
+        exec_fn(fn);
337
+
338
+        // Cambia la puntuación
339
+        if (picked == 0)  {new_score(-3);}
340
+        else              {new_score(3);}
341
+
342
+        // Guardado de los datos
343
+        localStorage.setItem(bomb_bonus, JSON.stringify(final_pos));
344
+
345
+        return  '<span style="font-size:'   +
346
+                game_opt.lt_chance_fontSize +
347
+                ';vertical-align:.25em;">'  +
348
+                (picked == '0' ? '-' : '+') +
349
+                '</span>3';
350
+      } else {
351
+        return  '<span style="color:#999">' +
352
+                '<span style="font-size:'   +
353
+                game_opt.lt_chance_fontSize +
354
+                ';vertical-align:.25em;">'  +
355
+                (picked == '0' ? '-' : '+') +
356
+                '</span>3</span>';
357
+      }
358
+    }
288 359
 
289
-                    bomb_bonus_array.push(position)
360
+    if (!game_opt.win) {
361
+      var element      = e.currentTarget,
362
+          p            = element.getElementsByTagName('p')[0],
363
+          status       = element.getAttribute('data-status'),
364
+          picked       = element.getAttribute('data-picked'),
365
+          picked_i     = element.getAttribute('data-picked_i'),
366
+          letters      = element.getAttribute('data-letters')
367
+                                .split('¤'),
368
+          chances      = element.getAttribute('data-chances'),
369
+          loops        = element.getAttribute('data-loops'),
370
+          on_st_change = element.getAttribute('data-on_first_change'),
371
+          on_change    = element.getAttribute('data-on_change'),
372
+          on_loop      = element.getAttribute('data-on_loop');
373
+
374
+      game_opt.currentTarget = element;
375
+
376
+      // Se fijan variables
377
+      game_opt.letter = element;
378
+      foo.data.letter = game_opt.letter;
379
+
380
+      // Evita falsos positivos
381
+      clearInterval(touch_timer);
382
+
383
+      /* Para dedos rápidos, si se cambió de ficha,
384
+         quita una oportunidad a la ficha antigua */
385
+      if (touch_old != element && touch_old != null) {
386
+        compute_letter(touch_old);
387
+      }
388
+
389
+      // Solo si todavía hay oportunidades disponibles
390
+      if (chances > 0 && status == 'enable') {
391
+        // Corre función cuando se está cambiando de letra
392
+        exec_fn(on_change);
393
+
394
+        // Corre función cuando es la 1ra vez que se cambia de letra
395
+        if (loops == 0 && picked == letters[0]) {
396
+          exec_fn(on_st_change);
397
+        }
290 398
 
291
-                    final_pos = bomb_bonus_array.slice()
292
-                }
399
+        // Iteración para encontrar la siguiente letra
400
+        for (var i = 0; i < letters.length; i++) {
401
+          if (i == picked_i) {
402
+            var label;
293 403
 
294
-                // Si es válido, se ejecuta, cambia la puntuación y guarda el registro de las bombas o los bonos activados
295
-                if (valid) {
296
-                    // Corre función cuando hay bomba o bono
297
-                    exec_fn(fn)
404
+            // Obtiene la siguiente letra
405
+            if (i != letters.length - 1) {
406
+              picked_i = i + 1;
407
+            } else {
408
+              picked_i = 0;
409
+              loops  = parseInt(loops) + 1;
410
+              element.setAttribute('data-loops', loops);
298 411
 
299
-                    // Cambia la puntuación
300
-                    if (picked == 0) new_score(-3)
301
-                    else             new_score(3)
412
+              // Corre función cuando se ha dado una vuelta
413
+              exec_fn(on_loop);
414
+            }
302 415
 
303
-                    // Guardado de los datos
304
-                    localStorage.setItem(bomb_bonus, JSON.stringify(final_pos))
416
+            // Cambia la letra elegida
417
+            picked = letters[picked_i];
418
+            element.setAttribute('data-picked',   picked);
419
+            element.setAttribute('data-picked_i', picked_i);
305 420
 
306
-                    return '<span style="font-size:' + game_opt.lt_chance_fontSize + ';vertical-align:.25em;">' + (picked == '0' ? '-' : '+') + '</span>3'
307
-                } else
308
-                    return '<span style="color:#999"><span style="font-size:' + game_opt.lt_chance_fontSize + ';vertical-align:.25em;">' + (picked == '0' ? '-' : '+') + '</span>3</span>'
309
-            }
310
-                
311
-            // Evita falsos positivos
312
-            clearInterval(touch_timer)
313
-
314
-            // Para dedos rápidos, si se cambió de ficha, quita una oportunidad a la ficha antigua
315
-            if (touch_old != element && touch_old != null)
316
-                compute_letter(touch_old)
317
-
318
-            // Solo si todavía hay oportunidades disponibles
319
-            if (chances > 0 && status == 'enable') {
320
-                // Corre función cuando se está cambiando de letra
321
-                exec_fn(on_change)
322
-
323
-                // Corre función cuando es la primera vez que se cambia de letra
324
-                if (loops == 0 && picked == letters[0])
325
-                    exec_fn(on_first_change)                
326
-
327
-                // Iteración para encontrar la siguiente letra
328
-                for (i = 0; i < letters.length; i++) {
329
-                    if (i == picked_i) {
330
-                        // Obtiene la siguiente letra
331
-                        if (i != letters.length - 1) {
332
-                            picked_i = i + 1
333
-                        } else {
334
-                            picked_i = 0
335
-                            loops  = parseInt(loops) + 1
336
-                            element.setAttribute('data-loops', loops)
337
-
338
-                            // Corre función cuando se ha dado una vuelta a las opciones
339
-                            exec_fn(on_loop)
340
-                        }
341
-
342
-                        // Cambia la letra elegida
343
-                        picked = letters[picked_i]
344
-                        element.setAttribute('data-picked',   picked)
345
-                        element.setAttribute('data-picked_i', picked_i)
346
-
347
-                        // Etiqueta según si es letra, bomba o bonus
348
-                        if (picked != 0 && picked != 1)
349
-                            label = picked
350
-                        else
351
-                            label = trigger_bomb_bonus(element, i + 1)
352
-
353
-                        // Cambia la etiqueta
354
-                        draw_label(label)
355
-                        p.innerHTML = label
356
-
357
-                        break
358
-                    }
359
-                }
421
+            // Etiqueta según si es letra, bomba o bonus
422
+            if (picked != 0 && picked != 1) {
423
+              label = picked;
424
+            } else {
425
+              label = trigger_bomb_bonus(element, i + 1);
360 426
             }
361 427
 
362
-            // Evita falsos positivos
363
-            touch_old = element
364
-        }
365
-    }
366
-
367
-    // Cuando termina el touch
368
-    function on_touchend (e) {
369
-        var element = e.currentTarget
370
-
371
-        // El usuario tiene cierto tiempo antes de perder una oportunidad
372
-        if (!game_opt.win) {
373
-            // Si el touch empezó en el mismo elemento que finaliza
374
-            if (element == game_opt.currentTarget)
375
-                touch_timer = setInterval(compute_letter, 500, this)
376
-            else
377
-                touch_timer = setInterval(compute_letter, 500, game_opt.currentTarget)
378
-        }
379
-    }
428
+            // Cambia la etiqueta
429
+            draw_label(label);
430
+            p.innerHTML = label;
380 431
 
381
-    // Analiza todo lo relativo a las letras
382
-    function compute_letter (e) {
383
-        var chances = e.getAttribute('data-chances'),
384
-            status  = e.getAttribute('data-status'),
385
-            d_add   = document.getElementById('foo-adds')
386
-
387
-        // Reseteo de varios elementos para evitar falsos positivos
388
-        clearInterval(touch_timer)
389
-        touch_old           = null
390
-        d_add.style.display = 'none'
391
-        if (document.getElementById('foo-token'))
392
-            document.getElementById('foo-token').parentElement.removeChild(document.getElementById('foo-token'))
393
-
394
-        // Cambia la cantidad de oportunidades y si ya se llegó a 0, se cambia de estado y color
395
-        if (chances > 0 && status == 'enable') {
396
-            e.setAttribute('data-chances', chances - 1)
397
-            e.getElementsByTagName('p')[1].innerHTML = chances - 1
398
-
399
-            if (chances - 1 == 0) {
400
-                e.setAttribute('data-status', 'disable')
401
-                e.style.background  = game_opt.lt_bkcolor_disable
402
-                e.style.cssText    += '-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;'
403
-
404
-                // Reemplaza para eliminar los disparadores
405
-                e_new = e.cloneNode(true)
406
-                e.parentNode.replaceChild(e_new, e)
407
-                e = e_new
408
-            }
432
+            break;
433
+          }
409 434
         }
435
+      }
410 436
 
411
-        // Analiza si la línea se completó y en cuál condición
412
-        compute_line(e.parentNode)
437
+      // Evita falsos positivos
438
+      touch_old = element;
439
+    }
440
+  }
441
+
442
+  // Cuando termina el touch
443
+  function on_touchend (e) {
444
+    var element = e.currentTarget;
445
+
446
+    // El usuario tiene cierto tiempo antes de perder una oportunidad
447
+    if (!game_opt.win) {
448
+      // Si el touch empezó en el mismo elemento que finaliza
449
+      if (element == game_opt.currentTarget) {
450
+        touch_timer = setInterval(compute_letter, 500, this);
451
+      } else {
452
+        touch_timer = setInterval(
453
+                        compute_letter, 500, game_opt.currentTarget
454
+                      );
455
+      }
456
+    }
457
+  }
458
+
459
+  // Analiza todo lo relativo a las letras
460
+  function compute_letter (e) {
461
+    var chances = e.getAttribute('data-chances'),
462
+        status  = e.getAttribute('data-status'),
463
+        d_add   = document.getElementById('foo-adds');
464
+
465
+    // Reseteo de varios elementos para evitar falsos positivos
466
+    clearInterval(touch_timer);
467
+    touch_old       = null;
468
+    d_add.style.display = 'none';
469
+    if (document.getElementById('foo-token')) {
470
+      document.getElementById('foo-token')
471
+              .parentElement
472
+              .removeChild(document.getElementById('foo-token'));
473
+    }
413 474
 
414
-        // Guarda el estado del juego
415
-        foo.save()
475
+    /* Cambia la cantidad de oportunidades y si ya se llegó a 0,
476
+       se cambia de estado y color */
477
+    if (chances > 0 && status == 'enable') {
478
+      e.setAttribute('data-chances', chances - 1);
479
+      e.getElementsByTagName('p')[1].innerHTML = chances - 1;
480
+
481
+      if (chances - 1 == 0) {
482
+        e.setAttribute('data-status', 'disable');
483
+        e.style.background  = game_opt.lt_bkcolor_disable;
484
+        e.style.cssText    += '-webkit-user-select:none;' +
485
+                              '-moz-user-select:none;'    +
486
+                              '-ms-user-select:none;'     +
487
+                              'user-select:none;';
488
+
489
+        // Reemplaza para eliminar los disparadores
490
+        var e_new = e.cloneNode(true);
491
+
492
+        e.parentNode.replaceChild(e_new, e);
493
+        e = e_new;
494
+      }
416 495
     }
417 496
 
418
-    // Analiza todo lo relativo a las líneas
419
-    function compute_line (e) {
420
-        var foo_game          = document.getElementById('foo-game'),
421
-            on_line_completed = e.getAttribute('data-on_line_completed'),
422
-            on_line_correct   = e.getAttribute('data-on_line_correct'),
423
-            on_line_incorrect = e.getAttribute('data-on_line_incorrect'),
424
-            childrens         = e.childNodes,
425
-            disable           = true,
426
-            correct           = true,
427
-            next              = true,
428
-            score             = 0,
429
-            bar_resize        = 1,
430
-            results           = [],
431
-            line_text         = '',
432
-            line_text_correct = '',
433
-            line_completed    = (childrens[0].getAttribute('data-status') != 'correct') && (childrens[0].getAttribute('data-status') != 'incorrect')
434
-
435
-        // Cambia el color de la letra según esté correcta o no
436
-        function change_color (childrens) {
437
-            for (k = 0; k < childrens.length; k++) {
438
-                var child         = childrens[k],
439
-                    child_type    = child.getAttribute('data-type'),
440
-                    child_status  = child.getAttribute('data-status'),
441
-                    child_correct = child.getAttribute('data-correct'),
442
-                    child_picked  = child.getAttribute('data-picked')
443
-
444
-                if (child_type == '1') {
445
-                    if (child_correct == child_picked) {
446
-                        child.style.background = game_opt.lt_bkcolor_correct
447
-                        child.setAttribute('data-status', 'correct')
448
-                    }
449
-                    else {
450
-                        child.style.background = game_opt.lt_bkcolor_incorrect
451
-                        child.setAttribute('data-status', 'incorrect')
452
-                    }
453
-                }
454
-            }
497
+    // Analiza si la línea se completó y en cuál condición
498
+    compute_line(e.parentNode);
499
+
500
+    // Guarda el estado del juego
501
+    foo.save();
502
+  }
503
+
504
+  // Analiza todo lo relativo a las líneas
505
+  function compute_line (e) {
506
+    var foo_game          = document.getElementById('foo-game'),
507
+        on_line_completed = e.getAttribute('data-on_line_completed'),
508
+        on_line_correct   = e.getAttribute('data-on_line_correct'),
509
+        on_line_incorrect = e.getAttribute('data-on_line_incorrect'),
510
+        childrens         = e.childNodes,
511
+        disable           = true,
512
+        correct           = true,
513
+        next              = true,
514
+        score             = 0,
515
+        bar_resize        = 1,
516
+        results           = [],
517
+        line_text         = '',
518
+        line_text_correct = '',
519
+        line_completed    =
520
+          (childrens[0].getAttribute('data-status') != 'correct'  ) &&
521
+          (childrens[0].getAttribute('data-status') != 'incorrect');
522
+
523
+    // Cambia el color de la letra según esté correcta o no
524
+    function change_color (childrens) {
525
+      for (var k = 0; k < childrens.length; k++) {
526
+        var child         = childrens[k],
527
+            child_type    = child.getAttribute('data-type'),
528
+            child_correct = child.getAttribute('data-correct'),
529
+            child_picked  = child.getAttribute('data-picked');
530
+
531
+        if (child_type == '1') {
532
+          if (child_correct == child_picked) {
533
+            child.style.background = game_opt.lt_bkcolor_correct;
534
+            child.setAttribute('data-status', 'correct');
535
+          }
536
+          else {
537
+            child.style.background = game_opt.lt_bkcolor_incorrect;
538
+            child.setAttribute('data-status', 'incorrect');
539
+          }
455 540
         }
541
+      }
542
+    }
456 543
 
457
-        // Se fijan variables
458
-        game_opt.line = e
459
-        foo.data.line = game_opt.line
460
-
461
-        // Iteración de las letras para determinar el estado de la línea
462
-        for (i = 0; i < childrens.length; i++) {
463
-            var child         = childrens[i],
464
-                child_status  = child.getAttribute('data-status'),
465
-                child_correct = child.getAttribute('data-correct'),
466
-                child_picked  = child.getAttribute('data-picked'),
467
-                child_chances = child.getAttribute('data-chances')
468
-
469
-            // Quiere decir que la línea no tiene todas las letras deshabilitadas
470
-            if (child_status  == 'enable') {
471
-                disable = false
472
-                results.push(child_correct == child_picked ? true : false)
473
-            }
474
-
475
-            // Quiere decir que la línea no tiene todas las letras correctas
476
-            if (child_correct != child_picked) {
477
-                correct = false
478
-                results.push(child_status == 'enable' ? false : true)
479
-            }
544
+    // Se fijan variables
545
+    game_opt.line = e;
546
+    foo.data.line = game_opt.line;
547
+
548
+    // Iteración de las letras para determinar el estado de la línea
549
+    for (var i = 0; i < childrens.length; i++) {
550
+      var child     = childrens[i],
551
+          child_status  = child.getAttribute('data-status'),
552
+          child_correct = child.getAttribute('data-correct'),
553
+          child_picked  = child.getAttribute('data-picked'),
554
+          child_chances = child.getAttribute('data-chances');
555
+
556
+      // Implica que la línea no tiene todas las letras deshabilitadas
557
+      if (child_status  == 'enable') {
558
+        disable = false;
559
+        results.push(child_correct == child_picked ? true : false);
560
+      }
561
+
562
+      // Quiere decir que la línea no tiene todas las letras correctas
563
+      if (child_correct != child_picked) {
564
+        correct = false;
565
+        results.push(child_status == 'enable' ? false : true);
566
+      }
567
+
568
+      // Va obteniendo la puntuación de la línea
569
+      if (child_chances != null) {
570
+        score += parseInt(child_chances);
571
+      }
572
+
573
+      // Obtiene la oración completa
574
+      line_text += (child_picked != null ? child_picked : ' ');
575
+
576
+      // Obtiene la línea correcta
577
+      line_text_correct += (child_correct != null ?
578
+                            child_correct : ' ');
579
+    }
480 580
 
481
-            // Va obteniendo la puntuación de la línea
482
-            if (child_chances != null)
483
-                score += parseInt(child_chances)
581
+    // Evita un falso negativo
582
+    for (var j = 0; j < results.length; j++) {
583
+      if (results[j] == false) {next = false;}
584
+    }
484 585
 
485
-            // Obtiene la oración completa
486
-            line_text += (child_picked != null ? child_picked : ' ')
586
+    /* Una línea se considera completada cuando ya no hay letras
587
+       habilitadas o es correcta la elección o mezcla de ambas */
588
+    if (disable || correct || next) {
487 589
 
488
-            // Obtiene la línea correcta
489
-            line_text_correct += (child_correct != null ? child_correct : ' ')
490
-        }
590
+      // Añade el puntaje
591
+      if (line_completed) {new_score(score);}
491 592
 
492
-        // Evita un falso negativo
493
-        for (j = 0; j < results.length; j++)
494
-            if (results[j] == false) next = false
593
+      // Cambia el color de las letras
594
+      change_color(childrens);
495 595
 
496
-        // Una línea se considera completada cuando ya no hay letras habilitadas o es correcta la elección o mezcla de ambas
497
-        if (disable || correct || next) {
596
+      // Se fija variable
597
+      game_opt.line_correct = line_text_correct.trim();
598
+      foo.data.line_correct = game_opt.line_correct;
498 599
 
499
-            // Añade el puntaje
500
-            if (line_completed)
501
-                new_score(score)
600
+      // Analiza las palabras en búsqueda de alguna nueva
601
+      compute_words(line_text, line_text_correct);
502 602
 
503
-            // Cambia el color de las letras
504
-            change_color(childrens)
603
+      // Corre función cuando se completa la línea correctamente
604
+      if (correct) {
605
+        if (line_completed) {
505 606
 
506
-            // Se fija variable
507
-            game_opt.line_correct = line_text_correct.trim()
508
-            foo.data.line_correct = game_opt.line_correct
607
+          // Si se trata del modo arcade, además agrega unos segundos
608
+          if (game_opt.type == 'arcade') {
609
+            new_time(score);
610
+          }
509 611
 
510
-            // Analiza las palabras en búsqueda de alguna nueva
511
-            compute_words(line_text, line_text_correct)
612
+          // Se suma una línea correcta
613
+          foo.data.lines_correct++;
512 614
 
513
-            // Corre función cuando se completa la línea correctamente
514
-            if (correct) {
515
-                if (line_completed) {
615
+          // Corre función cuando se completa la línea correctamente
616
+          exec_fn(on_line_correct);
617
+        }
618
+      // Corre función cuando se completa incorrectamente
619
+      } else {
620
+        if (line_completed) {
516 621
 
517
-                    // Si se trata del modo arcade, además agrega unos segundos
518
-                    if (game_opt.type == 'arcade')
519
-                        new_time(score)
622
+          // Se suma una línea correcta
623
+          foo.data.lines_incorrect++;
520 624
 
521
-                    // Se suma una línea correcta
522
-                    foo.data.lines_correct++
625
+          // Corre función cuando se completa la línea incorrectamente
626
+          exec_fn(on_line_incorrect);
627
+        }
628
+      }
629
+
630
+      // Habilita la siguiente línea o termina el juego
631
+      if (e.nextSibling != null) {
632
+        if (e.nextSibling.childNodes.length == 1) {
633
+          e.nextSibling.setAttribute('data-visible', true);
634
+          e.nextSibling.style.display = 'initial';
635
+          e.nextSibling.nextSibling
636
+                       .setAttribute('data-visible', true);
637
+          e.nextSibling.nextSibling.style.display = 'initial';
638
+          bar_resize = 2;
639
+        } else {
640
+          e.nextSibling.setAttribute('data-visible', true);
641
+          e.nextSibling.style.display = 'initial';
642
+        }
643
+      } else {
644
+        if (line_completed) {win();}
645
+      }
523 646
 
524
-                    // Corre función cuando se completa la línea correctamente
525
-                    exec_fn(on_line_correct)
526
-                }
527
-            // Corre función cuando se completa incorrectamente
528
-            } else {
529
-                if (line_completed) {
647
+      // Incrementa la barra de progreso
648
+      if (line_completed) {resize_bar(bar_resize);}
530 649
 
531
-                    // Se suma una línea correcta
532
-                    foo.data.lines_incorrect++
650
+      // Muestra la nueva línea
651
+      scrollTo(foo_game, foo_game.scrollHeight, 500);
533 652
 
534
-                    // Corre función cuando se completa la línea incorrectamente
535
-                    exec_fn(on_line_incorrect)
536
-                }
537
-            }
653
+      // Corre función cuando se completa la línea
654
+      if (line_completed) {exec_fn(on_line_completed);}
655
+    }
656
+  }
657
+
658
+  // Analiza todo lo relativo a las palabras
659
+  function compute_words (line_text, line_text_correct) {
660
+    var string_w  = game_opt.type + '_words_' + game_opt.lang,
661
+        sav_words = localStorage.getItem(string_w) != null ?
662
+                    JSON.parse(localStorage.getItem(string_w)) :
663
+                    { total: game_opt.data.words.length,
664
+                      current: 0,
665
+                      words: []
666
+                    },
667
+        current_w = line_text.replace(/[^(\s|A-zÀ-ÿ|\-)]/, '')
668
+                             .split(/\s+/).clear(),
669
+        correct_w = line_text_correct.replace(/[^(\s|A-zÀ-ÿ|\-)]/, '')
670
+                                     .split(/\s+/).clear();
671
+
672
+    // Añade la palabra al guardado local y a 'foo.data'
673
+    function add_word (w) {
674
+      w = w.capitalize();
675
+
676
+      // Si la palabra no está en el conjunto
677
+      if (sav_words.words.indexOf(w) == -1) {
678
+        sav_words.words   = sav_words.words.concat(w);
679
+        sav_words.current = sav_words.words.length;
538 680
 
539
-            // Habilita la siguiente línea o termina el juego
540
-            if (e.nextSibling != null) {
541
-                if (e.nextSibling.childNodes.length == 1) {
542
-                    e.nextSibling.setAttribute('data-visible', true)
543
-                    e.nextSibling.style.display = 'initial'
544
-                    e.nextSibling.nextSibling.setAttribute('data-visible', true)
545
-                    e.nextSibling.nextSibling.style.display = 'initial'
546
-                    bar_resize = 2
547
-                } else {
548
-                    e.nextSibling.setAttribute('data-visible', true)
549
-                    e.nextSibling.style.display = 'initial'           
550
-                }
551
-            } else
552
-                if (line_completed)
553
-                    win()
681
+        // Se fija variable
682
+        game_opt.new_words = sav_words.words.slice();
683
+        foo.data.new_words = game_opt.new_words.slice();
554 684
 
555
-            // Incrementa la barra de progreso
556
-            if (line_completed)
557
-                resize_bar(bar_resize)
685
+        // Guarda los datos
686
+        localStorage.setItem(string_w, JSON.stringify(sav_words));
558 687
 
559
-            // Muestra la nueva línea
560
-            scrollTo(foo_game, foo_game.scrollHeight, 500)
688
+        // Corre función cuando hay una nueva palabra
689
+        exec_fn(game_opt.on_new_word);
690
+      }
691
+    }
561 692
 
562
-            // Corre función cuando se completa la línea
563
-            if (line_completed)
564
-                exec_fn(on_line_completed)
693
+    // Analiza la línea palabra por palabra
694
+    for (var i = 0; i < current_w.length; i++) {
695
+      var word = '';
696
+
697
+      // Si la palabra es la correcta
698
+      if (current_w[i] == correct_w[i]) {
699
+        // Guarda la palabra si tiene separación silábica
700
+        if (correct_w[i].charAt(correct_w[i].length - 1) == '-') {
701
+          localStorage.setItem('incomplete_word', correct_w[i]);
702
+        // Se completa una palabra si estaba separada
703
+        } else if (
704
+          localStorage.getItem('incomplete_word') != null   &&
705
+          localStorage.getItem('incomplete_word') != 'null' &&
706
+          localStorage.getItem('incomplete_word') != '') {
707
+            word = (localStorage.getItem('incomplete_word') +
708
+                    correct_w[i]).replace('-', '');
709
+            localStorage.removeItem('incomplete_word');
710
+        // Palabra no interrumpida por separación silábica
711
+        } else if (
712
+            localStorage.getItem('incomplete_word') != 'null') {
713
+              word = correct_w[i];
565 714
         }
566
-    }
567 715
 
568
-    // Analiza todo lo relativo a las palabras
569
-    function compute_words (line_text, line_text_correct) {
570
-        var sav_words = localStorage.getItem(game_opt.type + '_words_' + game_opt.lang) != null ? JSON.parse(localStorage.getItem(game_opt.type + '_words_' + game_opt.lang)) : {total: game_opt.data.words.length, current: 0, words: []},
571
-            current_w = line_text.replace(/[^(\s|A-zÀ-ÿ|\-)]/, '').split(/\s+/).clear(),
572
-            correct_w = line_text_correct.replace(/[^(\s|A-zÀ-ÿ|\-)]/, '').split(/\s+/).clear()
716
+        // Resetea
717
+        if (localStorage.getItem('incomplete_word') == 'null') {
718
+          localStorage.removeItem('incomplete_word');
719
+        }
573 720
 
574
-        // Añade la palabra al guardado local y a 'foo.data'
575
-        function add_word (w) {
576
-            w = w.capitalize()
721
+        // Añade la palabra
722
+        if (word != '') {add_word(word);}
723
+
724
+      // Si no es correcta, limpia la palabra guardada
725
+      } else {
726
+        /* Cuando era una palabra con separación silábica se marca
727
+           nulo para detectarla y rechazar el resto de la palabra */
728
+        if (correct_w[i].charAt(correct_w[i].length - 1) == '-') {
729
+          localStorage.setItem('incomplete_word', null);
730
+        // Resetea
731
+        } else {localStorage.removeItem('incomplete_word');}
732
+      }
733
+    }
734
+  }
735
+
736
+  // Analiza si hay nuevos récords
737
+  function highest_scores () {
738
+    var hig_score   = localStorage.getItem(game_opt.type +
739
+                      '_highest_scores_' + game_opt.lang),
740
+        new_score   = foo.data.score,
741
+        new_words   = game_opt.new_words;
742
+
743
+    // Con esta manera se evitan falsos positivos
744
+    if (typeof new_words !== 'undefined') {
745
+
746
+      // Verifica y guarda posibles nuevos récords
747
+      if (hig_score != null) {
748
+        hig_score = JSON.parse(hig_score);
749
+      } else {
750
+        hig_score = [];
751
+      }
752
+
753
+      // Indaga si hay un nuevo récord
754
+      if (hig_score.length < 10) {
755
+        exec_fn(game_opt.on_new_record);
756
+      } else {
757
+        if (new_score > hig_score[hig_score.length - 1]) {
758
+          exec_fn(game_opt.on_new_record);
759
+        }
760
+      }
577 761
 
578
-            // Si la palabra no está en el conjunto
579
-            if (sav_words.words.indexOf(w) == -1) {
580
-                sav_words.words   = sav_words.words.concat(w)
581
-                sav_words.current = sav_words.words.length
762
+      // Añade el récord
763
+      hig_score.push(new_score);
764
+      hig_score = hig_score.sort().reverse();
582 765
 
583
-                // Se fija variable
584
-                game_opt.new_words = sav_words.words.slice()
585
-                foo.data.new_words = game_opt.new_words.slice()
766
+      // Disminuye los récords si son más de lo deseado
767
+      if (hig_score.length > game_opt.top) {
768
+        hig_score = hig_score.slice(0, game_opt.top);
769
+      }
586 770
 
587
-                // Guarda los datos
588
-                localStorage.setItem(game_opt.type + '_words_' + game_opt.lang, JSON.stringify(sav_words))
771
+      // Guarda los récords
772
+      localStorage.setItem(game_opt.type      +
773
+                           '_highest_scores_' +
774
+                           game_opt.lang, JSON.stringify(hig_score));
775
+    }
776
+  }
777
+
778
+  // Cuando se gana el juego
779
+  function win () {
780
+    // Si se reinicia un juego que ya está completo
781
+    if (foo.data.letter == null) {
782
+      foo.data.active   = false;
783
+      foo.data.finished = true;
784
+
785
+      // Detiene el temporizador
786
+      clearInterval(game_timer);
787
+    // Cuando se gana la primera vez
788
+    } else {
789
+      var additional_score = parseInt(foo.data.time / 4);
790
+
791
+      // Incrementa la barra de progreso
792
+      resize_bar(1);
793
+
794
+      // Hay un límite de 100 puntos
795
+      if (additional_score > 100) {
796
+        additional_score = 100;
797
+      }
798
+
799
+      // En el modo historia se restan puntos relativos al tiempo
800
+      if (game_opt.type == 'story') {
801
+        new_score(-Math.abs(additional_score));
802
+      // En el modo arcade se suman puntos relativos al tiempo
803
+      } else {
804
+        new_score(additional_score);
805
+      }
806
+
807
+      // Analiza si hay nuevos récords
808
+      highest_scores();
809
+
810
+      // Se fijan variables
811
+      game_opt.finished = true;
812
+      foo.data.finished = game_opt.finished;
813
+      foo.disable();
814
+
815
+      // Corre función cuando se completa el juego satisfactoriamente
816
+      exec_fn(game_opt.on_win);
817
+    }
589 818
 
590
-                // Corre función cuando se completa el juego satisfactoriamente
591
-                exec_fn(game_opt.on_new_word)
592
-            }
593
-        }
819
+    // Ya no es necesario y puede ser conflictivo
820
+    localStorage.removeItem('incomplete_word');
821
+
822
+    game_opt.win = true;
823
+  }
824
+
825
+  // Cuando se pierde el juego
826
+  function lose () {
827
+    foo.disable();
828
+
829
+    // Corre función cuando se completa el juego insatisfactoriamente
830
+    exec_fn(game_opt.on_lose);
831
+
832
+    foo.data.active   = false;
833
+    foo.data.finished = true;
834
+  }
835
+
836
+  return {
837
+    // Permite acceso a datos del juego
838
+    data : {
839
+      type            : null,
840
+      active          : null,
841
+      finished        : null,
842
+      time            : null,
843
+      score           : null,
844
+      past_score      : null,
845
+      line            : null,
846
+      line_correct    : null,
847
+      lines_correct   : null,
848
+      lines_incorrect : null,
849
+      letter          : null,
850
+      new_words       : []
851
+    },
852
+
853
+    // Crea el juego
854
+    create : function (opt) {
855
+      opt.type                    =
856
+        opt.type                  || null;
857
+      opt.url                     =
858
+        opt.url                   || null;
859
+      opt.lang                    =
860
+        opt.lang                  || 'es';
861
+      opt.size                    =
862
+        opt.size                  || [7, 13];
863
+      opt.margin                  =
864
+        opt.margin                || [2, 1, 1, 1];
865
+      opt.border                  =
866
+        opt.border                || [0,0];
867
+      opt.score_position          =
868
+        opt.score_position        || 2;
869
+      opt.timer_position          =
870
+        opt.timer_position        || 0;
871
+      opt.top                     =
872
+        opt.top                   || 10;
873
+      opt.score_label             =
874
+        opt.score_label           || 'Puntos';
875
+      opt.timer_label             =
876