やりたいこと
データを変形・加工するスクリプトをURL上にホストをして、
ポータブルで永続的でブラウザで完結するWebアプリを作れるようにします!
Rubyはデータ加工に最適!
Rubyは豊富なメソッドが標準で用意されていて、特にArray
, Enumerator
, String
などよくデータを加工したいときに使えるメソッドがとても豊富です。
メソッドチェーンでつなげていけるところが、人の思考にあっていて、どんどんデータを変えていけるところがRubyの良さだと思います。
そのためデフォルトの言語はRubyです。JavaScriptもオプションで選べるようになってます。
そこで、 データ加工に便利なRubyで書いた小さいWebアプリをURL上にホストします!
URLにすべてのコードを載せてしまえば究極的にポータブルなWebアプリになるのではないかという発想です。Itty.bittyにインスパイアされてます!
ブラウザ上でRubyを動かす技術に関しては後述します。
なにを解決したいか
サーバーサイドで実行するわけではなくクライアントサイドで完結かつURLにコードをホストさせるため、
以下のような心配ごとが解消するはずです。
- サービス終了でコードがなくなることがない
- 大体のオンライン実行環境で与えられるIDのようなものではなくコード自体がURLに残る
- すべてのコードがURLに刻まれるため、コードが失われる心配はないです
- コードがサービスに漏れない1
- 大体のオンライン実行環境はコードをサーバに転送して実行する
- コードの実行環境がサーバでないため、コードを送信する必要がない
- URLの
#
以降にコードを埋め込むため、Webサーバのログにも残らないはず - 完璧に静的にホストできる
- GitHub Pagesで動く
- AWS S3でも動く
- コスト0 - 今の世の中だと無償で提供し続けられる
Webアプリにすることで、pry
(=irb
の便利版)のときに少し不便に感じてた以下のようなことが解決できると思います。
- 複数行をヒアドキュメント
<<EOS
で貼り付ける手間 - 文字列を変更したくなったらもう一度貼り付け直す手間
- 加工したい文字列に対しての結果が、リアルタイムに反映される
アプリケーション
アプリケーション(GitHub Pages): https://nwtgck.github.io/nipp
リポジトリ(GitHub): https://github.com/nwtgck/nipp
追記(2019/01/18):
カスタムドメインnipp.cfが使えなくなってしまったので、普通のgithub.ioにしました。
以下はnipp.cf
=> github.io
への移行用のNippです。入力欄に今までのURLを貼り付けると新しいURLを出力します。ご不便申し訳ありません。
https://nwtgck.github.io/nipp/#Convert_n...
使い方や例などは後述します。
Nippの名前の由来は 「Mi ni A pp」からです。
小さめのWebアプリをホストするところから来ました。ですが、Rubyの枠を超えてJavaScriptを動かせるので、結構いろいろできます(後述します)。
使い方
上
の欄にRubyのスクリプトを入力して、
左
の欄に文字列を入力して、
右
の欄にRubyが実行後の出力が出る
左
に入力した文字列はs
っていう変数に入ってます。s
はStringのsのつもりです。
入力したコードは圧縮されURLに載ります。デフォルトだとdeflateのレベル9で圧縮され、リアルタイムにURLが変わっていきます。
URLがリアルタイムに変わるおかげで、間違ってページをリロードしても書いたコードは保持されます。
出来上がったところで、ブックマークしたりツイートしたりして、共有できます。
Vueとかのライブラリも動く!
(追記:ES2017が使えることを上の方にも追記しました)
言語はRuby以外にJavaScript(ES2017)が使えます。
<script src="...">
動的に追加できるので、実はCDN経由などで、他のライブラリも使えます!
以下は、Vue製のシンプルなTodoリストです。
Nipp: https://nwtgck.github.io/nipp/#Vue_Todo_List/func_es20...
一瞬だけNippのページが見えるのが、Nippで動いている証拠です。詳しくは後述します。
使用例
実際に作ってみて、使ったものの中で他の人も使えそうなものを選びました。
NippのURLを開いて、入力例を打ち込んで色々試したりコードを改良したりしてみてください。
複数行文字列 ==> "....\n" + <改行> "...\n" + <改行>...
ヒアドキュメントがない言語のときに便利かもしれません。
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
==>
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed \n" +
"do eiusmod tempor incididunt ut labore et dolore magna \n" +
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation \n" +
"ullamco laboris nisi ut aliquip ex ea commodo consequat. \n" +
"Duis aute irure dolor in reprehenderit in voluptate velit esse \n" +
"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat \n" +
"cupidatat non proident, sunt in culpa qui officia deserunt \n" +
"mollit anim id est laborum."
Markdownの表からCSV
CSV => Markdown は調べるとよく見つかるのですが、
Markdown => CSV はあんまりないので、
Markdown => CSVの変換器です。2
Markdownの表だと、編集しにくいなって思ったとき使えるかもしれません。
| Item | Count |
|--------|------:|
| apple | 12 |
| orange | 23 |
| tomato | 3 |
==>
Item,Count
apple,12
orange,54
tomato,3
とりあえず、小数第2位にしたいとき
文字列中に現れる長い小数を、とりあえず全部小数第2位にしたいときとかに使えます3。
\begin{tabular}{llr}
Animal & Price \\
\hline
Gnat & 13.65434 \\
& 0.018492 \\
Gnu & 92.44325 \\
Emu & 33.23443 \\
Armadillo & 8.919998 \\
\end{tabular}
==>
\begin{tabular}{llr}
Animal & Price \\
\hline
Gnat & 13.65 \\
& 0.02 \\
Gnu & 92.44 \\
Emu & 33.23 \\
Armadillo & 8.92 \\
\end{tabular}
例の入力はLaTeXの表みたいなものですが、小数を含むどんな文字でも切り捨てをします。
長い桁の数を数えてほしいとき
無量大数まで数を日本語で読める形に変えてくれます。
numpyの行列をコピペしやすくする
numpyの行列の出力が以下ようになって、このままだとコピペしてもPythonのコードにならなくて不便だったりします4。
[[1 2 3]
[4 5 6]
[7 8 9]]
==>
それを以下のように変えてコピペが捗るようになります。
np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
単語数を数えたいとき
他にも単語ごとに数を数えたいときとか、
pry入力をコピペしやすくしたり
これもまた、pry
でインタラクティブに打った内容をちゃんとコピペしても使えるようにしたり、
(これはpry
機能でありそうな気もしますが)
[14] pry(10):1> a = 10
=> 10
[15] pry(10):1> b = a * 3
=> 30
[16] pry(10):1> class MyClass
[16] pry(10):1* def my_method(n)
[16] pry(10):1* n * 8
[16] pry(10):1* end
[16] pry(10):1* end
=> :my_method
==>
a = 10
b = a * 3
class MyClass
def my_method(n)
n * 8
end
end
Pythonのもコピペしやすくしたり
(標準でなんかしらの機能でできそうな気もするのですが...)
>>> a = 10
>>> b = a * 3
>>> class MyClass:
... def my_method(self, n):
... return n * 8
...
==>
a = 10
b = a * 3
class MyClass:
def my_method(self, n):
return n * 8
Markdownで引用しやすくしたいとき
先頭に>
をつけるだけです。
Nipp: https://nwtgck.github.io/nipp/#引用しやすくする//K9ZLTUzOiM/JzEvVy00sqK5JrVFQslNQUtBWSK3Vy8rPzAMA
厳密にはOpal
便宜的にRubyといっていますが、ブラウザ上でRubyをJavaScriptにトランスパイルするためにOpalを使っています。
Opal is a Ruby to JavaScript source-to-source compiler.
It comes packed with the Ruby corelib you know and love.
It is both fast as a runtime and small in its footprint.
(from: Opal)
JavaScript(ES2017)も動く!
NippはRuby以外にJavaScript(ES2017)も動かせます!(Babelでトランスパイルしてます)
以下みたいにして、JSでも文字列長さをはかるNippも作れるのですが...
https://nwtgck.github.io/nipp/#文字列の長さ/es2017/K9bLSc1LL8kAAA==
JSが使えるになると、こういった用途よりももっと他のことがしたくなってきます...(最後の方に少し紹介します)
プログレッシブウェブアプリ(PWA)対応
ポータビリティがとても大事なため、**プログレッシブウェブアプリ(PWA)**対応しました。
そのため一度読み込まれるとページがキャッシュされるため、ネット環境がないところでも動作したり、Androidとかだとアプリぽくインストールすることができます。ネットから遮断されたローカル環境でもRubyが動くようにNippが動きます!
使用されている技術
Opal
OpalはNippの要のようなプロジェクトです。
リポジトリ:https://github.com/opal/opal
JavaScript上で、Ruby(Opal)コードをトランスパイルするために、opal.js
とopal-parser.js
を使います。
JS上で動かす方法は公式ドキュメントのCompiling Ruby code from HTMLに動作するコードが書かれています。このドキュメントにはインラインに<script type="text/ruby">
としてJSのようにRubyコードを埋め込む例が紹介されていますが、Nippではユーザー入力の文字列をトランスパイルしたいので、Opal.compile(Rubyのスクリプトの文字列)
でトランスパイルしています。
Opalはあまりnpm
から使うのが向いてなく、gem
でインストールするようになっている感じがあります(npmの更新が止まっています)。Bowerの更新は生きていたのでNippではbower経由でOpalを管理しています(その他のライブラリはnpmで管理しています)。実際にはNippを管理するpackage.json
のdevDependencies
にbower
を入れて、npm run build
とかとすると、bower install
などをやるようにして、npm
グローバルにインストールしていれば、Nippをビルドできるようにしました。
@babel/standalone
BabelはWebアプリをビルドするマシンだけで使うのが通常の利用だと思います。しかしNippはトランスパイルをブラウザ上で行います。そのため、普通のBabelではなく@babel/standalone
を利用しています。
リポジトリ:https://github.com/babel/babel/tree/master/packages/babel-standalone
(babel/babel-standalone
というプロジェクトがググるとトップに出てきますが、上に書いたリポジトリがメンテナンスされているものなのでこちらを使います)
以下のように実行すると、トランスパイルされたJSが文字列として返ってきます。実行するにはこれをeval()
をするか(new Function(トランスパイル文字列))()
をします。
Babel.transform(ES2017で書かれたJSのスクリプト文字列, {presets: ["es2017"]}).code;
リアルタイムな実行とトランスパイル
入力欄の文字列をもとに、出力欄に反映するとき話です。
コードの入力欄に変更があるときだけに、トランスパイルします。そしてRuby(Opal)の場合は、トランスパイルされたFunctionオブジェクトを保持します。
入力欄が変更されたときは、そのFunctionオブジェクトを実行します。入力欄によって出力が変わるのは、s
がwindow.INPUT
を参照するにしているからです。
window.INPUT
は入力に変化があった場合に再代入されて、そのあとにFunctionオブジェクトが実行されます。
入力ごとにトランスパイルされることはないため、そこそこ高速に動き、モバイル端末でもリアルタイムな実行ができています。
deflate/LZMA (圧縮技術)
Nippでは、deflateもしくは、LZMAを使ってコードを圧縮します。Itty.bittyでもLZMAを使って圧縮しています。
自分の理解では、deflateはアルゴリズムの名前で、zipやgzipで実際に使われているアルゴリズムです。LZMAは圧縮率が高くて有名(多分)な.xzなどで使われているアルゴリズムです。2つ用意しているのは、場合によってdeflateの方が短くできたり、LZMA方が短くできたりがあるからです。
経験的には、元々短いコードはdeflateの方が短いが、長いコードはLZMAのほうが短くできるという印象です。NippのURLを最適な短さにするときは、各アルゴリズムを各圧縮レベルで試します(低いレベルの方が短くなるケースがあることもありました)。以下は、この最適化もNipp上で作れます。以下がそのNippへのリンクです。
URL最適化: https://nwtgck.github.io/nipp/#⚡Optimize_URL/es2017/dU...
使い方は、Input欄にNippのURLを入れるとOutputに最適なURLを吐き出します。
deflateのヘッダとチェックサム
なるべくURLを短くしたいので、deflateはヘッダとチェックサムを取り除いています。この取り除く機能は標準にあり、windowBits
に負の数を入れると取り除けます。@7shi さんの ShideShare Deflate で知りました。このスライドではDeflateに関して詳しくまとまっていて勉強になります。
JavaScript/CSS ライブラリ
JavaScriptのフレームワークは少し古めの技術ですが、AngularJS(1.x系)を使ってます。いつもちょこっとしてWebアプリを作るときは、AngularJSを使うのが慣れているので使ってます。
もう少ししっかりと開発したいときは、React/TypeScriptを使っていますが、AngularJSからReactよりも、Vueへの移行の方がしやすそうなので、Vueになるかもしれません。
(追記 2019/04/06: Vue + TypeScriptに移行しました🎉)
CSSはモバイル端末で見やすくするために、レスポンシブデザインにしたかったので、Pure.cssを使ってます。レスポンシブデザインさえできたら良かったのミニマムなものを選びました。またCSSのクラス名にプレフィックスがあるおかげで名前が汚染されない感じが気に入って選んでます。
NippのURLの構造
以下がURLの構造です。
nwtgck.github.io/nipp/#ページタイトル
/オプションたち
/圧縮されたBase64 encodeされたコード
ページタイトル
に半角スペースは_
に置換されます。Itty.bittyでも置換されています。
オプションたち
はオプションをカンマ区切りで並べます。
Nippのオプション
現在利用可能なオプションは以下の4つです。
-
es2017
: ES2017を使う(デフォルト:Ruby(Opal)) -
func_es2017
: (new Function(...))()を使って、ES2017を実行する(デフォルト:eval()で実行) (速度が向上します) -
lzma
: LZMAを使ってコードを圧縮します(デフォルト:defalte) -
click_run
: ボタンをクリックして実行するようにします(デフォルト:リアルタイム実行)
オプションをクエリパラメタみたいにkey=value
にしなかったのは、すこしでもURLを短くするためです。
オプションの区切りが-
ではなく_
なのは、マウスでダブルクリックして選択するときにオプションを選択しやくしたかったからです。
DOMをいじる!
Nippでテトリス
JSが動かせるので、DOMなども普通にいじれます。そこでだいぶ前に話題になったわずか565バイトテトリスのプログラミング解説をNipp上で動くようにしてみました!
(オリジナルのコードのとの変更点は、直接document.bodyを書き換えていたのを、一つ要素をJSで作ってその要素を書き換えるように変更しただけです)
UIを完全に置き換える!
document.body.innerHTML
などをいじれば、Nippの本来のUIを置き換えることができます。
自分でもUI置き換えをしたい人向けのTipsは後述します。
NippでVue
VueをCDNから使います。
Vue製のTodoリストがNipp上で動きます。JSFiddleにあったソースコードを元にしました。
Nipp: https://nwtgck.github.io/nipp/#Vue_Todo_List/func_es20...
JSが使えれば、動的にscriptタグを生成して好きなライブラリをCDN経由で持ってくれます。
好きにjQueryでもVueでも他のJSライブラリでも使えてしまいます!
Nippでサマー・ウォーズ
サマー・ウォーズのワールドクロックです!
動的にd3.jsなどのライブラリを読み込んだりしています。
Nipp: https://nwtgck.github.io/nipp/#World_Clock/es2017,lzma...
SHIMIZUさんのブログから元になっています: https://shimz.me/blog/d3-js/4360
ありがとうございます!
SHIMIZUさんブログは他にもいろんな技術を使っていて面白いのでおすすめです。
GUNMA GIS GEEK – 地図と視覚化についての技術情報
Nippで2048で遊ぶ
https://github.com/gabrielecirulli/2048 のコードをもとに、Lebab(Babelの逆)したりuglifyしてコードを小さくしたものをホストしました。
この2048は標準ライブラリだけで書かれているので、完璧にポータブルです! PWAでローカルにキャッシュされていれば、オフラインでも遊べます。
Nipp: https://nwtgck.github.io/nipp/#2048/func_es2017,lzma/XQAAA...
UI置き換えのTips
UI置き換えをしたい人向けのTipsです。
基本形は以下のようになります。<head>
と<body>
内を好きに変更できて、描画後に好きにJavaScriptを動かせました。
(なるべく一般的に使える形を模索した結果です)
追記:
以下のようにdocument.write
を使ったほうがよりシンプルになりそうです。
document.write(`
<html>
...
<html>
`)
`
が文字列内に混じっていいてヒアドキュメントが作りづらい場合は、
文字列エスケープ: https://nwtgck.github.io/nipp/#Escaping_String//K9bLzCsuSE0uAQA=
を使って、文字列をエスケープすると楽かもしれません。
document.head.innerHTML = `
... <head>内の内容 ...
`;
document.body.innerHTML = `
... <body>内の内容 ...
`;
const a = document.createElement("a");
a.onclick = ()=>{
... 読み込まれたときに実行したいJavaScript ...
}
a.click();
この方法のいいところは、
-
onclick = ()=>{}
でES2017が使えるところと、 - 全体をminifyしやすい
- JavaScriptを
""
内に書くとminifyされないため
- JavaScriptを
### UI置き換えの具体例
<head>
にCSSを書いたり、好きにHTMLのbodyを作って、JavaScriptで置き換え後のNippのDOMを動的にいじってます。
Nipp: https://nwtgck.github.io/nipp/#Dynamic_Replacement/es2...
// <head>内を置き換える
document.head.innerHTML = `
<title>Hello World</title>
<style>
h1 {
color: #ff8000;
}
</style>
`;
// <body>内を置き換える
document.body.innerHTML = `
<h1>hello, world</h1>
<div id="time"></div>
`;
const a = document.createElement("a");
a.onclick = ()=>{
// 書き換えたい要素を取得
const timeElem = document.getElementById("time");
// 毎500msで繰り返す
(function loop(){
// 現在時刻で要素を書き換える
timeElem.innerText = new Date();
setTimeout(loop, 500);
})();
}
// スクリプトを実行
a.click();
ライブラリを使いたいとき
以下のような動的に<script src="...">
タグを生成する関数を作っていおいて、
function js(url, onload) {
const s = document.createElement("script");
s.src = url;
s.onload = onload;
document.head.appendChild(s);
}
以下ように、CDN経由でライブラリを読み込んで使うことができます。
js("https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js", () => {
js("https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment-with-locales.min.js", () => {
// 全ライブラリが読み終わった
// ... DOMをいじったり、好きなJavaScriptを書く ...
この場合はjs
関数がのコールバックがあるため、a.onclick = ()=>{}
を使う必要がなくなります。
UI置き換えのTipはこんな感じです。コールバックが深くなるので、適宜Promise
などを使って平らにするといいかもしれません。
ブラウザのURLの文字制限
Chrome/Safari/Firefoxあたりをお使いなら、長さ1万のURLぐらいなら対応しているということがわかりました。
詳しくは、別記事にまとめました。
各種OS/ブラウザでのURLの長さの上限を調べてみました - Qiita
ツイートのURLの文字制限
長さ4088のURLならツイートできることが分かりました。すこしブラウザ版とモバイル版で差があります。
こちらも詳しくは、別記事にまとめました。
ツイートで使えるURLの長さの制限 - ブラウザ版、iOS版、Android版で違いがありました - Qiita
4088文字以下なら大丈夫です。
セキュリティに関して思うこと
結論としては、信用のできない人のNipp URLは開かないほうがいいと思ってます。
UIを置き換えで分かる通り、NippのURLが違えば、別のサイトと同じ力があります。
よく分からないメールにあるリンクを踏んだらまずいのと同じように、どんなサイトが待ち構えているか分からないため避けるのが無難だと思っています。
普通のサイトと違うのは、違うサイトは別のドメインなのが普通ですがNippだと同じドメインになるため、CookieやLocalStorageにアクセスできます。
そのため、漏れるべきではない情報をCookieなどに保存するNippを作るのは、避けたほうがいいと思っています。
あと、PWAを使った攻撃方法がある場合、XSSがキャッシュがクリアされるまで残るので、とても恐ろしいです。
PWAのService Workerを任意のものする場合同じオリジンのscriptしか登録できないため、基本的には問題ないと思っています。ただ、セキュリティ関連の攻撃な自分の知らない技術を使ったり、トリッキーな発想がよくあるので正直わかりません。PWAに関する攻撃手法は、Masato Kinugawaさんの攻撃者視点で見る Service Worker / PWA Study SW - Speaker Deckを参考にしました。
上記のようなことを考えた結果、「信用のできない人のNipp URLは開かないほうがいい」という結論になりました。
以下は、セキュリティ対策に多少は貢献するかもしれないNippです。
コードを実行せずに取り出す:https://nwtgck.github.io/nipp/#Code_Extractor/es2017/r...
PWAのキャッシュクリア:https://nwtgck.github.io/nipp/#PWA_Cache_Clear/es2017/...
Nipp URLを開いたあとにJSを実行すれば、Nippの入力欄やNipp URLは書き換えができるので、
URLを開く前にコードを取り出して、コードを確認する用途に作りました。
PWAキャッシュクリアは、PWAのService Workerが汚染されたときにクリアするためのNippです。ブラウザの開発者ツールでもできます。
Nippで確認するのも怖い場合は、以下のRubyスクリプトでローカルでコードを確認するこができます。
require 'zlib'
puts Zlib::Inflate.new(-8).inflate("K9YrzqxKBQA=".unpack('m').first)
=> "s.size"
Nippのサイトを自分で立ててすこし安全に
あと、自分用にNippのサイト自体を別に作るのも手だと思います。ドメインなどが変われば、Cookieが取り出せる話などが関係なくなると思います。NippはHTML/JS/CSSをファイルを置くだけでいいので、
手軽な方法は
- https://github.com/nwtgck/nipp をforkする
- fork後に
gh-pages
ブランチからCNAME
ファイルを削除5
です。
絶対に起こるはずのないこと
絶対に起こるはずのないことは、
- Nippのアカウントが乗っ取られる
- NippのサーバがSQLインジェクションされる
です。
Nippにアカウントという機能自体がないので、ありえません。
Nippのサーバは静的にHTML/JS/CSSのファイルを置いているだけで、SQLとかデータベースとか使っていないので、SQLインジェクションはありえません。
任意のコードが実行できるからといって、Nippのセッションなどが盗まれるとかということはありません。
Nippはアカウントやログインやセッションなどの機能がそもそもないので、起こりえません。
便利さと危険を知ってこそだと思っているので、ネガティブなことですがセキュリティについて思うことを書きました。
[おまけ] Tips
Ruby(Opal)内でJavaScriptを動かしたい
Opalだと、` `
や%x{ }
の中にJavaScriptを書くことができます
%x{document.title = s}
なども有効なOpalになります。
JavaScriptのオブジェクトをRuby(Opal)で扱う
Ruby(Opal)らしくJavaScriptを使いたいときの方法です。
以下のように、OpalでNative
を使うことで可能になります。
require 'native'
# Googleを開く
win = Native(`window`)
win.open("https://google.com")
# setIntervalを使ってみる
interval = Native(`window.setInterval`)
interval.call(->{
puts("Current time: #{Time.now}")
}, 1000)
Nipp: https://nwtgck.github.io/nipp/#Native_Usage/click_run/...
console.log
の値を見たい
Rubyのputs
やJavaScriptのconsole.log
は開発者ツールを開いて、ログを見ることで確認できます。
しかし、モバイル端末上で開発したときにも確認したくなったら、以下のNippが便利かもしれません。
console.log
を上書きして、DOM上のTextareaにもconsole.logの内容が追記されるようにします。
もともとのconsole.log
はwindow.consoleLog
に退避されていて、上書き後のconsole.log
も元々のconsole.log
を呼ぶようになっているため、
開発者ツールでのログが確認できます。
if (typeof window.consoleLog === 'undefined') {
// Backup real console.log
window.consoleLog = console.log;
// Create log element
var logElem = document.createElement('textarea');
logElem.rows = "10";
logElem.cols = "50";
logElem.placeholder = "console.log";
// Create clear button
var clearButton = document.createElement("button");
clearButton.innerText = "Clear log";
clearButton.onclick = function(){
logElem.value = "";
};
// Append the textarea and the button
document.body.appendChild(logElem);
document.body.appendChild(clearButton);
// Replace console.log
console.log = function(){
// Output the arguments to the textarea
logElem.value += Array.prototype.slice.call(arguments).join(" ") + "\n";
// Pass the arguments to real console.log
window.consoleLog.apply(null, arguments);
}
}
(開発中はプラグインのようにコードの先頭に書く感じを想定してます)
Nipp: https://nwtgck.github.io/nipp/#Virtual_Console_Log//dU...
最後までありがとうございました!