必然性は全くなかったのですが、Fluxを試してみたかったので作ってみました。
http://tb.antiscroll.com/static/nehan-demo/
使ってみた感想
あんまり感触は良くないかも…
ただ今はなんとなく全体像を掴んだかもっていう段階なので、もっと複雑なUIを作るとなったら、こういうアーキテクチャが生きてくるケースもあるのかも? しれません。
必然性は全くなかったのですが、Fluxを試してみたかったので作ってみました。
http://tb.antiscroll.com/static/nehan-demo/
あんまり感触は良くないかも…
ただ今はなんとなく全体像を掴んだかもっていう段階なので、もっと複雑なUIを作るとなったら、こういうアーキテクチャが生きてくるケースもあるのかも? しれません。
2005年、つまり今からちょうど10年ぐらい前、アジア〜中東〜アフリカ〜ヨーロッパを旅行したロバ中山さんの旅行記がすごく面白いです。*1
http://www.sakaguti.org/honmon%20page/top%20page/top%20page.htm
今は大変な状況になってしまったシリアやイエメンにも行ってますし、イラン、ヨルダン、イスラエルにも行ってますね。
面白くて二度読みしている最中なのですが、現代の中東情勢を伺い知るには、もってこいの内容じゃないでしょうか。
ただし、そのままだと読みづらい気がしたので、自分は NehanReader で次のような設定をして読みました。
www.sakaguti.org, td[width="491"], table>tbody>tr>td
こうして設定した後、NehanReader で読むと、こんな感じで読めます。
非常に長い日記ですが、最初から読む必要はなく、興味がある国だけ読んでも面白いと思います。
それにしてもイエメンのヤヒヤさんやアミンさんは無事なのだろうか…と思ってしまいますね。
[2014/02/14 追加]
現在サイトは閉鎖されてしまったようですが、以下の本などで同じような内容が読めるようです。
*1:2017/02/14 現在は閉鎖されています。
Nehan Reader ver0.9.63から、サイトごとに変換対象を設定できるようになりました。
「涅」のボタンを右クリック→「オプション」と進みます。
「table of selectors to main article」の欄に
[siteのURL], [selector]
を改行で区切って記述し「Save」ボタンで保存します。
www3.nhk.or.jp, #news toyokeizai.net/articles, #article-body www.asahi.com/articles, #Main togetter.com, .contents_main ncode.syosetu.com, #novel_contents lifehacker.jp, article gigazine.net, #maincol blog.livedoor.jp, .article-outer news.yahoo.co.jp, #main headlines.yahoo.co.jp, #main hatenablog.com, #main hatenablog.jp, #main hateblo.jp, #main hatenadiary.com, #main hatenadiary.jp, #main anond.hatelabo.jp, .day www.huffingtonpost.jp, article.entry wired.jp, .article_maincontents
例えば上の内容をコピペするだけで、ヤフーニュースを始めとするニュース記事や、小説家になろうの小説、はてなブログの記事などが読みやすくなります。
また変換するのがメインの文章だけになるので、高速にもなります。
chrome拡張とかでも、普段使っているcssフレームワークが使いたくなることがあります。
しかし大抵のcssフレームワークは、グローバルな名前空間でスタイルを宣言しています。そのまま導入すると、拡張機能のCSSが読み込まれてしまった結果、訪問したサイトの本来のスタイルを崩してしまうことでしょう。
もちろん拡張機能を特定のサイトでだけ動くようなポリシーにすれば防げます。しかし、NehanReaderのように、全てのサイトで使えるようにしている拡張機能も、たくさんあると思います。
そういう場合、フレームワークで宣言されている全てのセレクタに、強引に何かのプレフィックスを足してしまえば良いわけですが、手動でやるのは流石にキツイ、というか無理です。
だから「なんか良いツールないかなあ」と探して見つけたのがrework-mutate-selectorsです。
今回はgulpを使いたかったので、gulp-reworkも一緒に導入しました。
npm install rework npm install rework-mutate-selectors npm install gulp-rework
gulpやgulp-renameがない人は、先にインストールしておいて下さい。
var gulp = require("gulp"); var rework = require("gulp-rework"); var selectors = require("rework-mutate-selectors"); var rename = require("gulp-rename"); gulp.task("default", function(){ return gulp.src("framework.css") .pipe(rework(selectors.prefix(".my-module"))) // prefixに".my-module"を追加 .pipe(rename("my-module.framework.css")) .pipe(gulp.dest(".")); });
こうすると例えば
/* framework.css */ div a{ color:red }
が、
/* my-module.framework.css */ .my-module div a{ color:red }
と出力されます。
一瞬、自分で作ろうかとも考えましたが、諦めずに探して良かったとです。
まだ安定していないのでリリースまではしてないのですが、自分の中で懸案だった幾つかのトラブルが解消したっぽいので、nehan.jsのバージョンを5.0.5から、5.1.0へと上げました。
ちなみに安定していなくても、縦書き文庫では問答無用で最新版を走らせています。
利用者がバグを見つけてくれることが多いからです…
5.0系列では(主に横書きの時に)バグがあったのですが、5.1系では縦書き横書き共に複数ページに渡るfloatが正しく最後まで表示されるようになりました。
IE8を考慮したlexerの処理を刷新したことで、lexing中に取れる情報を利用できるようになり、結果としてlast-child系のpseudo-class(last-child/last-of-type/only-child/only-of-type)を利用できるようになりました。
これまでのLexerは、主にIE8でフリーズさせないために、バッファリングしながら処理させていたのですが、これだと同一階層のDOMにおける先のlexing内容が不定になるため、first-child/nth-child/nth-of-typeなどはとれても、末尾からの情報は取得できていませんでした。
これまでは複数のInlineElementをまたぐ構造になる時、行末の禁則処理が正しく動作していなかった場合があったのですが、この不具合の大半(残念ながら全てではない…)を改善することが出来ました。
ページの途中で途切れた場合にもborderの最後が表示されてしまっていたのですが、これが消えるように(ただしたまに表示されることもある)なりました。
どうやっても指定されたページの中に表示できないレイアウト(深すぎるテーブルとか)はスキップして続きのレイアウトから表示できるようになりました。
一部のロールバックが必要だった処理をロールバック不要な処理に改善し、全体のパーススピードが上がりました。
onload
/oncreate
に加えて、onblock
/online
/ontext
というコールバックが追加されました。
それぞれブロックレベルの作成タイミング、その中の匿名ラインボックスの作成タイミング、更にその中のテキストブロックの作成タイミングをフック出来ます。
この辺のことについては、いずれチュートリアルと共に別エントリで説明する予定です。
通常のPrepared Statementを少し使いやすくする処理系 eps
を作りました。
epsはExtended Prepared Statementの略です。
簡単に言うと、こんな感じでPrepared Statementを記述したくて作ったものです。
prepare foo(age:int, name:text="no name!") as select * from people where name={name} and age={age};
ようするに
わけでした。
上のSQLをtest.sql
というファイルで保存したとして、
eps.exe -input test.sql -format sql
とすると
prepare foo(int, text) as select * from people where name = $2 and age = $1;
が出力されます。また
eps.exe -input test.sql -format ocaml
とすると
let prep_foo = "prepare foo(int, text) as select * from people where age = $1 and name = $2;" let exec_foo ~age ?(name="no name!") () = Printf.sprintf "execute foo(%d, '%s');" age name
のように、デフォルト引数、ラベル付き引数、型制限が付いたコードが出力されます。
Kefir.jsを使ったのは、ドキュメントがわかりやすくて綺麗だったからです。
page_count_stream
を作る。page_index_stream
をる。page_count_stream
を購読し、$(“#page-count”)を更新する。page_index_stream
を購読し、$(“#screen”)に該当ページを出力しつつ、$(“#page-no”)にページ番号を出力する。page_count_stream
とpage_index_stream
を宣言的に(ステートを持たないように)表現できたら成功。
nehan.jsが作ったページ数を受け取るストリームをKefir.bus()
で作成しておきます。
var page_count_stream = Kefir.bus();
NEXT/PREVのクリックをそれぞれ+1
と-1
を出力するストリームにし、それらを一つに束ねたものをclick_value_stream
として定義します。
var next_click_stream = $("#next").asKefirStream("click"); var prev_click_stream = $("#prev").asKefirStream("click"); var click_value_stream = Kefir.merge([ next_click_stream.mapTo(1), prev_click_stream.mapTo(-1) ]);
ここからは少し面倒です。
こういうnext/prevでカウントするサンプルって、大抵はストリームの値を合算したものを現在値みたいにするケースが殆どです。
// これだと使えない var sum_stream = Kefir.scan(click_value_stream, function(acm, value){ return acm + value; });
しかしこれだと上手くいかないのです。
なぜならこちらが欲しいのは、下限と上限付きの値だからです。
言うまでもなく、下限は先頭ページ位置の0
で、上限は「現在の」出力可能なページ数をマイナス1した値です。
ページ数が4(最大インデックス3)なのに、4とか5を出力されても困りますよね。
例えば2回だけ上限オーバーした数値は、PREVを2回押して戻さないと使えないってことになりますので。
だから単なる合算ではなく、現在のページ数を参照して合算するストリームが必要になります。
これを実現するにあたって最初にミスったのはKefir.zip
を使って、クリックのストリームとページ数のストリームを合成することでした。
しかしこれは上手く行きません。
なぜならクリックのストリームがclickイベントが発生する度に出力する無限ストリームであるのに対し、ページ数のストリームは最終ページを出力したらもう新しい値を出力しない有限サイズのストリームだからです。
有限のストリームと無限のストリームをzipすると、短い方に合わせて有限のストリームが出来上がります。
つまりKefir.zip
を使って合成したストリームを購読しても、有限回しか操作できません。
こういう2つのストリームを一つの無限ストリームに束ねるにはどうしたらいいのでしょう。
そこでドキュメントをざっと眺めて見つけたのがKefir.combine
です。
この関数は、対象ストリームの「最新の値」を使ってストリームを合成できます。
つまり片方の長さが3しかなくても、もう片方が無限なら、有限な方の最後の値が無限に参照できます。
結果、こんな実装になりました。
var page_index_stream = Kefir.combine([ click_value_stream, page_count_stream ], function(value, count){ return {value:value, count:count}; }).scan(function(acm, cur){ return { // 0 ~ [cur.count - 1] value:Math.max(0, Math.min(cur.count - 1, acm.value + cur.value)), count:cur.count }; }, {value:0, count:0}).map(function(obj){ return obj.value; });
さて、なんとかpage_count_stream
とpage_index_stream
を定義することができました。
後は、page_count_stream
とpage_index_stream
をそれぞれ購読し、必要なUIを更新する処理を定義するだけです。
ページ出力のタイミングでpage_count_stream
に最新のページ数がemit
されるので、それに合わせて、紐付いたUIが連動して変化するようになっています。
var paged_element = Nehan.createPagedElement(); // ページ数を購読 page_count_stream.onValue(function(count){ $("#page-count").html(count); }); // UI操作とページ数によって決まる現在のページ位置を購読 page_index_stream.onValue(function(index){ $("#page-no").html(index + 1); paged_element.setPage(index); // 現在位置を更新 }); // ページ計算を開始 paged_element.setStyle("body", { "flow":"tb-rl", // or "lr-tb" "font-size":16, "width":500, "height":300 }).setContent($("#screen").html(), { onProgress:function(tree){ // ページ出力のタイミングで現在のページ数を出力 page_count_stream.emit(tree.pageNo + 1); } }); // 画面上に組版結果を表示する $("#screen").after(paged_element.getElement());
色々なUI要素が絡むような複雑なアプリなら、注目している値を透過的に取得できるので、安全にUIを更新できそうな気がしますが、今回のような単純なケースでは少し面倒かもしれません。
既にあるのかもしれませんが、だからといってどうやって検索したらいいかわからないものは自分で作るしかない…ということで表題のものを作りました。
tategakibunko/jquery.image-size-assign · GitHub
どういうものかというと、ようするに以下のようなことをするものです。
<!-- before --> <img src="http://placehold.it/350x150"> <!-- after --> <img src="http://placehold.it/350x150" width="350" height="150">
こんな感じです。
$(function(){ $("img").imageSizeAssign({ maxSize:{ width:500, height:500 }, onSize:function(size){ return size; }, onComplete:function(){ //console.log("all sizes are set"); } }); });
オプションにmaxSize
を指定すると、あふれた時に元サイズのレートを保ったままリサイズします。
例えば上の例だとmaxSize
が500x500なので、1000x1000の画像だったら、500x500にリサイズされます。
javascript - Can I sync up multiple image onload calls? - Stack Overflow