はじめに
自作のJavaScript数値計算ライブラリの確認用デモページのために作る。
同様なサービスなどは大量にあると思うがよりシンプルに...というか個人開発で賄える範囲のものに留める(ようにする)。
使ったライブラリ
Ace
ブラウザ上でリアルタイムに編集できてシンタックスハイライトが付けられるコードエディタ。
HTMLで<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ace.js"></script>
のように読み込む
私は以下のようにユーティリティ化している
let editor=null;
export default {
set: (id, lang=void 0, theme=void 0)=>{
if( typeof(id)==='object' ){
if( id.lang !=null ) lang=id.lang;
if( id.theme!=null ) theme=id.theme;
}
editor=ace.edit(id);
editor.setTheme(theme);
editor.session.setMode(lang);
console.log(editor);
},
setValue: (text, point=-1)=>{ editor.setValue(text, point); },
getValue: ()=>{ return editor.getValue(); },
loadText: async (url)=>{ return fetch(url, { cache: "no-store" } ).then(res=> res.text()).then(text=> editor.setValue(text, -1)); }
}
HTMLのID(多分、エレメント自体でもOK)でエレメントを指定するとその下に<texterea>
と色々作られる。
(textareaが本体だがそれにラッパーで色付けとか付けられた透過するdiv
が作られる)
編集エリアからの入出力はsetValue
とgetValue
で行われる。
setValue
のpositionは位置を表すが負の値を与えると0になるようである。
コードの実行
function run(){
// const { code }=Babel.transform(aceUtil.getValue(), { presets: ['env'], plugins: ['operator_overload'] });
// const f=Function(code);
const f=Function(aceUtil.getValue());
if( document.getElementById('auto-clear').checked ) clearResult();
f();
}
これだけす。Functionを作って実行するだけです。
後々は少しトランスパイルを通しますがその場合は上のようなコードになります。
これには別途、色々設定が必要です。
モジュールのロード
ただ上のコードではグローバルにあるものしか使えません。
そこで以下のようなloadMdule関数を使って自作ライブラリをロードしています。
export default async (filepath)=>{
return await import(filepath).then(module=>{
for( const [key, val] of Object.entries(module) ){
if( window[key]==null ){
window[key]=val;
console.log('Set Global Property '+key);
}
else{
console.error('! Global.'+key+' already defined');
}
}
});
}
exportされたオブジェクトのプロパティをブラウザのグローバルであるwindowにセットしているだけです。
もちろん、名前がかぶると衝突してエラーメッセージを吐きます。
export側はこんな感じになってます。
...
export {add, sub, mul, div, // 四則演算
sqrt, pow, factorial, exp, sin, cos, tan, sinh, cosh, tanh, // 初等関数
specialFunc,
diff, integral, // 微分、積分
Complex, // 複素数クラス
Vector, Matrix, // ベクトルクラス、行列クラス
linearAlgebra,
solver,
};
...
の部分で本体を定義import
する形になっています。
JS標準のようにMathクラスのような、MyMathとかでラップしても良かったですがユーザーが編集するにはめんどくさいと思ったので裸でexportしました。
アウトプット
アウトプットはconsoleのようにoutput.log(...)
とすると下のid="result"
に出るようにしてあります。
下のようにdiv
要素を作ってそこに入れているだけです。
また、output.math(...)
を使うとMathjaxを使った、形式で出してくれるようにしてあります。
function resetMathJax(...)
の部分は追加があった場合の再描写です。
const output={
log: (...args)=>{
const elem=document.getElementById('result');
const div=document.createElement('div');
const text=args.reverse().reduce((a, sum)=> sum+' '+a, '');
div.innerHTML=text;
elem.appendChild(div);
},
math: (...args)=>{
const elem=document.getElementById('result');
const div=document.createElement('div');
div.innerHTML+='\\[ ';
args.forEach(a=>{ div.innerHTML+=typeof a.toTex ==='function' ? a.toTex(): a });
div.innerHTML+=' \\]';
elem.appendChild(div);
restartMathJax();
}
};
export { output };
function restartMathJax(){ MathJax.Hub.Queue(["Typeset", MathJax.Hub, "dynamicElement"]); }
成果物
gitpubリポジトリ、色々付け加えてしかも途中ですが...orz
githubページ