-
Notifications
You must be signed in to change notification settings - Fork 0
/
humble-helper.js
165 lines (143 loc) · 5.25 KB
/
humble-helper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Set some convenience globals.
var manifest = browser.runtime.getManifest();
var extension_id = manifest.applications.gecko.id;
var display_order = [
"bundle", "display_order", "name", "selected", "steam", "drmfree",
"genres", "platforms", "developers",
"url_youtube", "url_steam", "url_image",
"msrp", "id",
];
var rawinfo = null;
// Function that extracts information from the raw info dump.
function parseRawInfo(rawinfo) {
var games_info = rawinfo.contentChoiceData.initial.content_choices;
var display_order = rawinfo.contentChoiceData.initial.display_order;
var bundle = rawinfo.contentChoiceData.initial.title;
var choices_made = rawinfo.contentChoicesMade.initial.choices_made;
var choices_info = {};
for (var g in games_info) {
// main game info and shorthands for long paths
var gi = games_info[g];
var ytl = gi.carousel_content["youtube-link"];
var steamid = gi.tpkds[0]["steam_app_id"];
// sort stuff
gi.genres.sort();
gi.platforms.sort();
// assemble exported info dictionary
var info = {
"id": g,
"bundle": bundle,
"name": gi.title,
"genres": gi.genres.join(", "),
"platforms": gi.platforms.join(", "),
"msrp": gi["msrp|money"].amount + gi["msrp|money"].currency,
"developers": gi.developers.join(", "),
"url_youtube": (ytl.length > 0) ? "https://youtu.be/" + ytl[0] : "",
"url_steam": (steamid != "") ? "https://store.steampowered.com/app/" + steamid : "",
"url_image": gi.image,
"steam": gi.delivery_methods.includes("steam"),
"drmfree": gi.delivery_methods.includes("download"),
"selected": false,
"display_order": -1
};
choices_info[g] = info;
}
// update selected games
choices_made.forEach(function(g, i) {
choices_info[g].selected = true;
});
// games in displayed order
var choices_info_o = [];
display_order.forEach(function(g, i) {
choices_info[g].display_order = i+1;
choices_info_o.push(choices_info[g]);
});
return choices_info_o;
}
// Function that converts raw info to TSV format.
function convertToTSV(rawinfo) {
var info = parseRawInfo(rawinfo);
var tsv = "";
info.forEach(function(g, i) {
display_order.forEach(function(k, j) {
tsv += g[k] + "\t";
});
tsv += "\n";
});
return {"format": "TSV", "count": info.length, "data": tsv};
}
// Function that converts raw info to JSON format.
function convertToJSON(rawinfo) {
var info = parseRawInfo(rawinfo);
var json = JSON.stringify(info, null, 2);
return {"format": "JSON", "count": info.length, "data": json};
}
// Function to copy game information from a dictionary to clipboard.
function copyToClipboard(contents) {
var success = false;
navigator.clipboard.writeText(contents).then(function() {
success = true;
}, function() {
success = false;
console.log(copied);
});
return success;
}
// show the presence of the extension
document.body.style.border = "10px solid rgb(151, 177, 71)";
// Observer used to intercept raw data from the DOM, before they are removed.
// Content script must be set to run at document_end.
const removal_observer = new MutationObserver(function(mutations, observer) {
for(let m of mutations) {
if (m.type === "childList" && m.removedNodes.length > 0) {
if (m.removedNodes.length > 1) {
window.alert(manifest.name + ": Unexpected removal of multiple DOM items.");
}
if (m.removedNodes[0].id != "webpack-monthly-product-data") {
continue;
}
var parsed = JSON.parse(m.removedNodes[0].innerText);
if ("contentChoiceOptions" in parsed) {
rawinfo = parsed.contentChoiceOptions;
observer.disconnect();
console.log(manifest.name + ": raw info retrieved");
break;
}
}
}
});
// register observer for DOM changes
console.log(manifest.name + ": waiting for raw info to appear");
removal_observer.observe(document.body, { childList: true });
// register listener for messages from page action script
browser.runtime.onMessage.addListener(function(message, sender, sendResponse) {
var error = true;
var response = "unknown";
const valid_buttons = ["humble-helper-copy-tsv", "humble-helper-copy-json", "humble-helper-copy-html"];
if (sender.id != extension_id) {
response = "Received message from unknown sender: " + sender.id;
} else if (!("humble_helper" in message)) {
response = "Received invalid message.";
} else if (!valid_buttons.includes(message.humble_helper)) {
response = "Invalid request in message: " + message.humble_helper;
} else if (rawinfo == null) {
response = "No game information have been found.";
} else {
// all good - convert data
var converted = null;
if (message.humble_helper == "humble-helper-copy-tsv") {
converted = convertToTSV(rawinfo);
} else if (message.humble_helper == "humble-helper-copy-json") {
converted = convertToJSON(rawinfo);
} else {
response = "Requested format not yet supported.";
}
// copy to clipboard
if (converted != null) {
error = copyToClipboard(converted.data);
response = (error ? "Failed to copy" : "Copied") + ` ${converted.count} items in ${converted.format} format to clipboard.`;
}
}
sendResponse({"error": error, "message": response});
});
// vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab cursorline