JavaScriptで数値をn進法で表現する

自分用メモ。しょぼい話。
数値を2進法とか16進法とかで表現したいときにべんり。(冪乗を効率的に計算する場合に、指数を2進法で表現したくなったりするのだ)

const i = 256;
console.log(i.toString(2)); // 100000000
console.log(i.toString(16)); // 100
const j = -5;
console.log(j.toString(2)); // -101

Number.prototype.toString() – JavaScript | MDNによると、引数は基数を指定すれば良く、2〜36の整数が許容されるらしい。
気をつけるべきは、負数を2進法で表現しようとした場合は、補数表現ではなく、先頭に-符号がついた正の表現が返される(-5は補数表現するなら8ビットの場合は11111011とかになるはず)。それと、戻りは当然ながらstring(typeof(i.toString(2))stringになる)なので、場合によっては気をつけるべきかも?

Nuxt.jsを使ってみる

参考:nuxtjsでtodo アプリケーションを作成 – Qiita
ま、内容自体はほぼ焼き直しだけど、自分の解釈を加えるって感じ。環境はてきとうなArchLinuxだよ。 まずは環境構築。

# pacman -S nodejs npm
# npm install -g vue @vue/cli @vue/cli-init

プロジェクトの作成。

$ vue init nuxt-community/starter-template todos

nuxt-community/starter-templateはテンプレート名。nuxt-community/starter-template: DEPRECATED: use create-nuxt-app insteadがネタ元か?templateとして使えるリポジトリを指定すれば良いのだろうか。このtemplateはDeprecatedらしいので今やるならリンクが張られているnuxt/create-nuxt-appをどうにかして使うのが良いのかな。

$ cd todos && npm install && npm run dev

起動完了したら、http://localhost:3000にアクセスしてそれっぽい画面が出ればおk。今後はソースを編集すると自動でリロードされるっぽい。 つぎ。ルーティングの追加。pages以下にディレクトリを作成すれば、それがそのままルーティングの設定になる。

$ mkdir pages/todos
$ vi pages/todos/index.vue


<template>
  <div>
    todos
  </div>
</template>

<script>
export default {}
</script>

<style>
</style>

そのままhttp://localhost:3000/todosにアクセスすればpages/todos/index.vueの内容が見れる。 さて、Vuexの話。まずは有効化。nuxt.config.jshead:の次に以下の記述を追加。

$ vi nuxt.config.js


  build: {
    vendor: [
      'vuex',
    ]
  },

storeの作成。

$ vi store/todos.js


export const state = () => ({
  list: []
})

export const mutations = {
  add (state, text) {
    state.list.push({
      text: text,
      done: false,
      id: state.list.length + 1,
    })
  },
  remove (state, { todo }) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle (state, todo) {
    todo.done = !todo.done
  }
}

ところでVuexとかStoreとかってなんなのだ。 Vuexは状態管理パターン+ライブラリとかいう記述が公式サイトにある。ここでの状態管理とは、ページ上の見た目(内容)・状態・アクション(それぞれView, State, Actions)の管理のことのようだ。単純な場合は利用しなくても良いけれど、複数のViewがStateを共有したりとか、異なるViewのActionsから同一のStateを変更したりとかすると、脳みそで管理するのはしんどいので、使ってくれという事みたい。 Storeは、VuexでのStateを保持するためのコンテナらしい。これはstatemutationsから成っている。mutationsは英単語としての意味は「変化」であり、これを使って状態を変更する。 最後に、index.vueから機能を利用するように変更する。

$ vi pages/todos/index.vue


<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <input type="checkbox" :checked="todo.done" @change="toggle(todo)">
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
      <button @click="removeTodo(todo)">remove</button>
    </li>
    <li><input placeholder="What needs to be done?" @keyup.enter="addTodo"></li>
  </ul>
</template>

<script>
import { mapMutations } from 'vuex';

export default {
  computed: {
    todos () { return this.$store.state.todos.list }
  },
  methods: {
    removeTodo (todo) {
      this.$store.commit('todos/remove', todo);
    },
    addTodo (e) {
      this.$store.commit('todos/add', e.target.value);
      e.target.value = '';
    },
    ...mapMutations({
      toggle: 'todos/toggle'
    })
  }
}
</script>

<style>
.done {
  text-decoration: line-through;
}
</style>

mapMutationsを使っている意味はよくわからない。

toggle (todo) {
  this.$store.commit('todos/toggle', todo);
}

でも意図した動作はするのだけど…何か意味があるんかな? このままだとremoveがどのボタン押しても最新のやつからしか削除されないのでつらい。store/todos.jsの13行目にある{ todo }todoにすれば、押したものから削除されるが、寧ろ波括弧でくくったこの記述はどんなときに使うのだろう?VuexのMutationsについてのガイドを見ると、複数のデータを渡したい場合には、オブジェクトにして送れというような内容が書かれている。あんまり気にしないで良さそうかな。 以上。てきとうにイジればそれっぽく使える気配がしてきた。Node.js+ExpressでWebアプリを作って遊んでいたのだけど、それを作り直してみようかな?と思ったりもする。それか、まるっきり別のものを作ってみるのも良いかもね。
何にしろ、ろくすっぽ勉強せず取り敢えず触ってみるのは、手探りつらいってのもあるけど、やっぱり楽しいね。

dconfを使ってみる

EPSON DS-50000とかいうのを買ってみたのだけど(大変高価でした、ノリで購入するべきものではない)、Arch Linuxで使うにはimagescanを入れれば解決するとか。SANE/スキャナー別の問題 - ArchWiki で、こいつはutsushiとかいうフロントエンドを使えと要求するのだけど、微妙に使い勝手が悪い。ひとまず、保存先くらいは毎度初期値じゃなくて前回のものを使ってほしいなぁと思う。 utsushiはGPLv3っぽいので好きに改造して良いはずなので、いじってみた。いじったのはottyajp/utsushi – githubに置いたので、設定をいじる話だけこちらに書く。 schema定義をつくってコンパイルする。この後、dconf-editorでいじれるようになる。

$ cat /usr/share/glib-2.0/schemas/apps.utsushi.gschema.xml
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="utsushi">

  <schema path="/apps/utsushi/" id="apps.utsushi">

    <key type="s" name="previous-path">
      <default>"~/"</default>
      <summary>previous path</summary>
      <description>previous path.
      </description>
    </key>

    <key type="i" name="resolution">
      <default>100</default>
      <summary>scan resolution</summary>
      <description>scan resolution.
      </description>
    </key>

  </schema>
</schemalist>
$ sudo glib-compile-schemas /usr/share/glib-2.0/schemas

C++から設定を読み込んだり書き込んだりする。

#include <gio/gio.h>

// load settings
GSettings *settings = g_settings_new("apps.utsushi");
GVariant *gvar = g_settings_get_value(settings, "previous-path");
gchar *path;
g_variant_get(gvar, "s", &amp;path);

// save settings
GSettings *settings = g_settings_new("apps.utsushi");
string str = "hoge";
GVariant *gvar = g_variant_new_string(str.c_str());
g_settings_set_value(settings, "previous-path", gvar);

本当はこれまで使っていたCanoScan LiDE 210みたいに、simple-scanでほいほい使えるとよかったんだけどなぁ。まぁ、DS-50000のスキャンはマジで早いしきれいだし、ハードウェアに文句は無いけどね。

印刷禁止サイトを印刷する

下書きにおいてあったけど公開してなかったシリーズ。

 

日本電気技術者協会の解説講座、学生時代は結構お世話になったんだけど、研究で何度も見に行くうちに、あーこれ印刷して机にでも貼り付けておきたいなと思うようになった。

でもここ印刷禁止なのよね。

 

当時は大学でやってる研究が目的で印刷したかったんだけど、著作権法を眺めてると「授業の過程における使用に供することを目的とする場合には、必要と認められる限度において、公表された著作物を複製することができる」って記述があるんだよね。

研究も、履修登録を要する仕組みなので、「授業」の一環だよね!と解釈すれば、複製可能なはず。あくまで個人の勝手な解釈です。あと当該サイトに印刷に関する記述がぱっと見つけられないのも怖い。

 

という訳で、てきとうにソースコード眺めてると、window.onbeforeprintとかいうものが。

これ、最初はerase関数を実行するようになっているので、これを別ので再定義してしまえば良いのでは?と発想。

CSSでbodyにdisplay:noneを設定しているみたいなので、これをblockにしてしまえ

というわけで

window.onbeforeprint=function(){
document.body.style.display='block';
};

これであとは印刷し放題

 

ソース読めばそれとなくわかるし、大して難しい事もしてないけど、本当におkなのかは知りません。JSが読み書きできると、Webに関するいろんな事ができるよ、の一例でした。

PhantomJSで遊ぶ

Javascriptでいじれる、webkitベースのヘッドレスブラウザであるPhantomJSでスクレイピングを行う。

だいぶ前に、ファミマTなクレジットカードのログイン画面がクソ認証を導入したのでログインにマウス操作が必要になってしまった。どうせ見に行く情報も限られているし、この際スクレイピングしてしまおう。

クソな画像認証

 

 

まずはログイン画面の仕組みを探す。いじってみると、正解の記号(ここでは星)のみ掴めるようになっていて、他はただ置いてあるだけ。droppable関係を見てみれば良いのかな?とか思いながらソースコードやインスペクタとにらめっこしていると、「画像認証に必要なJS」とかいういかにもなコメントの下にjsファイルが3つくっついていた。

これを見てみる(HTMLにもJSにも丁寧にコメントが書いてあるので解釈が捗る)と、droppableの設定の下にdropした時の挙動が書いてあり、その辺の動作を真似てしまえば画像認証を突破できるのでは?という気持ちに。…突破も何も、ただソース読めば誰にだってわかる内容である、難読の工夫は見られないし、ご丁寧にコメントで可読性もバシバシ向上。

 

という訳でPhantomJSのお話。

てきとうなjsファイルを作成して、それをphantomjsに食わせると動く。

$ sudo pacman -S phantomjs
$ cat hello.js
console.log('hello phantom');
phantom.exit();
$ phantomjs hello.js
hello phantom

 

 

取り敢えずログイン画面を表示してスクショをとってみるのが以下。

var page = require('webpage').create();
page.open('https://wis.pocketcard.co.jp/netservice/login?type=ft', function(status) {
 console.log(&quot;Status: &quot; + status);
 if(status === &quot;success&quot;) {
  page.render('screen_shot.png');
 }
 phantom.exit();
});

 

実際にはページ遷移を挟むので参考URLを見ながらそれらしいものを書いたのが以下。今月の請求額をぴょろっと吐き出して終了する。

var page = require('webpage').create();

page.onInitialized = function() 
 page.evaluate(function() {
  document.addEventListener('DOMContentLoaded', function() {
   window.callPhantom('DOMContentLoaded');
  }, false);
 });
};

var funcs = function(funcs) {
 this.funcs = funcs;
 this.init();
};
funcs.prototype = {
 init: function() {
  var self = this;
  page.onCallback = function(data) {
   if(data === 'DOMContentLoaded') self.next();
  }
 },
 next: function() {
  var func = this.funcs.shift();
  if(func !== undefined) {
   func();
  } else {
   page.onCallback = function(){};
  }
 }
};

new funcs([
 function() {
  page.open('https://wis.pocketcard.co.jp/netservice/login?type=ft');
 },
 function() {
  page.evaluate(function() {
   $('#username').val('USERNAME');
   $('#password').val('PASSWORD');
   $('#imgCertf').val('1');
   $('#command').submit();
  });
 },
 function() {
  page.render('out.png');
  var seikyu = page.evaluate(function() {
   return $.trim($('div.table-cell-width501:first div span.bold').text());
  });
  console.log(&quot;seikyu:&quot; + seikyu);
  phantom.exit();
 }
]).next();

 

 

 

ページ内でJSな処理を行うには、page.evaluate(function(){処理});みたいな感じでやれば良く、データを取り出すには、その処理からreturnしてあげれば良い。単純だ。

 

スクレイピングと言えば、コニカミノルタの強そうな複合機の消耗品残量を見れるWebページから情報を取り出してチャットツールに放り込むスクリプトを書いた事があるけど、その時はScrapyを使った。Python何もわからんところからスタートしてやっていくのとてもとてもしんどかったし、Scrapyなんだかんだ複雑だった…(しかもそのWebページはJavascriptで情報更新だったのでただ見に行くだけでは情報は得られず、ScrapinghubのSplashを使って情報を取得したのだった)

Javascriptは遊んだ経験が多少あったので、違和感なく簡単にコードが書けてよかった。

ただ、ES6の構文(アロー関数とか)を何も言わずに使うと処理も進まず思考停止みたいな感じになってしまってつらいことがあった。しょうがないのでES6の構文は使わずに上記コードを書いていたけど、github(ariya/phantomjs)では

Hi. Answering to your questions:

  1. When we solve all issues listed here #14458
  2. Yes. 2.5 will have full support for ES2015.

とある。pacmanで降ってきたのは2.1.1だったし、gihubの最新Releaseも2.1.3ということで、まだのようだ。

それからエラーを何も吐いてくれない感じだったので(ここでは閉じカッコの対応を間違えるというただのSyntax errorだったけど)参考URLの通り、nodeに読み込ませるとスッと教えてくれた。なんなんだろ。

 

 

参考URL:

PhantomJS でログインが必要なページでも自由自在にスクレイピング – 凹みTips

CasperJS/PhantomJSでシンタックスエラー行を取得する方法 – yohgaki’s blog

 

 

蛇足:

研究室では今までMajestouch NINJA(茶軸)を使っていたんだけど、追加で自分用にリアフォ(104UB-S)を買った。本当は少しお値段ケチって静音モデルでない方(104UB)が欲しかったんだけど、どこにも在庫が無くてネット販売でも取り寄せだったので、諦めて静音の方。

打ち心地は勿論最高に良いです。ずっと触っていたくなる…という訳でこのキーボードは研究が落ち着くまで研究室に置きっぱなしにしておく()

んで、このリアフォで書く最初の(趣味の)プログラムがこれ、というわけ。最近ずっと研究関係のものしか書いてなかったので時間があいてしまった。これからよろしくね♡

$(window).load(function() {});の形が廃止された

今更なはなし。

要素のロードが完了したら実行してくれるのを考えて、Webページのロードが完了したらJS関数を実行して欲しいというのを書いたけど、$(window).loadの形が、廃止されたらしい。

今の形はまた違うようだ。

 

以下が廃止された形

$(window).load(function() {
ほにゃらら
});

以下が今使える形

$(window).on('load', function() {
ほにゃらら
});

 

そもそもdeprecatedだったらしいので、まぁいつかはこうなる運命だったんだな

multerを使ってアップロードしたときのファイル名

Node.js + Expressでなんか作ってるとき、multerを使ってファイルをアップロードしたときのファイル名の操作

githubに書いてある通りなのだけど、メモ

 

「filenameを指定しないと拡張子を一切含まないランダムな名前が付加されるぜ!」

 

つーわけで以下のような感じでfilenameを指定する

const multer = require('multer');

const multerStorage = multer.diskStorage({
destination (req, file, cb) {
cb(null, './public/uploads/');
},
filename (req, file, cb) {
cb(null, Date.now() + file.originalname);
}
});
const upload = multer({ storage: multerStorage });

 

cbの第二引数(ここではDate.now() + file.originalname)でファイル名を指定する。file.originalnameを付けているので拡張子も保持されるはず

Webページのロードが完了したらJS関数を実行して欲しい

Masonryを使ってレイアウトさせてたりとかしたんだけど、画像読込のタイミング、ないしはWebフォント読み込み完了のタイミングでもう一度位置計算して欲しいというお話。

HTMLデータ読込→Masonryによる位置計算、配置→画像が読込完了(Webフォント読み込み完了)になると、Masonryの枠が重なっちゃうんですよね。なので、それぞれ読込が完了したときに位置計算して欲しい。

前者、画像とかについては、$(window).load()みたいなノリでやればおk

後者、Webフォントについては、FontLoaderを使う事にした

 

ひとまずは以下のような感じで、Masonryの位置計算部分を関数の形で記述しておく。(再計算用のメソッドとかありそうだけどし〜らない)

function mason(){
$('article').masonry(){
〜Masonryのオプション〜
});
}

 

・画像とか

よく見るサンプルコードは(上記のように関数の形で用意しておいたなら)

$(function(){
mason();
〜その他やりたいこと〜
});

という感じだけど、$(function(){ほにゃらら})の形は、HTMLを読み込み終えたら実行されてしまうらしい。

やることは簡単。$(window).load()をに差し替えればおk

$(window).load(function(){
mason();
〜その他やりたいこと〜
});

 

・Webフォント

WebFontLoaderを使えば良いらしい。文字幅の変化を検知してWebフォント読み込み完了をキャッチするとかいう手法も見かけたけど、面倒そうなのでやめとく()

WebFontLoaderはGoogle Fonts, Typekit, Fonts.com, Fontdeckを読み込める…らしいが、他のものも使えるようで、今回はそっちの記述で。

<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js" type="text/javascript"></script>
var fontconfig = {
custom:{
families:['フォント名'],
urls:['WebフォントCSSのURL']
},
active: function(){mason();},
inactive: function(){mason();}
};
WebFont.load(fontconfig);

active:Webフォントの読み込みが完了(レンダリングが完了)したら実行したい内容

inactive:ブラウザでWebフォントが有効でない場合もしくはフォントが読み込めなかった時に実行したい内容

 

そもそもJavascriptとか全く勉強しないで触ってるんだけどすげえつらい(基礎くらい勉強しような)

PHPでiCalendarなデータをパースする

PukiWikiにGoogleカレンダーのデータを表示したかったんだけど、それらしいプラグインがいまいち見つからなかったので自前で書くことにした。

Googleカレンダーでicsファイルへのリンクを取得して、それをプラグインで読み込んで、あとはてきとうにパースして整形して〜と思っていたけど、パース書くの面倒だなぁと思ってぐぐったら、File_IMCを使えば良いようなので使うことにした。

File_IMCはPearを通してインストールする…がPearもデフォルトでは入っていないのでインストールしよう。

環境はUbuntu 16.04.2

$ sudo apt-get install php-pear
$ pear list
Installed packages, channel pear.php.net:
=========================================
Package Version State
Archive_Tar 1.4.0 stable
Console_Getopt 1.4.1 stable
PEAR 1.10.1 stable
PEAR_Manpages 1.10.0 stable
Structures_Graph 1.1.1 stable
XML_Util 1.3.0 stable
$ pear install File_IMC
Failed to download pear/File_IMC within preferred state "stable", latest release is version 0.5.0, stability "beta", use "channel://pear.php.net/File_IMC-0.5.0" to install
install failed
$ pear install File_IMC-0.5.0 [Ret:1 16:11:41]
Cannot install, php_dir for channel "pear.php.net" is not writeable by the current user
$ sudo pear install File_IMC-0.5.0 [Ret:1 16:11:54]
downloading File_IMC-0.5.0.tgz ...
Starting to download File_IMC-0.5.0.tgz (32,161 bytes)
.....done: 32,161 bytes
install ok: channel://pear.php.net/File_IMC-0.5.0

以上でFile_IMCのインストールはおしまい。

 

使ってみる。

require_once('File/IMC.php');
$ical = file_get_contents('うんたらかんたら.ics');
$parse = File_IMC::parse('vCalendar');
$parse->fromText($ical);
$events = $parse->getEvents();

$data = array();
while ($events->valid()) {
$event = $events->current();
$data[] = array(
'start' => $event->getStart(),
'end' => $event->getEnd(),
'summary' => $event->getSummary(),
'description' => $event->getDescription()
);
$events->next();
}

print_r($data);

startの値でソートしたければ、

foreach((array) $data as $key => $value) {
$sort[$key] = $value['start'];
}
array_multisort($sort, SORT_ASC, $data);

 

あとはてきとうに整形するなりなんなりでいいんだけど…Googleカレンダーでは場所情報が記録できるのに、File_IMCにgetLocation()つーメソッドは無い。

見たとこイベント開始/終了時刻、タイトル(summary)、詳細(description)しか拾えないっぽいので強引になんとかしてやるぜ!

/usr/share/php/File/IMC/Parse/Vcalendar/Event.phpに各メソッドが記述されているので、これをそれらしく書き換えればなんとかなるのでは?

というわけで下記コードをてきとうに追加

public function getLocation()
{
return $this->data['LOCATION'][0]['value'][0][0];
}

あとは同じ感覚で$event->getLocation()とかすれば拾い出せるはず。

PHP欠片も勉強しないで触ってもなんとかなるもんだね(目的のプラグインが作れたので)

まーセキュリティは何も考えてないんですけど

関数の引数として関数をとる

C++なら関数オブジェクト使えとかいう記述をどこかで見かけたんだけど、結局よくわからなかったので関数ポインタを使う事にした。

引数の型 (*関数ポインタ名)(その関数がとる引数の型)

という形で宣言して使う。

例えばintで1つ引数をとって、5足したものを返してくれる関数を考えてみる。

int calc(int in){
return in+5;
}

int main(){
int (*fp)(int);
fp = calc;
fp(2)    // 7が返ってくる
return 0;
}

細かいアレはともかくとして、fpが関数ポインタ。こういう使い方だと今のとこあんま面白くないけど、関数ポインタを使えば関数の引数に関数を使える。

与えられた関数から返ってきた値の正負を判定する関数pmを考える。

int calc(int in){
return in+5;
}

bool pm(int (*fp)(int), int in){
if(fp(in)<0){
return false;
}else{
return true;
}
}

int main(){
pm(calc, -6)    //false
pm(calc, 3)    //true
return 0;
}

使いどころは、計算の一部分を利用者から与えてもらって、それを使って全体の計算をやるって場面。(ていうかそういう場面があったのでこうやって書きました)

具体的に言うと、数値解析のときにつかうテクニックがあるんだけど、これを毎度書いてるとウザいので、オレオレライブラリにしてやろうという感じ。テクニックのアルゴリズム自体はライブラリ側で全部書いておいて、利用者は目的関数の定義だけしてあとはそれを放り込むだけ〜というやつ。

ぶっちゃけこういう内容1年くらい前にやるべきみたいな雰囲気はある(今年度何した?に答えたくない)