Last active
May 21, 2022 09:45
-
-
Save xiejiangzhi/ef0f50f29d5dece5e08d37d0dbdccbad to your computer and use it in GitHub Desktop.
Support javascript for Desmos
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
// ==UserScript== | |
// @name Desmos script | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description try to take over the world! | |
// @author You | |
// @match https://*.desmos.com/calculator* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=desmos.com | |
// @grant unsafeWindow | |
// @grant GM_xmlhttpRequest | |
// ==/UserScript== | |
/* | |
Write script in comment | |
Example: | |
create latex for string | |
js: output(`a = ${Date.now()}`) | |
create table for array | |
js: output( [ { latex: 'x', values: [1,2,3, 4, 5 ] }, { latex: 'x+2'} ]); | |
just run script | |
js: console.log(123); | |
helper functions: | |
output(string or array); // string for latex, array for table | |
exp_val('a + 1', (v) => output(v)); // get exp value | |
get_remote_data('http://api.ipify.org', (v) => output(v)); // get remote data | |
run_remote_js('http://xxx.js', () => { }); // run js by a url and also can use helper function to generate data. | |
import_remote_js('http://xxx.js', () => { }); // import a js library, execute once. | |
https://www.desmos.com/calculator/fi0tmugjq2 | |
*/ | |
(function() { | |
'use strict'; | |
var Calc; | |
var WaitDesmosFunc, StartFunc; | |
WaitDesmosFunc = function() { | |
Calc = unsafeWindow.Calc; | |
if (Calc) { | |
StartFunc(); | |
} else { | |
setTimeout(WaitDesmosFunc, 100); | |
} | |
} | |
setTimeout(WaitDesmosFunc, 500); | |
let Scripts = {}; | |
var TodoJobs = {}; | |
function ProcessJobs() { | |
try { | |
for (let id in TodoJobs) { | |
let script = TodoJobs[id]; | |
if (Scripts[id] != script) { | |
console.log("Exec script ", id); | |
Scripts[id] = script; | |
UpdateExp(id, script); | |
} | |
} | |
TodoJobs = {}; | |
} catch (err) { | |
console.error(err); | |
} | |
setTimeout(ProcessJobs, 500); | |
} | |
setTimeout(ProcessJobs, 500); | |
function OnExpChange(){ | |
for (const c of Calc.getState().expressions.list) { | |
if (c.type == 'text' && c.text) { | |
var r = c.text.match(/^js:\s*(.+)$/); | |
if (r) { | |
TodoJobs[c.id] = r[1]; | |
} | |
} | |
} | |
} | |
StartFunc = function(){ | |
console.log("Start custom script"); | |
Calc.observeEvent('change', OnExpChange); | |
OnExpChange() // init | |
} | |
function GetExpValue(latex, cb, script_data) { | |
let exp = script_data.helper_exps[latex]; | |
if (!exp) { | |
exp = Calc.HelperExpression({ latex: latex }) | |
script_data.helper_exps[latex] = exp; | |
} | |
exp.observe('numericValue', () => { cb(exp.numericValue); }); | |
if (exp.numericValue) { cb(exp.numericValue); } | |
exp.observe('listValue', () => { cb(exp.listValue); }); | |
if (exp.listValue) { cb(exp.listValue); } | |
return exp; | |
} | |
function BuildScriptFn(code, script_data) { | |
let script_fn = Function(` | |
var DesmosScriptExt = arguments[0]; | |
var output = DesmosScriptExt.output; | |
var exp_val = DesmosScriptExt.exp_val; | |
var get_remote_data = DesmosScriptExt.get_remote_data; | |
var run_remote_js = DesmosScriptExt.run_remote_js; | |
var import_remote_js = DesmosScriptExt.import_remote_js; | |
console.log('run code', '${script_data.tid}'); | |
${code} | |
console.log('finish script', '${script_data.tid}'); | |
`); | |
return function(args) { | |
script_fn(script_data.export, args) | |
} | |
} | |
function GetRemoteData(url, cb) { | |
console.log('req', url) | |
let res = GM_xmlhttpRequest({ | |
method: "GET", | |
url: url, | |
synchronous: true, | |
onload: function(res) { | |
console.log('http response code', res.status) | |
if (res.status == 200) { | |
cb(res.responseText); | |
} else { | |
cb(); | |
} | |
}, | |
}); | |
} | |
let JSLibCache = {} | |
function ImportRemoteJS(url, cb, script_data) { | |
let loaded = JSLibCache[url]; | |
if (loaded) { | |
if (cb) { cb(); } | |
} else { | |
GetRemoteData(url, function(code) { | |
if (code) { | |
JSLibCache[url] = true; | |
Function(` | |
var define = undefined; | |
var exports = undefined; | |
var module = undefined; | |
var DesmosScriptExt = arguments[0]; | |
var output = DesmosScriptExt.output; | |
var exp_val = DesmosScriptExt.exp_val; | |
var get_remote_data = DesmosScriptExt.get_remote_data; | |
var run_remote_js = DesmosScriptExt.run_remote_js; | |
var import_remote_js = DesmosScriptExt.import_remote_js; | |
${code} | |
`)(script_data.export); | |
if (cb) { cb(); } | |
} | |
}) | |
} | |
} | |
function RunRemoteJS(url, userdata, cb, script_data) { | |
let code = GetRemoteData(url, function(code){ | |
if (code) { | |
let fn = BuildScriptFn(code, script_data) | |
fn(userdata) | |
if (cb) { cb(userdata) } | |
} | |
}); | |
} | |
let ScriptsData = {} | |
function InitScriptData(tid) { | |
let script_data = ScriptsData[tid]; | |
if (!script_data) { | |
script_data = { tid: tid, helper_exps: {} }; | |
ScriptsData[tid] = script_data; | |
} else { | |
let helper_exps = script_data.helper_exps; | |
for (let k in helper_exps) { | |
helper_exps[k].unobserveAll(); | |
} | |
} | |
return script_data; | |
} | |
function UpdateExp(id, script) { | |
var tid = `script_${id}`; | |
console.log('update exp', tid) | |
let script_data = InitScriptData(tid); | |
script_data.export = { | |
output: (val) => { ProcessResult(val, script_data); }, | |
exp_val: (exp, cb) => { GetExpValue(exp, cb, script_data); }, | |
run_remote_js: (url, userdata, cb) => { RunRemoteJS(url, userdata, cb, script_data); }, | |
import_remote_js: (url, cb) => { ImportRemoteJS(url, cb, script_data); }, | |
get_remote_data: GetRemoteData | |
} | |
try { | |
var script_fn = BuildScriptFn(script, script_data); | |
script_fn(); | |
} catch (err) { | |
console.warn(err) | |
} | |
} | |
function ProcessResult(val, script_data) { | |
if (!val) { return; } | |
let tid = script_data.tid; | |
switch (typeof(val)) { | |
case 'string': | |
Calc.setExpression({ id: tid, latex: `${val}`}); | |
break; | |
case 'object': | |
if (Array.isArray(val)) { | |
Calc.setExpression({ id: tid, type: 'table', columns: val }); | |
} | |
break; | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment