2014-12-13 ,

Windows8のスタートメニューアプリ

Windows8.1での環境を整えていたのですが、問題はアプリケーションの起動をどうするかでした。

私はWindows95からずっと、基本的にはスタートメニューをキー操作で使ってアプリを起動してきました。もちろんタスクバーやデスクトップから起動することもありますが、よく使うものはスタートメニューにフォルダを作ったり、ショートカットの名前を工夫したりして、最小限のキー操作で好きなアプリを素早く起動できるようにしていました。InternetフォルダのPuttyならば、Ctrl+Escでスタートメニューを開き、i→pと押すことで起動できました。Vistaでクラシックスタートメニューが使えなくなってからは、検索ボックスでアプリ名の数文字を入力してEnterで起動していました。今までの習慣を引き継ぐために、「ip Putty」という名前のショートカットを登録したりもしていました。そうすれば、スタートメニューを開いて i p Enter と入力すれば、ほぼ今まで通りPuttyが起動するという寸法です(Enterは必要になりましたが)。

Windows8からはスタートメニューが無くなりました。しかしWin+Qでアプリ名での検索が出来るので、アプリ名を数文字入力してEnterで起動はできます。スタート画面へのショートカットの登録も、ピン留め機能では好きな名前に出来ないようでしたが、エクスプローラを使えば任意の名前でショートカットを登録することもできました。一応最低限のカスタマイズで使うならこれで我慢することになるのだと思います。

しかし、私はWinキーを使うということが我慢なりませんでした。Winキーは正直押しづらいです。ノートだと特にそうです。自宅のデスクトップのキーボード(Realforce106)だとそもそもWinキーがありません。スタートメニューはCtrl(Capsの位置にリマップ済み)+Escで開いていました。でもWindows8ではCtrl+Escを押してもスタート画面が開くだけで検索ワードを入力する画面にはなりません。

仕方がないので噂のスタートメニューアプリを探すことにしました。

Windows8 でスタートメニューを取り戻す方法 - NAVER まとめ

なんか胡散臭そうな企業の製品が多いんですよね。今のご時世、あまり信用できない企業のアプリを入れるのは抵抗があるのですが……。まぁ、Windowsを使っていて、今更ですけどね。

秀丸スタートメニューなんかはその点で安心できそうですね。でも検索ボックスが無さそうなのでやめておきました(設定で出せたりする?)。

オープンソースのものがないのかなと検索すると、Classic Shell(SourceForge)というのが見つかりました。ただし、オープンソースなのはVersion3.6.8までで、Version3.9.0からはソース非公開になったようです。

Classic Shell - Start menu and other Windows enhancements

オープンソースじゃ無くなったことについてはFAQで理由が書いていますが(Classic Shell: FAQ)、第一にWindowsのシェル拡張などの開発方法について他の開発者と一般的な知識を共有したかったがアプリが複雑化するにつれてその意味が薄れていったこと、第二にClassic Shellをほとんど手を加えずそのままコピーして売るものが現れたこと(OSSのライセンス的には合法だが、作者の意図ではなかった)を挙げています。

作者は個人名のようですし、開発用フォーラムでバグ報告、機能リクエスト、翻訳作業などは行われていて、正当な理由があればソースコードを見せるとも言ってます。開発用フォーラムでは日本語への翻訳も行われています。

このあたりが無難かなぁと。私が必要とする機能は揃っていました。

2014-12-13

新ノートPC新調(VAIO Pro 13)

ノートPCを新調しました。現在使っていたLet's note CF-R8はWindows Vista世代で、OS的にもVisual Studio 2013が入りませんし、メモリも1GBと少なく、ちょっと何かするとすぐHDDカリカリでもっさりしてしまうので。

新しいPCを選定するにあたっての条件は以下のようなものでした。

  • 軽量 (1kgくらいまで)
  • 画面大きめ (12インチ以上)
  • タッチパネル必須
  • 十分な性能 (Visual StudioやPhotoshopを難なく使える程度)
  • 許容できるキー配列 (極端な変態配列は不可)
  • キックスタンドNG (限られた場所でも自立すること)

軽量と言えばNEC Lavie Z(Lavie G type Z)。持ってみるとその軽さにびっくりします。でもタッチパネルモデルはそこまで軽くないし、性能面でのバリエーションは限られます。i5/メモリ4GBが上限の割に価格はVAIOのi7/メモリ8GBモデルくらいなので、少し割高感もあります。(今SSDアップグレードが半額になるキャンペーン中らしいですが)

もう少し重い機種(1.5kgまでくらい)を許容するならLenovoのYOGA等、選択肢は広がるのですが、外で使うことを考えると重量はこだわりたいところ。現行のCF-R8が930gくらいなので、そこから大幅に増えるのはちょっと。

画面の大きさは、長いことCF-R8を使っていてもう少し大きい方が良いかなと思っていたので、今回は大きめで。

タッチパネルは必須。この条件を入れると大分機種が絞られるので便利。私は前々からPCにはデスクトップかノートかに関わらずタッチパネルが必要だと言い続けていたので、ようやく手が届くタッチパネル搭載機が出た今、選ばずにはいられない。

Let's note MX3はキーボード右下の超変態配列によって候補から外れました。?とか_とかまともに打てないよコレ。慣れれば打てるのかもしれないけど、慣れたくないでしょ。他の点では魅力がある製品なだけに惜しい。

Surface Pro 3はキックスタンドなので候補から外れました。ただ、良い点も色々あるのでもう少し詳しく検討したかったところ。

と、言うわけで、これらの条件を満たすものを探した結果、VAIO Pro 13となりました。

Intelの新しいCPUが出そうな時期なので、正直もう少し待とうかとも思ったのですが、我慢しきれず買ってしまいました。しばらくはこれを使っていこうかと思います。

今回は性能を重視したので、Visual StudioもPhotoshopも難なく動きます。SSD搭載機は初めてですが、素晴らしいですね。

今のところおおむね満足ですが、一つだけ悪いところが。それはタッチパッド。正直このタッチパッドはクソです。ホームポジションに手を置くと必ず手がタッチパッドに触れてしまいます。もちろん誤動作しないように、タッチする表面積が多いときは反応しないようになっているのですが、それでもキーを打っていると、たまにクリックしてしまいます。特に右クリックを押してしまいます。クリックすると別のウィンドウにフォーカスが移ったり、キャレットの位置があらぬ所に飛んで酷いことになります。あと、たまにキーを打っていると突然チャームが出現したり。タッチパッドの設定を調整したら大分マシになりましたが、それでもたまに誤操作してしまいます。完全に防ぐことは難しそうです。

2014-11-19 ,

window.orientationプロパティ

window.orientationというプロパティがあるみたいですね?

公式の資料として見つかったのはこれくらいでした。MDNには書いてませんでしたし。

IEはどうか分かりませんが、iOSやAndroidでは対応しているようです。対応しているのはモバイル版のみで、デスクトップ版では対応していないとか何とか。実際AndroidのChromeでは数値が返ってきますが、デスクトップ版のChromeではundefinedになります。

以前DeviceOrientationの使い方について書きましたが、その際にscreen.orientation.angleを使って画面の向き(回転角)を取得する方法についても言及しました。

DeviceOrientationイベントは画面の向きを考慮しない値を提供するだけなので、画面の向き(角度)を別途取得するか、画面の向きをロックするかしないと全く使い物にならないんですよね。

このwindow.orientationが使えれば、screen.orientation.angleが使えない場合でも画面の向きを角度で取得することが可能です。

なので、js_deviceorientationjs_skigameを修正してみました。これで加速度センサーで操作できるかな。

2014-11-18

HTML+CSSで縦書き

そういえば結局縦書きってどうなったんだっけ? と思ったのでテスト。

隴西(ろうさい)の李徴(りちょう)は博学才穎(さいえい)、天宝の末年、若くして名を虎榜(こぼう)に連ね、ついで江南尉(こうなんい)に補せられたが、性、狷介(けんかい)、自(みずか)ら恃(たの)むところ頗(すこぶ)る厚く、賤吏(せんり)に甘んずるを潔(いさぎよ)しとしなかった。いくばくもなく官を退いた後は、故山(こざん)、※(「埒のつくり+虎」、第3水準1-91-48)略(かくりゃく)に帰臥(きが)し、人と交(まじわり)を絶って、ひたすら詩作に耽(ふけ)った。下吏となって長く膝(ひざ)を俗悪な大官の前に屈するよりは、詩家としての名を死後百年に遺(のこ)そうとしたのである。しかし、文名は容易に揚らず、生活は日を逐(お)うて苦しくなる。李徴は漸(ようや)く焦躁(しょうそう)に駆られて来た。この頃(ころ)からその容貌(ようぼう)も峭刻(しょうこく)となり、肉落ち骨秀(ひい)で、眼光のみ徒(いたず)らに炯々(けいけい)として、曾(かつ)て進士に登第(とうだい)した頃の豊頬(ほうきょう)の美少年の俤(おもかげ)は、何処(どこ)に求めようもない。数年の後、貧窮に堪(た)えず、妻子の衣食のために遂(つい)に節を屈して、再び東へ赴き、一地方官吏の職を奉ずることになった。一方、これは、己(おのれ)の詩業に半ば絶望したためでもある。曾ての同輩は既に遥(はる)か高位に進み、彼が昔、鈍物として歯牙(しが)にもかけなかったその連中の下命を拝さねばならぬことが、往年の儁才(しゅんさい)李徴の自尊心を如何(いか)に傷(きずつ)けたかは、想像に難(かた)くない。彼は怏々(おうおう)として楽しまず、狂悖(きょうはい)の性は愈々(いよいよ)抑え難(がた)くなった。一年の後、公用で旅に出、汝水(じょすい)のほとりに宿った時、遂に発狂した。或(ある)夜半、急に顔色を変えて寝床から起上ると、何か訳の分らぬことを叫びつつそのまま下にとび下りて、闇(やみ)の中へ駈出(かけだ)した。彼は二度と戻(もど)って来なかった。附近の山野を捜索しても、何の手掛りもない。その後李徴がどうなったかを知る者は、誰(だれ)もなかった。
<div style="width:100%; height:30em; overflow:scroll; padding: 1em; font-family: '@MS 明朝'; writing-mode: tb-rl; writing-mode: vertical-rl; -moz-writing-mode: vertical-rl; -webkit-writing-mode: vertical-rl; ">
本文
</div>

(本文は中島敦 山月記より)

WebKit系とIEはいけるみたいですね。

あと、フォントは縦書き用じゃないと色々と不都合が出る気が。

JavaScriptライブラリも色々あるみたいですね。

2014-11-18 ,

org-mode文書をHTMLでエクスポートするときにLaTeX部分をMathMLへ変換する

先日の修正をdefadviceの形に直した。HTMLエクスポート時にMathjaxを使わずに直接MathMLでエクスポートする。

;; org-html-mathml-export.el

;; 次の二つの関数はhtmlエクスポート時にlatex部分をhtmlへ変換する。
;; - org-html-latex-environment
;; - org-html-latex-fragment
;; この関数において、processing-typeがmathmlのときはmathjaxのときと同じように
;; org-html-format-latexを呼び出すようにする。
;; org-html-format-latexはorg-format-latexを呼び出す。
;; org-format-latexはprocessing-typeがmathmlのときorg-format-latex-as-mathmlを呼び出す。

(defadvice org-html-latex-environment
  (around org-html-latex-environment--html-mathml
          (latex-environment contents info)
          activate)
  (setq ad-return-value
        (let ((processing-type (plist-get info :with-latex)))
          (case processing-type
            ((mathml)
             (let ((org-format-latex-as-mathml-format 'mathml))
               (org-html-format-latex
                (org-remove-indentation
                 (org-element-property :value latex-environment)) ;;latex-frag
                'mathml
                info)))
            ((t)
             ad-do-it)))))

(defadvice org-html-latex-fragment
  (around org-html-latex-fragment--html-mathml
          (latex-fragment contents info)
          activate)

  (setq ad-return-value
        (let ((processing-type (plist-get info :with-latex)))
          (case processing-type
            ((mathml)
             (let ((org-format-latex-as-mathml-format 'mathml))
               (org-html-format-latex
                (org-element-property :value latex-fragment) ;;latex-frag
                'mathml
                info)))
            ((t)
             ad-do-it)))))


(defvar org-format-latex-as-mathml-format 'link
  "org-format-latex-as-mathml関数がどのような形式の文字列を返すかどうかを指定する変数。'linkのときMathMLファイルへのリンクを返す。'mathmlのときMathMLの文字列自体を返す(XMLヘッダー無し)。")

;;
;; org-format-latex-as-mathmlはLaTeX表記をMathMLへ変換する。
;; OpenDocumentエクスポートの時はファイルへのリンクへ変換する。
;; HTMLエクスポートの時はMathMLそのものへ変換する。
;;

(defadvice org-format-latex-as-mathml
  (around org-format-latex-as-mathml--html-mathml
          (latex-frag latex-frag-type prefix &optional dir)
          activate)

  (setq ad-return-value
        (if (eq org-format-latex-as-mathml-format 'mathml)
            (org-format-latex-as-mathml-not-file latex-frag latex-frag-type prefix dir)
          ad-do-it)))

(defun org-format-latex-as-mathml-not-file (latex-frag latex-frag-type
                                                       prefix &optional dir)
  "Use `org-create-math-formula' but check local cache first."
  (let* ((print-length nil) (print-level nil) ;;必要?
         (mathml (org-create-math-formula latex-frag)))

    ;; Successful conversion.  Return the MathML.
    (if mathml
        (progn
          ;; Erase XML header
          (if (eql (string-match "^<\\?xml [^>]+>" mathml) 0)
              (setq mathml (replace-match "" t t mathml)))
          ;; Erase all line breaks
          (setq mathml (replace-regexp-in-string "[\n\r]" "" mathml))

          ;; Add props
          (org-add-props
              mathml

              (list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
                    'org-latex-src-embed-type (if latex-frag-type
                                                  'paragraph 'character))))
      ;; Failed conversion.  Return the LaTeX fragment verbatim
      latex-frag)))

使い方:

  • これを適当な場所でloadする。
  • mathtoweb.jarをダウンロードして適当な所に置く。
  • 変数をカスタマイズする。
    • org-latex-to-mathml-convert-command を "java -jar %j -unicode -force -df %o %I"
    • org-latex-to-mathml-jar-file を "(ファイルを置いた場所)/mathtoweb.jar"
  • org文書の #+OPTIONS:tex:mathml を書き加える。

Org2Blogでこれを使うなら:

  • org2blog/wp-use-wp-latex を nil にする。
2014-11-14 , ,

org-modeで数式を書く(HTMLへMathMLでエクスポートする)

このブログ(Org2Blog使用)で数式を書くにはどうすれば良いのだろうか。

もともとorg-modeはLaTeXの表記が使えるのだけど、今の環境にはTeX関連のソフトウェアは入ってないし、画像化して埋め込むというのは面倒くさそうだ。

出力はHTML限定なのだから、MathMLを #+BEGIN_HTML#+END_HTML に書けば良さそうだ。でもMathMLはとても手で書ける代物では無いので、何らかのエディタを使わなければならない。MathMLが出力できる手軽なエディタはあるのだろうか。数式エディタは沢山ありすぎて正直よく分からない。

とりあえず http://www.mathtowebonline.com/ でLaTeXからの変換が出来るようだ。

例えば $a^2=b$ とすると、次のMathMLが得られる。

#+BEGIN_HTML
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
        <msup>
                <mi>a</mi>
                <mn>2</mn>
        </msup>
        <mo>=</mo>
        <mi>b</mi>
</mrow>
</math>
#+END_HTML

これを実際に書くと↓こんな感じ?(要MathML対応ブラウザ)

a2=b

エクスポートするときにこの変換を自動的にしてくれれば良いのに。調べてみるとorg-modeはLaTeXで書かれた部分をhtmlへエクスポートする際、Mathjaxというものを使うらしい。

うはぁ、JavaScriptを使って html内のLaTeX表記 をブラウザ側でMathMLや(ブラウザが対応していなければ)HTML+CSSへ置換していくらしい。実際試しにエクスポートしてみたら、 LaTeXの表記はそのまま本文に出力 され、ヘッダーで http://orgmode.org/mathjax/MathJax.js を読み込むようなコードが出力されていた。FirefoxはMathMLに対応しているので最終的にMathMLで表示されているかと思いきや、spanとimgタグになっていた。どうもorgmode.orgに置いてあるMathJax.jsがversion="1.0.1"と古いせいのようだ。最新の2.4を使うように修正したらちゃんとMathMLへ置換されるようになった。確かにMathMLに対応していないブラウザでも表示させたければ、こういう方法しか無いのかもしれないけど……。

と、ここまで来て気がついたが、Chromeは未だにMathMLに対応していないらしい。

Issue 152430 - chromium - Enabling support for MathML - An open-source project to help move the web forward. - Google Project Hosting

なるほど、Chromeで使えないとなるとJavaScriptで何とかするのも仕方が無い。しかし、html内の$や\を自動的に置換されてしまうと既存の文章の意味が変わってしまう。私は日常的に数式を書くわけでは無いし、普段は$や\をエスケープせずにそのままの意味で使いたい。ブログ全体にLaTeX→MathML変換を適用するのは得策では無いだろう。

MathjaxにはMathMLに対応していないブラウザー向けにMathMLをHTML+CSSに置換する機能があり、LaTeXからの変換はしないで、それだけをすることも出来るようだ。ここはやはり エクスポート時点でMathMLへ変換 し、Chrome等が対応するまでMathjaxをブログのヘッダーに埋め込んでおき、将来ほとんどのブラウザーが対応したらMathjaxだけ取り外す、ということができると一番良いのではないか。

というわけで、エクスポート時に何とかorgのLaTeX部分をMathMLへ変換したい。

org-modeに既にMathML関連の関数が無いかと探してみると、 org-format-latex-mathml というものが見つかった。それに関連して二つのカスタマイズ可能な変数 org-latex-to-mathml-convert-command と org-latex-to-mathml-jar-file も見つかった。どうもこれはOpenDocumentエクスポートで使用する関数らしい。

MathToWeb というJavaで作られたプログラムを使用してLaTeX表記をMathMLへ変換するようだ。これは使える。

OpenDocumentエクスポートの時は #+OPTIONS: tex:mathml と書けばMathMLでエクスポートされる。これをHTMLエクスポートの時でも使えるようにしたい。

ox-html.elとorg.elを詳しく見た結果、次のように変更することで実現できることが分かった。

まず、ox-html.el内のorg-html-latex-environmentとorg-html-latex-fragmentはlatexで書かれた部分をhtmlへ変換する関数なので、これを修正する。(変更箇所はコメント;;BEGINと;;ENDで挟んである)

(defun org-html-latex-environment (latex-environment contents info)
  "Transcode a LATEX-ENVIRONMENT element from Org to HTML.
CONTENTS is nil.  INFO is a plist holding contextual information."
  (let ((processing-type (plist-get info :with-latex))
        (latex-frag (org-remove-indentation
                     (org-element-property :value latex-environment)))
        (attributes (org-export-read-attribute :attr_html latex-environment)))
    (case processing-type
      ((t mathjax)
       (org-html-format-latex latex-frag 'mathjax info))
;;BEGIN ADD
      ((mathml)
       (org-html-format-latex latex-frag 'mathml info))
;;END ADD
      ((dvipng imagemagick)
       (let ((formula-link
              (org-html-format-latex latex-frag processing-type info)))
         (when (and formula-link (string-match "file:\\([^]]*\\)" formula-link))
           ;; Do not provide a caption or a name to be consistent with
           ;; `mathjax' handling.
           (org-html--wrap-image
            (org-html--format-image
             (match-string 1 formula-link) attributes info) info))))
      (t latex-frag))))

(defun org-html-latex-fragment (latex-fragment contents info)
  "Transcode a LATEX-FRAGMENT object from Org to HTML.
CONTENTS is nil.  INFO is a plist holding contextual information."
  (let ((latex-frag (org-element-property :value latex-fragment))
        (processing-type (plist-get info :with-latex)))
    (case processing-type
      ((t mathjax)
       (org-html-format-latex latex-frag 'mathjax info))
;;BEGIN ADD
      ((mathml)
       (org-html-format-latex latex-frag 'mathml info))
;;END ADD
      ((dvipng imagemagick)
       (let ((formula-link
              (org-html-format-latex latex-frag processing-type info)))
         (when (and formula-link (string-match "file:\\([^]]*\\)" formula-link))
           (org-html--format-image (match-string 1 formula-link) nil info))))
      (t latex-frag))))

processing-typeがmathmlのとき、mathjaxと同じようにorg-html-format-latexを呼び出すようにした。ただし、引数のprocessing-typeは'mathmlとする。

それで呼ばれるorg-html-format-latex関数(ox-html.el内)は変更無し。ただし、この関数はorg-format-latex(org.el内)を呼び出す。

呼ばれるorg-format-latex関数(org.el内)も変更無し。ただし、この関数はorg-format-latex-as-mathml(org.el内)を呼び出す。

呼ばれるorg-format-latex-as-mathml関数(org.el内)はorg-create-math-formula関数(org.el内)を呼び出してLaTeX表記をMathMLへ変換する。そしてOpenDocument用にMathMLファイルへのリンク文字列([[file:hoge-formula-0780fe71dd8921d4e3ff293d9e68b588e526cd9c.mathml]] みたいなの)を生成する。これをリンクでは無く、変換後のMathMLの内容になるように修正する。

ただし、キャッシュを使うかどうかで迷う。元の実装は数式のSHA1ハッシュ値をファイル名にすることで、同じ数式は1回しか変換しないようになっている。この仕組みを流用すれば、同様に同じ数式を何回も変換することを避けることが出来る。その場合は、すでにキャッシュファイルを作るところまでは出来ているので、リンク文字列の代わりにキャッシュファイルの内容を読み込んで文字列にし、それを結果とすれば良い。

(defun org-format-latex-as-mathml (latex-frag latex-frag-type
                                              prefix &optional dir)
  "Use `org-create-math-formula' but check local cache first."
  (let* ((absprefix (expand-file-name prefix dir))
         (print-length nil) (print-level nil)
         (formula-id (concat
                      "formula-"
                      (sha1
                       (prin1-to-string
                        (list latex-frag
                              org-latex-to-mathml-convert-command)))))
         (formula-cache (format "%s-%s.mathml" absprefix formula-id))
         (formula-cache-dir (file-name-directory formula-cache)))

    (unless (file-directory-p formula-cache-dir)
      (make-directory formula-cache-dir t))

    (unless (file-exists-p formula-cache)
      (org-create-math-formula latex-frag formula-cache))

    (if (file-exists-p formula-cache)
        ;; Successful conversion.  Return the link to MathML file.
        (org-add-props
;;BEGIN DEL
;;          (format  "[[file:%s]]" (file-relative-name formula-cache dir))
;;END DEL
;;BEGIN ADD
            (with-temp-buffer (insert-file-contents formula-cache) (buffer-string))
;;END ADD

            (list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
                  'org-latex-src-embed-type (if latex-frag-type
                                                'paragraph 'character)))
      ;; Failed conversion.  Return the LaTeX fragment verbatim
      latex-frag)))

もし余計なファイルを生成するのが煩わしい場合は、毎回org-create-math-formulaを呼び出せば良い。一つの記事に数式がそれほど多くないのであればこれでも良いと思う(全体的に関数を差し替え)。

(defun org-format-latex-as-mathml (latex-frag latex-frag-type
                                              prefix &optional dir)
  "Use `org-create-math-formula' but check local cache first."
  (let* ((print-length nil) (print-level nil) ;;必要?
         (mathml (org-create-math-formula latex-frag)))

    ;; Successful conversion.  Return the MathML.
    (if mathml
        (org-add-props
            mathml

            (list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
                  'org-latex-src-embed-type (if latex-frag-type
                                                'paragraph 'character)))
      ;; Failed conversion.  Return the LaTeX fragment verbatim
      latex-frag)))

最後にorg-create-math-formula(org.el内)関数。余計なxmlヘッダーを付けるコードが入っているので修正する。

(defun org-create-math-formula (latex-frag &optional mathml-file)
  "Convert LATEX-FRAG to MathML and store it in MATHML-FILE.
Use `org-latex-to-mathml-convert-command'.  If the conversion is
sucessful, return the portion between \"<math...> </math>\"
elements otherwise return nil.  When MATHML-FILE is specified,
write the results in to that file.  When invoked as an
interactive command, prompt for LATEX-FRAG, with initial value
set to the current active region and echo the results for user
inspection."
  (interactive (list (let ((frag (when (org-region-active-p)
                                   (buffer-substring-no-properties
                                    (region-beginning) (region-end)))))
                       (read-string "LaTeX Fragment: " frag nil frag))))
  (unless latex-frag (error "Invalid LaTeX fragment"))
  (let* ((tmp-in-file (file-relative-name
                       (make-temp-name (expand-file-name "ltxmathml-in"))))
         (ignore (write-region latex-frag nil tmp-in-file))
         (tmp-out-file (file-relative-name
                        (make-temp-name (expand-file-name  "ltxmathml-out"))))
         (cmd (format-spec
               org-latex-to-mathml-convert-command
               `((?j . ,(shell-quote-argument
                         (expand-file-name org-latex-to-mathml-jar-file)))
                 (?I . ,(shell-quote-argument tmp-in-file))
                 (?o . ,(shell-quote-argument tmp-out-file)))))
         mathml shell-command-output)
    (when (org-called-interactively-p 'any)
      (unless (org-format-latex-mathml-available-p)
        (user-error "LaTeX to MathML converter not configured")))
    (message "Running %s" cmd)
    (setq shell-command-output (shell-command-to-string cmd))
    (setq mathml
          (when (file-readable-p tmp-out-file)
            (with-current-buffer (find-file-noselect tmp-out-file t)
              (goto-char (point-min))
              (when (re-search-forward
                     (concat
                      (regexp-quote
                       "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">")
                      "\\(.\\|\n\\)*"
                      (regexp-quote "</math>")) nil t)
                (prog1 (match-string 0) (kill-buffer))))))
    (cond
     (mathml
;;BEGIN DEL
;;      (setq mathml
;;          (concat "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" mathml))
;;END DEL
      (when mathml-file
        (write-region mathml nil mathml-file))
      (when (org-called-interactively-p 'any)
        (message mathml)))
     ((message "LaTeX to MathML conversion failed")
      (message shell-command-output)))
    (delete-file tmp-in-file)
    (when (file-exists-p tmp-out-file)
      (delete-file tmp-out-file))
    mathml))

というわけで、

  • 以上のように関数を修正(修正するとOpenDocumentエクスポートが壊れるので注意。私はOpenDocumentでエクスポートしないので気にしていない)
  • mathtoweb.jarをダウンロードして適当なところに置く
  • 変数をカスタマイズ
    • org-latex-to-mathml-convert-command を "java -jar %j -unicode -force -df %o %I"
    • org-latex-to-mathml-jar-file を "~/bin/mathtoweb.jar"
  • 数式を使いたい記事を書くorgファイルの先頭ある #+OPTIONS:tex:mathml を書き加える

これで式が書けるようになる。

T = [ cos θ - sin θ 0 sin θ cos θ 0 0 0 1 ]

\begin{displaymath}
T = \left[
\begin{array}{rrr}
\cos \theta & -\sin \theta & 0\\
\sin \theta & \cos \theta & 0\\
0 & 0 & 1
\end{array}
\right]
\end{displaymath}

原理的に式番号等の連番は振れないのが残念。

後はChromeでも表示できるように、Blogのヘッダーに次のhtmlを入れる……んだけど、本当に全体に適用しちゃっていいのかな。

<script type="text/javascript"
  src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=MML_HTMLorMML">
</script>

後でもう少し調査する。

Org2Blogで使う上で少し修正。

  • org2blog/wp-use-wp-latexをnilにする。tだとWP LaTeX用の変換が使われてしまうので。
  • 変換後のMathMLから改行を取り除く。WordPress側で<br>が挿入されてしまうので。org-format-latex-as-mathmlを修正する。
(defun org-format-latex-as-mathml (latex-frag latex-frag-type
                                              prefix &optional dir)
  "Use `org-create-math-formula' but check local cache first."
  (let* ((print-length nil) (print-level nil) ;;必要?
         (mathml (org-create-math-formula latex-frag)))

    ;; Successful conversion.  Return the MathML.
    (if mathml
        (org-add-props
            (replace-regexp-in-string "[\n\r]" " " mathml) ;; 改行を削除する。WordPressが<br>を挿入しやがるので

            (list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
                  'org-latex-src-embed-type (if latex-frag-type
                                                'paragraph 'character)))
      ;; Failed conversion.  Return the LaTeX fragment verbatim
      latex-frag)))

WP LaTeXを使うという手もあったんですが、まぁ、今回はあくまでMathMLでエクスポートしたかったと言うことで。

(追記: 上記の修正をadviceの形でまとめた org-modeで数式を書く(HTMLへMathMLでエクスポートする) )

2014-11-12 ,

マルチプルタブハンドラ(Firefox拡張)

Feedlyでタブを一気に開くと、それを閉じるのが結構面倒くさい。タブをマウスのShift+クリックで範囲選択して、一気に閉じられたらなぁと思って調べてみた。

すでに入っていたTab Mix Plusにそんな機能は無いかなとShift+クリックしてみたらタブがピン留めされた。Ctrl+クリックはマージがどうのという機能らしいのだけどよく分からず。いずれにせよ、Tab Mix Plusにはそんな機能は無いようだ。

Google検索で調べた結果、Selecting multiple tabs, e.g. for mass window migrations によると、Multiple Tab HandlerかSession Managerを使うか、Bug 566510 – Allow multiselect operations on tabsの実現を待てということらしい。

マルチプルタブハンドラ (Multiple Tab Handler) :: Add-ons for Firefox を入れてみた。

すでにTab Mix Plusが入っているところにインストールすると、Tab Mix Plusで使われている操作と衝突するけどどうするかと聞かれた。素晴らしい気遣い! 当然マルチプルタブハンドラの操作だけを使うようにした。すると、タブを複数選択できるようになり、一括で閉じることが出来るようになった。

2014-11-12 , ,

Feedly Open All Unread Button Nov.2014

(2021-07-22追記:新しいのを作りました)

Feedlyに対する色々なユーザースクリプトが大分前に動かなくなったのを面倒くさいので放置していたのですが、重い腰を上げて未読を一気に別タブで開くのだけ実装しました。探せばどこかにあるのかもしれませんが、userscripts.orgが止まってからというものそれも面倒になりましたし、度々使えなくなるものなので、この際自分で調べて実装することにしました。

Feedlyサイト内の一つ一つのエントリーは.u0Entry(Title Only表示時)とか.u4Entry(Magazine表示時)とか.u5Entry(Cards表示時)とか.u100Frame(Full Articles表示時)とか、表示モードによって違うクラス名のdiv(以下、エントリーdiv)に囲まれるようですね。 エントリーdivではdata-で始まる属性で記事における様々な情報を表現しているようです。URLなんかはありますが、残念ながら既読未読の情報は無いようです。

未読だけを列挙するには、セレクターa.title.unreadにマッチするものを列挙すれば良さそうです。このa要素はエントリーdivの子孫ですが、何段下になるかは状況によって変わるようです。つまり、a.parentNode.parentNodeが必ずしもエントリーdivになるとは限りません。ただ、parentNodeをたどっていくといつかはエントリーdivにたどり着くようではあります。

記事のオリジナルURLは、aのhref=から求めても良いですし、エントリーdivのdata-alternate-link=から求めても良いでしょう。

URLを開いた後、記事を既読としてマークして一覧から消したいところです。Title Only表示の時はimg[title="Mark as read and hide"]にマッチする要素を、それ以外はspan.action[title="mark as read and remove from list"]にマッチする要素をクリックすることで実現できます。

後はUIです。開くボタンや一度に開く記事数のUIをページ内に追加したいです。しかしfeedlyが余分な要素を削除してしまう場合があるようだったので、1秒ごとに document.bodyの直下 (2014-11-13変更: .pageActionBarの直下) に挿入し直すようにしてみます。

以上を勘案して、次のようなスクリプトになりました。

function openUnreadEntries(limit){
    var unreads = document.querySelectorAll(".title.unread");
    var count = Math.min(unreads.length, limit || 5);
    for(var i = 0; i < count; ++i){
        // determine elements
        var title = unreads[i];
        var entry = (function(e){
            while(e && !e.getAttribute("data-alternate-link"))
                e = e.parentNode;
            return e;})(title.parentNode);

        // open url
        var url = (entry && entry.getAttribute("data-alternate-link")) ||
                title.getAttribute("href");
        window.open(url, "_blank");

        // mark as read and hide
        var readAndHideButton = entry && (
            entry.querySelector('.condensedTools img[title="Mark as read and hide"]') || //Title Only
            entry.querySelector('.action[title="mark as read and remove from list"]') //Magazine, Card, Full Articles
        );
        (readAndHideButton || entry || title).click();
    };
}
var BUTTON_CLASS_NAME = "openUnreadButton";
function createButton(){
    var div = document.createElement("div");
    div.style.display = "inline-block";
    div.style.border = "1px solid #bbb";
    div.style.borderRadius = "3px";
    div.style.verticalAlign = "top";
    div.className = "pageAction " + BUTTON_CLASS_NAME;

    div.innerHTML =
        '<input type="number" value="5" style="width:3em">'+
        '<input type="button" value="Open Unread">';

    var count = div.querySelector('input[type="number"]');
    var button = div.querySelector('input[type="button"]');
    button.addEventListener("click", function(e){
        openUnreadEntries(parseInt(count.value, 10));
    }, false);
    return div;
}
function setupUI(){
    var bars = document.getElementsByClassName("pageActionBar");
    for(var i = 0; i < bars.length; ++i){
        var bar = bars[i];
        if(!bar.querySelector("."+BUTTON_CLASS_NAME)){
            bar.insertBefore(createButton(), bar.firstChild);
        }
    }
}
setInterval(setupUI, 1000);

あとはこれをGreasemonkeyに登録して、 http://feedly.com/* で有効になるようにすればOKです。