├── .gitignore
├── gulpfile.js
├── package.json
├── examples
└── save.txt
├── README.md
└── ChoiceScriptSavePlugin.js
/.gitignore:
--------------------------------------------------------------------------------
1 | dist/*
2 | node_modules/*
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var uglify = require('gulp-uglify');
3 | var pump = require('pump');
4 |
5 | gulp.task('compress', function(cb) {
6 | pump([
7 | gulp.src('ChoiceScriptSavePlugin.js'),
8 | uglify(),
9 | gulp.dest('dist')
10 | ],
11 | cb
12 | );
13 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "choicescript-save-plugin",
3 | "version": "1.0.0",
4 | "description": "A small addon that allows for control over persistent saving in ChoiceScript games.",
5 | "main": "ChoiceScriptSavePlugin.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "min": "npx uglifyjs ChoiceScriptSavePlugin.js > ChoiceScriptSavePlugin.min.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/ChoicescriptIDE/ChoiceScriptSavePlugin.git"
13 | },
14 | "author": "CJW",
15 | "bugs": {
16 | "url": "https://github.com/ChoicescriptIDE/ChoiceScriptSavePlugin/issues"
17 | },
18 | "homepage": "https://github.com/ChoicescriptIDE/ChoiceScriptSavePlugin#readme",
19 | "devDependencies": {},
20 | "dependencies": {
21 | "uglify-js": "^3.17.4"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/save.txt:
--------------------------------------------------------------------------------
1 | *comment "ENUM" like constants to represent types of ACTION.
2 | *temp ACTION_SYNC 1
3 | *temp ACTION_SAVE 2
4 | *temp ACTION_LOAD 3
5 | *temp ACTION_DELETE 4
6 |
7 | *temp action ACTION_SYNC
8 |
9 | *comment Saving/loading is asynchronous whilst
10 | *comment ChoiceScript execution is synchronous. This means
11 | *comment we have to arbitrarily 'delay' access to save data.
12 | *comment Forcing a page_break between actions seemed the
13 | *comment simplest compromise for now.
14 | *label update
15 | *comment populate the save/load helper variable
16 | *sm_update
17 | Confirming
18 | *gosub print_action_id_as_string action "."
19 | *comment force a page_break to give the async update
20 | *comment time to populate the helper variables
21 | *page_break
22 | *set action ACTION_SAVE
23 | *label top
24 | Detected ${_sm_save_count} saves.
25 | *line_break
26 | *if (_sm_save_count >= 1)
27 | Last save was ${_sm_save_name[0]} on ${_sm_save_date[0]}
28 |
29 | *choice
30 | # [Quick Save]
31 | *sm_save "Quick Save"
32 | *set action ACTION_SAVE
33 | *comment update the helper variables
34 | *goto update
35 | # [New Save]
36 | *temp save_name ""
37 | What would you like to call this save?
38 | *input_text save_name
39 | *sm_save save_name
40 | *set action ACTION_SAVE
41 | *comment update the helper variables
42 | *goto update
43 | # Load
44 | *set action ACTION_LOAD
45 | *goto menu
46 | # Delete
47 | *set action ACTION_DELETE
48 | *goto menu
49 | # Toggle Quick Save Menu
50 | *comment toggles visibility if no parameter is given
51 | *sm_menu
52 | *goto top
53 |
54 | *label menu
55 | *temp offset 0
56 | *label menu_offset
57 | *temp selected_save_id 0
58 | What would you like to
59 | *gosub print_action_id_as_string action "?"
60 | *comment present paginated results for delete/load
61 | *fake_choice
62 | *if (_sm_save_count > offset) # ${_sm_save_name[offset]} ${_sm_save_date[offset]}
63 | *set selected_save_id _sm_save_id[offset]
64 | *if (_sm_save_count > (offset + 1)) # ${_sm_save_name[offset+1]} ${_sm_save_date[offset+1]}
65 | *set selected_save_id _sm_save_id[offset+1]
66 | *if (_sm_save_count > (offset + 2)) # ${_sm_save_name[offset+2]} ${_sm_save_date[offset+2]}
67 | *set selected_save_id _sm_save_id[offset+2]
68 | *if (_sm_save_count > (offset + 3)) # ${_sm_save_name[offset+3]} ${_sm_save_date[offset+3]}
69 | *set selected_save_id _sm_save_id[offset+3]
70 | *if (_sm_save_count > (offset + 4)) # ${_sm_save_name[offset+4]} ${_sm_save_date[offset+4]}
71 | *set selected_save_id _sm_save_id[offset+4]
72 | *if (_sm_save_count > (offset + 5)) # --> Next
73 | *set offset + 5
74 | *goto menu_offset
75 | *if (offset > 0) # <-- Previous
76 | *set offset - 5
77 | *goto menu_offset
78 | # Return
79 | *goto top
80 | *if (action = ACTION_LOAD) and (selected_save_id > 0)
81 | *sm_load selected_save_id
82 | *if (action = ACTION_DELETE) and (selected_save_id > 0)
83 | *sm_delete selected_save_id
84 | *goto update
85 |
86 | *comment helper routine to turn an ACTION_{TYPE} into
87 | *comment a human readable string, with an optional suffix.
88 | *label print_action_id_as_string
89 | *params aid
90 | @{action sync|save|load|delete}${param_2}
91 | *return
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The ChoiceScript Save Plugin
2 | Is a small addon script for ChoiceScript to allow control over persistent (hard) saves.
3 |
4 | 
5 |
6 | It provides both a Visual Novel-esque quick save menu, and some additional ChoiceScript commands, in order to give you the greatest flexibility over how and where you game can be saved.
7 |
8 | ## Installation
9 |
10 | As the ChoiceScript save plugin is an unofficial 'addon' for ChoiceScript, there are a few technical steps required to get it up and running.
11 |
12 | First and foremost, you'll need to download the latest copy of the JavaScript file here. Next, you'll need to add that file to your game's 'web' folder, next to the other script files, as shown below.
13 |
14 | 
15 |
16 | Now you'll need to make two small edits to your game's index.html file (the one in the 'mygame' folder, not the one located in the 'web' folder).
17 |
18 | The first edit is to include the script you just downloaded, so that the game knows where to find it.
19 | If you've got the previous steps correct and have the right index.html file, this should be a simple copy and paste of the following line: ``````. The positioning is important, so make sure to place it between navigator.js and mygame.js, as shown below.
20 |
21 | 
22 |
23 | Last, but not least, you'll need to provide a unique name to identify your game and its saves, as shown below. Note that this should be something that is unlikely to change or clash with another game's. "MY_GAME" is a bad example, as is "MY_WIP".
24 |
25 | 
26 |
27 | Also note that if you ever wish to disable the 'persistent session' behaviour in ChoiceScript, you can set this back to ```window.storeName = null;```, though this will also disable the saving mod.
28 |
29 | ## Usage Notes
30 |
31 | Unlike the previous save plugins (save.js, smPlugin.js and smPluginMenuAddon.js) this plugin leverages heavily on save behaviour that is already present within the ChoiceScript interpreter. Whilst this greatly simplifies and improves the stability of the plugin, it does come with a few caveats. Most notably is the requirement to enable the 'persistent storage' behaviour of ChoiceScript games. This is where the game will 'remember' where you left off, should you leave and return to the page. Much the same as the games published on the choiceofgames.com site. Whilst helpful when playing, this behaviour can prove rather detrimental during development. It is suggested that you disable this when developing your game, via setting ```window.storeName``` back to ```null```.
32 |
33 | Though both built upon the system mentioned above, the ChoiceScript save plugin provides two different interfaces for handling saving and loading in your ChoiceScript game.
34 |
35 | ### Quick Save Menu
36 | The recommended way to use the plugin is via 'quick save menu' and its associated controls.
37 | If you've installed the plugin correctly then this menu should appear automatically, and will self-populate with any saves based on the ```window.storeName``` you provided. Aside from the obvious UI injection, this interface is relatively non-invasive and shouldn't heavily effect your development workflow.
38 |
39 | ### Save Mod Commands
40 | Unlike the quick save menu, the sm_ commands (sm_load, and sm_save) are a highly invasive way of interfacing with the save/load functionality provided by the plugin and ChoiceScript interpreter. As they are new and unofficial commands, they will at the very least, cause issues with QuickTest and RandomTest (and will likely require commenting out). Usage of these commands may also produce some unexpected behaviour, such as load/save loops, or duplicate saves (when loading/refreshing *into* a page containing a save command).
41 |
42 | 
43 |
44 | Note that unlike the previous smPlugin.js addon, the \*sm_save command will **not** save the game at the exact position it appears, but will rather in effect save from the start of the *current* visible page (regardless of where it is placed in the code).
45 |
46 | That said, usage of these commands does still allow for more developer flexibility over how saving/loading is implemented in the game. Some examples being the implementation of autosaves, or restricting saving/loading to certain menus or game segments.
47 |
48 | 
49 |
50 | You can see the ```save.txt``` file in this repostiory's examples folder for inspiration on how to use the following commands.
51 |
52 | #### \*sm_save
53 | Creates a new save at the point of the last 'page' turn. Optionally, a string name for the save can be provided.
54 |
55 | ```*sm_save```, ```*sm_save "My Save Name"``` or ```*sm_save my_string_var``` etc.
56 |
57 | #### \*sm_load
58 |
59 | Loads the game with the given id.
60 |
61 | ```*sm_load my_save_id``` or ```*sm_load 1390323109``` etc.
62 |
63 | #### \*sm_delete
64 |
65 | Deletes the save with the given id.
66 |
67 | ```*sm_delete my_save_id``` or ```*sm_delete 3211245125``` etc.
68 |
69 | ### \*sm_update
70 |
71 | > Due to the nature of ChoiceScript execution (synchronous) and the nature of saving/loading (asynchronous),
72 | > it is difficult to guarantee the accurate population of these variables in time for ChoiceScript's execution.
73 | > The recommendation thus is to use *sm_update way in advance of requiring it, to ensure you don't encounter
74 | > stale information or unintalized variables.
75 |
76 | Populates (or updates) the following helper variables:
77 |
78 | - (number) ```_sm_save_count```: The number of saves detected.
79 | - (array of numbers) ```_sm_save_id_x```: These variables hold the ID (used for loading and deleting) of the given save.
80 | - (array of strings) ```_sm_save_name_x```: These variables hold the name for the given save (if it has one).
81 | - (array of strings) ```_sm_save_date_x```: These variables hold a string formatted date marking the save time.
82 |
83 | #### \*sm_menu
84 |
85 | Disable (hide) or enable (show) the quick save menu.
86 |
87 | ```*sm_menu true``` enables (shows).
88 | ```*sm_menu false``` disables (hides).
89 | ```*sm_menu``` toggles the current state.
90 |
91 |
--------------------------------------------------------------------------------
/ChoiceScriptSavePlugin.js:
--------------------------------------------------------------------------------
1 | /* ----- New ChoiceScript Commands ----- */
2 | Scene.prototype.sm_save = function(line) {
3 | var stack = this.tokenizeExpr(line);
4 | if (stack.length > 2)
5 | throw new Error("sm_save: Invalid number of arguments, expected 0, 1 (save name) or 2 (id).");
6 | ChoiceScriptSavePlugin._save(new Date().getTime(), stack.length == 1 ? this.evaluateExpr(stack) : null);
7 | }
8 |
9 | Scene.prototype.sm_load = function(line) {
10 | var stack = this.tokenizeExpr(line);
11 | var variable = this.evaluateExpr(stack);
12 | //if (stack.length === 0)
13 | this.finished = true;
14 | this.skipFooter = true;
15 | this.screenEmpty = true;
16 | ChoiceScriptSavePlugin._load(variable);
17 | }
18 |
19 | Scene.prototype.sm_delete = function(line) {
20 | var stack = this.tokenizeExpr(line);
21 | if (stack.length != 1)
22 | throw new Error("sm_delete: Invalid number of arguments, expected 1.");
23 | ChoiceScriptSavePlugin._delete(this.evaluateExpr(stack));
24 | }
25 |
26 | Scene.prototype.sm_update = function() {
27 | if (typeof this.stats._sm_save_count === "undefined")
28 | this.stats._sm_save_count = 0;
29 | ChoiceScriptSavePlugin._getSaveList(function(saveList) {
30 | if (!saveList)
31 | return;
32 | ChoiceScriptSavePlugin._syncHelperVariables(saveList, function() {});
33 | });
34 | }
35 |
36 | Scene.prototype.sm_menu = function(data) {
37 | data = data || "";
38 | data = data.toLowerCase();
39 | var selectEle = document.getElementById("quickSaveMenu");
40 | if (!selectEle)
41 | return;
42 | var active = false;
43 | if (data === "false") {
44 | active = false;
45 | } else if (data === "true") {
46 | active = true;
47 | } else if (!data) { // toggle
48 | active = selectEle.style.display == "none";
49 | } else {
50 | throw new Error("*sm_menu: expected true, false (or nothing) as an argument!");
51 | }
52 | selectEle.style.display = active ? "inline" : "none";
53 | var btns = document.getElementsByClassName("savePluginBtn");
54 | for (var i = 0; i < btns.length; i++) {
55 | btns[i].style.display = active ? "inline" : "none";
56 | }
57 | }
58 |
59 | Scene.validCommands["sm_save"] = 1;
60 | Scene.validCommands["sm_load"] = 1;
61 | Scene.validCommands["sm_delete"] = 1;
62 | Scene.validCommands["sm_update"] = 1;
63 | Scene.validCommands["sm_menu"] = 1;
64 |
65 | /* ----- FrameWork Functionality (Internal) ----- */
66 |
67 | var ChoiceScriptSavePlugin = {}
68 |
69 | ChoiceScriptSavePlugin._CSS =
70 | "#quickSaveMenu {\
71 | margin: 5px;\
72 | width: 100px;\
73 | }";
74 |
75 | /* Saving once a page has finished loading causes a lot of problems.
76 | However, ChoiceScript already stores a working save at the top of every page,
77 | so we can just copy that save over to the specified slot. */
78 | ChoiceScriptSavePlugin._save = function(saveId, saveName) {
79 | restoreObject(initStore(), "state", null, function(baseSave) {
80 | if (baseSave) {
81 | baseSave.stats["_smSaveName"] = saveName || "";
82 | baseSave.stats["_smSaveDateId"] = saveId;
83 | ChoiceScriptSavePlugin._addToSaveList(saveId, function(success) {
84 | if (!success)
85 | return;
86 | saveCookie(function() {}, ChoiceScriptSavePlugin._formatSlotName(saveId), baseSave.stats, baseSave.temps, baseSave.lineNum, baseSave.indent, this.debugMode, this.nav);
87 | /* Attempt to re-populate the quick save menu.
88 | This might not actually exist when an sm_save is run,
89 | so we have to wait a few seconds. If it still doesn't exist
90 | it's not the end of the world, but the save won't appear until
91 | the next refresh. */
92 | setTimeout(function() {
93 | var selectEle = document.getElementById("quickSaveMenu");
94 | if (selectEle) {
95 | selectEle.innerHTML = "";
96 | ChoiceScriptSavePlugin._populateSaveMenu(selectEle);
97 | }
98 | }, 3000);
99 | });
100 | } else {
101 | /* ChoiceScript hasn't created a save we can use yet.
102 | This happens when we try to save right after the game
103 | starts (or a save has just been loaded).
104 | */
105 | }
106 | });
107 | }
108 |
109 | /* Utility function to grab a slots (near) full name:
110 | Save data is stored in the form:
111 | 'state' + STORE_NAME + '_SAVE_' + dateId
112 | Where 'state' is something ChoiceScript uses internally,
113 | STORE_NAME is provided in the game's index.html,
114 | and dateId is the unique handle/key stored in the save list.
115 |
116 | Note that 'state' is not included here, as we use some internal
117 | CS functions that already add it. Instead we hard-code it in the
118 | few places we rely directly on the persist.js API.
119 | */
120 | ChoiceScriptSavePlugin._formatSlotName = function(saveId){
121 | return (window.storeName + '_SAVE_' + saveId);
122 | }
123 |
124 | ChoiceScriptSavePlugin._load = function(saveId) {
125 | clearScreen(loadAndRestoreGame.bind(stats.scene, ChoiceScriptSavePlugin._formatSlotName(saveId)));
126 | }
127 |
128 | ChoiceScriptSavePlugin._delete = function(saveId) {
129 | ChoiceScriptSavePlugin._removeFromSaveList(saveId, function(success) {
130 | if (!success)
131 | return;
132 | var select = document.getElementById("quickSaveMenu");
133 | if (select) {
134 | var deletedOption = select.options[select.selectedIndex];
135 | if (deletedOption)
136 | deletedOption.parentElement.removeChild(deletedOption);
137 | }
138 | initStore().remove("state" + ChoiceScriptSavePlugin._formatSlotName(saveId), function(success, val) {
139 | // Likely there's nothing to delete
140 | });
141 | });
142 | }
143 |
144 | ChoiceScriptSavePlugin._createQuickSaveMenu = function() {
145 |
146 | var p = document.getElementById("menuButton");
147 |
148 | if (!p) {
149 | alert("Error: unable to attach quick save menu");
150 | return;
151 | }
152 |
153 | p = p.parentElement;
154 |
155 | // CSS
156 | var head = document.getElementsByTagName("head")[0];
157 | var style = document.createElement("style");
158 | style.innerHTML = ChoiceScriptSavePlugin._CSS;
159 | head.appendChild(style);
160 |
161 | // HTML
162 | var selectEle = document.createElement("select");
163 | selectEle.setAttribute("id", "quickSaveMenu");
164 |
165 | p.appendChild(selectEle);
166 |
167 | var buttonArr = [{
168 | "innerHTML": "New Save",
169 | "clickFunc": "ChoiceScriptSavePlugin.save();"
170 | },
171 | {
172 | "innerHTML": "Load",
173 | "clickFunc": "ChoiceScriptSavePlugin.load();"
174 | },
175 | {
176 | "innerHTML": "Delete",
177 | "clickFunc": "ChoiceScriptSavePlugin.delete();"
178 | }
179 | ];
180 |
181 | for (var i = 0; i < buttonArr.length; i++) {
182 | var btn = document.createElement("button");
183 | btn.innerHTML = buttonArr[i].innerHTML;
184 | btn.setAttribute("class", "spacedLink savePluginBtn");
185 | btn.setAttribute("onclick", buttonArr[i].clickFunc);
186 | p.appendChild(btn);
187 | }
188 |
189 | return selectEle;
190 | }
191 |
192 | /* Add the 'option' elements to the given selection input */
193 | ChoiceScriptSavePlugin._populateSaveMenu = function(selectEle) {
194 | ChoiceScriptSavePlugin._getSaveList(function(saveList) {
195 | if (!saveList)
196 | return;
197 | saveList.forEach(function(saveId) {
198 | /* Grab the save data, so we can give it a nice title via _saveName */
199 | ChoiceScriptSavePlugin._getSaveData(saveId, function(saveData) {
200 | if (!saveData) {
201 | return;
202 | }
203 | var option = document.createElement("option");
204 | option.setAttribute("value", saveData.stats._smSaveDateId /* time/date */ );
205 | if (!saveData) {
206 | option.innerHTML = "Failed to load save.";
207 | } else {
208 | var slotDesc = saveData.stats.sceneName + '.txt (' + simpleDateTimeFormat(new Date(parseInt(saveData.stats._smSaveDateId))) + ')';
209 | if (saveData.stats._smSaveName) {
210 | slotDesc = saveData.stats._smSaveName + " — " + slotDesc;
211 | }
212 | option.innerHTML = slotDesc;
213 | }
214 | selectEle.appendChild(option);
215 | });
216 | });
217 | });
218 | }
219 |
220 | ChoiceScriptSavePlugin._getSaveData = function(saveId, callback) {
221 | restoreObject(initStore(), "state" + ChoiceScriptSavePlugin._formatSlotName(saveId), null, function(saveData) {
222 | if (saveData) {
223 | callback(saveData);
224 | } else {
225 | /* Something went wrong. */
226 | callback(null);
227 | }
228 | });
229 | }
230 |
231 | /* The save list is a space separated string of date identifiers, e.g.
232 | "1581976656199 1581976297095 1581976660752"
233 | We use this to keep a record of stored save keys/handles.
234 | */
235 | ChoiceScriptSavePlugin._removeFromSaveList = function(saveId, callback) {
236 | ChoiceScriptSavePlugin._getSaveList(function(saveList) {
237 | if (!saveList)
238 | return;
239 | var index = saveList.indexOf(saveId.toString());
240 | if (index > -1)
241 | saveList.splice(index, 1);
242 | initStore().set("save_list", saveList.join(" "), function(success, val) {
243 | ChoiceScriptSavePlugin._syncHelperVariables(saveList, function() {
244 | callback(success);
245 | })
246 | });
247 | });
248 | }
249 |
250 | ChoiceScriptSavePlugin._addToSaveList = function(saveId, callback) {
251 | ChoiceScriptSavePlugin._getSaveList(function(saveList) {
252 | if (!saveList)
253 | return;
254 | saveList.push(saveId.toString());
255 | initStore().set("save_list", saveList.join(" "), function(success, val) {
256 | ChoiceScriptSavePlugin._syncHelperVariables(saveList, function() {
257 | callback(success);
258 | })
259 | });
260 | });
261 | }
262 |
263 | ChoiceScriptSavePlugin._syncHelperVariables = function(saveList, callback) {
264 | self.stats._sm_save_count = saveList.length;
265 | saveList.forEach(function(save, index) {
266 | ChoiceScriptSavePlugin._getSaveData(save, function(saveData) {
267 | if (saveData) {
268 | self.stats["_sm_save_id_" + index] = save;
269 | self.stats["_sm_save_name_" + index] = saveData.stats._smSaveName || "";
270 | self.stats["_sm_save_date_" + index] = simpleDateTimeFormat(new Date(parseInt(save)));
271 | }
272 | });
273 | });
274 | callback();
275 | }
276 |
277 | /* Pull the list of stored 'saves' from the store by store name */
278 | ChoiceScriptSavePlugin._getSaveList = function(callback) {
279 | initStore().get("save_list", function(success, val) {
280 | if (!success)
281 | callback(null);
282 | if (!val)
283 | callback([]);
284 | else
285 | callback(saveList = val.split(" ").sort(function(a, b) {
286 | return b - a;
287 | }));
288 | });
289 | }
290 |
291 | ChoiceScriptSavePlugin._init = function() {
292 | // don't initialize until files have been uploaded (CS commit: 8092aedf17505bd5f9b46c76acf082b89d494a03)
293 | if (("file:" === window.location.protocol) &&
294 | (typeof window.uploadedFiles === "undefined" && typeof allScenes === "undefined")) {
295 | setTimeout(ChoiceScriptSavePlugin._init, 3000);
296 | return;
297 | }
298 | if (!window.storeName) {
299 | // disallow sm_ commands as they depend on a store
300 | Scene.validCommands["sm_save"] = 0;
301 | Scene.validCommands["sm_load"] = 0;
302 | Scene.validCommands["sm_delete"] = 0;
303 | Scene.validCommands["sm_menu"] = 0;
304 | Scene.validCommands["sm_menu"] = 0;
305 | return alertify.error("Disabling ChoiceScript Save Plugin as there is no storeName detected. Please check your index.html.");
306 | }
307 | ChoiceScriptSavePlugin._populateSaveMenu(ChoiceScriptSavePlugin._createQuickSaveMenu());
308 | }
309 |
310 | /* ----- FrameWork Functionality (External) ----- */
311 |
312 | ChoiceScriptSavePlugin.save = function() {
313 | if (stats.sceneName == "choicescript_stats") {
314 | alert("Error: Unable to save at this point.");
315 | return;
316 | }
317 | var date = new Date();
318 | var message = "What would you like to call this save?
Leaving this blank will result in a scene and date identifier.";
319 |
320 | alertify.prompt(message, function(e, saveName) {
321 | if (e) {
322 | ChoiceScriptSavePlugin._save(date.getTime(), saveName);
323 | } else {
324 | // user cancelled
325 | }
326 | }, "Quick Save" /* default value */ );
327 | }
328 |
329 | ChoiceScriptSavePlugin.delete = function() {
330 | var select = document.getElementById("quickSaveMenu");
331 | if (select.value <= 0)
332 | return;
333 | var message = "Delete save '" + select.options[select.selectedIndex].text + '\'?
This cannot be undone!';
334 | alertify.confirm(message, function(result) {
335 | if (!result) {
336 | return;
337 | } else {
338 | ChoiceScriptSavePlugin._delete(parseInt(select.value));
339 | }
340 | });
341 | }
342 |
343 | ChoiceScriptSavePlugin.load = function() {
344 | var select = document.getElementById("quickSaveMenu");
345 | if (select.value <= 0)
346 | return;
347 | alertify.confirm("Are you sure you wish to load this save?
Current progress will be lost!", function(result) {
348 | if (!result) {
349 | return;
350 | } else {
351 | ChoiceScriptSavePlugin._load(select.value);
352 | }
353 | });
354 | }
355 |
356 | // initialize after a small delay, so everything else can catch up.
357 | setTimeout(ChoiceScriptSavePlugin._init, 3000);
358 |
--------------------------------------------------------------------------------