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