Well, yes it does, but it doesn't need FastClick.
[mlpccg.git] / mlpccg.js
1 /* The person who associated a work with this deed has dedicated the
2 work to the public domain by waiving all of his or her rights to
3 the work worldwide under copyright law, including all related and
4 neighboring rights, to the extent allowed by law.
5
6 You can copy, modify, distribute and perform the work, even for
7 commercial purposes, all without asking permission.
8
9 See https://creativecommons.org/publicdomain/zero/1.0/ for details.
10 */
11
12 "use strict";
13
14 var APP = "com.yukkurigames.mlpccg";
15
16 var THEMES = [
17 "applejack",
18 "cadance",
19 "celestia",
20 "chrysalis",
21 "dash",
22 "discord",
23 "fluttershy",
24 "luna",
25 "maud",
26 "muffins",
27 "pinkie",
28 "pon3",
29 "rarity",
30 "spike",
31 "twilight",
32 ];
33
34 window.requestAnimationFrame = (
35 window.requestAnimationFrame
36 || window.mozRequestAnimationFrame
37 || window.webkitRequestAnimationFrame);
38 window.cancelAnimationFrame = (
39 window.cancelAnimationFrame
40 || window.mozCancelAnimationFrame
41 || window.webkitCancelAnimationFrame);
42
43 function debounce (callback, wait) {
44 wait = wait || 100;
45 var this_ = null;
46 var args = null;
47 var id = null;
48
49 function _ () {
50 callback.apply(this_, args);
51 this_ = null;
52 args = null;
53 id = null;
54 }
55
56 return function () {
57 clearTimeout(id);
58 this_ = this;
59 args = arguments;
60 id = setTimeout(_, wait);
61 };
62 }
63
64 function iclamp (x, lo, hi) {
65 return Math.max(Math.min(x | 0, hi), lo);
66 }
67
68 function broadcast (data, source) {
69 source = source || window;
70 if (window.parent && window.parent !== source && window.parent !== window) {
71 window.parent.postMessage(data, "*");
72 }
73 for (var i = 0; i < window.frames.length; ++i) {
74 var w = window.frames[i].window;
75 if (w !== source) {
76 w.postMessage(data, "*");
77 }
78 }
79 }
80
81 var messages = {};
82
83 window.addEventListener('message', function (event) {
84 broadcast(event.data, event.source);
85 if (messages[event.data.type])
86 messages[event.data.type](event.data);
87 });
88
89 function stop (event) {
90 event.preventDefault();
91 }
92
93 function nextTheme (element) {
94 var nextId = (THEMES.indexOf(element.className) + 1) % THEMES.length;
95 var theme = THEMES[nextId];
96 element.className = theme;
97 var id = element.id ? element.id
98 : element === document.body.parentNode ? "html"
99 : null;
100 if (id) {
101 var key = [APP, "theme", window.name || "window", id].join(".");
102 localStorage[key] = theme;
103 }
104 }
105
106 var repaint = debounce(function () {
107 document.body.removeChild(
108 document.body.appendChild(
109 document.createElement('style')));
110 }, 50);
111
112 window.addEventListener('touchmove', stop);
113
114 // Work around incorrect viewport units in (mostly Mobile) Safari. At
115 // page-load time they are often calculated as 0 (because the viewport
116 // isn't ready yet?) and they are not refreshed when the viewport does
117 // change size.
118 window.addEventListener('load', repaint);
119 window.addEventListener('resize', repaint);
120 window.addEventListener('orientationchange', repaint);
121
122 function randomTheme () {
123 return THEMES[(Math.random() * THEMES.length) | 0];
124 }
125
126 function storedTheme () {
127 return localStorage[
128 [APP, "theme", window.name || "window", "html"].join(".")];
129 }
130
131 function loadTheme () {
132 if (THEMES.indexOf(location.hash.slice(1)) >= 0) {
133 document.body.parentNode.className = location.hash.slice(1);
134 } else if (document.body.parentNode.className === "stored-theme") {
135 var random = THEMES[(Math.random() * THEMES.length) | 0];
136 document.body.parentNode.className = storedTheme() || randomTheme();
137 } else if (document.body.parentNode.className === "random-theme") {
138 document.body.parentNode.className = randomTheme();
139 }
140 }
141
142 window.addEventListener('hashchange', function () {
143 var theme = location.hash.slice(1);
144 if (THEMES.indexOf(theme) >= 0) {
145 document.body.parentNode.className = theme;
146 }
147 });
148
149 window.addEventListener('pageshow', repaint);
150
151 function removeStatusBar () {
152 document.body.className = "";
153 }
154
155 function addStatusBar () {
156 document.body.className = "statusbar";
157 }
158
159 function open (event) {
160 (this.target === "_parent" ? window.parent : window).location.href
161 = this.href;
162 event.preventDefault();
163 }
164
165 window.addEventListener('DOMContentLoaded', function () {
166 if (navigator.standalone) {
167 var links = document.querySelectorAll("a[href]:not([target=_blank])");
168 for (var i = 0; i < links.length; ++i) {
169 links[i].addEventListener('click', open);
170 }
171 }
172
173 if (typeof FastClick !== "undefined")
174 FastClick.attach(document.body, { tapDelay: 50 });
175 loadTheme();
176 });
177
178 function afterAnimationFrame (f) {
179 /* DOM class modifications intended to trigger transitions
180 must be delayed for at least one frame after the element is
181 created, i.e. after it has gone through at least one full
182 repaint.
183 */
184 window.requestAnimationFrame(function () {
185 window.requestAnimationFrame(f);
186 });
187 }
188
189 function spin () {
190 var orig = document.getElementById("spinner");
191 var spinner = orig.cloneNode();
192 spinner.className = 'spinner';
193 spinner.style.opacity = 1;
194 spinner.style.transform = spinner.style["-webkit-transform"] = "rotateZ(0deg)";
195 document.body.replaceChild(spinner, orig);
196 afterAnimationFrame(function () {
197 var z = (10 + (Math.random() * 2) | 0) * 180;
198 var transform = "rotateZ(" + z + "deg)";
199 spinner.style.transform = spinner.style["-webkit-transform"] = transform;
200 spinner.style.opacity = 0;
201 });
202 }
203
204 function roll (n) {
205 var die = document.createElement("div");
206 die.className = 'die rolling d' + n;
207 document.getElementById("triggered").appendChild(die);
208 die.setAttribute('data-roll', 1 + (Math.random() * n) | 0);
209 var id = setInterval(function () {
210 die.setAttribute('data-roll', 1 + (Math.random() * n) | 0);
211 }, 430);
212
213 die.addEventListener('animationend', function _ () {
214 die.removeEventListener('animationend', _);
215 die.parentNode.removeChild(die);
216 });
217 die.style.transform = die.style["-webkit-transform"] = "rotateY(-270deg)";
218 afterAnimationFrame(function () {
219 die.style.transform = die.style["-webkit-transform"] = "rotateY(360deg)";
220 function _ () {
221 clearInterval(id);
222 die.removeEventListener('transitionend', _);
223 die.removeEventListener('webkitTransitionEnd', _);
224 }
225 die.addEventListener('transitionend', _);
226 die.addEventListener('webkitTransitionEnd', _);
227 });
228 }
229
230 if (applicationCache && applicationCache.status) {
231 applicationCache.update();
232 applicationCache.addEventListener('updateready', function () {
233 if (applicationCache.status === applicationCache.UPDATEREADY) {
234 applicationCache.swapCache();
235 }
236 });
237 }
238
239 function dismiss () {
240 var open = document.querySelectorAll(".dialog.open");
241 for (var i = 0; i < open.length; ++i)
242 open[i].className = "dialog";
243 }
244
245 function show (id) {
246 dismiss();
247 var target = document.getElementById(id);
248 target.className += " open";
249 }
250