2015/05/22(金)Version 2.28dリリース情報
Ver2.28c→Ver2.28dの変更点
- コメントのロボットSPAM避けルーチンが、Perl 5.20で完全に死んでたいたので修正しました。
- adiary形式/はてな形式でexportするとき、<等の実体参照があると正しくエスケープされない不具合を修正しました。
Version2.00(β含む)以降からの乗り換え
- Ver2.11以前の場合、uploader.conf.cgi をサンプルから再生成してください。
Perl 5.20にしたら、DBIが何やら不可解な動作をするようになりました。
どうやら UTF8 を扱う機能が増えたみたいです。DBIの接続時のパラメーターにこんなものが増えています。
pg_enable_utf8 => 1 , # 結果をUTF8フラグ付きにする(PostgreSQL)
mysql_enable_utf8 => 1 , # 結果をUTF8フラグ付きにする(MySQL)
PerlのDBIモジュールで自動的にUTF8フラグを付ける
この影響で、PostgreSQL運用のシステムを Perl 5.14 から 5.20 にアップグレードしたところ文字化け発生。また厄介なことをしてくれたものです(汗)
普通に使っているのに「Wide character in print at」の警告がなぜか出まくります。日本語UTF-8として保存した文字列が文字化けしまくるのですが、そのデータだけ単独に取り出して表示しても化けない。
色々調べてみると以下のことがわかりました。
PostgreSQLではこんな感じです。
同じUTF8文字列でありながら、違う内部表現になっています。当然、元の「あいう」と文字列比較を行うと異なると判定されます。
MySQLではこんな感じです。
問題を整理すると2つに集約できます。
後者も問題と言えば問題ですが、前者はとても気持ち悪い問題です。
utf8フラグはうまく使えば「Perlによきに計らってもらい」文字コードの問題が楽になるものですが、utf8文字列をutf8として「そのまま扱いたい」時には問題が多すぎる仕組みです。入出力の都度にencode、decodeが発生し処理を重くする原因にもなります。
またutf8フラグありとフラグなしのUTF8文字列が混在すると、それらを連結したときに文字化けする危険があります。
そうなるとutf8フラグなしで扱いたいのですが、そうするためにはどうしたらいいのでしょうか。
保存時の挙動。
取り出す時用に「pg_enable_utf8 => 0」を設定して、保存時や「検索時」にutf8フラグを付けるのがよさそうです。
追記参照のこと。
DBD::Pgとは実装がまるで違う事がわかります。
ドキュメントを読んだところ「SET NAMES utf8;」というSQL文を発行することで、同じ効果を得て、かつutf8フラグに影響がでないことがわかりました。ただこの状態では、特殊エンコードされた文字列を読み込むときに文字化けするので注意が必要です。
DBD::Pgの実装のほうが謎かもしれない。DBD::Pgはトランザクション処理がおかしいという素敵な実績があるからなあ……。「pg_enable_utf8 => 0」のとき保存データのutf8フラグも無視しないと矛盾するのはすぐに分かりそうなもんなんだけど。
Perl supports two kinds of strings: Unicode (utf8 internally) and non-Unicode (defaults to iso-8859-1 if forced to assume an encoding). Drivers should accept both kinds of strings and, if required, convert them to the character set of the database being used. Similarly, when fetching from the database character data that isn't iso-8859-1 the driver should convert it into utf8.
http://search.cpan.org/~timb/DBI-1.633/DBI.pm
Perl は Unicode(内部表現utf8)と 非Unicode(iso-8859-1)の2つの文字コードをサポートします。DBDドライバはこの2つの文字コードを受け付けるべきで、必要に応じて database で使用可能な文字列に変換する必要があります。
同様に、iso-8859-1ではない文字列は database から取り出す段階でutf8に変換すべきです。
仕様変更というよりBug Fixされ「CHANGES」を読むと、DBD Version 3.6.0あたりから挙動が異なる(正しい挙動に近くなっている)ようです。
しかし、この変更のせいで「pg_enable_utf8 => 0」のときutf8フラグ付の文字列を渡すと「Wide character in subroutine entry」エラーが発生します。
ですので「pg_enable_utf8 => 0」のときは、$DBD::Pg::VERSIONを参照し「3.3.0~3.5.3の間の時だけUTF8フラグをつけてDBIに渡す」必要があります。
クソバグを仕込んだ上に、互換性考えない中途半端なクソ修正をして、そびえ立つクソ状態なDBD::Pgには呆れるばかり……。
本当はRC1の予定だったのですが、ゴールデウィークにがんばった影響か、あまりにも変更点が積み重なってきたのでひっそりβ公開しておきます。もう仕様変更することはないと思いたい……。
「PostgreSQL/MySQL + Perl 5.20」での運用時にDBに保存される文字列がUTF8にならない(サイズが増える)という問題が見つかったため対応しました。
β6以前で該当環境で運用していてる方は、ブログのデータを一度エクスポートした上で、アップグレード後に再度インポートしなおしてください。
jQuery UI や dynatree を使用していて、ドラッグアンドドロップ操作が必須であるにも関わらずスマホでは何もできないので、汎用的な実装をjQuery pluginで実現しました。
タッチパネル系イベントは独特らしく、短くタップしたときのみ mousedown や click 等のマウスイベントが発生してくれますが、長く触っているとマウスイベントは発生しないようです。
ですので、以下のように実装しました。
mouseover, mouseoutも実装はできますが無視しました。jQuery UIが問題なく動く程度には実装しているつもりです。
エミュレーションがonの状態で短くタップすると、mousedown/mouseupイベントが2重に発生する可能性がありますが、解決策がないので保留です。
修正BSDライセンスとします。jQuery pluginですので適当に読み込ませて次のように使ってください。
$(dom).dndEmulation();
以下のソースは最新でない可能性があります。最新版は、adiaryのサイトからGitHub経由で「js/adiary.js」を参照して該当部のみ抜粋してください。該当部のみ抜粋する限り修正BSDライセンスで扱って構いません。
var TouchDnDTime = 700; $.fn.extend({ ////////////////////////////////////////////////////////////////////////////// // Copyright (C)2015 nabe@abk, New BSD License. ////////////////////////////////////////////////////////////////////////////// dndEmulation: function(){ var self = this[0]; if (!self) return; // mouseイベント作成 function make_mouse_event(name, evt, touch) { var e = $.Event(name); e.altKey = evt.altKey; e.metaKey = evt.metaKey; e.ctrlKey = evt.ctrlKey; e.shiftKey = evt.shiftKey; e.clientX = touch.clientX; e.clientY = touch.clientY; e.screenX = touch.screenX; e.screenY = touch.screenY; e.pageX = touch.pageX; e.pageY = touch.pageY; e.which = 1; return e; } // 自分自身を含めた親要素をすべて取得 function get_par_elements(dom) { var ary = []; while(dom) { ary.push( dom ); if (dom == self) break; dom = dom.parentNode; } return ary; } // クロージャ変数 var prev; var flag; // mousedownエミュレーション this.bind('touchstart', function(_evt){ var evt = _evt.originalEvent; prev = evt.target; var e = make_mouse_event('mousedown', evt, evt.touches[0]); $( prev ).trigger(e); // ある程度時間が経過しないときは処理を無効化する。 flag = false; setTimeout(function(){ flag=true; }, TouchDnDTime) }); // mouseupエミュレーション this.bind('touchend', function(_evt){ var evt = _evt.originalEvent; var e = make_mouse_event('mouseup', evt, evt.changedTouches[0]); $( evt.target ).trigger(e); }); // ドラッグエミュレーション this.bind('touchmove', function(_evt){ var evt = _evt.originalEvent; // 一定時間立たなければ、処理を開始しない if (!flag) return; var touch = evt.changedTouches[0]; var dom = document.elementFromPoint(touch.clientX, touch.clientY); var enter = get_par_elements(dom); // マウス移動イベント var e = make_mouse_event('mousemove', evt, touch); $(enter).trigger(e); // 要素移動がなければこれで終了 evt.preventDefault(); if (dom == prev) return; // 要素移動があれば leave と enter イベント生成 var leave = get_par_elements(prev); // 重複要素を除去 while(leave.length && enter.length && leave[leave.length -1] == enter[enter.length -1]) { leave.pop(); enter.pop(); } // イベント発火 var e_leave = make_mouse_event('mouseleave', evt, touch); var e_enter = make_mouse_event('mouseenter', evt, touch); $(leave).trigger( e_leave ); $(leave).trigger( e_enter ); // 新しい要素を保存 prev=dom; }); } ////////////////////////////////////////////////////////////////////////////// });
充てたというかパッチしました。作りました。jQueryのみです。
画像をめくるときにタッチパネルのスワイプ操作でも反応するようになっています。特にアニメーションはしません。
jQueryのバグなのか「touchstart」「touchmove」イベントを全く拾わないので、「window.addEventListener」に逃げました。もしもっとスマートな解決方法を見つけたら教えてください。
これを使用しているadiaryもよろしくお願いします。
#lightbox-min-width { display: none; width: 300px; }
と設定すると、横幅が小さすぎる画像を表示するとき、指定したピクセルまでアスペクト比を保持して拡大して表示します。
極端に縦長の画像の場合はアスペクト比を保持できて、画面に収まる範囲内で拡大されます。