Add rrr to appcache.
[ogre.git] / ogre.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 if (!String.prototype.repeat)
15 Object.defineProperty(String.prototype, "repeat", {
16 value: function (count) {
17 var string = this.toString();
18 var result = '';
19 var n = count | 0;
20 while (n) {
21 if (n % 2 === 1)
22 result += string;
23 if (n > 1)
24 string += string;
25 n >>= 1;
26 }
27 return result;
28 }
29 });
30
31 if (!Array.prototype.fill)
32 Object.defineProperty(Array.prototype, "fill", {
33 value: function (value) {
34 var beg = arguments.length > 1 ? +arguments[1] : 0;
35 var end = arguments.length > 2 ? +arguments[2] : this.length;
36 if (beg < 0) beg += this.length;
37 if (end < 0) end += this.length;
38 for (var i = beg; i < end; ++i)
39 this[i] = value;
40 return this;
41 }
42 });
43
44 if (!Element.prototype.matches)
45 Object.defineProperty(Element.prototype, "matches", {
46 value: Element.prototype.matchesSelector
47 || Element.prototype.mozMatchesSelector
48 || Element.prototype.webkitMatchesSelector
49 });
50
51 function removeUnit (element) {
52 element.parentNode.removeChild(element);
53 }
54
55 var EMPTY = "☐";
56 var CHECK = "☒";
57 var FIRST_EMPTY = /☐/;
58 var LAST_TICKED = /☒([^☒]*)$/;
59
60 function randomName (scheme) {
61 return G.expand(scheme);
62 }
63
64 var STATS = ["attack", "range", "defense", "aside"];
65 function createUnit (dfnName) {
66 var dfn = UNITS[dfnName];
67 var weapons = dfn.weapons || [];
68 var unit = document.getElementById("unit-template").cloneNode(true);
69 unit.removeAttribute('id');
70 unit.setAttribute("data-dfn", dfnName);
71 unit.querySelector(".name").textContent = randomName(dfn.nameScheme);
72 unit.querySelector(".type").textContent = dfnName;
73
74 if (dfn.aside) {
75 var aside = unit.appendChild(document.createElement("div"));
76 aside.className = "aside";
77 aside.innerHTML = dfn.aside;
78 }
79 if (weapons.length) {
80 var weaponList = unit.appendChild(document.createElement('ul'));
81 weaponList.className = "weapons";
82 }
83
84 weapons.forEach(function (weaponRef) {
85 var count = parseInt(weaponRef, 10) || 1;
86 var weaponName = weaponRef.replace(/^[0-9 ]*/, "");
87 var weapon = WEAPONS[weaponName]
88 || WEAPONS[weaponName.replace(/s+$/, "")];
89 var weaponItem = document.createElement("li");
90 weaponItem.setAttribute('data-name', weaponName);
91 weaponItem.setAttribute('data-count', count);
92 weaponItem.setAttribute('data-remaining', count);
93 var stats = document.createElement('ul');
94 stats.className = "stats";
95 for (var j = 0; j < STATS.length; ++j) {
96 if (weapon[STATS[j]] !== undefined) {
97 var stat = document.createElement('li');
98 stat.className = STATS[j];
99 stat.innerHTML = weapon[STATS[j]];
100 stats.appendChild(stat);
101 }
102 }
103 if (stats.children.length)
104 weaponItem.appendChild(stats);
105 var ticks = document.createElement('div');
106 ticks.className = "ticks";
107 ticks.innerHTML = ticksText(count);
108 weaponItem.appendChild(ticks);
109 weaponList.appendChild(weaponItem);
110 });
111
112 if (dfn.tread) {
113 var move = dfn.move || 3;
114 var per = dfn.tread / move;
115 var treads = document.createElement('ol');
116 treads.className = "treads";
117 treads.appendChild(document.createElement("li"));
118 treads.setAttribute('data-count', dfn.tread);
119 treads.setAttribute('data-remaining', dfn.tread);
120 for (var i = 0; i < move; ++i) {
121 var tread = treads.appendChild(document.createElement("li"));
122 tread.className = "ticks";
123 tread.innerHTML = ticksText(per);
124 }
125 unit.appendChild(treads);
126 }
127 if (dfn.propulsion) {
128 var move = dfn.move || 3;
129 var per = dfn.propulsion / move;
130 var treads = document.createElement('ol');
131 treads.className = "treads propulsion";
132 treads.appendChild(document.createElement("li"));
133 treads.setAttribute('data-count', dfn.propulsion);
134 treads.setAttribute('data-remaining', dfn.propulsion);
135 for (var i = 0; i < move; ++i) {
136 var tread = treads.appendChild(document.createElement("li"));
137 tread.className = "ticks";
138 tread.innerHTML = ticksText(per);
139 }
140 unit.appendChild(treads);
141 }
142
143 return unit;
144 }
145
146 function addUnit (dfnName) {
147 document.querySelector('main').appendChild(createUnit(dfnName));
148 }
149
150 function spre (c) {
151 return new RegExp("(^|\\s+)" + c + "(\\s+|$)");
152 }
153
154 function hasClass (e, n) {
155 return !!e.className.match(spre(n));
156 }
157
158 function addClass (e, n) {
159 if (!hasClass(e, n))
160 e.className += " " + n;
161 }
162
163 function removeClass (e, n) {
164 e.className = e.className.replace(spre(n), "");
165 }
166
167 function show (id) {
168 addClass(document.getElementById(id.id || id), "visible");
169 }
170 function hide (id) {
171 removeClass(document.getElementById(id.id || id), "visible");
172 }
173 function handleTray (evt) {
174 var id = this.getAttribute('data-tray');
175 var el = document.getElementById(id);
176 var f = (hasClass(el, "visible") ? hide : show)
177 autoClose();
178 f(el);
179 evt.stopPropagation();
180 }
181
182 function ticksText (n) {
183 var blocks = [EMPTY.repeat(n)];
184 for (var i = 5; i >= 2; --i) {
185 if (n > i && n % i === 0) {
186 blocks = (new Array(n / i)).fill(EMPTY.repeat(i));
187 break;
188 }
189 }
190 return "<span>" + blocks.join("</span><wbr><span>") + "</span>";
191 }
192
193 function rub (content) {
194 return content.replace(LAST_TICKED, EMPTY + "$1");
195 }
196 function tick (content) {
197 return content.replace(FIRST_EMPTY, CHECK);
198 }
199
200 function findParent (el, selector) {
201 while (el && el !== document && !el.matches(selector))
202 el = el.parentNode;
203 return el === document ? null : el;
204 }
205
206 function boxes (event) {
207 var target = event.target;
208 if (!target.innerHTML.match(/☐|☒/))
209 return;
210 var par = findParent(target, '[data-count]');
211 var ticks = findParent(target, '.ticks');
212 if (!ticks || !par)
213 return;
214 var content = target.innerHTML;
215 var rect = target.getBoundingClientRect();
216 var total = target.innerHTML.match(/☐|☒/g);
217 var ticked = target.innerHTML.match(/☒/g);
218 var p = (event.clientX - rect.left) / rect.width;
219 var pr = (total && total.length)
220 ? (ticked ? ticked.length : 0) / total.length : 0;
221 par.innerHTML = ((p < pr) ? rub : tick)(par.innerHTML);
222 var rem = par.innerHTML.match(/☐/g);
223 par.setAttribute('data-remaining', rem ? rem.length : 0);
224 event.preventDefault();
225 event.stopPropagation();
226 }
227
228 function fade (p) {
229 return p * p * p * (p * (p * 6.0 - 15.0) + 10.0);
230 }
231
232 function scroll (y1, t) {
233 var y0 = document.body.scrollTop || document.documentElement.scrollTop;
234 var n = (t || 150) / 15;
235 var i = 0;
236 clearInterval(scroll.owner);
237 scroll.owner = setInterval(function () {
238 var p = Math.max(0, Math.min(++i / n, 1));
239 document.body.scrollTop
240 = document.documentElement.scrollTop
241 = y0 + (y1 - y0) * fade(p);
242 if (i >= n) clearInterval(scroll.owner);
243 }, 15);
244 }
245
246 function next (el) {
247 scroll(el.nextElementSibling.offsetTop - el.parentNode.offsetTop);
248 }
249
250 function previous (el) {
251 scroll(el.previousElementSibling.offsetTop - el.parentNode.offsetTop);
252 }
253
254 function autoClose () {
255 var open = document.querySelectorAll(".tray.visible");
256 for (var i = 0; i < open.length; ++i)
257 hide(open[i]);
258 return open && open.length;
259 }
260
261 window.addEventListener("DOMContentLoaded", function () {
262 if (navigator.standalone)
263 document.body.className += " standalone";
264 var units = document.getElementById("addUnit");
265 Object.keys(UNITS).sort(function (a, b) {
266 return (b.indexOf("Ogre Mk") - a.indexOf("Ogre Mk"))
267 || (a > b) - (a < b);
268 }).forEach(function (unitName) {
269 var unit = units.appendChild(document.createElement("li"));
270 unit.textContent = unitName;
271 unit.addEventListener("click", function () {
272 addUnit(unitName);
273 hide('addUnit');
274 });
275 });
276 addUnit('Ogre Mk. V');
277 FastClick.attach(document.body, { tapDelay: 50 });
278
279 var trays = document.querySelectorAll('[data-tray]');
280 for (var i = 0; i < trays.length; ++i)
281 trays[i].addEventListener('click', handleTray);
282 window.addEventListener('click', function (evt) {
283 if (!findParent(evt.target, ".tray.visible"))
284 autoClose();
285 });
286 });