1 // http://creativecommons.org/publicdomain/zero/1.0/
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.
9 Array
.prototype.firstWhere = function (callback
, thisArg
) {
10 for (var i
= 0; i
< this.length
; ++i
)
11 if (callback
.call(thisArg
, this[i
]))
15 Array
.prototype.contains = function (o
) {
16 return this.indexOf(o
) >= 0;
19 Array
.prototype.lacks = function (o
) {
20 return !this.contains(o
);
23 Array
.prototype.containsSubset = function (o
) {
24 return o
.every(this.contains
, this);
27 Array
.prototype.pushUnique = function (o
) {
33 helmets
: ["hats", "accessories"],
34 wetsuits
: ["tops", "bottoms", "socks", "dresses"],
35 dresses
: ["tops", "bottoms"]
37 Object
.keys(ACNL_WEARABLES
).forEach(function (type
) {
38 BLOCKS
[type
] = (BLOCKS
[type
] || []).concat(type
);
40 Object
.keys(BLOCKS
).forEach(function (k
) {
41 BLOCKS
[k
].forEach(function (v
) {
42 BLOCKS
[v
].pushUnique(k
);
46 function choice (os
) {
47 return os
[Math
.floor(os
.length
* Math
.random())];
50 function category (item
) {
51 return Object
.keys(ACNL_WEARABLES
).firstWhere(function (type
) {
52 return ACNL_WEARABLES
[type
].contains(item
);
56 function notLewd (outfit
) {
57 var categories
= outfit
.map(category
);
58 return (categories
.contains("wetsuits")
59 || categories
.contains("dresses")
60 || categories
.containsSubset(["tops", "bottoms"]));
63 function buildArray (a
, l
) {
64 return (a
|| []).concat(l
);
67 function impossibilities (categories
) {
68 return categories
.map(function (c
) { return BLOCKS
[c
]; }
69 ).reduce(buildArray
, []);
72 function possibilities (outfit
) {
73 return Object
.keys(ACNL_WEARABLES
74 ).filter(Array
.prototype.lacks
, impossibilities(outfit
.map(category
))
75 ).map(function (k
) { return ACNL_WEARABLES
[k
] }
76 ).reduce(buildArray
, []);
79 function isFinished(outfit
) {
80 return notLewd(outfit
) && Math
.random() > 0.5;
83 function nextItem (outfit
) {
84 return !isFinished(outfit
) && choice(possibilities(outfit
));
90 while (next
= nextItem(ps
))
95 function article (item
) {
96 if (item
[0].toUpperCase() === item
[0] && item
.indexOf("'s") >= 0)
98 else if (item
.substr(-1) === "s" && item
.substr(-2) !== "ss"
99 && item
.substr(-6) !== "cosmos") // :(
101 else if ("aeiouAEIO".indexOf(item
[0]) >= 0
102 || ("FHLMNRSX".indexOf(item
[0]) >= 0
103 && item
.split(" ")[0].toUpperCase() == item
.split(" ")[0]))
109 function dblink (name
) {
110 return ["<a target=\"_blank\" href=\"http://moridb.com/items/search?q=",
111 encodeURIComponent(name
),
112 "\">", name
.replace("&", "&"), "</a>"].join("");
115 function litanize (names
, link
) {
116 names
= names
.map(function (name
) {
117 return article(name
) + (link
? dblink(name
) : name
);
119 switch (names
.length
) {
122 names
[names
.length
- 1] = (choice(["and ", "with "])
123 + names
[names
.length
- 1]);
126 if (Math
.random() > 0.5 && names
.length
>= 4) {
127 names
[names
.length
- 2] = "and " + names
[names
.length
- 2];
128 names
[names
.length
- 1] = "with " + names
[names
.length
- 1];
130 names
[names
.length
- 1] = "and " + names
[names
.length
- 1];
135 return names
.join(names
.length
== 2 ? " " : ", ");
138 function litanizeElement (eid
, tid
) {
140 document
.getElementById(eid
).innerHTML
= litanize(o
, true) + "?";
141 // TODO: This often generates > 140 characters. Do I care?
142 // Maybe pick shorter prefixes if it would help.
143 var t
= document
.getElementById(tid
);
144 t
.href
= t
.href
.replace(
145 /&text=[^&]+/, "&text=" + encodeURIComponent(
146 choice(["Going out in ",
147 "Labelle suggested ",
148 "I'll wear "]) + litanize(o
, false) + "."));