· 8 years ago · May 03, 2018, 04:04 PM
1<!DOCTYPE html>
2<!-- saved from url=(0029)chrome-error://chromewebdata/ -->
3<html dir="ltr" lang="pt" i18n-processed=""><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
4
5 <meta name="viewport" content="width=device-width, initial-scale=1.0,
6 maximum-scale=1.0, user-scalable=no">
7 <title>coinhive.com</title>
8 <style>/* Copyright 2017 The Chromium Authors. All rights reserved.
9 * Use of this source code is governed by a BSD-style license that can be
10 * found in the LICENSE file. */
11
12a {
13 color: rgb(88, 88, 88);
14}
15
16body {
17 background-color: rgb(247, 247, 247);
18 color: rgb(100, 100, 100);
19 word-wrap: break-word;
20}
21
22#details-button {
23 background: inherit;
24 border: 0;
25 float: none;
26 margin: 0;
27 padding: 10px 0;
28 text-transform: uppercase;
29}
30
31.hidden {
32 display: none;
33}
34
35html {
36 -webkit-text-size-adjust: 100%;
37 font-size: 125%;
38}
39
40.icon {
41 background-repeat: no-repeat;
42 background-size: 100%;
43}</style>
44 <style>/* Copyright 2014 The Chromium Authors. All rights reserved.
45 Use of this source code is governed by a BSD-style license that can be
46 found in the LICENSE file. */
47
48button {
49 border: 0;
50 border-radius: 2px;
51 box-sizing: border-box;
52 color: #fff;
53 cursor: pointer;
54 float: right;
55 font-size: .875em;
56 margin: 0;
57 padding: 10px 24px;
58 transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1);
59 user-select: none;
60}
61
62[dir='rtl'] button {
63 float: left;
64}
65
66.bad-clock button,
67.captive-portal button,
68.main-frame-blocked button,
69.neterror button,
70.offline button,
71.pdf button,
72.ssl button {
73 background: rgb(66, 133, 244);
74}
75
76button:active {
77 background: rgb(50, 102, 213);
78 outline: 0;
79}
80
81button:hover {
82 box-shadow: 0 1px 3px rgba(0, 0, 0, .50);
83}
84
85#debugging {
86 display: inline;
87 overflow: auto;
88}
89
90.debugging-content {
91 line-height: 1em;
92 margin-bottom: 0;
93 margin-top: 1em;
94}
95
96.debugging-content-fixed-width {
97 display: block;
98 font-family: monospace;
99 font-size: 1.2em;
100 margin-top: 0.5em;
101}
102
103.debugging-title {
104 font-weight: bold;
105}
106
107#details {
108 color: #696969;
109 margin: 0 0 50px;
110}
111
112#details p:not(:first-of-type) {
113 margin-top: 20px;
114}
115
116#details-button:hover {
117 box-shadow: inherit;
118 text-decoration: underline;
119}
120
121.error-code {
122 color: #646464;
123 font-size: .86667em;
124 text-transform: uppercase;
125}
126
127#error-debugging-info {
128 font-size: 0.8em;
129}
130
131h1 {
132 color: #333;
133 font-size: 1.6em;
134 font-weight: normal;
135 line-height: 1.25em;
136 margin-bottom: 16px;
137}
138
139h2 {
140 font-size: 1.2em;
141 font-weight: normal;
142}
143
144.icon {
145 height: 72px;
146 margin: 0 0 40px;
147 width: 72px;
148}
149
150input[type=checkbox] {
151 opacity: 0;
152}
153
154input[type=checkbox]:focus ~ .checkbox {
155 outline: -webkit-focus-ring-color auto 5px;
156}
157
158.interstitial-wrapper {
159 box-sizing: border-box;
160 font-size: 1em;
161 line-height: 1.6em;
162 margin: 14vh auto 0;
163 max-width: 600px;
164 width: 100%;
165}
166
167#main-message > p {
168 display: inline;
169}
170
171#extended-reporting-opt-in {
172 font-size: .875em;
173 margin-top: 39px;
174}
175
176#extended-reporting-opt-in label {
177 position: relative;
178 display: flex;
179 align-items: flex-start;
180}
181
182.nav-wrapper {
183 margin-top: 51px;
184}
185
186.nav-wrapper::after {
187 clear: both;
188 content: '';
189 display: table;
190 width: 100%;
191}
192
193.small-link {
194 color: #696969;
195 font-size: .875em;
196}
197
198.checkboxes {
199 flex: 0 0 24px;
200}
201
202.checkbox {
203 background: transparent;
204 border: 1px solid white;
205 border-radius: 2px;
206 display: block;
207 height: 14px;
208 left: 0;
209 position: absolute;
210 right: 0;
211 top: 3px;
212 width: 14px;
213}
214
215.checkbox::before {
216 background: transparent;
217 border: 2px solid white;
218 border-right-width: 0;
219 border-top-width: 0;
220 content: '';
221 height: 4px;
222 left: 2px;
223 opacity: 0;
224 position: absolute;
225 top: 3px;
226 transform: rotate(-45deg);
227 width: 9px;
228}
229
230input[type=checkbox]:checked ~ .checkbox::before {
231 opacity: 1;
232}
233
234@media (max-width: 700px) {
235 .interstitial-wrapper {
236 padding: 0 10%;
237 }
238
239 #error-debugging-info {
240 overflow: auto;
241 }
242}
243
244@media (max-height: 600px) {
245 .error-code {
246 margin-top: 10px;
247 }
248}
249
250@media (max-width: 420px) {
251 button,
252 [dir='rtl'] button,
253 .small-link {
254 float: none;
255 font-size: .825em;
256 font-weight: 400;
257 margin: 0;
258 text-transform: uppercase;
259 width: 100%;
260 }
261
262 #details {
263 margin: 20px 0 20px 0;
264 }
265
266 #details p:not(:first-of-type) {
267 margin-top: 10px;
268 }
269
270 #details-button {
271 display: block;
272 margin-top: 20px;
273 text-align: center;
274 width: 100%;
275 }
276
277 .interstitial-wrapper {
278 padding: 0 5%;
279 }
280
281 #extended-reporting-opt-in {
282 margin-top: 24px;
283 }
284
285 .nav-wrapper {
286 margin-top: 30px;
287 }
288}
289
290/**
291 * Mobile specific styling.
292 * Navigation buttons are anchored to the bottom of the screen.
293 * Details message replaces the top content in its own scrollable area.
294 */
295
296@media (max-width: 420px) {
297 #details-button {
298 border: 0;
299 margin: 8px 0 0;
300 }
301
302 .secondary-button {
303 -webkit-margin-end: 0;
304 margin-top: 16px;
305 }
306}
307
308/* Fixed nav. */
309@media (min-width: 240px) and (max-width: 420px) and
310 (min-height: 401px),
311 (min-width: 421px) and (min-height: 240px) and
312 (max-height: 560px) {
313 body .nav-wrapper {
314 background: #f7f7f7;
315 bottom: 0;
316 box-shadow: 0 -22px 40px rgb(247, 247, 247);
317 left: 0;
318 margin: 0 auto;
319 max-width: 736px;
320 padding-left: 24px;
321 padding-right: 24px;
322 position: fixed;
323 right: 0;
324 width: 100%;
325 z-index: 2;
326 }
327
328 .interstitial-wrapper {
329 max-width: 736px;
330 }
331
332 #details,
333 #main-content {
334 padding-bottom: 40px;
335 }
336
337 #details {
338 padding-top: 5.5vh;
339 }
340
341 #details-button:hover {
342 box-shadow: none;
343 }
344}
345
346@media (max-width: 420px) and (orientation: portrait),
347 (max-height: 560px) {
348 body {
349 margin: 0 auto;
350 }
351
352 button,
353 [dir='rtl'] button,
354 button.small-link {
355 font-family: Roboto-Regular,Helvetica;
356 font-size: .933em;
357 font-weight: 600;
358 margin: 6px 0;
359 text-transform: uppercase;
360 transform: translatez(0);
361 }
362
363 .nav-wrapper {
364 box-sizing: border-box;
365 padding-bottom: 8px;
366 width: 100%;
367 }
368
369 .error-code {
370 margin-top: 0;
371 }
372
373 #details {
374 box-sizing: border-box;
375 height: auto;
376 margin: 0;
377 opacity: 1;
378 transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);
379 }
380
381 #details.hidden,
382 #main-content.hidden {
383 display: block;
384 height: 0;
385 opacity: 0;
386 overflow: hidden;
387 padding-bottom: 0;
388 transition: none;
389 }
390
391 #details-button {
392 padding-bottom: 16px;
393 padding-top: 16px;
394 }
395
396 h1 {
397 font-size: 1.5em;
398 margin-bottom: 8px;
399 }
400
401 .icon {
402 margin-bottom: 5.69vh;
403 }
404
405 .interstitial-wrapper {
406 box-sizing: border-box;
407 margin: 7vh auto 12px;
408 padding: 0 24px;
409 position: relative;
410 }
411
412 .interstitial-wrapper p {
413 font-size: .95em;
414 line-height: 1.61em;
415 margin-top: 8px;
416 }
417
418 #main-content {
419 margin: 0;
420 transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1);
421 }
422
423 .small-link {
424 border: 0;
425 }
426
427 .suggested-left > #control-buttons,
428 .suggested-right > #control-buttons {
429 float: none;
430 margin: 0;
431 }
432}
433
434@media (min-width: 421px) and (min-height: 500px) and (max-height: 560px) {
435 .interstitial-wrapper {
436 margin-top: 10vh;
437 }
438}
439
440@media (min-height: 400px) and (orientation:portrait) {
441 .interstitial-wrapper {
442 margin-bottom: 145px;
443 }
444}
445
446@media (min-height: 299px) {
447 .nav-wrapper {
448 padding-bottom: 16px;
449 }
450}
451
452@media (min-height: 500px) and (max-height: 650px) and (max-width: 414px) and
453 (orientation: portrait) {
454 .interstitial-wrapper {
455 margin-top: 7vh;
456 }
457}
458
459@media (min-height: 650px) and (max-width: 414px) and (orientation: portrait) {
460 .interstitial-wrapper {
461 margin-top: 10vh;
462 }
463}
464
465/* Small mobile screens. No fixed nav. */
466@media (max-height: 400px) and (orientation: portrait),
467 (max-height: 239px) and (orientation: landscape),
468 (max-width: 419px) and (max-height: 399px) {
469 .interstitial-wrapper {
470 display: flex;
471 flex-direction: column;
472 margin-bottom: 0;
473 }
474
475 #details {
476 flex: 1 1 auto;
477 order: 0;
478 }
479
480 #main-content {
481 flex: 1 1 auto;
482 order: 0;
483 }
484
485 .nav-wrapper {
486 flex: 0 1 auto;
487 margin-top: 8px;
488 order: 1;
489 padding-left: 0;
490 padding-right: 0;
491 position: relative;
492 width: 100%;
493 }
494}
495
496@media (max-width: 239px) and (orientation: portrait) {
497 .nav-wrapper {
498 padding-left: 0;
499 padding-right: 0;
500 }
501}
502</style>
503 <style>/* Copyright 2013 The Chromium Authors. All rights reserved.
504 * Use of this source code is governed by a BSD-style license that can be
505 * found in the LICENSE file. */
506
507/* Don't use the main frame div when the error is in a subframe. */
508html[subframe] #main-frame-error {
509 display: none;
510}
511
512/* Don't use the subframe error div when the error is in a main frame. */
513html:not([subframe]) #sub-frame-error {
514 display: none;
515}
516
517#diagnose-button {
518 -webkit-margin-start: 0;
519 float: none;
520 margin-bottom: 10px;
521 margin-top: 20px;
522}
523
524h1 {
525 margin-top: 0;
526 word-wrap: break-word;
527}
528
529h1 span {
530 font-weight: 500;
531}
532
533h2 {
534 color: #666;
535 font-size: 1.2em;
536 font-weight: normal;
537 margin: 10px 0;
538}
539
540a {
541 color: rgb(17, 85, 204);
542 text-decoration: none;
543}
544
545.icon {
546 -webkit-user-select: none;
547 display: inline-block;
548}
549
550.icon-generic {
551 /**
552 * Can't access chrome://theme/IDR_ERROR_NETWORK_GENERIC from an untrusted
553 * renderer process, so embed the resource manually.
554 */
555 content: -webkit-image-set(
556 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAABlBMVEUAAABTU1OoaSf/AAAAAXRSTlMAQObYZgAAAENJREFUeF7tzbEJACEQRNGBLeAasBCza2lLEGx0CxFGG9hBMDDxRy/72O9FMnIFapGylsu1fgoBdkXfUHLrQgdfrlJN1BdYBjQQm3UAAAAASUVORK5CYII=) 1x,
557 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAACQAQMAAADdiHD7AAAABlBMVEUAAABTU1OoaSf/AAAAAXRSTlMAQObYZgAAAFJJREFUeF7t0cENgDAMQ9FwYgxG6WjpaIzCCAxQxVggFuDiCvlLOeRdHR9yzjncHVoq3npu+wQUrUuJHylSTmBaespJyJQoObUeyxDQb3bEm5Au81c0pSCD8HYAAAAASUVORK5CYII=) 2x);
558}
559
560.icon-offline {
561 content: -webkit-image-set(
562 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAABlBMVEUAAABTU1OoaSf/AAAAAXRSTlMAQObYZgAAAGxJREFUeF7tyMEJwkAQRuFf5ipMKxYQiJ3Z2nSwrWwBA0+DQZcdxEOueaePp9+dQZFB7GpUcURSVU66yVNFj6LFICatThZB6r/ko/pbRpUgilY0Cbw5sNmb9txGXUKyuH7eV25x39DtJXUNPQGJtWFV+BT/QAAAAABJRU5ErkJggg==) 1x,
563 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAACQBAMAAAAVaP+LAAAAGFBMVEUAAABTU1NNTU1TU1NPT09SUlJSUlJTU1O8B7DEAAAAB3RSTlMAoArVKvVgBuEdKgAAAJ1JREFUeF7t1TEOwyAMQNG0Q6/UE+RMXD9d/tC6womIFSL9P+MnAYOXeTIzMzMzMzMzaz8J9Ri6HoITmuHXhISE8nEh9yxDh55aCEUoTGbbQwjqHwIkRAEiIaG0+0AA9VBMaE89Rogeoww936MQrWdBr4GN/z0IAdQ6nQ/FIpRXDwHcA+JIJcQowQAlFUA0MfQpXLlVQfkzR4igS6ENjknm/wiaGhsAAAAASUVORK5CYII=) 2x);
564 position: relative;
565}
566
567.icon-disabled {
568 content: -webkit-image-set(
569 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABICAMAAAAZF4G5AAAABlBMVEVMaXFTU1OXUj8tAAAAAXRSTlMAQObYZgAAASZJREFUeAHd11Fq7jAMRGGf/W/6PoWB67YMqv5DybwG/CFjRuR8JBw3+ByiRjgV9W/TJ31P0tBfC6+cj1haUFXKHmVJo5wP98WwQ0ZCbfUc6LQ6VuUBz31ikADkLMkDrfUC4rR6QGW+gF6rx7NaHWCj1Y/W6lf4L7utvgBSt3rBFSS/XBMPUILcJINHCBWYUfpWn4NBi1ZfudIc3rf6/NGEvEA+AsYTJozmXemjXeLZAov+mnkN2HfzXpMSVQDnGw++57qNJ4D1xitA2sJ+VAWMygSEaYf2mYPTjZfk2K8wmP7HLIH5Mg4/pP+PEcDzUvDMvYbs/2NWwPO5vBdMZE4EE5UTQLiBFDaUlTDPBRoJ9HdAYIkIo06og3BNXtCzy7zA1aXk5x+tJARq63eAygAAAABJRU5ErkJggg==) 1x,
570 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOAAAACQAQMAAAArwfVjAAAABlBMVEVMaXFTU1OXUj8tAAAAAXRSTlMAQObYZgAAAYdJREFUeF7F1EFqwzAUBNARAmVj0FZe5QoBH6BX+dn4GlY2PYNzGx/A0CvkCIJuvIraKJKbgBvzf2g62weDGD7CYggpfFReis4J0ey9EGFIiEQQojFSlA9kSIiqd0KkFjKsewgRbStEN19mxUPTtmW9HQ/h6tyqNQ8NlSMZdzyE6qkoE0trVYGFm0n1WYeBhduzwbwBC7voS+vIxfeMjeaiLxsMMtQNwMPtuew+DjzcTHk8YMfDknEcIUOtf2lVfgVH3K4Xv5PRYAXRVMtItIJ3rfaCIVn9DsTH2NxisAVRex2Hh3hX+/mRUR08bAwPEYsI51ZxWH4Q0SpicQRXeyEaIug48FEdegARfMz/tADVsRciwTAxW308ehmC2gLraC+YCbV3QoTZexa+zegAEW5PhhgYfmbvJgcRqngGByOSXdFJcLk2JeDPEN0kxe1JhIt5FiFA+w+ItMELsUyPF2IaJ4aILqb4FbxPwhImwj6JauKgDUCYaxmYIsd4KXdMjIC9ItB5Bn4BNRwsG0XM2nwAAAAASUVORK5CYII=) 2x);
571 width: 112px;
572}
573
574.error-code {
575 display: block;
576 font-size: .8em;
577}
578
579#content-top {
580 margin: 20px;
581}
582
583#help-box-inner {
584 background-color: #f9f9f9;
585 border-top: 1px solid #EEE;
586 color: #444;
587 padding: 20px;
588 text-align: start;
589}
590
591.hidden {
592 display: none;
593}
594
595#suggestion {
596 margin-top: 15px;
597}
598
599#suggestions-list p {
600 -webkit-margin-after: 0;
601}
602
603#suggestions-list ul {
604 margin-top: 0;
605}
606
607.single-suggestion {
608 list-style-type: none;
609 padding-left: 0;
610}
611
612#short-suggestion {
613 margin-top: 5px;
614}
615
616#sub-frame-error-details {
617
618 color: #8F8F8F;
619
620 /* Not done on mobile for performance reasons. */
621 text-shadow: 0 1px 0 rgba(255,255,255,0.3);
622
623}
624
625[jscontent=hostName],
626[jscontent=failedUrl] {
627 overflow-wrap: break-word;
628}
629
630#search-container {
631 /* Prevents a space between controls. */
632 display: flex;
633 margin-top: 20px;
634}
635
636#search-box {
637 border: 1px solid #cdcdcd;
638 flex-grow: 1;
639 font-size: 1em;
640 height: 26px;
641 margin-right: 0;
642 padding: 1px 9px;
643}
644
645#search-box:focus {
646 border: 1px solid rgb(93, 154, 255);
647 outline: none;
648}
649
650#search-button {
651 border: none;
652 border-bottom-left-radius: 0;
653 border-top-left-radius: 0;
654 box-shadow: none;
655 display: flex;
656 height: 30px;
657 margin: 0;
658 padding: 0;
659 width: 60px;
660}
661
662#search-image {
663 content:
664 -webkit-image-set(
665 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAPCAQAAAB+HTb/AAAArElEQVR4Xn3NsUoCUBzG0XvB3U0chR4geo5qihpt6gkCx0bXFsMERWj2KWqIanAvmlUUoQapwU6g4l8H5bd9Z/iSPS0hu/RqZqrncBuzLl7U3Rn4cSpQFTeroejJl1Lgs7f4ceDPdeBMXYp86gaONYJkY83AnqHiGk9wHnjk16PKgo5N9BUCkzPf5j6M0PfuVg5MymoetFwoaKAlB26WdXAvJ7u5mezitqtkT//7Sv/u96CaLQAAAABJRU5ErkJggg==) 1x,
666 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAeCAQAAACVzLYUAAABYElEQVR4Xr3VMUuVURzH8XO98jgkGikENkRD0KRGDUVDQy0h2SiC4IuIiktL4AvQt1CDBJUJwo1KXXS6cWdHw7tcjWwoC5Hrx+UZgnNO5CXiO/75jD/+QZf9MzjskVU7DrU1zRv9G9ir5hsA4Nii83+GA9ZI1nI1D6tWAE1TRlQMuuuFDthzMQefgo4nKr+f3dIGDdUUHPYD1ISoMQdgJgUfgqaKEOcxWE/BVTArJBvwC0cGY7gNLgiZNsD1GP4EPVn4EtyLYRuczcJ34HYMP4E7GdajDS7FcB48z8AJ8FmI4TjouBkzZ2yBuRQMlsButIZ+dfDVUBqOaIHvavpLVHXfFmAqv45r9gEHNr3y3hcAfLSgSMPgiiZR+6Z9AMuKNAwqpjUcA2h55pxgAfBWkYRlQ254YMJloaxPHbCkiGCymL5RlLA7GnRDXyuC7uhicLoKdRyaDE5Pl00K//93nABqPgBDK8sfWgAAAABJRU5ErkJggg==) 2x);
667 margin: auto;
668}
669
670.secondary-button {
671 -webkit-margin-end: 16px;
672 background: #d9d9d9;
673 color: #696969;
674}
675
676.snackbar {
677 background: #323232;
678 border-radius: 2px;
679 bottom: 24px;
680 box-sizing: border-box;
681 color: #fff;
682 font-size: .87em;
683 left: 24px;
684 max-width: 568px;
685 min-width: 288px;
686 opacity: 0;
687 padding: 16px 24px 12px;
688 position: fixed;
689 transform: translateY(90px);
690 will-change: opacity, transform;
691 z-index: 999;
692}
693
694.snackbar-show {
695 -webkit-animation:
696 show-snackbar .25s cubic-bezier(0.0, 0.0, 0.2, 1) forwards,
697 hide-snackbar .25s cubic-bezier(0.4, 0.0, 1, 1) forwards 5s;
698}
699
700@-webkit-keyframes show-snackbar {
701 100% {
702 opacity: 1;
703 transform: translateY(0);
704 }
705}
706
707@-webkit-keyframes hide-snackbar {
708 0% {
709 opacity: 1;
710 transform: translateY(0);
711 }
712 100% {
713 opacity: 0;
714 transform: translateY(90px);
715 }
716}
717
718.suggestions {
719 margin-top: 18px;
720}
721
722.suggestion-header {
723 font-weight: bold;
724 margin-bottom: 4px;
725}
726
727.suggestion-body {
728 color: #777;
729}
730
731/* Increase line height at higher resolutions. */
732@media (min-width: 641px) and (min-height: 641px) {
733 #help-box-inner {
734 line-height: 18px;
735 }
736}
737
738/* Decrease padding at low sizes. */
739@media (max-width: 640px), (max-height: 640px) {
740 h1 {
741 margin: 0 0 15px;
742 }
743 #content-top {
744 margin: 15px;
745 }
746 #help-box-inner {
747 padding: 20px;
748 }
749 .suggestions {
750 margin-top: 10px;
751 }
752 .suggestion-header {
753 margin-bottom: 0;
754 }
755}
756
757/* Don't allow overflow when in a subframe. */
758html[subframe] body {
759 overflow: hidden;
760}
761
762#sub-frame-error {
763 -webkit-align-items: center;
764 background-color: #DDD;
765 display: -webkit-flex;
766 -webkit-flex-flow: column;
767 height: 100%;
768 -webkit-justify-content: center;
769 left: 0;
770 position: absolute;
771 text-align: center;
772 top: 0;
773 transition: background-color .2s ease-in-out;
774 width: 100%;
775}
776
777#sub-frame-error:hover {
778 background-color: #EEE;
779}
780
781#sub-frame-error .icon-generic {
782 margin: 0 0 16px;
783}
784
785#sub-frame-error-details {
786 margin: 0 10px;
787 text-align: center;
788 visibility: hidden;
789}
790
791/* Show details only when hovering. */
792#sub-frame-error:hover #sub-frame-error-details {
793 visibility: visible;
794}
795
796/* If the iframe is too small, always hide the error code. */
797/* TODO(mmenke): See if overflow: no-display works better, once supported. */
798@media (max-width: 200px), (max-height: 95px) {
799 #sub-frame-error-details {
800 display: none;
801 }
802}
803
804/* Adjust icon for small embedded frames in apps. */
805@media (max-height: 100px) {
806 #sub-frame-error .icon-generic {
807 height: auto;
808 margin: 0;
809 padding-top: 0;
810 width: 25px;
811 }
812}
813
814/* details-button is special; it's a <button> element that looks like a link. */
815#details-button {
816 box-shadow: none;
817 min-width: 0;
818}
819
820/* Styles for platform dependent separation of controls and details button. */
821.suggested-left > #control-buttons,
822.suggested-left #stale-load-button,
823.suggested-right > #details-button {
824 float: left;
825}
826
827.suggested-right > #control-buttons,
828.suggested-right #stale-load-button,
829.suggested-left > #details-button {
830 float: right;
831}
832
833.suggested-left .secondary-button {
834 -webkit-margin-end: 0px;
835 -webkit-margin-start: 16px;
836}
837
838#details-button.singular {
839 float: none;
840}
841
842/* download-button shows both icon and text. */
843#download-button {
844 box-shadow: none;
845 position: relative;
846}
847
848#download-button:before {
849 -webkit-margin-end: 4px;
850 background: -webkit-image-set(
851 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAO0lEQVQ4y2NgGArgPxIY1YChsOE/LtBAmpYG0mxpIOSDBpKUo2lpIDZxNJCkHKqlYZAla3RAHQ1DFgAARRroHyLNTwwAAAAASUVORK5CYII=) 1x,
852 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAAZElEQVRYw+3Ruw3AMAwDUY3OzZUmRRD4E9iim9wNwAdbEURHyk4AAAAATiCVK8lLyPsKeT9K3lsownnunfkPxO78hKiYHxBV8x2icr5BVM+/CMf8g3DN34Rzns6ViwHUAUQ/6wIAd5Km7l6c8AAAAABJRU5ErkJggg==) 2x)
853 no-repeat;
854 content: '';
855 display: inline-block;
856 width: 24px;
857 height: 24px;
858 vertical-align: middle;
859}
860
861#download-button:disabled {
862 background: rgb(180, 206, 249);
863 color: rgb(255, 255, 255);
864}
865
866#buttons::after {
867 clear: both;
868 content: '';
869 display: block;
870 width: 100%;
871}
872
873/* Offline page */
874.offline {
875 transition: -webkit-filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1),
876 background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1);
877 will-change: -webkit-filter, background-color;
878}
879
880.offline #main-message > p {
881 display: none;
882}
883
884.offline.inverted {
885 -webkit-filter: invert(100%);
886 background-color: #000;
887}
888
889.offline .interstitial-wrapper {
890 color: #2b2b2b;
891 font-size: 1em;
892 line-height: 1.55;
893 margin: 0 auto;
894 max-width: 600px;
895 padding-top: 100px;
896 width: 100%;
897}
898
899.offline .runner-container {
900 direction: ltr;
901 height: 150px;
902 max-width: 600px;
903 overflow: hidden;
904 position: absolute;
905 top: 35px;
906 width: 44px;
907}
908
909.offline .runner-canvas {
910 height: 150px;
911 max-width: 600px;
912 opacity: 1;
913 overflow: hidden;
914 position: absolute;
915 top: 0;
916 z-index: 2;
917}
918
919.offline .controller {
920 background: rgba(247,247,247, .1);
921 height: 100vh;
922 left: 0;
923 position: absolute;
924 top: 0;
925 width: 100vw;
926 z-index: 1;
927}
928
929#offline-resources {
930 display: none;
931}
932
933@media (max-width: 420px) {
934 .suggested-left > #control-buttons,
935 .suggested-right > #control-buttons {
936 float: none;
937 }
938
939 .snackbar {
940 left: 0;
941 bottom: 0;
942 width: 100%;
943 border-radius: 0;
944 }
945}
946
947@media (max-height: 350px) {
948 h1 {
949 margin: 0 0 15px;
950 }
951
952 .icon-offline {
953 margin: 0 0 10px;
954 }
955
956 .interstitial-wrapper {
957 margin-top: 5%;
958 }
959
960 .nav-wrapper {
961 margin-top: 30px;
962 }
963}
964
965@media (min-width: 420px) and (max-width: 736px) and
966 (min-height: 240px) and (max-height: 420px) and
967 (orientation:landscape) {
968 .interstitial-wrapper {
969 margin-bottom: 100px;
970 }
971}
972
973@media (max-width: 360px) and (max-height: 480px) {
974 .offline .interstitial-wrapper {
975 padding-top: 60px;
976 }
977
978 .offline .runner-container {
979 top: 8px;
980 }
981}
982
983@media (min-height: 240px) and (orientation: landscape) {
984 .offline .interstitial-wrapper {
985 margin-bottom: 90px;
986 }
987
988 .icon-offline {
989 margin-bottom: 20px;
990 }
991}
992
993@media (max-height: 320px) and (orientation: landscape) {
994 .icon-offline {
995 margin-bottom: 0;
996 }
997
998 .offline .runner-container {
999 top: 10px;
1000 }
1001}
1002
1003@media (max-width: 240px) {
1004 button {
1005 padding-left: 12px;
1006 padding-right: 12px;
1007 }
1008
1009 .interstitial-wrapper {
1010 overflow: inherit;
1011 padding: 0 8px;
1012 }
1013}
1014
1015@media (max-width: 120px) {
1016 button {
1017 width: auto;
1018 }
1019}
1020
1021.arcade-mode,
1022.arcade-mode .runner-container,
1023.arcade-mode .runner-canvas {
1024 image-rendering: pixelated;
1025 max-width: 100%;
1026 overflow: hidden;
1027}
1028
1029.arcade-mode #buttons,
1030.arcade-mode #main-content {
1031 opacity: 0;
1032 overflow: hidden;
1033}
1034
1035.arcade-mode .interstitial-wrapper {
1036 height: 100vh;
1037 max-width: 100%;
1038 overflow: hidden;
1039}
1040
1041.arcade-mode .runner-container {
1042 left: 0;
1043 margin: auto;
1044 right: 0;
1045 transform-origin: top center;
1046 transition: transform 250ms cubic-bezier(0.4, 0.0, 1, 1) .4s;
1047 z-index: 2;
1048}
1049</style>
1050 <script>// Copyright 2017 The Chromium Authors. All rights reserved.
1051// Use of this source code is governed by a BSD-style license that can be
1052// found in the LICENSE file.
1053
1054// This is the shared code for security interstitials. It is used for both SSL
1055// interstitials and Safe Browsing interstitials.
1056
1057// Should match security_interstitials::SecurityInterstitialCommand
1058/** @enum| {string} */
1059var SecurityInterstitialCommandId = {
1060 CMD_DONT_PROCEED: 0,
1061 CMD_PROCEED: 1,
1062 // Ways for user to get more information
1063 CMD_SHOW_MORE_SECTION: 2,
1064 CMD_OPEN_HELP_CENTER: 3,
1065 CMD_OPEN_DIAGNOSTIC: 4,
1066 // Primary button actions
1067 CMD_RELOAD: 5,
1068 CMD_OPEN_DATE_SETTINGS: 6,
1069 CMD_OPEN_LOGIN: 7,
1070 // Safe Browsing Extended Reporting
1071 CMD_DO_REPORT: 8,
1072 CMD_DONT_REPORT: 9,
1073 CMD_OPEN_REPORTING_PRIVACY: 10,
1074 CMD_OPEN_WHITEPAPER: 11,
1075 // Report a phishing error.
1076 CMD_REPORT_PHISHING_ERROR: 12
1077};
1078
1079var HIDDEN_CLASS = 'hidden';
1080
1081/**
1082 * A convenience method for sending commands to the parent page.
1083 * @param {string} cmd The command to send.
1084 */
1085function sendCommand(cmd) {
1086 if (window.certificateErrorPageController) {
1087 switch (cmd) {
1088 case SecurityInterstitialCommandId.CMD_DONT_PROCEED:
1089 certificateErrorPageController.dontProceed();
1090 break;
1091 case SecurityInterstitialCommandId.CMD_PROCEED:
1092 certificateErrorPageController.proceed();
1093 break;
1094 case SecurityInterstitialCommandId.CMD_SHOW_MORE_SECTION:
1095 certificateErrorPageController.showMoreSection();
1096 break;
1097 case SecurityInterstitialCommandId.CMD_OPEN_HELP_CENTER:
1098 certificateErrorPageController.openHelpCenter();
1099 break;
1100 case SecurityInterstitialCommandId.CMD_OPEN_DIAGNOSTIC:
1101 certificateErrorPageController.openDiagnostic();
1102 break;
1103 case SecurityInterstitialCommandId.CMD_RELOAD:
1104 certificateErrorPageController.reload();
1105 break;
1106 case SecurityInterstitialCommandId.CMD_OPEN_DATE_SETTINGS:
1107 certificateErrorPageController.openDateSettings();
1108 break;
1109 case SecurityInterstitialCommandId.CMD_OPEN_LOGIN:
1110 certificateErrorPageController.openLogin();
1111 break;
1112 case SecurityInterstitialCommandId.CMD_DO_REPORT:
1113 certificateErrorPageController.doReport();
1114 break;
1115 case SecurityInterstitialCommandId.CMD_DONT_REPORT:
1116 certificateErrorPageController.dontReport();
1117 break;
1118 case SecurityInterstitialCommandId.CMD_OPEN_REPORTING_PRIVACY:
1119 certificateErrorPageController.openReportingPrivacy();
1120 break;
1121 case SecurityInterstitialCommandId.CMD_OPEN_WHITEPAPER:
1122 certificateErrorPageController.openWhitepaper();
1123 break;
1124 case SecurityInterstitialCommandId.CMD_REPORT_PHISHING_ERROR:
1125 certificateErrorPageController.reportPhishingError();
1126 break;
1127 }
1128 return;
1129 }
1130//
1131 window.domAutomationController.send(cmd);
1132//
1133//
1134}
1135
1136/**
1137 * Call this to stop clicks on <a href="#"> links from scrolling to the top of
1138 * the page (and possibly showing a # in the link).
1139 */
1140function preventDefaultOnPoundLinkClicks() {
1141 document.addEventListener('click', function(e) {
1142 var anchor = findAncestor(/** @type {Node} */ (e.target), function(el) {
1143 return el.tagName == 'A';
1144 });
1145 // Use getAttribute() to prevent URL normalization.
1146 if (anchor && anchor.getAttribute('href') == '#')
1147 e.preventDefault();
1148 });
1149}
1150</script>
1151 <script>// Copyright 2015 The Chromium Authors. All rights reserved.
1152// Use of this source code is governed by a BSD-style license that can be
1153// found in the LICENSE file.
1154
1155var mobileNav = false;
1156
1157/**
1158 * For small screen mobile the navigation buttons are moved
1159 * below the advanced text.
1160 */
1161function onResize() {
1162 var helpOuterBox = document.querySelector('#details');
1163 var mainContent = document.querySelector('#main-content');
1164 var mediaQuery = '(min-width: 240px) and (max-width: 420px) and ' +
1165 '(min-height: 401px), ' +
1166 '(max-height: 560px) and (min-height: 240px) and ' +
1167 '(min-width: 421px)';
1168
1169 var detailsHidden = helpOuterBox.classList.contains(HIDDEN_CLASS);
1170 var runnerContainer = document.querySelector('.runner-container');
1171
1172 // Check for change in nav status.
1173 if (mobileNav != window.matchMedia(mediaQuery).matches) {
1174 mobileNav = !mobileNav;
1175
1176 // Handle showing the top content / details sections according to state.
1177 if (mobileNav) {
1178 mainContent.classList.toggle(HIDDEN_CLASS, !detailsHidden);
1179 helpOuterBox.classList.toggle(HIDDEN_CLASS, detailsHidden);
1180 if (runnerContainer) {
1181 runnerContainer.classList.toggle(HIDDEN_CLASS, !detailsHidden);
1182 }
1183 } else if (!detailsHidden) {
1184 // Non mobile nav with visible details.
1185 mainContent.classList.remove(HIDDEN_CLASS);
1186 helpOuterBox.classList.remove(HIDDEN_CLASS);
1187 if (runnerContainer) {
1188 runnerContainer.classList.remove(HIDDEN_CLASS);
1189 }
1190 }
1191 }
1192}
1193
1194function setupMobileNav() {
1195 window.addEventListener('resize', onResize);
1196 onResize();
1197}
1198
1199document.addEventListener('DOMContentLoaded', setupMobileNav);
1200</script>
1201 <script>// Copyright 2013 The Chromium Authors. All rights reserved.
1202// Use of this source code is governed by a BSD-style license that can be
1203// found in the LICENSE file.
1204
1205function toggleHelpBox() {
1206 var helpBoxOuter = document.getElementById('details');
1207 helpBoxOuter.classList.toggle(HIDDEN_CLASS);
1208 var detailsButton = document.getElementById('details-button');
1209 if (helpBoxOuter.classList.contains(HIDDEN_CLASS))
1210 detailsButton.innerText = detailsButton.detailsText;
1211 else
1212 detailsButton.innerText = detailsButton.hideDetailsText;
1213
1214 // Details appears over the main content on small screens.
1215 if (mobileNav) {
1216 document.getElementById('main-content').classList.toggle(HIDDEN_CLASS);
1217 var runnerContainer = document.querySelector('.runner-container');
1218 if (runnerContainer) {
1219 runnerContainer.classList.toggle(HIDDEN_CLASS);
1220 }
1221 }
1222}
1223
1224function diagnoseErrors() {
1225//
1226 if (window.errorPageController)
1227 errorPageController.diagnoseErrorsButtonClick();
1228//
1229//
1230}
1231
1232// Subframes use a different layout but the same html file. This is to make it
1233// easier to support platforms that load the error page via different
1234// mechanisms (Currently just iOS).
1235if (window.top.location != window.location)
1236 document.documentElement.setAttribute('subframe', '');
1237
1238// Re-renders the error page using |strings| as the dictionary of values.
1239// Used by NetErrorTabHelper to update DNS error pages with probe results.
1240function updateForDnsProbe(strings) {
1241 var context = new JsEvalContext(strings);
1242 jstProcess(context, document.getElementById('t'));
1243}
1244
1245// Given the classList property of an element, adds an icon class to the list
1246// and removes the previously-
1247function updateIconClass(classList, newClass) {
1248 var oldClass;
1249
1250 if (classList.hasOwnProperty('last_icon_class')) {
1251 oldClass = classList['last_icon_class'];
1252 if (oldClass == newClass)
1253 return;
1254 }
1255
1256 classList.add(newClass);
1257 if (oldClass !== undefined)
1258 classList.remove(oldClass);
1259
1260 classList['last_icon_class'] = newClass;
1261
1262 if (newClass == 'icon-offline') {
1263 document.body.classList.add('offline');
1264 new Runner('.interstitial-wrapper');
1265 } else {
1266 document.body.classList.add('neterror');
1267 }
1268}
1269
1270// Does a search using |baseSearchUrl| and the text in the search box.
1271function search(baseSearchUrl) {
1272 var searchTextNode = document.getElementById('search-box');
1273 document.location = baseSearchUrl + searchTextNode.value;
1274 return false;
1275}
1276
1277// Use to track clicks on elements generated by the navigation correction
1278// service. If |trackingId| is negative, the element does not come from the
1279// correction service.
1280function trackClick(trackingId) {
1281 // This can't be done with XHRs because XHRs are cancelled on navigation
1282 // start, and because these are cross-site requests.
1283 if (trackingId >= 0 && errorPageController)
1284 errorPageController.trackClick(trackingId);
1285}
1286
1287// Called when an <a> tag generated by the navigation correction service is
1288// clicked. Separate function from trackClick so the resources don't have to
1289// be updated if new data is added to jstdata.
1290function linkClicked(jstdata) {
1291 trackClick(jstdata.trackingId);
1292}
1293
1294// Implements button clicks. This function is needed during the transition
1295// between implementing these in trunk chromium and implementing them in
1296// iOS.
1297function reloadButtonClick(url) {
1298 if (window.errorPageController) {
1299 errorPageController.reloadButtonClick();
1300 } else {
1301 location = url;
1302 }
1303}
1304
1305function showSavedCopyButtonClick() {
1306 if (window.errorPageController) {
1307 errorPageController.showSavedCopyButtonClick();
1308 }
1309}
1310
1311function downloadButtonClick() {
1312 if (window.errorPageController) {
1313 errorPageController.downloadButtonClick();
1314 var downloadButton = document.getElementById('download-button');
1315 downloadButton.disabled = true;
1316 downloadButton.textContent = downloadButton.disabledText;
1317 }
1318}
1319
1320function detailsButtonClick() {
1321 if (window.errorPageController)
1322 errorPageController.detailsButtonClick();
1323}
1324
1325/**
1326 * Replace the reload button with the Google cached copy suggestion.
1327 */
1328function setUpCachedButton(buttonStrings) {
1329 var reloadButton = document.getElementById('reload-button');
1330
1331 reloadButton.textContent = buttonStrings.msg;
1332 var url = buttonStrings.cacheUrl;
1333 var trackingId = buttonStrings.trackingId;
1334 reloadButton.onclick = function(e) {
1335 e.preventDefault();
1336 trackClick(trackingId);
1337 if (window.errorPageController) {
1338 errorPageController.trackCachedCopyButtonClick();
1339 }
1340 location = url;
1341 };
1342 reloadButton.style.display = '';
1343 document.getElementById('control-buttons').hidden = false;
1344}
1345
1346var primaryControlOnLeft = true;
1347//
1348
1349function onDocumentLoad() {
1350 var controlButtonDiv = document.getElementById('control-buttons');
1351 var reloadButton = document.getElementById('reload-button');
1352 var detailsButton = document.getElementById('details-button');
1353 var showSavedCopyButton = document.getElementById('show-saved-copy-button');
1354 var downloadButton = document.getElementById('download-button');
1355
1356 var reloadButtonVisible = loadTimeData.valueExists('reloadButton') &&
1357 loadTimeData.getValue('reloadButton').msg;
1358 var showSavedCopyButtonVisible =
1359 loadTimeData.valueExists('showSavedCopyButton') &&
1360 loadTimeData.getValue('showSavedCopyButton').msg;
1361 var downloadButtonVisible =
1362 loadTimeData.valueExists('downloadButton') &&
1363 loadTimeData.getValue('downloadButton').msg;
1364
1365 var primaryButton, secondaryButton;
1366 if (showSavedCopyButton.primary) {
1367 primaryButton = showSavedCopyButton;
1368 secondaryButton = reloadButton;
1369 } else {
1370 primaryButton = reloadButton;
1371 secondaryButton = showSavedCopyButton;
1372 }
1373
1374 // Sets up the proper button layout for the current platform.
1375 if (primaryControlOnLeft) {
1376 buttons.classList.add('suggested-left');
1377 controlButtonDiv.insertBefore(secondaryButton, primaryButton);
1378 } else {
1379 buttons.classList.add('suggested-right');
1380 controlButtonDiv.insertBefore(primaryButton, secondaryButton);
1381 }
1382
1383 // Check for Google cached copy suggestion.
1384 if (loadTimeData.valueExists('cacheButton')) {
1385 setUpCachedButton(loadTimeData.getValue('cacheButton'));
1386 }
1387
1388 if (reloadButton.style.display == 'none' &&
1389 showSavedCopyButton.style.display == 'none' &&
1390 downloadButton.style.display == 'none') {
1391 detailsButton.classList.add('singular');
1392 }
1393
1394 // Show control buttons.
1395 if (reloadButtonVisible || showSavedCopyButtonVisible ||
1396 downloadButtonVisible) {
1397 controlButtonDiv.hidden = false;
1398
1399 // Set the secondary button state in the cases of two call to actions.
1400 if ((reloadButtonVisible || downloadButtonVisible) &&
1401 showSavedCopyButtonVisible) {
1402 secondaryButton.classList.add('secondary-button');
1403 }
1404 }
1405}
1406
1407document.addEventListener('DOMContentLoaded', onDocumentLoad);
1408</script>
1409 <script>// Copyright (c) 2014 The Chromium Authors. All rights reserved.
1410// Use of this source code is governed by a BSD-style license that can be
1411// found in the LICENSE file.
1412(function() {
1413'use strict';
1414/**
1415 * T-Rex runner.
1416 * @param {string} outerContainerId Outer containing element id.
1417 * @param {Object} opt_config
1418 * @constructor
1419 * @export
1420 */
1421function Runner(outerContainerId, opt_config) {
1422 // Singleton
1423 if (Runner.instance_) {
1424 return Runner.instance_;
1425 }
1426 Runner.instance_ = this;
1427
1428 this.outerContainerEl = document.querySelector(outerContainerId);
1429 this.containerEl = null;
1430 this.snackbarEl = null;
1431
1432 this.config = opt_config || Runner.config;
1433 // Logical dimensions of the container.
1434 this.dimensions = Runner.defaultDimensions;
1435
1436 this.canvas = null;
1437 this.canvasCtx = null;
1438
1439 this.tRex = null;
1440
1441 this.distanceMeter = null;
1442 this.distanceRan = 0;
1443
1444 this.highestScore = 0;
1445
1446 this.time = 0;
1447 this.runningTime = 0;
1448 this.msPerFrame = 1000 / FPS;
1449 this.currentSpeed = this.config.SPEED;
1450
1451 this.obstacles = [];
1452
1453 this.activated = false; // Whether the easter egg has been activated.
1454 this.playing = false; // Whether the game is currently in play state.
1455 this.crashed = false;
1456 this.paused = false;
1457 this.inverted = false;
1458 this.invertTimer = 0;
1459 this.resizeTimerId_ = null;
1460
1461 this.playCount = 0;
1462
1463 // Sound FX.
1464 this.audioBuffer = null;
1465 this.soundFx = {};
1466
1467 // Global web audio context for playing sounds.
1468 this.audioContext = null;
1469
1470 // Images.
1471 this.images = {};
1472 this.imagesLoaded = 0;
1473
1474 if (this.isDisabled()) {
1475 this.setupDisabledRunner();
1476 } else {
1477 this.loadImages();
1478 }
1479}
1480window['Runner'] = Runner;
1481
1482
1483/**
1484 * Default game width.
1485 * @const
1486 */
1487var DEFAULT_WIDTH = 600;
1488
1489/**
1490 * Frames per second.
1491 * @const
1492 */
1493var FPS = 60;
1494
1495/** @const */
1496var IS_HIDPI = window.devicePixelRatio > 1;
1497
1498/** @const */
1499var IS_IOS = /iPad|iPhone|iPod/.test(window.navigator.platform);
1500
1501/** @const */
1502var IS_MOBILE = /Android/.test(window.navigator.userAgent) || IS_IOS;
1503
1504/** @const */
1505var ARCADE_MODE_URL = 'chrome://dino/';
1506
1507/**
1508 * Default game configuration.
1509 * @enum {number}
1510 */
1511Runner.config = {
1512 ACCELERATION: 0.001,
1513 BG_CLOUD_SPEED: 0.2,
1514 BOTTOM_PAD: 10,
1515 CLEAR_TIME: 3000,
1516 CLOUD_FREQUENCY: 0.5,
1517 GAMEOVER_CLEAR_TIME: 750,
1518 GAP_COEFFICIENT: 0.6,
1519 GRAVITY: 0.6,
1520 INITIAL_JUMP_VELOCITY: 12,
1521 INVERT_FADE_DURATION: 12000,
1522 INVERT_DISTANCE: 700,
1523 MAX_BLINK_COUNT: 3,
1524 MAX_CLOUDS: 6,
1525 MAX_OBSTACLE_LENGTH: 3,
1526 MAX_OBSTACLE_DUPLICATION: 2,
1527 MAX_SPEED: 13,
1528 MIN_JUMP_HEIGHT: 35,
1529 MOBILE_SPEED_COEFFICIENT: 1.2,
1530 RESOURCE_TEMPLATE_ID: 'audio-resources',
1531 SPEED: 6,
1532 SPEED_DROP_COEFFICIENT: 3,
1533 ARCADE_MODE_INITIAL_TOP_POSITION: 35,
1534 ARCADE_MODE_TOP_POSITION_PERCENT: 0.1
1535};
1536
1537
1538/**
1539 * Default dimensions.
1540 * @enum {string}
1541 */
1542Runner.defaultDimensions = {
1543 WIDTH: DEFAULT_WIDTH,
1544 HEIGHT: 150
1545};
1546
1547
1548/**
1549 * CSS class names.
1550 * @enum {string}
1551 */
1552Runner.classes = {
1553 ARCADE_MODE: 'arcade-mode',
1554 CANVAS: 'runner-canvas',
1555 CONTAINER: 'runner-container',
1556 CRASHED: 'crashed',
1557 ICON: 'icon-offline',
1558 INVERTED: 'inverted',
1559 SNACKBAR: 'snackbar',
1560 SNACKBAR_SHOW: 'snackbar-show',
1561 TOUCH_CONTROLLER: 'controller'
1562};
1563
1564
1565/**
1566 * Sprite definition layout of the spritesheet.
1567 * @enum {Object}
1568 */
1569Runner.spriteDefinition = {
1570 LDPI: {
1571 CACTUS_LARGE: {x: 332, y: 2},
1572 CACTUS_SMALL: {x: 228, y: 2},
1573 CLOUD: {x: 86, y: 2},
1574 HORIZON: {x: 2, y: 54},
1575 MOON: {x: 484, y: 2},
1576 PTERODACTYL: {x: 134, y: 2},
1577 RESTART: {x: 2, y: 2},
1578 TEXT_SPRITE: {x: 655, y: 2},
1579 TREX: {x: 848, y: 2},
1580 STAR: {x: 645, y: 2}
1581 },
1582 HDPI: {
1583 CACTUS_LARGE: {x: 652, y: 2},
1584 CACTUS_SMALL: {x: 446, y: 2},
1585 CLOUD: {x: 166, y: 2},
1586 HORIZON: {x: 2, y: 104},
1587 MOON: {x: 954, y: 2},
1588 PTERODACTYL: {x: 260, y: 2},
1589 RESTART: {x: 2, y: 2},
1590 TEXT_SPRITE: {x: 1294, y: 2},
1591 TREX: {x: 1678, y: 2},
1592 STAR: {x: 1276, y: 2}
1593 }
1594};
1595
1596
1597/**
1598 * Sound FX. Reference to the ID of the audio tag on interstitial page.
1599 * @enum {string}
1600 */
1601Runner.sounds = {
1602 BUTTON_PRESS: 'offline-sound-press',
1603 HIT: 'offline-sound-hit',
1604 SCORE: 'offline-sound-reached'
1605};
1606
1607
1608/**
1609 * Key code mapping.
1610 * @enum {Object}
1611 */
1612Runner.keycodes = {
1613 JUMP: {'38': 1, '32': 1}, // Up, spacebar
1614 DUCK: {'40': 1}, // Down
1615 RESTART: {'13': 1} // Enter
1616};
1617
1618
1619/**
1620 * Runner event names.
1621 * @enum {string}
1622 */
1623Runner.events = {
1624 ANIM_END: 'webkitAnimationEnd',
1625 CLICK: 'click',
1626 KEYDOWN: 'keydown',
1627 KEYUP: 'keyup',
1628 POINTERDOWN: 'pointerdown',
1629 POINTERUP: 'pointerup',
1630 RESIZE: 'resize',
1631 TOUCHEND: 'touchend',
1632 TOUCHSTART: 'touchstart',
1633 VISIBILITY: 'visibilitychange',
1634 BLUR: 'blur',
1635 FOCUS: 'focus',
1636 LOAD: 'load'
1637};
1638
1639Runner.prototype = {
1640 /**
1641 * Whether the easter egg has been disabled. CrOS enterprise enrolled devices.
1642 * @return {boolean}
1643 */
1644 isDisabled: function() {
1645 return loadTimeData && loadTimeData.valueExists('disabledEasterEgg');
1646 },
1647
1648 /**
1649 * For disabled instances, set up a snackbar with the disabled message.
1650 */
1651 setupDisabledRunner: function() {
1652 this.containerEl = document.createElement('div');
1653 this.containerEl.className = Runner.classes.SNACKBAR;
1654 this.containerEl.textContent = loadTimeData.getValue('disabledEasterEgg');
1655 this.outerContainerEl.appendChild(this.containerEl);
1656
1657 // Show notification when the activation key is pressed.
1658 document.addEventListener(Runner.events.KEYDOWN, function(e) {
1659 if (Runner.keycodes.JUMP[e.keyCode]) {
1660 this.containerEl.classList.add(Runner.classes.SNACKBAR_SHOW);
1661 document.querySelector('.icon').classList.add('icon-disabled');
1662 }
1663 }.bind(this));
1664 },
1665
1666 /**
1667 * Setting individual settings for debugging.
1668 * @param {string} setting
1669 * @param {*} value
1670 */
1671 updateConfigSetting: function(setting, value) {
1672 if (setting in this.config && value != undefined) {
1673 this.config[setting] = value;
1674
1675 switch (setting) {
1676 case 'GRAVITY':
1677 case 'MIN_JUMP_HEIGHT':
1678 case 'SPEED_DROP_COEFFICIENT':
1679 this.tRex.config[setting] = value;
1680 break;
1681 case 'INITIAL_JUMP_VELOCITY':
1682 this.tRex.setJumpVelocity(value);
1683 break;
1684 case 'SPEED':
1685 this.setSpeed(value);
1686 break;
1687 }
1688 }
1689 },
1690
1691 /**
1692 * Cache the appropriate image sprite from the page and get the sprite sheet
1693 * definition.
1694 */
1695 loadImages: function() {
1696 if (IS_HIDPI) {
1697 Runner.imageSprite = document.getElementById('offline-resources-2x');
1698 this.spriteDef = Runner.spriteDefinition.HDPI;
1699 } else {
1700 Runner.imageSprite = document.getElementById('offline-resources-1x');
1701 this.spriteDef = Runner.spriteDefinition.LDPI;
1702 }
1703
1704 if (Runner.imageSprite.complete) {
1705 this.init();
1706 } else {
1707 // If the images are not yet loaded, add a listener.
1708 Runner.imageSprite.addEventListener(Runner.events.LOAD,
1709 this.init.bind(this));
1710 }
1711 },
1712
1713 /**
1714 * Load and decode base 64 encoded sounds.
1715 */
1716 loadSounds: function() {
1717 if (!IS_IOS) {
1718 this.audioContext = new AudioContext();
1719
1720 var resourceTemplate =
1721 document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content;
1722
1723 for (var sound in Runner.sounds) {
1724 var soundSrc =
1725 resourceTemplate.getElementById(Runner.sounds[sound]).src;
1726 soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1);
1727 var buffer = decodeBase64ToArrayBuffer(soundSrc);
1728
1729 // Async, so no guarantee of order in array.
1730 this.audioContext.decodeAudioData(buffer, function(index, audioData) {
1731 this.soundFx[index] = audioData;
1732 }.bind(this, sound));
1733 }
1734 }
1735 },
1736
1737 /**
1738 * Sets the game speed. Adjust the speed accordingly if on a smaller screen.
1739 * @param {number} opt_speed
1740 */
1741 setSpeed: function(opt_speed) {
1742 var speed = opt_speed || this.currentSpeed;
1743
1744 // Reduce the speed on smaller mobile screens.
1745 if (this.dimensions.WIDTH < DEFAULT_WIDTH) {
1746 var mobileSpeed = speed * this.dimensions.WIDTH / DEFAULT_WIDTH *
1747 this.config.MOBILE_SPEED_COEFFICIENT;
1748 this.currentSpeed = mobileSpeed > speed ? speed : mobileSpeed;
1749 } else if (opt_speed) {
1750 this.currentSpeed = opt_speed;
1751 }
1752 },
1753
1754 /**
1755 * Game initialiser.
1756 */
1757 init: function() {
1758 // Hide the static icon.
1759 document.querySelector('.' + Runner.classes.ICON).style.visibility =
1760 'hidden';
1761
1762 this.adjustDimensions();
1763 this.setSpeed();
1764
1765 this.containerEl = document.createElement('div');
1766 this.containerEl.className = Runner.classes.CONTAINER;
1767
1768 // Player canvas container.
1769 this.canvas = createCanvas(this.containerEl, this.dimensions.WIDTH,
1770 this.dimensions.HEIGHT, Runner.classes.PLAYER);
1771
1772 this.canvasCtx = this.canvas.getContext('2d');
1773 this.canvasCtx.fillStyle = '#f7f7f7';
1774 this.canvasCtx.fill();
1775 Runner.updateCanvasScaling(this.canvas);
1776
1777 // Horizon contains clouds, obstacles and the ground.
1778 this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions,
1779 this.config.GAP_COEFFICIENT);
1780
1781 // Distance meter
1782 this.distanceMeter = new DistanceMeter(this.canvas,
1783 this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH);
1784
1785 // Draw t-rex
1786 this.tRex = new Trex(this.canvas, this.spriteDef.TREX);
1787
1788 this.outerContainerEl.appendChild(this.containerEl);
1789
1790 this.startListening();
1791 this.update();
1792
1793 window.addEventListener(Runner.events.RESIZE,
1794 this.debounceResize.bind(this));
1795 },
1796
1797 /**
1798 * Create the touch controller. A div that covers whole screen.
1799 */
1800 createTouchController: function() {
1801 this.touchController = document.createElement('div');
1802 this.touchController.className = Runner.classes.TOUCH_CONTROLLER;
1803 this.touchController.addEventListener(Runner.events.TOUCHSTART, this);
1804 this.touchController.addEventListener(Runner.events.TOUCHEND, this);
1805 },
1806
1807 /**
1808 * Debounce the resize event.
1809 */
1810 debounceResize: function() {
1811 if (!this.resizeTimerId_) {
1812 this.resizeTimerId_ =
1813 setInterval(this.adjustDimensions.bind(this), 250);
1814 }
1815 },
1816
1817 /**
1818 * Adjust game space dimensions on resize.
1819 */
1820 adjustDimensions: function() {
1821 clearInterval(this.resizeTimerId_);
1822 this.resizeTimerId_ = null;
1823
1824 var boxStyles = window.getComputedStyle(this.outerContainerEl);
1825 var padding = Number(boxStyles.paddingLeft.substr(0,
1826 boxStyles.paddingLeft.length - 2));
1827
1828 this.dimensions.WIDTH = this.outerContainerEl.offsetWidth - padding * 2;
1829 if (this.isArcadeMode()) {
1830 this.dimensions.WIDTH = Math.min(DEFAULT_WIDTH, this.dimensions.WIDTH);
1831 if (this.activated) {
1832 this.setArcadeModeContainerScale();
1833 }
1834 }
1835
1836 // Redraw the elements back onto the canvas.
1837 if (this.canvas) {
1838 this.canvas.width = this.dimensions.WIDTH;
1839 this.canvas.height = this.dimensions.HEIGHT;
1840
1841 Runner.updateCanvasScaling(this.canvas);
1842
1843 this.distanceMeter.calcXPos(this.dimensions.WIDTH);
1844 this.clearCanvas();
1845 this.horizon.update(0, 0, true);
1846 this.tRex.update(0);
1847
1848 // Outer container and distance meter.
1849 if (this.playing || this.crashed || this.paused) {
1850 this.containerEl.style.width = this.dimensions.WIDTH + 'px';
1851 this.containerEl.style.height = this.dimensions.HEIGHT + 'px';
1852 this.distanceMeter.update(0, Math.ceil(this.distanceRan));
1853 this.stop();
1854 } else {
1855 this.tRex.draw(0, 0);
1856 }
1857
1858 // Game over panel.
1859 if (this.crashed && this.gameOverPanel) {
1860 this.gameOverPanel.updateDimensions(this.dimensions.WIDTH);
1861 this.gameOverPanel.draw();
1862 }
1863 }
1864 },
1865
1866 /**
1867 * Play the game intro.
1868 * Canvas container width expands out to the full width.
1869 */
1870 playIntro: function() {
1871 if (!this.activated && !this.crashed) {
1872 this.playingIntro = true;
1873 this.tRex.playingIntro = true;
1874
1875 // CSS animation definition.
1876 var keyframes = '@-webkit-keyframes intro { ' +
1877 'from { width:' + Trex.config.WIDTH + 'px }' +
1878 'to { width: ' + this.dimensions.WIDTH + 'px }' +
1879 '}';
1880 document.styleSheets[0].insertRule(keyframes, 0);
1881
1882 this.containerEl.addEventListener(Runner.events.ANIM_END,
1883 this.startGame.bind(this));
1884
1885 this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both';
1886 this.containerEl.style.width = this.dimensions.WIDTH + 'px';
1887
1888 if (this.touchController) {
1889 this.outerContainerEl.appendChild(this.touchController);
1890 }
1891 this.playing = true;
1892 this.activated = true;
1893 } else if (this.crashed) {
1894 this.restart();
1895 }
1896 },
1897
1898
1899 /**
1900 * Update the game status to started.
1901 */
1902 startGame: function() {
1903 if (this.isArcadeMode()) {
1904 this.setArcadeMode();
1905 }
1906 this.runningTime = 0;
1907 this.playingIntro = false;
1908 this.tRex.playingIntro = false;
1909 this.containerEl.style.webkitAnimation = '';
1910 this.playCount++;
1911
1912 // Handle tabbing off the page. Pause the current game.
1913 document.addEventListener(Runner.events.VISIBILITY,
1914 this.onVisibilityChange.bind(this));
1915
1916 window.addEventListener(Runner.events.BLUR,
1917 this.onVisibilityChange.bind(this));
1918
1919 window.addEventListener(Runner.events.FOCUS,
1920 this.onVisibilityChange.bind(this));
1921 },
1922
1923 clearCanvas: function() {
1924 this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH,
1925 this.dimensions.HEIGHT);
1926 },
1927
1928 /**
1929 * Update the game frame and schedules the next one.
1930 */
1931 update: function() {
1932 this.updatePending = false;
1933
1934 var now = getTimeStamp();
1935 var deltaTime = now - (this.time || now);
1936 this.time = now;
1937
1938 if (this.playing) {
1939 this.clearCanvas();
1940
1941 if (this.tRex.jumping) {
1942 this.tRex.updateJump(deltaTime);
1943 }
1944
1945 this.runningTime += deltaTime;
1946 var hasObstacles = this.runningTime > this.config.CLEAR_TIME;
1947
1948 // First jump triggers the intro.
1949 if (this.tRex.jumpCount == 1 && !this.playingIntro) {
1950 this.playIntro();
1951 }
1952
1953 // The horizon doesn't move until the intro is over.
1954 if (this.playingIntro) {
1955 this.horizon.update(0, this.currentSpeed, hasObstacles);
1956 } else {
1957 deltaTime = !this.activated ? 0 : deltaTime;
1958 this.horizon.update(deltaTime, this.currentSpeed, hasObstacles,
1959 this.inverted);
1960 }
1961
1962 // Check for collisions.
1963 var collision = hasObstacles &&
1964 checkForCollision(this.horizon.obstacles[0], this.tRex);
1965
1966 if (!collision) {
1967 this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame;
1968
1969 if (this.currentSpeed < this.config.MAX_SPEED) {
1970 this.currentSpeed += this.config.ACCELERATION;
1971 }
1972 } else {
1973 this.gameOver();
1974 }
1975
1976 var playAchievementSound = this.distanceMeter.update(deltaTime,
1977 Math.ceil(this.distanceRan));
1978
1979 if (playAchievementSound) {
1980 this.playSound(this.soundFx.SCORE);
1981 }
1982
1983 // Night mode.
1984 if (this.invertTimer > this.config.INVERT_FADE_DURATION) {
1985 this.invertTimer = 0;
1986 this.invertTrigger = false;
1987 this.invert();
1988 } else if (this.invertTimer) {
1989 this.invertTimer += deltaTime;
1990 } else {
1991 var actualDistance =
1992 this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan));
1993
1994 if (actualDistance > 0) {
1995 this.invertTrigger = !(actualDistance %
1996 this.config.INVERT_DISTANCE);
1997
1998 if (this.invertTrigger && this.invertTimer === 0) {
1999 this.invertTimer += deltaTime;
2000 this.invert();
2001 }
2002 }
2003 }
2004 }
2005
2006 if (this.playing || (!this.activated &&
2007 this.tRex.blinkCount < Runner.config.MAX_BLINK_COUNT)) {
2008 this.tRex.update(deltaTime);
2009 this.scheduleNextUpdate();
2010 }
2011 },
2012
2013 /**
2014 * Event handler.
2015 */
2016 handleEvent: function(e) {
2017 return (function(evtType, events) {
2018 switch (evtType) {
2019 case events.KEYDOWN:
2020 case events.TOUCHSTART:
2021 case events.POINTERDOWN:
2022 this.onKeyDown(e);
2023 break;
2024 case events.KEYUP:
2025 case events.TOUCHEND:
2026 case events.POINTERUP:
2027 this.onKeyUp(e);
2028 break;
2029 }
2030 }.bind(this))(e.type, Runner.events);
2031 },
2032
2033 /**
2034 * Bind relevant key / mouse / touch listeners.
2035 */
2036 startListening: function() {
2037 // Keys.
2038 document.addEventListener(Runner.events.KEYDOWN, this);
2039 document.addEventListener(Runner.events.KEYUP, this);
2040
2041 // Touch / pointer.
2042 this.containerEl.addEventListener(Runner.events.TOUCHSTART, this);
2043 document.addEventListener(Runner.events.POINTERDOWN, this);
2044 document.addEventListener(Runner.events.POINTERUP, this);
2045 },
2046
2047 /**
2048 * Remove all listeners.
2049 */
2050 stopListening: function() {
2051 document.removeEventListener(Runner.events.KEYDOWN, this);
2052 document.removeEventListener(Runner.events.KEYUP, this);
2053
2054 if (this.touchController) {
2055 this.touchController.removeEventListener(Runner.events.TOUCHSTART, this);
2056 this.touchController.removeEventListener(Runner.events.TOUCHEND, this);
2057 }
2058
2059 this.containerEl.removeEventListener(Runner.events.TOUCHSTART, this);
2060 document.removeEventListener(Runner.events.POINTERDOWN, this);
2061 document.removeEventListener(Runner.events.POINTERUP, this);
2062 },
2063
2064 /**
2065 * Process keydown.
2066 * @param {Event} e
2067 */
2068 onKeyDown: function(e) {
2069 // Prevent native page scrolling whilst tapping on mobile.
2070 if (IS_MOBILE && this.playing) {
2071 e.preventDefault();
2072 }
2073
2074 if (!this.crashed && !this.paused) {
2075 if (Runner.keycodes.JUMP[e.keyCode] ||
2076 e.type == Runner.events.TOUCHSTART) {
2077 e.preventDefault();
2078 // Starting the game for the first time.
2079 if (!this.playing) {
2080 // Started by touch so create a touch controller.
2081 if (!this.touchController && e.type == Runner.events.TOUCHSTART) {
2082 this.createTouchController();
2083 }
2084 this.loadSounds();
2085 this.playing = true;
2086 this.update();
2087 if (window.errorPageController) {
2088 errorPageController.trackEasterEgg();
2089 }
2090 }
2091 // Start jump.
2092 if (!this.tRex.jumping && !this.tRex.ducking) {
2093 this.playSound(this.soundFx.BUTTON_PRESS);
2094 this.tRex.startJump(this.currentSpeed);
2095 }
2096 } else if (this.playing && Runner.keycodes.DUCK[e.keyCode]) {
2097 e.preventDefault();
2098 if (this.tRex.jumping) {
2099 // Speed drop, activated only when jump key is not pressed.
2100 this.tRex.setSpeedDrop();
2101 } else if (!this.tRex.jumping && !this.tRex.ducking) {
2102 // Duck.
2103 this.tRex.setDuck(true);
2104 }
2105 }
2106 } else if (this.crashed && e.type == Runner.events.TOUCHSTART &&
2107 e.currentTarget == this.containerEl) {
2108 this.restart();
2109 }
2110 },
2111
2112
2113 /**
2114 * Process key up.
2115 * @param {Event} e
2116 */
2117 onKeyUp: function(e) {
2118 var keyCode = String(e.keyCode);
2119 var isjumpKey = Runner.keycodes.JUMP[keyCode] ||
2120 e.type == Runner.events.TOUCHEND ||
2121 e.type == Runner.events.POINTERUP;
2122
2123 if (this.isRunning() && isjumpKey) {
2124 this.tRex.endJump();
2125 } else if (Runner.keycodes.DUCK[keyCode]) {
2126 this.tRex.speedDrop = false;
2127 this.tRex.setDuck(false);
2128 } else if (this.crashed) {
2129 // Check that enough time has elapsed before allowing jump key to restart.
2130 var deltaTime = getTimeStamp() - this.time;
2131
2132 if (Runner.keycodes.RESTART[keyCode] || this.isLeftClickOnCanvas(e) ||
2133 (deltaTime >= this.config.GAMEOVER_CLEAR_TIME &&
2134 Runner.keycodes.JUMP[keyCode])) {
2135 this.restart();
2136 }
2137 } else if (this.paused && isjumpKey) {
2138 // Reset the jump state
2139 this.tRex.reset();
2140 this.play();
2141 }
2142 },
2143
2144 /**
2145 * Returns whether the event was a left click on canvas.
2146 * On Windows right click is registered as a click.
2147 * @param {Event} e
2148 * @return {boolean}
2149 */
2150 isLeftClickOnCanvas: function(e) {
2151 return e.button != null && e.button < 2 &&
2152 e.type == Runner.events.POINTERUP && e.target == this.canvas;
2153 },
2154
2155 /**
2156 * RequestAnimationFrame wrapper.
2157 */
2158 scheduleNextUpdate: function() {
2159 if (!this.updatePending) {
2160 this.updatePending = true;
2161 this.raqId = requestAnimationFrame(this.update.bind(this));
2162 }
2163 },
2164
2165 /**
2166 * Whether the game is running.
2167 * @return {boolean}
2168 */
2169 isRunning: function() {
2170 return !!this.raqId;
2171 },
2172
2173 /**
2174 * Game over state.
2175 */
2176 gameOver: function() {
2177 this.playSound(this.soundFx.HIT);
2178 vibrate(200);
2179
2180 this.stop();
2181 this.crashed = true;
2182 this.distanceMeter.achievement = false;
2183
2184 this.tRex.update(100, Trex.status.CRASHED);
2185
2186 // Game over panel.
2187 if (!this.gameOverPanel) {
2188 this.gameOverPanel = new GameOverPanel(this.canvas,
2189 this.spriteDef.TEXT_SPRITE, this.spriteDef.RESTART,
2190 this.dimensions);
2191 } else {
2192 this.gameOverPanel.draw();
2193 }
2194
2195 // Update the high score.
2196 if (this.distanceRan > this.highestScore) {
2197 this.highestScore = Math.ceil(this.distanceRan);
2198 this.distanceMeter.setHighScore(this.highestScore);
2199 }
2200
2201 // Reset the time clock.
2202 this.time = getTimeStamp();
2203 },
2204
2205 stop: function() {
2206 this.playing = false;
2207 this.paused = true;
2208 cancelAnimationFrame(this.raqId);
2209 this.raqId = 0;
2210 },
2211
2212 play: function() {
2213 if (!this.crashed) {
2214 this.playing = true;
2215 this.paused = false;
2216 this.tRex.update(0, Trex.status.RUNNING);
2217 this.time = getTimeStamp();
2218 this.update();
2219 }
2220 },
2221
2222 restart: function() {
2223 if (!this.raqId) {
2224 this.playCount++;
2225 this.runningTime = 0;
2226 this.playing = true;
2227 this.paused = false;
2228 this.crashed = false;
2229 this.distanceRan = 0;
2230 this.setSpeed(this.config.SPEED);
2231 this.time = getTimeStamp();
2232 this.containerEl.classList.remove(Runner.classes.CRASHED);
2233 this.clearCanvas();
2234 this.distanceMeter.reset(this.highestScore);
2235 this.horizon.reset();
2236 this.tRex.reset();
2237 this.playSound(this.soundFx.BUTTON_PRESS);
2238 this.invert(true);
2239 this.update();
2240 }
2241 },
2242
2243 /**
2244 * Whether the game should go into arcade mode.
2245 * @return {boolean}
2246 */
2247 isArcadeMode: function() {
2248 return document.title == ARCADE_MODE_URL;
2249 },
2250
2251 /**
2252 * Hides offline messaging for a fullscreen game only experience.
2253 */
2254 setArcadeMode: function() {
2255 document.body.classList.add(Runner.classes.ARCADE_MODE);
2256 this.setArcadeModeContainerScale();
2257 },
2258
2259 /**
2260 * Sets the scaling for arcade mode.
2261 */
2262 setArcadeModeContainerScale: function() {
2263 var windowHeight = window.innerHeight;
2264 var scaleHeight = windowHeight / this.dimensions.HEIGHT;
2265 var scaleWidth = window.innerWidth / this.dimensions.WIDTH;
2266 var scale = Math.max(1, Math.min(scaleHeight, scaleWidth));
2267 var scaledCanvasHeight = this.dimensions.HEIGHT * scale;
2268 // Positions the game container at 10% of the available vertical window
2269 // height minus the game container height.
2270 var translateY = Math.ceil(Math.max(0, (windowHeight - scaledCanvasHeight -
2271 Runner.config.ARCADE_MODE_INITIAL_TOP_POSITION) *
2272 Runner.config.ARCADE_MODE_TOP_POSITION_PERCENT)) *
2273 window.devicePixelRatio;
2274 this.containerEl.style.transform = 'scale(' + scale + ') translateY(' +
2275 translateY + 'px)';
2276 },
2277
2278 /**
2279 * Pause the game if the tab is not in focus.
2280 */
2281 onVisibilityChange: function(e) {
2282 if (document.hidden || document.webkitHidden || e.type == 'blur' ||
2283 document.visibilityState != 'visible') {
2284 this.stop();
2285 } else if (!this.crashed) {
2286 this.tRex.reset();
2287 this.play();
2288 }
2289 },
2290
2291 /**
2292 * Play a sound.
2293 * @param {SoundBuffer} soundBuffer
2294 */
2295 playSound: function(soundBuffer) {
2296 if (soundBuffer) {
2297 var sourceNode = this.audioContext.createBufferSource();
2298 sourceNode.buffer = soundBuffer;
2299 sourceNode.connect(this.audioContext.destination);
2300 sourceNode.start(0);
2301 }
2302 },
2303
2304 /**
2305 * Inverts the current page / canvas colors.
2306 * @param {boolean} Whether to reset colors.
2307 */
2308 invert: function(reset) {
2309 if (reset) {
2310 document.body.classList.toggle(Runner.classes.INVERTED, false);
2311 this.invertTimer = 0;
2312 this.inverted = false;
2313 } else {
2314 this.inverted = document.body.classList.toggle(Runner.classes.INVERTED,
2315 this.invertTrigger);
2316 }
2317 }
2318};
2319
2320
2321/**
2322 * Updates the canvas size taking into
2323 * account the backing store pixel ratio and
2324 * the device pixel ratio.
2325 *
2326 * See article by Paul Lewis:
2327 * http://www.html5rocks.com/en/tutorials/canvas/hidpi/
2328 *
2329 * @param {HTMLCanvasElement} canvas
2330 * @param {number} opt_width
2331 * @param {number} opt_height
2332 * @return {boolean} Whether the canvas was scaled.
2333 */
2334Runner.updateCanvasScaling = function(canvas, opt_width, opt_height) {
2335 var context = canvas.getContext('2d');
2336
2337 // Query the various pixel ratios
2338 var devicePixelRatio = Math.floor(window.devicePixelRatio) || 1;
2339 var backingStoreRatio = Math.floor(context.webkitBackingStorePixelRatio) || 1;
2340 var ratio = devicePixelRatio / backingStoreRatio;
2341
2342 // Upscale the canvas if the two ratios don't match
2343 if (devicePixelRatio !== backingStoreRatio) {
2344 var oldWidth = opt_width || canvas.width;
2345 var oldHeight = opt_height || canvas.height;
2346
2347 canvas.width = oldWidth * ratio;
2348 canvas.height = oldHeight * ratio;
2349
2350 canvas.style.width = oldWidth + 'px';
2351 canvas.style.height = oldHeight + 'px';
2352
2353 // Scale the context to counter the fact that we've manually scaled
2354 // our canvas element.
2355 context.scale(ratio, ratio);
2356 return true;
2357 } else if (devicePixelRatio == 1) {
2358 // Reset the canvas width / height. Fixes scaling bug when the page is
2359 // zoomed and the devicePixelRatio changes accordingly.
2360 canvas.style.width = canvas.width + 'px';
2361 canvas.style.height = canvas.height + 'px';
2362 }
2363 return false;
2364};
2365
2366
2367/**
2368 * Get random number.
2369 * @param {number} min
2370 * @param {number} max
2371 * @param {number}
2372 */
2373function getRandomNum(min, max) {
2374 return Math.floor(Math.random() * (max - min + 1)) + min;
2375}
2376
2377
2378/**
2379 * Vibrate on mobile devices.
2380 * @param {number} duration Duration of the vibration in milliseconds.
2381 */
2382function vibrate(duration) {
2383 if (IS_MOBILE && window.navigator.vibrate) {
2384 window.navigator.vibrate(duration);
2385 }
2386}
2387
2388
2389/**
2390 * Create canvas element.
2391 * @param {HTMLElement} container Element to append canvas to.
2392 * @param {number} width
2393 * @param {number} height
2394 * @param {string} opt_classname
2395 * @return {HTMLCanvasElement}
2396 */
2397function createCanvas(container, width, height, opt_classname) {
2398 var canvas = document.createElement('canvas');
2399 canvas.className = opt_classname ? Runner.classes.CANVAS + ' ' +
2400 opt_classname : Runner.classes.CANVAS;
2401 canvas.width = width;
2402 canvas.height = height;
2403 container.appendChild(canvas);
2404
2405 return canvas;
2406}
2407
2408
2409/**
2410 * Decodes the base 64 audio to ArrayBuffer used by Web Audio.
2411 * @param {string} base64String
2412 */
2413function decodeBase64ToArrayBuffer(base64String) {
2414 var len = (base64String.length / 4) * 3;
2415 var str = atob(base64String);
2416 var arrayBuffer = new ArrayBuffer(len);
2417 var bytes = new Uint8Array(arrayBuffer);
2418
2419 for (var i = 0; i < len; i++) {
2420 bytes[i] = str.charCodeAt(i);
2421 }
2422 return bytes.buffer;
2423}
2424
2425
2426/**
2427 * Return the current timestamp.
2428 * @return {number}
2429 */
2430function getTimeStamp() {
2431 return IS_IOS ? new Date().getTime() : performance.now();
2432}
2433
2434
2435//******************************************************************************
2436
2437
2438/**
2439 * Game over panel.
2440 * @param {!HTMLCanvasElement} canvas
2441 * @param {Object} textImgPos
2442 * @param {Object} restartImgPos
2443 * @param {!Object} dimensions Canvas dimensions.
2444 * @constructor
2445 */
2446function GameOverPanel(canvas, textImgPos, restartImgPos, dimensions) {
2447 this.canvas = canvas;
2448 this.canvasCtx = canvas.getContext('2d');
2449 this.canvasDimensions = dimensions;
2450 this.textImgPos = textImgPos;
2451 this.restartImgPos = restartImgPos;
2452 this.draw();
2453};
2454
2455
2456/**
2457 * Dimensions used in the panel.
2458 * @enum {number}
2459 */
2460GameOverPanel.dimensions = {
2461 TEXT_X: 0,
2462 TEXT_Y: 13,
2463 TEXT_WIDTH: 191,
2464 TEXT_HEIGHT: 11,
2465 RESTART_WIDTH: 36,
2466 RESTART_HEIGHT: 32
2467};
2468
2469
2470GameOverPanel.prototype = {
2471 /**
2472 * Update the panel dimensions.
2473 * @param {number} width New canvas width.
2474 * @param {number} opt_height Optional new canvas height.
2475 */
2476 updateDimensions: function(width, opt_height) {
2477 this.canvasDimensions.WIDTH = width;
2478 if (opt_height) {
2479 this.canvasDimensions.HEIGHT = opt_height;
2480 }
2481 },
2482
2483 /**
2484 * Draw the panel.
2485 */
2486 draw: function() {
2487 var dimensions = GameOverPanel.dimensions;
2488
2489 var centerX = this.canvasDimensions.WIDTH / 2;
2490
2491 // Game over text.
2492 var textSourceX = dimensions.TEXT_X;
2493 var textSourceY = dimensions.TEXT_Y;
2494 var textSourceWidth = dimensions.TEXT_WIDTH;
2495 var textSourceHeight = dimensions.TEXT_HEIGHT;
2496
2497 var textTargetX = Math.round(centerX - (dimensions.TEXT_WIDTH / 2));
2498 var textTargetY = Math.round((this.canvasDimensions.HEIGHT - 25) / 3);
2499 var textTargetWidth = dimensions.TEXT_WIDTH;
2500 var textTargetHeight = dimensions.TEXT_HEIGHT;
2501
2502 var restartSourceWidth = dimensions.RESTART_WIDTH;
2503 var restartSourceHeight = dimensions.RESTART_HEIGHT;
2504 var restartTargetX = centerX - (dimensions.RESTART_WIDTH / 2);
2505 var restartTargetY = this.canvasDimensions.HEIGHT / 2;
2506
2507 if (IS_HIDPI) {
2508 textSourceY *= 2;
2509 textSourceX *= 2;
2510 textSourceWidth *= 2;
2511 textSourceHeight *= 2;
2512 restartSourceWidth *= 2;
2513 restartSourceHeight *= 2;
2514 }
2515
2516 textSourceX += this.textImgPos.x;
2517 textSourceY += this.textImgPos.y;
2518
2519 // Game over text from sprite.
2520 this.canvasCtx.drawImage(Runner.imageSprite,
2521 textSourceX, textSourceY, textSourceWidth, textSourceHeight,
2522 textTargetX, textTargetY, textTargetWidth, textTargetHeight);
2523
2524 // Restart button.
2525 this.canvasCtx.drawImage(Runner.imageSprite,
2526 this.restartImgPos.x, this.restartImgPos.y,
2527 restartSourceWidth, restartSourceHeight,
2528 restartTargetX, restartTargetY, dimensions.RESTART_WIDTH,
2529 dimensions.RESTART_HEIGHT);
2530 }
2531};
2532
2533
2534//******************************************************************************
2535
2536/**
2537 * Check for a collision.
2538 * @param {!Obstacle} obstacle
2539 * @param {!Trex} tRex T-rex object.
2540 * @param {HTMLCanvasContext} opt_canvasCtx Optional canvas context for drawing
2541 * collision boxes.
2542 * @return {Array<CollisionBox>}
2543 */
2544function checkForCollision(obstacle, tRex, opt_canvasCtx) {
2545 var obstacleBoxXPos = Runner.defaultDimensions.WIDTH + obstacle.xPos;
2546
2547 // Adjustments are made to the bounding box as there is a 1 pixel white
2548 // border around the t-rex and obstacles.
2549 var tRexBox = new CollisionBox(
2550 tRex.xPos + 1,
2551 tRex.yPos + 1,
2552 tRex.config.WIDTH - 2,
2553 tRex.config.HEIGHT - 2);
2554
2555 var obstacleBox = new CollisionBox(
2556 obstacle.xPos + 1,
2557 obstacle.yPos + 1,
2558 obstacle.typeConfig.width * obstacle.size - 2,
2559 obstacle.typeConfig.height - 2);
2560
2561 // Debug outer box
2562 if (opt_canvasCtx) {
2563 drawCollisionBoxes(opt_canvasCtx, tRexBox, obstacleBox);
2564 }
2565
2566 // Simple outer bounds check.
2567 if (boxCompare(tRexBox, obstacleBox)) {
2568 var collisionBoxes = obstacle.collisionBoxes;
2569 var tRexCollisionBoxes = tRex.ducking ?
2570 Trex.collisionBoxes.DUCKING : Trex.collisionBoxes.RUNNING;
2571
2572 // Detailed axis aligned box check.
2573 for (var t = 0; t < tRexCollisionBoxes.length; t++) {
2574 for (var i = 0; i < collisionBoxes.length; i++) {
2575 // Adjust the box to actual positions.
2576 var adjTrexBox =
2577 createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox);
2578 var adjObstacleBox =
2579 createAdjustedCollisionBox(collisionBoxes[i], obstacleBox);
2580 var crashed = boxCompare(adjTrexBox, adjObstacleBox);
2581
2582 // Draw boxes for debug.
2583 if (opt_canvasCtx) {
2584 drawCollisionBoxes(opt_canvasCtx, adjTrexBox, adjObstacleBox);
2585 }
2586
2587 if (crashed) {
2588 return [adjTrexBox, adjObstacleBox];
2589 }
2590 }
2591 }
2592 }
2593 return false;
2594};
2595
2596
2597/**
2598 * Adjust the collision box.
2599 * @param {!CollisionBox} box The original box.
2600 * @param {!CollisionBox} adjustment Adjustment box.
2601 * @return {CollisionBox} The adjusted collision box object.
2602 */
2603function createAdjustedCollisionBox(box, adjustment) {
2604 return new CollisionBox(
2605 box.x + adjustment.x,
2606 box.y + adjustment.y,
2607 box.width,
2608 box.height);
2609};
2610
2611
2612/**
2613 * Draw the collision boxes for debug.
2614 */
2615function drawCollisionBoxes(canvasCtx, tRexBox, obstacleBox) {
2616 canvasCtx.save();
2617 canvasCtx.strokeStyle = '#f00';
2618 canvasCtx.strokeRect(tRexBox.x, tRexBox.y, tRexBox.width, tRexBox.height);
2619
2620 canvasCtx.strokeStyle = '#0f0';
2621 canvasCtx.strokeRect(obstacleBox.x, obstacleBox.y,
2622 obstacleBox.width, obstacleBox.height);
2623 canvasCtx.restore();
2624};
2625
2626
2627/**
2628 * Compare two collision boxes for a collision.
2629 * @param {CollisionBox} tRexBox
2630 * @param {CollisionBox} obstacleBox
2631 * @return {boolean} Whether the boxes intersected.
2632 */
2633function boxCompare(tRexBox, obstacleBox) {
2634 var crashed = false;
2635 var tRexBoxX = tRexBox.x;
2636 var tRexBoxY = tRexBox.y;
2637
2638 var obstacleBoxX = obstacleBox.x;
2639 var obstacleBoxY = obstacleBox.y;
2640
2641 // Axis-Aligned Bounding Box method.
2642 if (tRexBox.x < obstacleBoxX + obstacleBox.width &&
2643 tRexBox.x + tRexBox.width > obstacleBoxX &&
2644 tRexBox.y < obstacleBox.y + obstacleBox.height &&
2645 tRexBox.height + tRexBox.y > obstacleBox.y) {
2646 crashed = true;
2647 }
2648
2649 return crashed;
2650};
2651
2652
2653//******************************************************************************
2654
2655/**
2656 * Collision box object.
2657 * @param {number} x X position.
2658 * @param {number} y Y Position.
2659 * @param {number} w Width.
2660 * @param {number} h Height.
2661 */
2662function CollisionBox(x, y, w, h) {
2663 this.x = x;
2664 this.y = y;
2665 this.width = w;
2666 this.height = h;
2667};
2668
2669
2670//******************************************************************************
2671
2672/**
2673 * Obstacle.
2674 * @param {HTMLCanvasCtx} canvasCtx
2675 * @param {Obstacle.type} type
2676 * @param {Object} spritePos Obstacle position in sprite.
2677 * @param {Object} dimensions
2678 * @param {number} gapCoefficient Mutipler in determining the gap.
2679 * @param {number} speed
2680 * @param {number} opt_xOffset
2681 */
2682function Obstacle(canvasCtx, type, spriteImgPos, dimensions,
2683 gapCoefficient, speed, opt_xOffset) {
2684
2685 this.canvasCtx = canvasCtx;
2686 this.spritePos = spriteImgPos;
2687 this.typeConfig = type;
2688 this.gapCoefficient = gapCoefficient;
2689 this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH);
2690 this.dimensions = dimensions;
2691 this.remove = false;
2692 this.xPos = dimensions.WIDTH + (opt_xOffset || 0);
2693 this.yPos = 0;
2694 this.width = 0;
2695 this.collisionBoxes = [];
2696 this.gap = 0;
2697 this.speedOffset = 0;
2698
2699 // For animated obstacles.
2700 this.currentFrame = 0;
2701 this.timer = 0;
2702
2703 this.init(speed);
2704};
2705
2706/**
2707 * Coefficient for calculating the maximum gap.
2708 * @const
2709 */
2710Obstacle.MAX_GAP_COEFFICIENT = 1.5;
2711
2712/**
2713 * Maximum obstacle grouping count.
2714 * @const
2715 */
2716Obstacle.MAX_OBSTACLE_LENGTH = 3,
2717
2718
2719Obstacle.prototype = {
2720 /**
2721 * Initialise the DOM for the obstacle.
2722 * @param {number} speed
2723 */
2724 init: function(speed) {
2725 this.cloneCollisionBoxes();
2726
2727 // Only allow sizing if we're at the right speed.
2728 if (this.size > 1 && this.typeConfig.multipleSpeed > speed) {
2729 this.size = 1;
2730 }
2731
2732 this.width = this.typeConfig.width * this.size;
2733
2734 // Check if obstacle can be positioned at various heights.
2735 if (Array.isArray(this.typeConfig.yPos)) {
2736 var yPosConfig = IS_MOBILE ? this.typeConfig.yPosMobile :
2737 this.typeConfig.yPos;
2738 this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)];
2739 } else {
2740 this.yPos = this.typeConfig.yPos;
2741 }
2742
2743 this.draw();
2744
2745 // Make collision box adjustments,
2746 // Central box is adjusted to the size as one box.
2747 // ____ ______ ________
2748 // _| |-| _| |-| _| |-|
2749 // | |<->| | | |<--->| | | |<----->| |
2750 // | | 1 | | | | 2 | | | | 3 | |
2751 // |_|___|_| |_|_____|_| |_|_______|_|
2752 //
2753 if (this.size > 1) {
2754 this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width -
2755 this.collisionBoxes[2].width;
2756 this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width;
2757 }
2758
2759 // For obstacles that go at a different speed from the horizon.
2760 if (this.typeConfig.speedOffset) {
2761 this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset :
2762 -this.typeConfig.speedOffset;
2763 }
2764
2765 this.gap = this.getGap(this.gapCoefficient, speed);
2766 },
2767
2768 /**
2769 * Draw and crop based on size.
2770 */
2771 draw: function() {
2772 var sourceWidth = this.typeConfig.width;
2773 var sourceHeight = this.typeConfig.height;
2774
2775 if (IS_HIDPI) {
2776 sourceWidth = sourceWidth * 2;
2777 sourceHeight = sourceHeight * 2;
2778 }
2779
2780 // X position in sprite.
2781 var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1)) +
2782 this.spritePos.x;
2783
2784 // Animation frames.
2785 if (this.currentFrame > 0) {
2786 sourceX += sourceWidth * this.currentFrame;
2787 }
2788
2789 this.canvasCtx.drawImage(Runner.imageSprite,
2790 sourceX, this.spritePos.y,
2791 sourceWidth * this.size, sourceHeight,
2792 this.xPos, this.yPos,
2793 this.typeConfig.width * this.size, this.typeConfig.height);
2794 },
2795
2796 /**
2797 * Obstacle frame update.
2798 * @param {number} deltaTime
2799 * @param {number} speed
2800 */
2801 update: function(deltaTime, speed) {
2802 if (!this.remove) {
2803 if (this.typeConfig.speedOffset) {
2804 speed += this.speedOffset;
2805 }
2806 this.xPos -= Math.floor((speed * FPS / 1000) * deltaTime);
2807
2808 // Update frame
2809 if (this.typeConfig.numFrames) {
2810 this.timer += deltaTime;
2811 if (this.timer >= this.typeConfig.frameRate) {
2812 this.currentFrame =
2813 this.currentFrame == this.typeConfig.numFrames - 1 ?
2814 0 : this.currentFrame + 1;
2815 this.timer = 0;
2816 }
2817 }
2818 this.draw();
2819
2820 if (!this.isVisible()) {
2821 this.remove = true;
2822 }
2823 }
2824 },
2825
2826 /**
2827 * Calculate a random gap size.
2828 * - Minimum gap gets wider as speed increses
2829 * @param {number} gapCoefficient
2830 * @param {number} speed
2831 * @return {number} The gap size.
2832 */
2833 getGap: function(gapCoefficient, speed) {
2834 var minGap = Math.round(this.width * speed +
2835 this.typeConfig.minGap * gapCoefficient);
2836 var maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT);
2837 return getRandomNum(minGap, maxGap);
2838 },
2839
2840 /**
2841 * Check if obstacle is visible.
2842 * @return {boolean} Whether the obstacle is in the game area.
2843 */
2844 isVisible: function() {
2845 return this.xPos + this.width > 0;
2846 },
2847
2848 /**
2849 * Make a copy of the collision boxes, since these will change based on
2850 * obstacle type and size.
2851 */
2852 cloneCollisionBoxes: function() {
2853 var collisionBoxes = this.typeConfig.collisionBoxes;
2854
2855 for (var i = collisionBoxes.length - 1; i >= 0; i--) {
2856 this.collisionBoxes[i] = new CollisionBox(collisionBoxes[i].x,
2857 collisionBoxes[i].y, collisionBoxes[i].width,
2858 collisionBoxes[i].height);
2859 }
2860 }
2861};
2862
2863
2864/**
2865 * Obstacle definitions.
2866 * minGap: minimum pixel space betweeen obstacles.
2867 * multipleSpeed: Speed at which multiples are allowed.
2868 * speedOffset: speed faster / slower than the horizon.
2869 * minSpeed: Minimum speed which the obstacle can make an appearance.
2870 */
2871Obstacle.types = [
2872 {
2873 type: 'CACTUS_SMALL',
2874 width: 17,
2875 height: 35,
2876 yPos: 105,
2877 multipleSpeed: 4,
2878 minGap: 120,
2879 minSpeed: 0,
2880 collisionBoxes: [
2881 new CollisionBox(0, 7, 5, 27),
2882 new CollisionBox(4, 0, 6, 34),
2883 new CollisionBox(10, 4, 7, 14)
2884 ]
2885 },
2886 {
2887 type: 'CACTUS_LARGE',
2888 width: 25,
2889 height: 50,
2890 yPos: 90,
2891 multipleSpeed: 7,
2892 minGap: 120,
2893 minSpeed: 0,
2894 collisionBoxes: [
2895 new CollisionBox(0, 12, 7, 38),
2896 new CollisionBox(8, 0, 7, 49),
2897 new CollisionBox(13, 10, 10, 38)
2898 ]
2899 },
2900 {
2901 type: 'PTERODACTYL',
2902 width: 46,
2903 height: 40,
2904 yPos: [ 100, 75, 50 ], // Variable height.
2905 yPosMobile: [ 100, 50 ], // Variable height mobile.
2906 multipleSpeed: 999,
2907 minSpeed: 8.5,
2908 minGap: 150,
2909 collisionBoxes: [
2910 new CollisionBox(15, 15, 16, 5),
2911 new CollisionBox(18, 21, 24, 6),
2912 new CollisionBox(2, 14, 4, 3),
2913 new CollisionBox(6, 10, 4, 7),
2914 new CollisionBox(10, 8, 6, 9)
2915 ],
2916 numFrames: 2,
2917 frameRate: 1000/6,
2918 speedOffset: .8
2919 }
2920];
2921
2922
2923//******************************************************************************
2924/**
2925 * T-rex game character.
2926 * @param {HTMLCanvas} canvas
2927 * @param {Object} spritePos Positioning within image sprite.
2928 * @constructor
2929 */
2930function Trex(canvas, spritePos) {
2931 this.canvas = canvas;
2932 this.canvasCtx = canvas.getContext('2d');
2933 this.spritePos = spritePos;
2934 this.xPos = 0;
2935 this.yPos = 0;
2936 // Position when on the ground.
2937 this.groundYPos = 0;
2938 this.currentFrame = 0;
2939 this.currentAnimFrames = [];
2940 this.blinkDelay = 0;
2941 this.blinkCount = 0;
2942 this.animStartTime = 0;
2943 this.timer = 0;
2944 this.msPerFrame = 1000 / FPS;
2945 this.config = Trex.config;
2946 // Current status.
2947 this.status = Trex.status.WAITING;
2948
2949 this.jumping = false;
2950 this.ducking = false;
2951 this.jumpVelocity = 0;
2952 this.reachedMinHeight = false;
2953 this.speedDrop = false;
2954 this.jumpCount = 0;
2955 this.jumpspotX = 0;
2956
2957 this.init();
2958};
2959
2960
2961/**
2962 * T-rex player config.
2963 * @enum {number}
2964 */
2965Trex.config = {
2966 DROP_VELOCITY: -5,
2967 GRAVITY: 0.6,
2968 HEIGHT: 47,
2969 HEIGHT_DUCK: 25,
2970 INIITAL_JUMP_VELOCITY: -10,
2971 INTRO_DURATION: 1500,
2972 MAX_JUMP_HEIGHT: 30,
2973 MIN_JUMP_HEIGHT: 30,
2974 SPEED_DROP_COEFFICIENT: 3,
2975 SPRITE_WIDTH: 262,
2976 START_X_POS: 50,
2977 WIDTH: 44,
2978 WIDTH_DUCK: 59
2979};
2980
2981
2982/**
2983 * Used in collision detection.
2984 * @type {Array<CollisionBox>}
2985 */
2986Trex.collisionBoxes = {
2987 DUCKING: [
2988 new CollisionBox(1, 18, 55, 25)
2989 ],
2990 RUNNING: [
2991 new CollisionBox(22, 0, 17, 16),
2992 new CollisionBox(1, 18, 30, 9),
2993 new CollisionBox(10, 35, 14, 8),
2994 new CollisionBox(1, 24, 29, 5),
2995 new CollisionBox(5, 30, 21, 4),
2996 new CollisionBox(9, 34, 15, 4)
2997 ]
2998};
2999
3000
3001/**
3002 * Animation states.
3003 * @enum {string}
3004 */
3005Trex.status = {
3006 CRASHED: 'CRASHED',
3007 DUCKING: 'DUCKING',
3008 JUMPING: 'JUMPING',
3009 RUNNING: 'RUNNING',
3010 WAITING: 'WAITING'
3011};
3012
3013/**
3014 * Blinking coefficient.
3015 * @const
3016 */
3017Trex.BLINK_TIMING = 7000;
3018
3019
3020/**
3021 * Animation config for different states.
3022 * @enum {Object}
3023 */
3024Trex.animFrames = {
3025 WAITING: {
3026 frames: [44, 0],
3027 msPerFrame: 1000 / 3
3028 },
3029 RUNNING: {
3030 frames: [88, 132],
3031 msPerFrame: 1000 / 12
3032 },
3033 CRASHED: {
3034 frames: [220],
3035 msPerFrame: 1000 / 60
3036 },
3037 JUMPING: {
3038 frames: [0],
3039 msPerFrame: 1000 / 60
3040 },
3041 DUCKING: {
3042 frames: [262, 321],
3043 msPerFrame: 1000 / 8
3044 }
3045};
3046
3047
3048Trex.prototype = {
3049 /**
3050 * T-rex player initaliser.
3051 * Sets the t-rex to blink at random intervals.
3052 */
3053 init: function() {
3054 this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT -
3055 Runner.config.BOTTOM_PAD;
3056 this.yPos = this.groundYPos;
3057 this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT;
3058
3059 this.draw(0, 0);
3060 this.update(0, Trex.status.WAITING);
3061 },
3062
3063 /**
3064 * Setter for the jump velocity.
3065 * The approriate drop velocity is also set.
3066 */
3067 setJumpVelocity: function(setting) {
3068 this.config.INIITAL_JUMP_VELOCITY = -setting;
3069 this.config.DROP_VELOCITY = -setting / 2;
3070 },
3071
3072 /**
3073 * Set the animation status.
3074 * @param {!number} deltaTime
3075 * @param {Trex.status} status Optional status to switch to.
3076 */
3077 update: function(deltaTime, opt_status) {
3078 this.timer += deltaTime;
3079
3080 // Update the status.
3081 if (opt_status) {
3082 this.status = opt_status;
3083 this.currentFrame = 0;
3084 this.msPerFrame = Trex.animFrames[opt_status].msPerFrame;
3085 this.currentAnimFrames = Trex.animFrames[opt_status].frames;
3086
3087 if (opt_status == Trex.status.WAITING) {
3088 this.animStartTime = getTimeStamp();
3089 this.setBlinkDelay();
3090 }
3091 }
3092
3093 // Game intro animation, T-rex moves in from the left.
3094 if (this.playingIntro && this.xPos < this.config.START_X_POS) {
3095 this.xPos += Math.round((this.config.START_X_POS /
3096 this.config.INTRO_DURATION) * deltaTime);
3097 }
3098
3099 if (this.status == Trex.status.WAITING) {
3100 this.blink(getTimeStamp());
3101 } else {
3102 this.draw(this.currentAnimFrames[this.currentFrame], 0);
3103 }
3104
3105 // Update the frame position.
3106 if (this.timer >= this.msPerFrame) {
3107 this.currentFrame = this.currentFrame ==
3108 this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1;
3109 this.timer = 0;
3110 }
3111
3112 // Speed drop becomes duck if the down key is still being pressed.
3113 if (this.speedDrop && this.yPos == this.groundYPos) {
3114 this.speedDrop = false;
3115 this.setDuck(true);
3116 }
3117 },
3118
3119 /**
3120 * Draw the t-rex to a particular position.
3121 * @param {number} x
3122 * @param {number} y
3123 */
3124 draw: function(x, y) {
3125 var sourceX = x;
3126 var sourceY = y;
3127 var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ?
3128 this.config.WIDTH_DUCK : this.config.WIDTH;
3129 var sourceHeight = this.config.HEIGHT;
3130
3131 if (IS_HIDPI) {
3132 sourceX *= 2;
3133 sourceY *= 2;
3134 sourceWidth *= 2;
3135 sourceHeight *= 2;
3136 }
3137
3138 // Adjustments for sprite sheet position.
3139 sourceX += this.spritePos.x;
3140 sourceY += this.spritePos.y;
3141
3142 // Ducking.
3143 if (this.ducking && this.status != Trex.status.CRASHED) {
3144 this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY,
3145 sourceWidth, sourceHeight,
3146 this.xPos, this.yPos,
3147 this.config.WIDTH_DUCK, this.config.HEIGHT);
3148 } else {
3149 // Crashed whilst ducking. Trex is standing up so needs adjustment.
3150 if (this.ducking && this.status == Trex.status.CRASHED) {
3151 this.xPos++;
3152 }
3153 // Standing / running
3154 this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY,
3155 sourceWidth, sourceHeight,
3156 this.xPos, this.yPos,
3157 this.config.WIDTH, this.config.HEIGHT);
3158 }
3159 },
3160
3161 /**
3162 * Sets a random time for the blink to happen.
3163 */
3164 setBlinkDelay: function() {
3165 this.blinkDelay = Math.ceil(Math.random() * Trex.BLINK_TIMING);
3166 },
3167
3168 /**
3169 * Make t-rex blink at random intervals.
3170 * @param {number} time Current time in milliseconds.
3171 */
3172 blink: function(time) {
3173 var deltaTime = time - this.animStartTime;
3174
3175 if (deltaTime >= this.blinkDelay) {
3176 this.draw(this.currentAnimFrames[this.currentFrame], 0);
3177
3178 if (this.currentFrame == 1) {
3179 // Set new random delay to blink.
3180 this.setBlinkDelay();
3181 this.animStartTime = time;
3182 this.blinkCount++;
3183 }
3184 }
3185 },
3186
3187 /**
3188 * Initialise a jump.
3189 * @param {number} speed
3190 */
3191 startJump: function(speed) {
3192 if (!this.jumping) {
3193 this.update(0, Trex.status.JUMPING);
3194 // Tweak the jump velocity based on the speed.
3195 this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10);
3196 this.jumping = true;
3197 this.reachedMinHeight = false;
3198 this.speedDrop = false;
3199 }
3200 },
3201
3202 /**
3203 * Jump is complete, falling down.
3204 */
3205 endJump: function() {
3206 if (this.reachedMinHeight &&
3207 this.jumpVelocity < this.config.DROP_VELOCITY) {
3208 this.jumpVelocity = this.config.DROP_VELOCITY;
3209 }
3210 },
3211
3212 /**
3213 * Update frame for a jump.
3214 * @param {number} deltaTime
3215 * @param {number} speed
3216 */
3217 updateJump: function(deltaTime, speed) {
3218 var msPerFrame = Trex.animFrames[this.status].msPerFrame;
3219 var framesElapsed = deltaTime / msPerFrame;
3220
3221 // Speed drop makes Trex fall faster.
3222 if (this.speedDrop) {
3223 this.yPos += Math.round(this.jumpVelocity *
3224 this.config.SPEED_DROP_COEFFICIENT * framesElapsed);
3225 } else {
3226 this.yPos += Math.round(this.jumpVelocity * framesElapsed);
3227 }
3228
3229 this.jumpVelocity += this.config.GRAVITY * framesElapsed;
3230
3231 // Minimum height has been reached.
3232 if (this.yPos < this.minJumpHeight || this.speedDrop) {
3233 this.reachedMinHeight = true;
3234 }
3235
3236 // Reached max height
3237 if (this.yPos < this.config.MAX_JUMP_HEIGHT || this.speedDrop) {
3238 this.endJump();
3239 }
3240
3241 // Back down at ground level. Jump completed.
3242 if (this.yPos > this.groundYPos) {
3243 this.reset();
3244 this.jumpCount++;
3245 }
3246
3247 this.update(deltaTime);
3248 },
3249
3250 /**
3251 * Set the speed drop. Immediately cancels the current jump.
3252 */
3253 setSpeedDrop: function() {
3254 this.speedDrop = true;
3255 this.jumpVelocity = 1;
3256 },
3257
3258 /**
3259 * @param {boolean} isDucking.
3260 */
3261 setDuck: function(isDucking) {
3262 if (isDucking && this.status != Trex.status.DUCKING) {
3263 this.update(0, Trex.status.DUCKING);
3264 this.ducking = true;
3265 } else if (this.status == Trex.status.DUCKING) {
3266 this.update(0, Trex.status.RUNNING);
3267 this.ducking = false;
3268 }
3269 },
3270
3271 /**
3272 * Reset the t-rex to running at start of game.
3273 */
3274 reset: function() {
3275 this.yPos = this.groundYPos;
3276 this.jumpVelocity = 0;
3277 this.jumping = false;
3278 this.ducking = false;
3279 this.update(0, Trex.status.RUNNING);
3280 this.midair = false;
3281 this.speedDrop = false;
3282 this.jumpCount = 0;
3283 }
3284};
3285
3286
3287//******************************************************************************
3288
3289/**
3290 * Handles displaying the distance meter.
3291 * @param {!HTMLCanvasElement} canvas
3292 * @param {Object} spritePos Image position in sprite.
3293 * @param {number} canvasWidth
3294 * @constructor
3295 */
3296function DistanceMeter(canvas, spritePos, canvasWidth) {
3297 this.canvas = canvas;
3298 this.canvasCtx = canvas.getContext('2d');
3299 this.image = Runner.imageSprite;
3300 this.spritePos = spritePos;
3301 this.x = 0;
3302 this.y = 5;
3303
3304 this.currentDistance = 0;
3305 this.maxScore = 0;
3306 this.highScore = 0;
3307 this.container = null;
3308
3309 this.digits = [];
3310 this.achievement = false;
3311 this.defaultString = '';
3312 this.flashTimer = 0;
3313 this.flashIterations = 0;
3314 this.invertTrigger = false;
3315
3316 this.config = DistanceMeter.config;
3317 this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS;
3318 this.init(canvasWidth);
3319};
3320
3321
3322/**
3323 * @enum {number}
3324 */
3325DistanceMeter.dimensions = {
3326 WIDTH: 10,
3327 HEIGHT: 13,
3328 DEST_WIDTH: 11
3329};
3330
3331
3332/**
3333 * Y positioning of the digits in the sprite sheet.
3334 * X position is always 0.
3335 * @type {Array<number>}
3336 */
3337DistanceMeter.yPos = [0, 13, 27, 40, 53, 67, 80, 93, 107, 120];
3338
3339
3340/**
3341 * Distance meter config.
3342 * @enum {number}
3343 */
3344DistanceMeter.config = {
3345 // Number of digits.
3346 MAX_DISTANCE_UNITS: 5,
3347
3348 // Distance that causes achievement animation.
3349 ACHIEVEMENT_DISTANCE: 100,
3350
3351 // Used for conversion from pixel distance to a scaled unit.
3352 COEFFICIENT: 0.025,
3353
3354 // Flash duration in milliseconds.
3355 FLASH_DURATION: 1000 / 4,
3356
3357 // Flash iterations for achievement animation.
3358 FLASH_ITERATIONS: 3
3359};
3360
3361
3362DistanceMeter.prototype = {
3363 /**
3364 * Initialise the distance meter to '00000'.
3365 * @param {number} width Canvas width in px.
3366 */
3367 init: function(width) {
3368 var maxDistanceStr = '';
3369
3370 this.calcXPos(width);
3371 this.maxScore = this.maxScoreUnits;
3372 for (var i = 0; i < this.maxScoreUnits; i++) {
3373 this.draw(i, 0);
3374 this.defaultString += '0';
3375 maxDistanceStr += '9';
3376 }
3377
3378 this.maxScore = parseInt(maxDistanceStr);
3379 },
3380
3381 /**
3382 * Calculate the xPos in the canvas.
3383 * @param {number} canvasWidth
3384 */
3385 calcXPos: function(canvasWidth) {
3386 this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH *
3387 (this.maxScoreUnits + 1));
3388 },
3389
3390 /**
3391 * Draw a digit to canvas.
3392 * @param {number} digitPos Position of the digit.
3393 * @param {number} value Digit value 0-9.
3394 * @param {boolean} opt_highScore Whether drawing the high score.
3395 */
3396 draw: function(digitPos, value, opt_highScore) {
3397 var sourceWidth = DistanceMeter.dimensions.WIDTH;
3398 var sourceHeight = DistanceMeter.dimensions.HEIGHT;
3399 var sourceX = DistanceMeter.dimensions.WIDTH * value;
3400 var sourceY = 0;
3401
3402 var targetX = digitPos * DistanceMeter.dimensions.DEST_WIDTH;
3403 var targetY = this.y;
3404 var targetWidth = DistanceMeter.dimensions.WIDTH;
3405 var targetHeight = DistanceMeter.dimensions.HEIGHT;
3406
3407 // For high DPI we 2x source values.
3408 if (IS_HIDPI) {
3409 sourceWidth *= 2;
3410 sourceHeight *= 2;
3411 sourceX *= 2;
3412 }
3413
3414 sourceX += this.spritePos.x;
3415 sourceY += this.spritePos.y;
3416
3417 this.canvasCtx.save();
3418
3419 if (opt_highScore) {
3420 // Left of the current score.
3421 var highScoreX = this.x - (this.maxScoreUnits * 2) *
3422 DistanceMeter.dimensions.WIDTH;
3423 this.canvasCtx.translate(highScoreX, this.y);
3424 } else {
3425 this.canvasCtx.translate(this.x, this.y);
3426 }
3427
3428 this.canvasCtx.drawImage(this.image, sourceX, sourceY,
3429 sourceWidth, sourceHeight,
3430 targetX, targetY,
3431 targetWidth, targetHeight
3432 );
3433
3434 this.canvasCtx.restore();
3435 },
3436
3437 /**
3438 * Covert pixel distance to a 'real' distance.
3439 * @param {number} distance Pixel distance ran.
3440 * @return {number} The 'real' distance ran.
3441 */
3442 getActualDistance: function(distance) {
3443 return distance ? Math.round(distance * this.config.COEFFICIENT) : 0;
3444 },
3445
3446 /**
3447 * Update the distance meter.
3448 * @param {number} distance
3449 * @param {number} deltaTime
3450 * @return {boolean} Whether the acheivement sound fx should be played.
3451 */
3452 update: function(deltaTime, distance) {
3453 var paint = true;
3454 var playSound = false;
3455
3456 if (!this.achievement) {
3457 distance = this.getActualDistance(distance);
3458 // Score has gone beyond the initial digit count.
3459 if (distance > this.maxScore && this.maxScoreUnits ==
3460 this.config.MAX_DISTANCE_UNITS) {
3461 this.maxScoreUnits++;
3462 this.maxScore = parseInt(this.maxScore + '9');
3463 } else {
3464 this.distance = 0;
3465 }
3466
3467 if (distance > 0) {
3468 // Acheivement unlocked
3469 if (distance % this.config.ACHIEVEMENT_DISTANCE == 0) {
3470 // Flash score and play sound.
3471 this.achievement = true;
3472 this.flashTimer = 0;
3473 playSound = true;
3474 }
3475
3476 // Create a string representation of the distance with leading 0.
3477 var distanceStr = (this.defaultString +
3478 distance).substr(-this.maxScoreUnits);
3479 this.digits = distanceStr.split('');
3480 } else {
3481 this.digits = this.defaultString.split('');
3482 }
3483 } else {
3484 // Control flashing of the score on reaching acheivement.
3485 if (this.flashIterations <= this.config.FLASH_ITERATIONS) {
3486 this.flashTimer += deltaTime;
3487
3488 if (this.flashTimer < this.config.FLASH_DURATION) {
3489 paint = false;
3490 } else if (this.flashTimer >
3491 this.config.FLASH_DURATION * 2) {
3492 this.flashTimer = 0;
3493 this.flashIterations++;
3494 }
3495 } else {
3496 this.achievement = false;
3497 this.flashIterations = 0;
3498 this.flashTimer = 0;
3499 }
3500 }
3501
3502 // Draw the digits if not flashing.
3503 if (paint) {
3504 for (var i = this.digits.length - 1; i >= 0; i--) {
3505 this.draw(i, parseInt(this.digits[i]));
3506 }
3507 }
3508
3509 this.drawHighScore();
3510 return playSound;
3511 },
3512
3513 /**
3514 * Draw the high score.
3515 */
3516 drawHighScore: function() {
3517 this.canvasCtx.save();
3518 this.canvasCtx.globalAlpha = .8;
3519 for (var i = this.highScore.length - 1; i >= 0; i--) {
3520 this.draw(i, parseInt(this.highScore[i], 10), true);
3521 }
3522 this.canvasCtx.restore();
3523 },
3524
3525 /**
3526 * Set the highscore as a array string.
3527 * Position of char in the sprite: H - 10, I - 11.
3528 * @param {number} distance Distance ran in pixels.
3529 */
3530 setHighScore: function(distance) {
3531 distance = this.getActualDistance(distance);
3532 var highScoreStr = (this.defaultString +
3533 distance).substr(-this.maxScoreUnits);
3534
3535 this.highScore = ['10', '11', ''].concat(highScoreStr.split(''));
3536 },
3537
3538 /**
3539 * Reset the distance meter back to '00000'.
3540 */
3541 reset: function() {
3542 this.update(0);
3543 this.achievement = false;
3544 }
3545};
3546
3547
3548//******************************************************************************
3549
3550/**
3551 * Cloud background item.
3552 * Similar to an obstacle object but without collision boxes.
3553 * @param {HTMLCanvasElement} canvas Canvas element.
3554 * @param {Object} spritePos Position of image in sprite.
3555 * @param {number} containerWidth
3556 */
3557function Cloud(canvas, spritePos, containerWidth) {
3558 this.canvas = canvas;
3559 this.canvasCtx = this.canvas.getContext('2d');
3560 this.spritePos = spritePos;
3561 this.containerWidth = containerWidth;
3562 this.xPos = containerWidth;
3563 this.yPos = 0;
3564 this.remove = false;
3565 this.cloudGap = getRandomNum(Cloud.config.MIN_CLOUD_GAP,
3566 Cloud.config.MAX_CLOUD_GAP);
3567
3568 this.init();
3569};
3570
3571
3572/**
3573 * Cloud object config.
3574 * @enum {number}
3575 */
3576Cloud.config = {
3577 HEIGHT: 14,
3578 MAX_CLOUD_GAP: 400,
3579 MAX_SKY_LEVEL: 30,
3580 MIN_CLOUD_GAP: 100,
3581 MIN_SKY_LEVEL: 71,
3582 WIDTH: 46
3583};
3584
3585
3586Cloud.prototype = {
3587 /**
3588 * Initialise the cloud. Sets the Cloud height.
3589 */
3590 init: function() {
3591 this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL,
3592 Cloud.config.MIN_SKY_LEVEL);
3593 this.draw();
3594 },
3595
3596 /**
3597 * Draw the cloud.
3598 */
3599 draw: function() {
3600 this.canvasCtx.save();
3601 var sourceWidth = Cloud.config.WIDTH;
3602 var sourceHeight = Cloud.config.HEIGHT;
3603
3604 if (IS_HIDPI) {
3605 sourceWidth = sourceWidth * 2;
3606 sourceHeight = sourceHeight * 2;
3607 }
3608
3609 this.canvasCtx.drawImage(Runner.imageSprite, this.spritePos.x,
3610 this.spritePos.y,
3611 sourceWidth, sourceHeight,
3612 this.xPos, this.yPos,
3613 Cloud.config.WIDTH, Cloud.config.HEIGHT);
3614
3615 this.canvasCtx.restore();
3616 },
3617
3618 /**
3619 * Update the cloud position.
3620 * @param {number} speed
3621 */
3622 update: function(speed) {
3623 if (!this.remove) {
3624 this.xPos -= Math.ceil(speed);
3625 this.draw();
3626
3627 // Mark as removeable if no longer in the canvas.
3628 if (!this.isVisible()) {
3629 this.remove = true;
3630 }
3631 }
3632 },
3633
3634 /**
3635 * Check if the cloud is visible on the stage.
3636 * @return {boolean}
3637 */
3638 isVisible: function() {
3639 return this.xPos + Cloud.config.WIDTH > 0;
3640 }
3641};
3642
3643
3644//******************************************************************************
3645
3646/**
3647 * Nightmode shows a moon and stars on the horizon.
3648 */
3649function NightMode(canvas, spritePos, containerWidth) {
3650 this.spritePos = spritePos;
3651 this.canvas = canvas;
3652 this.canvasCtx = canvas.getContext('2d');
3653 this.xPos = containerWidth - 50;
3654 this.yPos = 30;
3655 this.currentPhase = 0;
3656 this.opacity = 0;
3657 this.containerWidth = containerWidth;
3658 this.stars = [];
3659 this.drawStars = false;
3660 this.placeStars();
3661};
3662
3663/**
3664 * @enum {number}
3665 */
3666NightMode.config = {
3667 FADE_SPEED: 0.035,
3668 HEIGHT: 40,
3669 MOON_SPEED: 0.25,
3670 NUM_STARS: 2,
3671 STAR_SIZE: 9,
3672 STAR_SPEED: 0.3,
3673 STAR_MAX_Y: 70,
3674 WIDTH: 20
3675};
3676
3677NightMode.phases = [140, 120, 100, 60, 40, 20, 0];
3678
3679NightMode.prototype = {
3680 /**
3681 * Update moving moon, changing phases.
3682 * @param {boolean} activated Whether night mode is activated.
3683 * @param {number} delta
3684 */
3685 update: function(activated, delta) {
3686 // Moon phase.
3687 if (activated && this.opacity == 0) {
3688 this.currentPhase++;
3689
3690 if (this.currentPhase >= NightMode.phases.length) {
3691 this.currentPhase = 0;
3692 }
3693 }
3694
3695 // Fade in / out.
3696 if (activated && (this.opacity < 1 || this.opacity == 0)) {
3697 this.opacity += NightMode.config.FADE_SPEED;
3698 } else if (this.opacity > 0) {
3699 this.opacity -= NightMode.config.FADE_SPEED;
3700 }
3701
3702 // Set moon positioning.
3703 if (this.opacity > 0) {
3704 this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED);
3705
3706 // Update stars.
3707 if (this.drawStars) {
3708 for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
3709 this.stars[i].x = this.updateXPos(this.stars[i].x,
3710 NightMode.config.STAR_SPEED);
3711 }
3712 }
3713 this.draw();
3714 } else {
3715 this.opacity = 0;
3716 this.placeStars();
3717 }
3718 this.drawStars = true;
3719 },
3720
3721 updateXPos: function(currentPos, speed) {
3722 if (currentPos < -NightMode.config.WIDTH) {
3723 currentPos = this.containerWidth;
3724 } else {
3725 currentPos -= speed;
3726 }
3727 return currentPos;
3728 },
3729
3730 draw: function() {
3731 var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 :
3732 NightMode.config.WIDTH;
3733 var moonSourceHeight = NightMode.config.HEIGHT;
3734 var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase];
3735 var moonOutputWidth = moonSourceWidth;
3736 var starSize = NightMode.config.STAR_SIZE;
3737 var starSourceX = Runner.spriteDefinition.LDPI.STAR.x;
3738
3739 if (IS_HIDPI) {
3740 moonSourceWidth *= 2;
3741 moonSourceHeight *= 2;
3742 moonSourceX = this.spritePos.x +
3743 (NightMode.phases[this.currentPhase] * 2);
3744 starSize *= 2;
3745 starSourceX = Runner.spriteDefinition.HDPI.STAR.x;
3746 }
3747
3748 this.canvasCtx.save();
3749 this.canvasCtx.globalAlpha = this.opacity;
3750
3751 // Stars.
3752 if (this.drawStars) {
3753 for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
3754 this.canvasCtx.drawImage(Runner.imageSprite,
3755 starSourceX, this.stars[i].sourceY, starSize, starSize,
3756 Math.round(this.stars[i].x), this.stars[i].y,
3757 NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE);
3758 }
3759 }
3760
3761 // Moon.
3762 this.canvasCtx.drawImage(Runner.imageSprite, moonSourceX,
3763 this.spritePos.y, moonSourceWidth, moonSourceHeight,
3764 Math.round(this.xPos), this.yPos,
3765 moonOutputWidth, NightMode.config.HEIGHT);
3766
3767 this.canvasCtx.globalAlpha = 1;
3768 this.canvasCtx.restore();
3769 },
3770
3771 // Do star placement.
3772 placeStars: function() {
3773 var segmentSize = Math.round(this.containerWidth /
3774 NightMode.config.NUM_STARS);
3775
3776 for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
3777 this.stars[i] = {};
3778 this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1));
3779 this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y);
3780
3781 if (IS_HIDPI) {
3782 this.stars[i].sourceY = Runner.spriteDefinition.HDPI.STAR.y +
3783 NightMode.config.STAR_SIZE * 2 * i;
3784 } else {
3785 this.stars[i].sourceY = Runner.spriteDefinition.LDPI.STAR.y +
3786 NightMode.config.STAR_SIZE * i;
3787 }
3788 }
3789 },
3790
3791 reset: function() {
3792 this.currentPhase = 0;
3793 this.opacity = 0;
3794 this.update(false);
3795 }
3796
3797};
3798
3799
3800//******************************************************************************
3801
3802/**
3803 * Horizon Line.
3804 * Consists of two connecting lines. Randomly assigns a flat / bumpy horizon.
3805 * @param {HTMLCanvasElement} canvas
3806 * @param {Object} spritePos Horizon position in sprite.
3807 * @constructor
3808 */
3809function HorizonLine(canvas, spritePos) {
3810 this.spritePos = spritePos;
3811 this.canvas = canvas;
3812 this.canvasCtx = canvas.getContext('2d');
3813 this.sourceDimensions = {};
3814 this.dimensions = HorizonLine.dimensions;
3815 this.sourceXPos = [this.spritePos.x, this.spritePos.x +
3816 this.dimensions.WIDTH];
3817 this.xPos = [];
3818 this.yPos = 0;
3819 this.bumpThreshold = 0.5;
3820
3821 this.setSourceDimensions();
3822 this.draw();
3823};
3824
3825
3826/**
3827 * Horizon line dimensions.
3828 * @enum {number}
3829 */
3830HorizonLine.dimensions = {
3831 WIDTH: 600,
3832 HEIGHT: 12,
3833 YPOS: 127
3834};
3835
3836
3837HorizonLine.prototype = {
3838 /**
3839 * Set the source dimensions of the horizon line.
3840 */
3841 setSourceDimensions: function() {
3842
3843 for (var dimension in HorizonLine.dimensions) {
3844 if (IS_HIDPI) {
3845 if (dimension != 'YPOS') {
3846 this.sourceDimensions[dimension] =
3847 HorizonLine.dimensions[dimension] * 2;
3848 }
3849 } else {
3850 this.sourceDimensions[dimension] =
3851 HorizonLine.dimensions[dimension];
3852 }
3853 this.dimensions[dimension] = HorizonLine.dimensions[dimension];
3854 }
3855
3856 this.xPos = [0, HorizonLine.dimensions.WIDTH];
3857 this.yPos = HorizonLine.dimensions.YPOS;
3858 },
3859
3860 /**
3861 * Return the crop x position of a type.
3862 */
3863 getRandomType: function() {
3864 return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0;
3865 },
3866
3867 /**
3868 * Draw the horizon line.
3869 */
3870 draw: function() {
3871 this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[0],
3872 this.spritePos.y,
3873 this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT,
3874 this.xPos[0], this.yPos,
3875 this.dimensions.WIDTH, this.dimensions.HEIGHT);
3876
3877 this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[1],
3878 this.spritePos.y,
3879 this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT,
3880 this.xPos[1], this.yPos,
3881 this.dimensions.WIDTH, this.dimensions.HEIGHT);
3882 },
3883
3884 /**
3885 * Update the x position of an indivdual piece of the line.
3886 * @param {number} pos Line position.
3887 * @param {number} increment
3888 */
3889 updateXPos: function(pos, increment) {
3890 var line1 = pos;
3891 var line2 = pos == 0 ? 1 : 0;
3892
3893 this.xPos[line1] -= increment;
3894 this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH;
3895
3896 if (this.xPos[line1] <= -this.dimensions.WIDTH) {
3897 this.xPos[line1] += this.dimensions.WIDTH * 2;
3898 this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH;
3899 this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x;
3900 }
3901 },
3902
3903 /**
3904 * Update the horizon line.
3905 * @param {number} deltaTime
3906 * @param {number} speed
3907 */
3908 update: function(deltaTime, speed) {
3909 var increment = Math.floor(speed * (FPS / 1000) * deltaTime);
3910
3911 if (this.xPos[0] <= 0) {
3912 this.updateXPos(0, increment);
3913 } else {
3914 this.updateXPos(1, increment);
3915 }
3916 this.draw();
3917 },
3918
3919 /**
3920 * Reset horizon to the starting position.
3921 */
3922 reset: function() {
3923 this.xPos[0] = 0;
3924 this.xPos[1] = HorizonLine.dimensions.WIDTH;
3925 }
3926};
3927
3928
3929//******************************************************************************
3930
3931/**
3932 * Horizon background class.
3933 * @param {HTMLCanvasElement} canvas
3934 * @param {Object} spritePos Sprite positioning.
3935 * @param {Object} dimensions Canvas dimensions.
3936 * @param {number} gapCoefficient
3937 * @constructor
3938 */
3939function Horizon(canvas, spritePos, dimensions, gapCoefficient) {
3940 this.canvas = canvas;
3941 this.canvasCtx = this.canvas.getContext('2d');
3942 this.config = Horizon.config;
3943 this.dimensions = dimensions;
3944 this.gapCoefficient = gapCoefficient;
3945 this.obstacles = [];
3946 this.obstacleHistory = [];
3947 this.horizonOffsets = [0, 0];
3948 this.cloudFrequency = this.config.CLOUD_FREQUENCY;
3949 this.spritePos = spritePos;
3950 this.nightMode = null;
3951
3952 // Cloud
3953 this.clouds = [];
3954 this.cloudSpeed = this.config.BG_CLOUD_SPEED;
3955
3956 // Horizon
3957 this.horizonLine = null;
3958 this.init();
3959};
3960
3961
3962/**
3963 * Horizon config.
3964 * @enum {number}
3965 */
3966Horizon.config = {
3967 BG_CLOUD_SPEED: 0.2,
3968 BUMPY_THRESHOLD: .3,
3969 CLOUD_FREQUENCY: .5,
3970 HORIZON_HEIGHT: 16,
3971 MAX_CLOUDS: 6
3972};
3973
3974
3975Horizon.prototype = {
3976 /**
3977 * Initialise the horizon. Just add the line and a cloud. No obstacles.
3978 */
3979 init: function() {
3980 this.addCloud();
3981 this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON);
3982 this.nightMode = new NightMode(this.canvas, this.spritePos.MOON,
3983 this.dimensions.WIDTH);
3984 },
3985
3986 /**
3987 * @param {number} deltaTime
3988 * @param {number} currentSpeed
3989 * @param {boolean} updateObstacles Used as an override to prevent
3990 * the obstacles from being updated / added. This happens in the
3991 * ease in section.
3992 * @param {boolean} showNightMode Night mode activated.
3993 */
3994 update: function(deltaTime, currentSpeed, updateObstacles, showNightMode) {
3995 this.runningTime += deltaTime;
3996 this.horizonLine.update(deltaTime, currentSpeed);
3997 this.nightMode.update(showNightMode);
3998 this.updateClouds(deltaTime, currentSpeed);
3999
4000 if (updateObstacles) {
4001 this.updateObstacles(deltaTime, currentSpeed);
4002 }
4003 },
4004
4005 /**
4006 * Update the cloud positions.
4007 * @param {number} deltaTime
4008 * @param {number} currentSpeed
4009 */
4010 updateClouds: function(deltaTime, speed) {
4011 var cloudSpeed = this.cloudSpeed / 1000 * deltaTime * speed;
4012 var numClouds = this.clouds.length;
4013
4014 if (numClouds) {
4015 for (var i = numClouds - 1; i >= 0; i--) {
4016 this.clouds[i].update(cloudSpeed);
4017 }
4018
4019 var lastCloud = this.clouds[numClouds - 1];
4020
4021 // Check for adding a new cloud.
4022 if (numClouds < this.config.MAX_CLOUDS &&
4023 (this.dimensions.WIDTH - lastCloud.xPos) > lastCloud.cloudGap &&
4024 this.cloudFrequency > Math.random()) {
4025 this.addCloud();
4026 }
4027
4028 // Remove expired clouds.
4029 this.clouds = this.clouds.filter(function(obj) {
4030 return !obj.remove;
4031 });
4032 } else {
4033 this.addCloud();
4034 }
4035 },
4036
4037 /**
4038 * Update the obstacle positions.
4039 * @param {number} deltaTime
4040 * @param {number} currentSpeed
4041 */
4042 updateObstacles: function(deltaTime, currentSpeed) {
4043 // Obstacles, move to Horizon layer.
4044 var updatedObstacles = this.obstacles.slice(0);
4045
4046 for (var i = 0; i < this.obstacles.length; i++) {
4047 var obstacle = this.obstacles[i];
4048 obstacle.update(deltaTime, currentSpeed);
4049
4050 // Clean up existing obstacles.
4051 if (obstacle.remove) {
4052 updatedObstacles.shift();
4053 }
4054 }
4055 this.obstacles = updatedObstacles;
4056
4057 if (this.obstacles.length > 0) {
4058 var lastObstacle = this.obstacles[this.obstacles.length - 1];
4059
4060 if (lastObstacle && !lastObstacle.followingObstacleCreated &&
4061 lastObstacle.isVisible() &&
4062 (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) <
4063 this.dimensions.WIDTH) {
4064 this.addNewObstacle(currentSpeed);
4065 lastObstacle.followingObstacleCreated = true;
4066 }
4067 } else {
4068 // Create new obstacles.
4069 this.addNewObstacle(currentSpeed);
4070 }
4071 },
4072
4073 removeFirstObstacle: function() {
4074 this.obstacles.shift();
4075 },
4076
4077 /**
4078 * Add a new obstacle.
4079 * @param {number} currentSpeed
4080 */
4081 addNewObstacle: function(currentSpeed) {
4082 var obstacleTypeIndex = getRandomNum(0, Obstacle.types.length - 1);
4083 var obstacleType = Obstacle.types[obstacleTypeIndex];
4084
4085 // Check for multiples of the same type of obstacle.
4086 // Also check obstacle is available at current speed.
4087 if (this.duplicateObstacleCheck(obstacleType.type) ||
4088 currentSpeed < obstacleType.minSpeed) {
4089 this.addNewObstacle(currentSpeed);
4090 } else {
4091 var obstacleSpritePos = this.spritePos[obstacleType.type];
4092
4093 this.obstacles.push(new Obstacle(this.canvasCtx, obstacleType,
4094 obstacleSpritePos, this.dimensions,
4095 this.gapCoefficient, currentSpeed, obstacleType.width));
4096
4097 this.obstacleHistory.unshift(obstacleType.type);
4098
4099 if (this.obstacleHistory.length > 1) {
4100 this.obstacleHistory.splice(Runner.config.MAX_OBSTACLE_DUPLICATION);
4101 }
4102 }
4103 },
4104
4105 /**
4106 * Returns whether the previous two obstacles are the same as the next one.
4107 * Maximum duplication is set in config value MAX_OBSTACLE_DUPLICATION.
4108 * @return {boolean}
4109 */
4110 duplicateObstacleCheck: function(nextObstacleType) {
4111 var duplicateCount = 0;
4112
4113 for (var i = 0; i < this.obstacleHistory.length; i++) {
4114 duplicateCount = this.obstacleHistory[i] == nextObstacleType ?
4115 duplicateCount + 1 : 0;
4116 }
4117 return duplicateCount >= Runner.config.MAX_OBSTACLE_DUPLICATION;
4118 },
4119
4120 /**
4121 * Reset the horizon layer.
4122 * Remove existing obstacles and reposition the horizon line.
4123 */
4124 reset: function() {
4125 this.obstacles = [];
4126 this.horizonLine.reset();
4127 this.nightMode.reset();
4128 },
4129
4130 /**
4131 * Update the canvas width and scaling.
4132 * @param {number} width Canvas width.
4133 * @param {number} height Canvas height.
4134 */
4135 resize: function(width, height) {
4136 this.canvas.width = width;
4137 this.canvas.height = height;
4138 },
4139
4140 /**
4141 * Add a new cloud to the horizon.
4142 */
4143 addCloud: function() {
4144 this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD,
4145 this.dimensions.WIDTH));
4146 }
4147};
4148})();
4149</script>
4150</head>
4151<body id="t" style="font-family: 'Segoe UI', Tahoma, sans-serif; font-size: 75%" jstcache="0" class="neterror">
4152 <div id="main-frame-error" class="interstitial-wrapper" jstcache="0">
4153 <div id="main-content" jstcache="0">
4154 <div class="icon icon-generic" jseval="updateIconClass(this.classList, iconClass)" alt="" jstcache="1"></div>
4155 <div id="main-message" jstcache="0">
4156 <h1 jsselect="heading" jsvalues=".innerHTML:msg" jstcache="5">Não é possÃvel acessar esse site</h1>
4157 <p jsselect="summary" jsvalues=".innerHTML:msg" jstcache="2"><strong jscontent="hostName" jstcache="16">coinhive.com</strong> demorou muito para responder.</p>
4158 <div id="suggestions-list" style="" jsdisplay="(suggestionsSummaryList && suggestionsSummaryList.length)" jstcache="6">
4159 <p jsvalues=".innerHTML:suggestionsSummaryListHeader" jstcache="13">Tente:</p>
4160 <ul jsvalues=".className:suggestionsSummaryList.length == 1 ? 'single-suggestion' : ''" jstcache="14" class="">
4161 <li jsselect="suggestionsSummaryList" jsvalues=".innerHTML:summary" jstcache="15" jsinstance="0">Verificar a conexão</li><li jsselect="suggestionsSummaryList" jsvalues=".innerHTML:summary" jstcache="15" jsinstance="1"><a href="chrome-error://chromewebdata/#buttons" onclick="toggleHelpBox()" jstcache="0">Verificar o proxy e o firewall</a></li><li jsselect="suggestionsSummaryList" jsvalues=".innerHTML:summary" jstcache="15" jsinstance="*2"><a href="javascript:diagnoseErrors()" id="diagnose-link" jstcache="0">Executar o Diagnóstico de Rede do Windows</a></li>
4162 </ul>
4163 </div>
4164 <div class="error-code" jscontent="errorCode" jstcache="7">ERR_CONNECTION_TIMED_OUT</div>
4165 <div id="diagnose-frame" class="hidden" jstcache="0"></div>
4166 </div>
4167 </div>
4168 <div id="buttons" class="nav-wrapper suggested-left" jstcache="0">
4169 <div id="control-buttons" jstcache="0">
4170 <button id="show-saved-copy-button" class="blue-button text-button" onclick="showSavedCopyButtonClick()" jsselect="showSavedCopyButton" jscontent="msg" jsvalues="title:title; .primary:primary" jstcache="9" style="display: none;">
4171 </button><button id="reload-button" class="blue-button text-button" onclick="trackClick(this.trackingId);
4172 reloadButtonClick(this.url);" jsselect="reloadButton" jsvalues=".url:reloadUrl; .trackingId:reloadTrackingId" jscontent="msg" jstcache="8">Recarregar</button>
4173
4174 <button id="download-button" class="blue-button text-button" onclick="downloadButtonClick()" jsselect="downloadButton" jscontent="msg" jsvalues=".disabledText:disabledMsg" jstcache="10" style="display: none;">
4175 </button>
4176 </div>
4177 <button id="details-button" class="text-button small-link" onclick="detailsButtonClick(); toggleHelpBox()" jscontent="details" jsdisplay="(suggestionsDetails && suggestionsDetails.length > 0) || diagnose" jsvalues=".detailsText:details; .hideDetailsText:hideDetails;" jstcache="3">Detalhes</button>
4178 </div>
4179 <div id="details" class="hidden" jstcache="0">
4180 <div class="suggestions" jsselect="suggestionsDetails" jstcache="4" jsinstance="0">
4181 <div class="suggestion-header" jsvalues=".innerHTML:header" jstcache="11">Verifique sua conexão com a Internet</div>
4182 <div class="suggestion-body" jsvalues=".innerHTML:body" jstcache="12">Verifique todos os cabos e reinicie todos os roteadores, modens ou outros
4183 dispositivos de rede que você estiver usando.</div>
4184 </div><div class="suggestions" jsselect="suggestionsDetails" jstcache="4" jsinstance="1">
4185 <div class="suggestion-header" jsvalues=".innerHTML:header" jstcache="11">Permitir que o Chrome acesse a rede nas configurações do seu
4186 firewall ou antivÃrus.</div>
4187 <div class="suggestion-body" jsvalues=".innerHTML:body" jstcache="12">Se ele já estiver listado como um programa que tem permissão para acessar a rede, tente
4188 removê-lo da lista e adicioná-lo novamente.</div>
4189 </div><div class="suggestions" jsselect="suggestionsDetails" jstcache="4" jsinstance="*2">
4190 <div class="suggestion-header" jsvalues=".innerHTML:header" jstcache="11">Se você usa um servidor proxy…</div>
4191 <div class="suggestion-body" jsvalues=".innerHTML:body" jstcache="12">Verifique suas configurações de proxy ou entre em contato com o administrador de rede para
4192 verificar se o servidor proxy está funcionando. Se você acredita que não deve
4193 usar um servidor proxy:
4194 Vá para
4195 o menu do Google Chrome >
4196 <span jscontent="settingsTitle" jstcache="17">Configurações</span>
4197 >
4198 <span jscontent="advancedTitle" jstcache="18">Mostrar configurações avançadas…</span>
4199 >
4200 <span jscontent="proxyTitle" jstcache="19">Alterar configurações de proxy…</span>
4201 >
4202 Configurações da LAN
4203 e desmarque a opção "Usar um servidor proxy para a rede local".</div>
4204 </div>
4205 </div>
4206 </div>
4207 <div id="sub-frame-error" jstcache="0">
4208 <!-- Show details when hovering over the icon, in case the details are
4209 hidden because they're too large. -->
4210 <div class="icon icon-generic" jseval="updateIconClass(this.classList, iconClass)" jstcache="1"></div>
4211 <div id="sub-frame-error-details" jsselect="summary" jsvalues=".innerHTML:msg" jstcache="2"><strong jscontent="hostName" jstcache="16">coinhive.com</strong> demorou muito para responder.</div>
4212 </div>
4213
4214 <div id="offline-resources" jstcache="0">
4215 <img id="offline-resources-1x" src="data:image/png;base64,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" jstcache="0">
4216 <img id="offline-resources-2x" src="data:image/png;base64,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" jstcache="0">
4217 <template id="audio-resources" jstcache="0"></template>
4218 </div>
4219
4220
4221<script jstcache="0">// Copyright (c) 2012 The Chromium Authors. All rights reserved.
4222// Use of this source code is governed by a BSD-style license that can be
4223// found in the LICENSE file.
4224
4225/**
4226 * @fileoverview This file defines a singleton which provides access to all data
4227 * that is available as soon as the page's resources are loaded (before DOM
4228 * content has finished loading). This data includes both localized strings and
4229 * any data that is important to have ready from a very early stage (e.g. things
4230 * that must be displayed right away).
4231 *
4232 * Note that loadTimeData is not guaranteed to be consistent between page
4233 * refreshes (https://crbug.com/740629) and should not contain values that might
4234 * change if the page is re-opened later.
4235 */
4236
4237/**
4238 * @typedef {{
4239 * substitutions: (Array<string>|undefined),
4240 * attrs: (Object<function(Node, string):boolean>|undefined),
4241 * tags: (Array<string>|undefined),
4242 * }}
4243 */
4244var SanitizeInnerHtmlOpts;
4245
4246/** @type {!LoadTimeData} */ var loadTimeData;
4247
4248// Expose this type globally as a temporary work around until
4249// https://github.com/google/closure-compiler/issues/544 is fixed.
4250/** @constructor */
4251function LoadTimeData(){}
4252
4253(function() {
4254 'use strict';
4255
4256 LoadTimeData.prototype = {
4257 /**
4258 * Sets the backing object.
4259 *
4260 * Note that there is no getter for |data_| to discourage abuse of the form:
4261 *
4262 * var value = loadTimeData.data()['key'];
4263 *
4264 * @param {Object} value The de-serialized page data.
4265 */
4266 set data(value) {
4267 expect(!this.data_, 'Re-setting data.');
4268 this.data_ = value;
4269 },
4270
4271 /**
4272 * Returns a JsEvalContext for |data_|.
4273 * @returns {JsEvalContext}
4274 */
4275 createJsEvalContext: function() {
4276 return new JsEvalContext(this.data_);
4277 },
4278
4279 /**
4280 * @param {string} id An ID of a value that might exist.
4281 * @return {boolean} True if |id| is a key in the dictionary.
4282 */
4283 valueExists: function(id) {
4284 return id in this.data_;
4285 },
4286
4287 /**
4288 * Fetches a value, expecting that it exists.
4289 * @param {string} id The key that identifies the desired value.
4290 * @return {*} The corresponding value.
4291 */
4292 getValue: function(id) {
4293 expect(this.data_, 'No data. Did you remember to include strings.js?');
4294 var value = this.data_[id];
4295 expect(typeof value != 'undefined', 'Could not find value for ' + id);
4296 return value;
4297 },
4298
4299 /**
4300 * As above, but also makes sure that the value is a string.
4301 * @param {string} id The key that identifies the desired string.
4302 * @return {string} The corresponding string value.
4303 */
4304 getString: function(id) {
4305 var value = this.getValue(id);
4306 expectIsType(id, value, 'string');
4307 return /** @type {string} */ (value);
4308 },
4309
4310 /**
4311 * Returns a formatted localized string where $1 to $9 are replaced by the
4312 * second to the tenth argument.
4313 * @param {string} id The ID of the string we want.
4314 * @param {...(string|number)} var_args The extra values to include in the
4315 * formatted output.
4316 * @return {string} The formatted string.
4317 */
4318 getStringF: function(id, var_args) {
4319 var value = this.getString(id);
4320 if (!value)
4321 return '';
4322
4323 var args = Array.prototype.slice.call(arguments);
4324 args[0] = value;
4325 return this.substituteString.apply(this, args);
4326 },
4327
4328 /**
4329 * Make a string safe for use with with Polymer bindings that are
4330 * inner-h-t-m-l (or other innerHTML use).
4331 * @param {string} rawString The unsanitized string.
4332 * @param {SanitizeInnerHtmlOpts=} opts Optional additional allowed tags and
4333 * attributes.
4334 * @return {string}
4335 */
4336 sanitizeInnerHtml: function(rawString, opts) {
4337 opts = opts || {};
4338 return parseHtmlSubset('<b>' + rawString + '</b>', opts.tags, opts.attrs)
4339 .firstChild.innerHTML;
4340 },
4341
4342 /**
4343 * Returns a formatted localized string where $1 to $9 are replaced by the
4344 * second to the tenth argument. Any standalone $ signs must be escaped as
4345 * $$.
4346 * @param {string} label The label to substitute through.
4347 * This is not an resource ID.
4348 * @param {...(string|number)} var_args The extra values to include in the
4349 * formatted output.
4350 * @return {string} The formatted string.
4351 */
4352 substituteString: function(label, var_args) {
4353 var varArgs = arguments;
4354 return label.replace(/\$(.|$|\n)/g, function(m) {
4355 assert(m.match(/\$[$1-9]/), 'Unescaped $ found in localized string.');
4356 return m == '$$' ? '$' : varArgs[m[1]];
4357 });
4358 },
4359
4360 /**
4361 * Returns a formatted string where $1 to $9 are replaced by the second to
4362 * tenth argument, split apart into a list of pieces describing how the
4363 * substitution was performed. Any standalone $ signs must be escaped as $$.
4364 * @param {string} label A localized string to substitute through.
4365 * This is not an resource ID.
4366 * @param {...(string|number)} var_args The extra values to include in the
4367 * formatted output.
4368 * @return {!Array<!{value: string, arg: (null|string)}>} The formatted
4369 * string pieces.
4370 */
4371 getSubstitutedStringPieces: function(label, var_args) {
4372 var varArgs = arguments;
4373 // Split the string by separately matching all occurrences of $1-9 and of
4374 // non $1-9 pieces.
4375 var pieces = (label.match(/(\$[1-9])|(([^$]|\$([^1-9]|$))+)/g) ||
4376 []).map(function(p) {
4377 // Pieces that are not $1-9 should be returned after replacing $$
4378 // with $.
4379 if (!p.match(/^\$[1-9]$/)) {
4380 assert(
4381 (p.match(/\$/g) || []).length % 2 == 0,
4382 'Unescaped $ found in localized string.');
4383 return {value: p.replace(/\$\$/g, '$'), arg: null};
4384 }
4385
4386 // Otherwise, return the substitution value.
4387 return {value: varArgs[p[1]], arg: p};
4388 });
4389
4390 return pieces;
4391 },
4392
4393 /**
4394 * As above, but also makes sure that the value is a boolean.
4395 * @param {string} id The key that identifies the desired boolean.
4396 * @return {boolean} The corresponding boolean value.
4397 */
4398 getBoolean: function(id) {
4399 var value = this.getValue(id);
4400 expectIsType(id, value, 'boolean');
4401 return /** @type {boolean} */ (value);
4402 },
4403
4404 /**
4405 * As above, but also makes sure that the value is an integer.
4406 * @param {string} id The key that identifies the desired number.
4407 * @return {number} The corresponding number value.
4408 */
4409 getInteger: function(id) {
4410 var value = this.getValue(id);
4411 expectIsType(id, value, 'number');
4412 expect(value == Math.floor(value), 'Number isn\'t integer: ' + value);
4413 return /** @type {number} */ (value);
4414 },
4415
4416 /**
4417 * Override values in loadTimeData with the values found in |replacements|.
4418 * @param {Object} replacements The dictionary object of keys to replace.
4419 */
4420 overrideValues: function(replacements) {
4421 expect(
4422 typeof replacements == 'object',
4423 'Replacements must be a dictionary object.');
4424 for (var key in replacements) {
4425 this.data_[key] = replacements[key];
4426 }
4427 }
4428 };
4429
4430 /**
4431 * Checks condition, displays error message if expectation fails.
4432 * @param {*} condition The condition to check for truthiness.
4433 * @param {string} message The message to display if the check fails.
4434 */
4435 function expect(condition, message) {
4436 if (!condition) {
4437 console.error(
4438 'Unexpected condition on ' + document.location.href + ': ' + message);
4439 }
4440 }
4441
4442 /**
4443 * Checks that the given value has the given type.
4444 * @param {string} id The id of the value (only used for error message).
4445 * @param {*} value The value to check the type on.
4446 * @param {string} type The type we expect |value| to be.
4447 */
4448 function expectIsType(id, value, type) {
4449 expect(
4450 typeof value == type, '[' + value + '] (' + id + ') is not a ' + type);
4451 }
4452
4453 expect(!loadTimeData, 'should only include this file once');
4454 loadTimeData = new LoadTimeData;
4455})();
4456</script><script jstcache="0">loadTimeData.data = {"details":"Detalhes","errorCode":"ERR_CONNECTION_TIMED_OUT","fontfamily":"'Segoe UI', Tahoma, sans-serif","fontsize":"75%","heading":{"hostName":"coinhive.com","msg":"Não é possÃvel acessar esse site"},"hideDetails":"Ocultar detalhes","iconClass":"icon-generic","language":"pt","reloadButton":{"msg":"Recarregar","reloadTrackingId":-1,"reloadUrl":"https://coinhive.com/lib/coinhive.min.js"},"suggestionsDetails":[{"body":"Verifique todos os cabos e reinicie todos os roteadores, modens ou outros\n dispositivos de rede que você estiver usando.","header":"Verifique sua conexão com a Internet"},{"body":"Se ele já estiver listado como um programa que tem permissão para acessar a rede, tente\n removê-lo da lista e adicioná-lo novamente.","header":"Permitir que o Chrome acesse a rede nas configurações do seu\n firewall ou antivÃrus."},{"advancedTitle":"Mostrar configurações avançadas…","body":"Verifique suas configurações de proxy ou entre em contato com o administrador de rede para\n verificar se o servidor proxy está funcionando. Se você acredita que não deve\n usar um servidor proxy:\n Vá para\n o menu do Google Chrome >\n \u003Cspan jscontent=\"settingsTitle\">\u003C/span>\n >\n \u003Cspan jscontent=\"advancedTitle\">\u003C/span>\n >\n \u003Cspan jscontent=\"proxyTitle\">\u003C/span>\n >\n Configurações da LAN\n e desmarque a opção \"Usar um servidor proxy para a rede local\".","header":"Se você usa um servidor proxy…","proxyTitle":"Alterar configurações de proxy…","settingsTitle":"Configurações"}],"suggestionsSummaryList":[{"summary":"Verificar a conexão"},{"summary":"\u003Ca href=\"#buttons\" onclick=\"toggleHelpBox()\">Verificar o proxy e o firewall\u003C/a>"},{"summary":"\u003Ca href=\"javascript:diagnoseErrors()\" id=\"diagnose-link\">Executar o Diagnóstico de Rede do Windows\u003C/a>"}],"suggestionsSummaryListHeader":"Tente:","summary":{"failedUrl":"https://coinhive.com/lib/coinhive.min.js","hostName":"coinhive.com","msg":"\u003Cstrong jscontent=\"hostName\">\u003C/strong> demorou muito para responder."},"textdirection":"ltr","title":"coinhive.com"};</script><script jstcache="0">// Copyright (c) 2012 The Chromium Authors. All rights reserved.
4457// Use of this source code is governed by a BSD-style license that can be
4458// found in the LICENSE file.
4459
4460// Note: vulcanize sometimes disables GRIT processing. If you're importing i18n
4461// stuff with <link rel="import">, you should probably be using
4462// html/i18n_template.html instead of this file.
4463
4464// // Copyright (c) 2012 The Chromium Authors. All rights reserved.
4465// Use of this source code is governed by a BSD-style license that can be
4466// found in the LICENSE file.
4467
4468/** @typedef {Document|DocumentFragment|Element} */
4469var ProcessingRoot;
4470
4471/**
4472 * @fileoverview This is a simple template engine inspired by JsTemplates
4473 * optimized for i18n.
4474 *
4475 * It currently supports three handlers:
4476 *
4477 * * i18n-content which sets the textContent of the element.
4478 *
4479 * <span i18n-content="myContent"></span>
4480 *
4481 * * i18n-options which generates <option> elements for a <select>.
4482 *
4483 * <select i18n-options="myOptionList"></select>
4484 *
4485 * * i18n-values is a list of attribute-value or property-value pairs.
4486 * Properties are prefixed with a '.' and can contain nested properties.
4487 *
4488 * <span i18n-values="title:myTitle;.style.fontSize:fontSize"></span>
4489 *
4490 * This file is a copy of i18n_template.js, with minor tweaks to support using
4491 * load_time_data.js. It should replace i18n_template.js eventually.
4492 */
4493
4494var i18nTemplate = (function() {
4495 /**
4496 * This provides the handlers for the templating engine. The key is used as
4497 * the attribute name and the value is the function that gets called for every
4498 * single node that has this attribute.
4499 * @type {!Object}
4500 */
4501 var handlers = {
4502 /**
4503 * This handler sets the textContent of the element.
4504 * @param {!HTMLElement} element The node to modify.
4505 * @param {string} key The name of the value in |data|.
4506 * @param {!LoadTimeData} data The data source to draw from.
4507 * @param {!Set<ProcessingRoot>} visited
4508 */
4509 'i18n-content': function(element, key, data, visited) {
4510 element.textContent = data.getString(key);
4511 },
4512
4513 /**
4514 * This handler adds options to a <select> element.
4515 * @param {!HTMLElement} select The node to modify.
4516 * @param {string} key The name of the value in |data|. It should
4517 * identify an array of values to initialize an <option>. Each value,
4518 * if a pair, represents [content, value]. Otherwise, it should be a
4519 * content string with no value.
4520 * @param {!LoadTimeData} data The data source to draw from.
4521 * @param {!Set<ProcessingRoot>} visited
4522 */
4523 'i18n-options': function(select, key, data, visited) {
4524 var options = data.getValue(key);
4525 options.forEach(function(optionData) {
4526 var option = typeof optionData == 'string' ?
4527 new Option(optionData) :
4528 new Option(optionData[1], optionData[0]);
4529 select.appendChild(option);
4530 });
4531 },
4532
4533 /**
4534 * This is used to set HTML attributes and DOM properties. The syntax is:
4535 * attributename:key;
4536 * .domProperty:key;
4537 * .nested.dom.property:key
4538 * @param {!HTMLElement} element The node to modify.
4539 * @param {string} attributeAndKeys The path of the attribute to modify
4540 * followed by a colon, and the name of the value in |data|.
4541 * Multiple attribute/key pairs may be separated by semicolons.
4542 * @param {!LoadTimeData} data The data source to draw from.
4543 * @param {!Set<ProcessingRoot>} visited
4544 */
4545 'i18n-values': function(element, attributeAndKeys, data, visited) {
4546 var parts = attributeAndKeys.replace(/\s/g, '').split(/;/);
4547 parts.forEach(function(part) {
4548 if (!part)
4549 return;
4550
4551 var attributeAndKeyPair = part.match(/^([^:]+):(.+)$/);
4552 if (!attributeAndKeyPair)
4553 throw new Error('malformed i18n-values: ' + attributeAndKeys);
4554
4555 var propName = attributeAndKeyPair[1];
4556 var propExpr = attributeAndKeyPair[2];
4557
4558 var value = data.getValue(propExpr);
4559
4560 // Allow a property of the form '.foo.bar' to assign a value into
4561 // element.foo.bar.
4562 if (propName[0] == '.') {
4563 var path = propName.slice(1).split('.');
4564 var targetObject = element;
4565 while (targetObject && path.length > 1) {
4566 targetObject = targetObject[path.shift()];
4567 }
4568 if (targetObject) {
4569 targetObject[path] = value;
4570 // In case we set innerHTML (ignoring others) we need to recursively
4571 // check the content.
4572 if (path == 'innerHTML') {
4573 for (var i = 0; i < element.children.length; ++i) {
4574 processWithoutCycles(element.children[i], data, visited, false);
4575 }
4576 }
4577 }
4578 } else {
4579 element.setAttribute(propName, /** @type {string} */ (value));
4580 }
4581 });
4582 }
4583 };
4584
4585 var prefixes = [''];
4586
4587 // Only look through shadow DOM when it's supported. As of April 2015, iOS
4588 // Chrome doesn't support shadow DOM.
4589 if (Element.prototype.createShadowRoot)
4590 prefixes.push('* /deep/ ');
4591
4592 var attributeNames = Object.keys(handlers);
4593 var selector = prefixes
4594 .map(function(prefix) {
4595 return prefix + '[' +
4596 attributeNames.join('], ' + prefix + '[') + ']';
4597 })
4598 .join(', ');
4599
4600 /**
4601 * Processes a DOM tree using a |data| source to populate template values.
4602 * @param {!ProcessingRoot} root The root of the DOM tree to process.
4603 * @param {!LoadTimeData} data The data to draw from.
4604 */
4605 function process(root, data) {
4606 processWithoutCycles(root, data, new Set(), true);
4607 }
4608
4609 /**
4610 * Internal process() method that stops cycles while processing.
4611 * @param {!ProcessingRoot} root
4612 * @param {!LoadTimeData} data
4613 * @param {!Set<ProcessingRoot>} visited Already visited roots.
4614 * @param {boolean} mark Whether nodes should be marked processed.
4615 */
4616 function processWithoutCycles(root, data, visited, mark) {
4617 if (visited.has(root)) {
4618 // Found a cycle. Stop it.
4619 return;
4620 }
4621
4622 // Mark the node as visited before recursing.
4623 visited.add(root);
4624
4625 var importLinks = root.querySelectorAll('link[rel=import]');
4626 for (var i = 0; i < importLinks.length; ++i) {
4627 var importLink = /** @type {!HTMLLinkElement} */ (importLinks[i]);
4628 if (!importLink.import) {
4629 // Happens when a <link rel=import> is inside a <template>.
4630 // TODO(dbeam): should we log an error if we detect that here?
4631 continue;
4632 }
4633 processWithoutCycles(importLink.import, data, visited, mark);
4634 }
4635
4636 var templates = root.querySelectorAll('template');
4637 for (var i = 0; i < templates.length; ++i) {
4638 var template = /** @type {HTMLTemplateElement} */ (templates[i]);
4639 if (!template.content)
4640 continue;
4641 processWithoutCycles(template.content, data, visited, mark);
4642 }
4643
4644 var isElement = root instanceof Element;
4645 if (isElement && root.webkitMatchesSelector(selector))
4646 processElement(/** @type {!Element} */ (root), data, visited);
4647
4648 var elements = root.querySelectorAll(selector);
4649 for (var i = 0; i < elements.length; ++i) {
4650 processElement(elements[i], data, visited);
4651 }
4652
4653 if (mark) {
4654 var processed = isElement ? [root] : root.children;
4655 if (processed) {
4656 for (var i = 0; i < processed.length; ++i) {
4657 processed[i].setAttribute('i18n-processed', '');
4658 }
4659 }
4660 }
4661 }
4662
4663 /**
4664 * Run through various [i18n-*] attributes and populate.
4665 * @param {!Element} element
4666 * @param {!LoadTimeData} data
4667 * @param {!Set<ProcessingRoot>} visited
4668 */
4669 function processElement(element, data, visited) {
4670 for (var i = 0; i < attributeNames.length; i++) {
4671 var name = attributeNames[i];
4672 var attribute = element.getAttribute(name);
4673 if (attribute != null)
4674 handlers[name](element, attribute, data, visited);
4675 }
4676 }
4677
4678 return {process: process};
4679}());
4680
4681// // Copyright 2017 The Chromium Authors. All rights reserved.
4682// Use of this source code is governed by a BSD-style license that can be
4683// found in the LICENSE file.
4684
4685i18nTemplate.process(document, loadTimeData);
4686
4687</script><script jstcache="0">// Copyright (c) 2012 The Chromium Authors. All rights reserved.
4688// Use of this source code is governed by a BSD-style license that can be
4689// found in the LICENSE file.
4690
4691// This file serves as a proxy to bring the included js file from /third_party
4692// into its correct location under the resources directory tree, whence it is
4693// delivered via a chrome://resources URL. See ../webui_resources.grd.
4694
4695// Note: this <include> is not behind a single-line comment because the first
4696// line of the file is source code (so the first line would be skipped) instead
4697// of a licence header.
4698// clang-format off
4699(function(){var i=null;function k(){return Function.prototype.call.apply(Array.prototype.slice,arguments)}function l(a,b){var c=k(arguments,2);return function(){return b.apply(a,c)}}function m(a,b){var c=new n(b);for(c.f=[a];c.f.length;){var e=c,d=c.f.shift();e.g(d);for(d=d.firstChild;d;d=d.nextSibling)d.nodeType==1&&e.f.push(d)}}function n(a){this.g=a}function o(a){a.style.display=""}function p(a){a.style.display="none"};var q=":",r=/\s*;\s*/;function s(){this.i.apply(this,arguments)}s.prototype.i=function(a,b){if(!this.a)this.a={};if(b){var c=this.a,e=b.a,d;for(d in e)c[d]=e[d]}else for(c in d=this.a,e=t,e)d[c]=e[c];this.a.$this=a;this.a.$context=this;this.d=typeof a!="undefined"&&a!=i?a:"";if(!b)this.a.$top=this.d};var t={$default:i},u=[];function v(a){for(var b in a.a)delete a.a[b];a.d=i;u.push(a)}function w(a,b,c){try{return b.call(c,a.a,a.d)}catch(e){return t.$default}}
4700function x(a,b,c,e){if(u.length>0){var d=u.pop();s.call(d,b,a);a=d}else a=new s(b,a);a.a.$index=c;a.a.$count=e;return a}var y="a_",z="b_",A="with (a_) with (b_) return ",D={};function E(a){if(!D[a])try{D[a]=new Function(y,z,A+a)}catch(b){}return D[a]}function F(a){for(var b=[],a=a.split(r),c=0,e=a.length;c<e;++c){var d=a[c].indexOf(q);if(!(d<0)){var f;f=a[c].substr(0,d).replace(/^\s+/,"").replace(/\s+$/,"");d=E(a[c].substr(d+1));b.push(f,d)}}return b};var G="jsinstance",H="jsts",I="*",J="div",K="id";function L(){}var M=0,N={0:{}},P={},Q={},R=[];function S(a){a.__jstcache||m(a,function(a){T(a)})}var U=[["jsselect",E],["jsdisplay",E],["jsvalues",F],["jsvars",F],["jseval",function(a){for(var b=[],a=a.split(r),c=0,e=a.length;c<e;++c)if(a[c]){var d=E(a[c]);b.push(d)}return b}],["transclude",function(a){return a}],["jscontent",E],["jsskip",E]];
4701function T(a){if(a.__jstcache)return a.__jstcache;var b=a.getAttribute("jstcache");if(b!=i)return a.__jstcache=N[b];for(var b=R.length=0,c=U.length;b<c;++b){var e=U[b][0],d=a.getAttribute(e);Q[e]=d;d!=i&&R.push(e+"="+d)}if(R.length==0)return a.setAttribute("jstcache","0"),a.__jstcache=N[0];var f=R.join("&");if(b=P[f])return a.setAttribute("jstcache",b),a.__jstcache=N[b];for(var h={},b=0,c=U.length;b<c;++b){var d=U[b],e=d[0],g=d[1],d=Q[e];d!=i&&(h[e]=g(d))}b=""+ ++M;a.setAttribute("jstcache",b);N[b]=
4702h;P[f]=b;return a.__jstcache=h}function V(a,b){a.h.push(b);a.k.push(0)}function W(a){return a.c.length?a.c.pop():[]}
4703L.prototype.e=function(a,b){var c=X(b),e=c.transclude;if(e)(c=Y(e))?(b.parentNode.replaceChild(c,b),e=W(this),e.push(this.e,a,c),V(this,e)):b.parentNode.removeChild(b);else if(c=c.jsselect){var c=w(a,c,b),d=b.getAttribute(G),f=!1;d&&(d.charAt(0)==I?(d=parseInt(d.substr(1),10),f=!0):d=parseInt(d,10));var h=c!=i&&typeof c=="object"&&typeof c.length=="number",e=h?c.length:1,g=h&&e==0;if(h)if(g)d?b.parentNode.removeChild(b):(b.setAttribute(G,"*0"),p(b));else if(o(b),d===i||d===""||f&&d<e-1){f=W(this);
4704d=d||0;for(h=e-1;d<h;++d){var j=b.cloneNode(!0);b.parentNode.insertBefore(j,b);Z(j,c,d);g=x(a,c[d],d,e);f.push(this.b,g,j,v,g,i)}Z(b,c,d);g=x(a,c[d],d,e);f.push(this.b,g,b,v,g,i);V(this,f)}else d<e?(f=c[d],Z(b,c,d),g=x(a,f,d,e),f=W(this),f.push(this.b,g,b,v,g,i),V(this,f)):b.parentNode.removeChild(b);else c==i?p(b):(o(b),g=x(a,c,0,1),f=W(this),f.push(this.b,g,b,v,g,i),V(this,f))}else this.b(a,b)};
4705L.prototype.b=function(a,b){var c=X(b),e=c.jsdisplay;if(e){if(!w(a,e,b)){p(b);return}o(b)}if(e=c.jsvars)for(var d=0,f=e.length;d<f;d+=2){var h=e[d],g=w(a,e[d+1],b);a.a[h]=g}if(e=c.jsvalues){d=0;for(f=e.length;d<f;d+=2)if(g=e[d],h=w(a,e[d+1],b),g.charAt(0)=="$")a.a[g]=h;else if(g.charAt(0)=="."){for(var g=g.substr(1).split("."),j=b,O=g.length,B=0,$=O-1;B<$;++B){var C=g[B];j[C]||(j[C]={});j=j[C]}j[g[O-1]]=h}else g&&(typeof h=="boolean"?h?b.setAttribute(g,g):b.removeAttribute(g):b.setAttribute(g,""+
4706h))}if(e=c.jseval){d=0;for(f=e.length;d<f;++d)w(a,e[d],b)}e=c.jsskip;if(!e||!w(a,e,b))if(c=c.jscontent){if(c=""+w(a,c,b),b.innerHTML!=c){for(;b.firstChild;)e=b.firstChild,e.parentNode.removeChild(e);b.appendChild(this.j.createTextNode(c))}}else{c=W(this);for(e=b.firstChild;e;e=e.nextSibling)e.nodeType==1&&c.push(this.e,a,e);c.length&&V(this,c)}};function X(a){if(a.__jstcache)return a.__jstcache;var b=a.getAttribute("jstcache");if(b)return a.__jstcache=N[b];return T(a)}
4707function Y(a,b){var c=document;if(b){var e=c.getElementById(a);if(!e){var e=b(),d=H,f=c.getElementById(d);if(!f)f=c.createElement(J),f.id=d,p(f),f.style.position="absolute",c.body.appendChild(f);d=c.createElement(J);f.appendChild(d);d.innerHTML=e;e=c.getElementById(a)}c=e}else c=c.getElementById(a);return c?(S(c),c=c.cloneNode(!0),c.removeAttribute(K),c):i}function Z(a,b,c){c==b.length-1?a.setAttribute(G,I+c):a.setAttribute(G,""+c)};window.jstGetTemplate=Y;window.JsEvalContext=s;window.jstProcess=function(a,b){var c=new L;S(b);c.j=b?b.nodeType==9?b:b.ownerDocument||document:document;var e=l(c,c.e,a,b),d=c.h=[],f=c.k=[];c.c=[];e();for(var h,g,j;d.length;)h=d[d.length-1],e=f[f.length-1],e>=h.length?(e=c,g=d.pop(),g.length=0,e.c.push(g),f.pop()):(g=h[e++],j=h[e++],h=h[e++],f[f.length-1]=e,g.call(c,j,h))};
4708})()
4709</script><script jstcache="0">var tp = document.getElementById('t');jstProcess(loadTimeData.createJsEvalContext(), tp);</script></body></html>