Last active
December 22, 2019 00:11
-
-
Save jonasjohansson/938e71f56ad76c01d9c6f2d7c2fed3c5 to your computer and use it in GitHub Desktop.
Bitsy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(bitsy) { | |
"use strict"; | |
bitsy = bitsy && bitsy.hasOwnProperty("default") ? bitsy["default"] : bitsy; | |
function inject(searchRegex, replaceString) { | |
// find the relevant script tag | |
var scriptTags = document.getElementsByTagName("script"); | |
var scriptTag; | |
var code; | |
for (var i = 0; i < scriptTags.length; ++i) { | |
scriptTag = scriptTags[i]; | |
var matchesSearch = scriptTag.textContent.search(searchRegex) !== -1; | |
var isCurrentScript = scriptTag === document.currentScript; | |
if (matchesSearch && !isCurrentScript) { | |
code = scriptTag.textContent; | |
break; | |
} | |
} | |
// error-handling | |
if (!code) { | |
throw "Couldn't find \"" + searchRegex + '" in script tags'; | |
} | |
// modify the content | |
code = code.replace(searchRegex, replaceString); | |
// replace the old script tag with a new one using our modified code | |
var newScriptTag = document.createElement("script"); | |
newScriptTag.textContent = code; | |
scriptTag.insertAdjacentElement("afterend", newScriptTag); | |
scriptTag.remove(); | |
} | |
function unique(array) { | |
return array.filter(function(item, idx) { | |
return array.indexOf(item) === idx; | |
}); | |
} | |
function before(targetFuncName, beforeFn) { | |
var kitsy = kitsyInit(); | |
kitsy.queuedBeforeScripts[targetFuncName] = kitsy.queuedBeforeScripts[targetFuncName] || []; | |
kitsy.queuedBeforeScripts[targetFuncName].push(beforeFn); | |
} | |
// Ex: after('load_game', function run() { alert('Loaded!'); }); | |
function after(targetFuncName, afterFn) { | |
var kitsy = kitsyInit(); | |
kitsy.queuedAfterScripts[targetFuncName] = kitsy.queuedAfterScripts[targetFuncName] || []; | |
kitsy.queuedAfterScripts[targetFuncName].push(afterFn); | |
} | |
function kitsyInit() { | |
// return already-initialized kitsy | |
if (bitsy.kitsy) { | |
return bitsy.kitsy; | |
} | |
// Initialize kitsy | |
bitsy.kitsy = { | |
queuedInjectScripts: [], | |
queuedBeforeScripts: {}, | |
queuedAfterScripts: {} | |
}; | |
var oldStartFunc = bitsy.startExportedGame; | |
bitsy.startExportedGame = function doAllInjections() { | |
// Only do this once. | |
bitsy.startExportedGame = oldStartFunc; | |
// Rewrite scripts and hook everything up. | |
doInjects(); | |
applyAllHooks(); | |
// Start the game | |
bitsy.startExportedGame.apply(this, arguments); | |
}; | |
return bitsy.kitsy; | |
} | |
function doInjects() { | |
bitsy.kitsy.queuedInjectScripts.forEach(function(injectScript) { | |
inject(injectScript.searchRegex, injectScript.replaceString); | |
}); | |
_reinitEngine(); | |
} | |
function applyAllHooks() { | |
var allHooks = unique(Object.keys(bitsy.kitsy.queuedBeforeScripts).concat(Object.keys(bitsy.kitsy.queuedAfterScripts))); | |
allHooks.forEach(applyHook); | |
} | |
function applyHook(functionName) { | |
var functionNameSegments = functionName.split("."); | |
var obj = bitsy; | |
while (functionNameSegments.length > 1) { | |
obj = obj[functionNameSegments.shift()]; | |
} | |
var lastSegment = functionNameSegments[0]; | |
var superFn = obj[lastSegment]; | |
var superFnLength = superFn ? superFn.length : 0; | |
var functions = []; | |
// start with befores | |
functions = functions.concat(bitsy.kitsy.queuedBeforeScripts[functionName] || []); | |
// then original | |
if (superFn) { | |
functions.push(superFn); | |
} | |
// then afters | |
functions = functions.concat(bitsy.kitsy.queuedAfterScripts[functionName] || []); | |
// overwrite original with one which will call each in order | |
obj[lastSegment] = function() { | |
var returnVal; | |
var args = [].slice.call(arguments); | |
var i = 0; | |
function runBefore() { | |
// All outta functions? Finish | |
if (i === functions.length) { | |
return returnVal; | |
} | |
// Update args if provided. | |
if (arguments.length > 0) { | |
args = [].slice.call(arguments); | |
} | |
if (functions[i].length > superFnLength) { | |
// Assume funcs that accept more args than the original are | |
// async and accept a callback as an additional argument. | |
return functions[i++].apply(this, args.concat(runBefore.bind(this))); | |
} else { | |
// run synchronously | |
returnVal = functions[i++].apply(this, args); | |
if (returnVal && returnVal.length) { | |
args = returnVal; | |
} | |
return runBefore.apply(this, args); | |
} | |
} | |
return runBefore.apply(this, arguments); | |
}; | |
} | |
function _reinitEngine() { | |
// recreate the script and dialog objects so that they'll be | |
// referencing the code with injections instead of the original | |
bitsy.scriptModule = new bitsy.Script(); | |
bitsy.scriptInterpreter = bitsy.scriptModule.CreateInterpreter(); | |
bitsy.dialogModule = new bitsy.Dialog(); | |
bitsy.dialogRenderer = bitsy.dialogModule.CreateRenderer(); | |
bitsy.dialogBuffer = bitsy.dialogModule.CreateBuffer(); | |
} | |
window.foo = { | |
after: after | |
}; | |
})(window); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment