Legal notices.
[labelle.git] / acnl-outfit.js
1 // http://creativecommons.org/publicdomain/zero/1.0/
2 //
3 // To the extent possible under law, the person who associated CC0
4 // with this work has waived all copyright and related or neighboring
5 // rights to this work.
6
7 "use strict";
8
9 Array.prototype.contains = function (o) {
10 return this.indexOf(o) >= 0;
11 };
12
13 Array.prototype.lacks = function (o) {
14 return !this.contains(o);
15 };
16
17 Array.prototype.containsSubset = function (o) {
18 return o.every(this.contains, this);
19 };
20
21 Array.prototype.pushUnique = function (o) {
22 if (this.lacks(o))
23 this.push(o);
24 };
25
26 var BLOCKS = {
27 helmets: ["hats", "accessories"],
28 wetsuits: ["tops", "bottoms", "socks", "dresses"],
29 dresses: ["tops", "bottoms"]
30 };
31 Object.keys(ACNL_WEARABLES).forEach(function (type) {
32 BLOCKS[type] = (BLOCKS[type] || []).concat(type);
33 });
34 Object.keys(BLOCKS).forEach(function (k) {
35 BLOCKS[k].forEach(function (v) {
36 BLOCKS[v].pushUnique(k);
37 });
38 });
39
40 function choice (os) {
41 return os[Math.floor(os.length * Math.random())];
42 }
43
44 var flatten1 = Array.prototype.concat.apply.bind(Array.prototype.concat, []);
45
46 function get (attr) {
47 return this[attr];
48 }
49
50 var category = get.bind(ACNL_WEARABLES_CATEGORY);
51 var wearables = get.bind(ACNL_WEARABLES);
52
53 function isLewd (outfit) {
54 return ![["wetsuits"], ["dresses"], ["tops", "bottoms"]].some(
55 Array.prototype.containsSubset, outfit.map(category));
56 }
57
58 function impossibilities (outfit) {
59 return flatten1(outfit.map(category).map(get, BLOCKS));
60 }
61
62 function possibilities (outfit) {
63 return Object.keys(ACNL_WEARABLES).filter(
64 Array.prototype.lacks, impossibilities(outfit));
65 }
66
67 function outfit () {
68 var ps = [];
69 while (isLewd(ps))
70 ps.push(choice(flatten1(possibilities(ps).map(wearables))));
71 return ps;
72 }
73
74 function isPosessive (item) {
75 return item[0].toUpperCase() === item[0] && item.indexOf("'s") >= 0;
76 }
77
78 function isPlural (item) {
79 return item.substr(-8) === "headgear"
80 || (item.substr(-1) === "s"
81 && item.substr(-2) !== "ss"
82 && item.substr(-6) !== "cosmos"); // :(
83 }
84
85 function article (item) {
86 if (item[0].toUpperCase() === item[0] && item.indexOf("'s") >= 0)
87 return "";
88 else if (isPlural(item))
89 return "";
90 else if ("aeiouAEIO".indexOf(item[0]) >= 0
91 || ("FHLMNRSX".indexOf(item[0]) >= 0
92 && item.split(" ")[0].toUpperCase() == item.split(" ")[0]))
93 return "an ";
94 else
95 return "a ";
96 }
97
98 function dblink (name) {
99 return ["<a target=\"_blank\" href=\"http://moridb.com/items/search?q=",
100 encodeURIComponent(name),
101 "\">", name.replace("&", "&amp;"), "</a>"].join("");
102 }
103
104 function litanize (names, link) {
105 names = names.map(function (name) {
106 return article(name) + (link ? dblink(name) : name);
107 });
108 switch (names.length) {
109 case 1: break;
110 case 2:
111 names[names.length - 1] = (choice(["and ", "with "])
112 + names[names.length - 1]);
113 break;
114 default:
115 if (Math.random() > 0.5 && names.length >= 4) {
116 names[names.length - 2] = "and " + names[names.length - 2];
117 names[names.length - 1] = "with " + names[names.length - 1];
118 } else {
119 names[names.length - 1] = "and " + names[names.length - 1];
120 }
121 break;
122 }
123
124 return names.join(names.length == 2 ? " " : ", ");
125 }
126
127 function updateTime () {
128 var date = new Date();
129 document.documentElement.className = "month" + date.getMonth()
130 document.body.className = "hour" + date.getHours();
131 if (window.navigator.standalone)
132 document.body.className += " standalone";
133 }
134 setInterval(updateTime, 20 * 60 * 1000);
135
136 function update () {
137 var o = outfit();
138 var wearing = document.getElementById('wearing');
139 wearing.innerHTML = litanize(o, true) + "?";
140 // TODO: This often generates > 140 characters. Do I care?
141 // Maybe pick shorter prefixes if it would help.
142 var twitter = document.getElementById('twitter');
143 twitter.href = twitter.href.replace(
144 /&text=[^&]+/, "&text=" + encodeURIComponent(
145 choice(["Going out in ",
146 "Labelle suggested ",
147 "I'll wear "]) + litanize(o, false) + "."));
148
149 var buttons = document.getElementById("buttons");
150 buttons.className = "";
151 setTimeout(function () {
152 buttons.className = "open";
153 }, 300);
154 updateTime();
155 }
156
157 window.addEventListener("DOMContentLoaded", update);
158
159 window.addEventListener('load', function () {
160 if (typeof FastClick !== "undefined")
161 FastClick.attach(document.body, { tapDelay: 50 });
162
163 if (applicationCache && applicationCache.status) {
164 applicationCache.update();
165 applicationCache.addEventListener('updateready', function () {
166 if (applicationCache.status === applicationCache.UPDATEREADY) {
167 applicationCache.swapCache();
168 }
169 });
170 }
171 });