ふにゃるんv2

もとは、http://d.hatena.ne.jp/Wacky/

オレオレ専用のはてぶWebアプリを作る

前回、ローカルアプリケーションキャッシュ機能を使って、Xmarksに替わる、オレ専用のブックマークWebアプリを作る - ふにゃるんを作った訳ですね。

それから更に調子に乗って、オレオレ専用のはてぶWebアプリを作ってみました。

何で作ろうと思ったか

iPodというかiPhoneには、元々はてぶアプリがあるんですが、出来るのはブックマークかWebブラウジングのみ。

iPodのはてぶアプリで、ブックマークしようとすると、以下の問題に突き当たります。

  1. 無線LANといえど、パソコンでWebブラウズする方が視認性が良いし速い。
    (現在の所、iPodのWebブラウズは補助的に使っている状態。未だにメインはパソコンなんですね)
  2. ブックマークする際、タグをiPodで打ち込むのは、恐ろしい苦行なり。
    (ブラインドタッチに慣れているので、思考と入力が一致しないのは、恐ろしく不便)
  3. パソコンでは、オレ専用のブックマークアプリを使って楽しているから、そっちで使いたい。
    (P4Dで、はてレジ(はてなブックマークを登録するツール)を作ってみました - ふにゃるんのことです)
  4. iPhoneなら常時ネット接続だが、iPodなので、常時ネット接続じゃないので、オフライン重視じゃないと困る。
    (オフラインサポートしていないニュース系アプリは即行削除行きだったり。:-P)


以上の理由と、手にした技術で遊んでみたいという強い欲求から、オレオレ専用な はてぶWebアプリを作ってみた次第です。

参考にしたWebサイト

今回参考にしたWebサイトは、以下の通りです。

使っている技術

参考にしたWebサイトでもわかる通り、今回使用した技術は、大体こんな感じです。

jQuery Mobile
前回は jQtouchでしたが、今回はjQuery Mobileです。
採用に特に意味は無く、一応本命と言われているので 試してみたくなっただけです。
Google Ajax Feeds API
今回、外部ドメインRSSデータを取得する必要がありまして、普通はCGIか何か置いて、CGIから外部ドメインRSSデータを取得したりするのがセオリーっぽかったんですが。
まぁ、これでも良いかな。と。
HTML5のローカルストレージ
ローカルに永続的にデータを格納できる技術として、ローカルストレージ技術とSQLを用いた技術があるようなんですが、今回は楽っぽい(キーバリューでOKなんですもん)ローカルストレージにしました。

ソースコード

てけとーではありますが、ソースコードを晒します。

<!DOCTYPE html>
<html manifest="test_hatebu.manifest">
<meta charset="UTF-8" />
<title>はてぶホットエントリをチェキ</title>
<link rel="stylesheet" href="lib/jquery.mobile-1.0a3.min.css" />
<script src="lib/jquery-1.5.min.js"></script>
<script src="lib/js_sprintf.js"></script>
<script src="https://www.google.com/jsapi?key=$ここに取得したキーを入れて" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
    function make_entry(feed){
        console.log("make_entry=%d", feed.entries.length);
        
        var ary = jQuery.map(feed.entries, function(n, i){
            var s = $.sprintf(  "<div class='ui-bar ui-bar-b'>                                      \
                                    %(no)d: %(title)s                                               \
                                </div>                                                              \
                                <div>                                                               \
                                    <div data-role='controlgroup' data-inline='true' align='right' data-type='horizontal'>      \
                                        <input type='checkbox' name='chk' id='chk-%(no)d' value='%(url)s'/>                     \
                                        <label for='chk-%(no)d' data-icon='start'>後で</label>                              \
                                        <span>&nbsp;</span>                                         \
                                        <a href='%(url)s' data-role='button' data-icon='arrow-r' target='_blank'>URL</a>    \
                                    </div>                                                          \
                                    <p>%(content)s</p>                                              \
                                </div>",
                { no: i+1, url: n.link, title: n.title, content: n.contentSnippet, detail: n.content });
            return s;
        });
        $("#rss").append(ary.join());
    }

    function load_entry() {
        console.log("load_entry");
        var feed_url = "http://b.hatena.ne.jp/hotentry.rss";
        var feed_content = null;
        try{
            console.log("no local storage[%s]", feed_url);
            var feed = new google.feeds.Feed(feed_url);
            feed.setNumEntries(100)
            feed.load(function(result) {
                feed_content = JSON.stringify(result.feed);
                localStorage[feed_url] = feed_content;
            });
            console.log("set local storage!!");
        }catch(e){
            console.log("sorry fail fetch:" + feed_url);
        }
        
        if(feed_content == null){
            feed_content = localStorage[feed_url];
        }
        var feed = JSON.parse(feed_content);
        make_entry(feed);
    }
    
    try{
        google.load("feeds", "1");
    }catch(e){
        console.log("fail google.load");
    }
    //google.setOnLoadCallback(load_entry);
    
</script>
<script type="text/javascript" charset="utf-8">
    console.log("custom script code!!");
    
    // 設定の為の初期化
    $(document).bind("mobileinit", function(){
        console.log("mobileinit");
    });
    
    // 普通の初期化
    $(function(){
        console.log("function!!");
        
        var clickTrigger = ($.support.touch)? "tap": "click";
        
        $("#call-mail").bind(clickTrigger, function(){
            //$("[name='select']:checked").each(function(){
            var chks = $(":checked").map(function(){
                return this.value;
            }).get();
            //alert("checkbox:" + chks.join(","));
            if(chks.length > 0){
                s = "subject=" + "after_check!!" + "&body=" + chks.join("\n");
                location.href = "mailto:$メアドを入れて?" + encodeURI(s);
            }
            return false;
        });
    });
    
    $('#main').live('pagebeforecreate',function(event){
        console.log("pagebeforecreate");
        // ページを作成する前に、はてぶエントリーを生成する
        // でないと、スマートフォンな外観にならないから
        load_entry();
    });

    $('#main').live('pagecreate',function(event){
        console.log("pagecreate");
    });
    
    console.log("end of script tag");
</script>
<script src="lib/jquery.mobile-1.0a3.min.js"></script>
</head>
<body> 

<!-- Start of first page -->
<div data-role="page" id="main">

    <div data-role="header" data-theme='d'>
        <h1>はてぶホットエントリ</h1>
        <a id="call-mail" href="#" class='ui-btn-right'>to メール</a>
    </div>

    <div data-role="content">
        <div id="rss"></div>
    </div>

    <div data-role="footer" data-theme='a' align='center'>
        <span>end of document&nbsp;</span>
        <a id="call-mail" href="#">to メール</a>
    </div>
</div>

</body>
</html>

あと、ローカルWeb化する為に、test_hatebu.manifest ファイルを同じフォルダに突っ込んでおきましょう。

CACHE MANIFEST
# rev1

CACHE:
lib/jquery.mobile-1.0a3.min.css
lib/jquery.mobile-1.0a3.min.js
lib/jquery-1.5.min.js
lib/js_sprintf.js

lib/images/ajax-loader.png
lib/images/form-check-off.png
lib/images/form-check-on.png
lib/images/form-radio-off.png
lib/images/form-radio-on.png
lib/images/icon-search-black.png
lib/images/icons-18-black.png
lib/images/icons-18-white.png
lib/images/icons-36-black.png
lib/images/icons-36-white.png

なお、lib/js_sprintf.js ファイルは、jQuery pluginの http://plugins.jquery.com/project/printf からゲットしたコードを置いています。

動かしてみる

WWWサーバーに適当に放り込んだファイルを、iPodSafariから参照すると、以下の画面が出て来ます。
(機内モードで、ちゃんと動いているのが判りますか?)

1
1 posted by (C)wacky


基本的に、「後で」にチェックを付けまくって、ヘッダもしくはフッタにある、「to メール」ボタンを押すと、チェックを付けたURLがメールの本文に適用される。
といった流れです。
一応オマケとして、「URL」ボタンを押すと、はてぶエントリのWebページに飛んでいきます。


例えば、2つほど、エントリに対して「後で」をチェックします。

2
2 posted by (C)wacky


ここで、「to メール」ボタンを押すと、メーラーが起動して、チェックしたURLが本文に適用されます。

3
3 posted by (C)wacky

後は、メールするだけです。
簡単ですね。


ちなみに、1回目の読み込みで、エントリのWeb作成に反映せず、2回目以降で成功したりするバグを抱えています。
が、とりあえず動いたので晒すという暴虐。

最後に

全体的に文字がバカでか過ぎるので、もちっとコンパクトにしたいなぁ。と思っています。特に、「後で」「URL」ボタンのデカさと言ったら…。ふぅ。


しかし、今回で2つですが、テキトーでも、それらしくアプリケーションのように振舞うWebソフトが出来るなんて、HTML5って便利な技術ですね。


ここまで便利に動くとなると、いわゆるガラケーと呼ばれる既存の携帯も、HTML5をサポートすれば良いのに。と、思ってしまいますね。
Flash Liteがあるので、もっと高度な遊びが出来るのは判りますが、HTML5は更に技術の敷居が低いので、開発者の裾野が広がると思うんですがねぇ。
(ハードをスマートフォン化するより、手っ取り早いと思うんだけどなぁ)

Xmarksに替わる、オレ専用のブックマークWebアプリを作る

要するに何をやりたかったかと言うと、↓こういう事です。

  1. パソコンで溜めたブックマークを、iPod touch(iPhoneじゃない所に哀愁を感じて!)でカウチポテトしながら見たい。
  2. ブックマーク同期として Xmarks を使ってみたが、一々ログインしなければならないし、階層を降りるのに時間が掛かるし、オフラインの時にリストも見られない(iPod touchの悲しさ)、後 ブックマークが昇順になっていないのも気に喰わない。
  3. 幾つかの非標準Web browserは、Webの保存機能を持っているようだから、自分でブックマークリストなHTMLを作って、それを保存して使いこなせば良いんじゃね?
  4. 更に調べていたら、HTML5のオフラインアプリケーション機能(mobile Safari実装済み)を使えば、HTMLをキャッシュしておいてオフラインでも使えるらしい。
  5. 早速、PythonでブックマークをHTML化して、オフラインでもリストが見られるように、ローカルWebアプリ化してみたよ。<今ココ

資料

今回、色々回り道を繰り返していましたが、参考にしたURLです。

@ITのこの記事で、「ローカルWebアプリ」が紹介されていたのですが、上の記事を読むまで 自分のやろうとしている事に転用できると、全然気づきませんでした。
(だって、その後のページで、ネイティブアプリの作成の話に行ってるんだもん)


次、ローカルWebアプリ化に際して、iPhone/iPod touchのユーザーインタフェースをWebで再現する為のライブラリ検討として、以下を参考にしました。


幾つかライブラリをダウンロードして、添付されていたデモを iPod touchで見て、今回は jQTouch にしました。

採用理由は、以下のものです。

  • 特にライブラリ本体を弄らなくても、iPhone/iPod touchのユーザーインタフェースになる。
  • 今回は階層を掘っていくイメージの為、1ファイルを複数ページに見せかける jQTouch が 動作が軽くなりそうだし、簡易だと思った。
  • サンプルコードが、結構判り易かった。

ソースコード

試行錯誤したままですが、ソースです。
整理もしていないので、かなり汚いです。すんません。
(しかも、デモコードから切り貼りをしていたりします)

#!/bin/env python
# -*- encoding: cp932 -*-
"""
    IEの「お気に入り」を、iPhone/iPod touch向けのHTMLに変換する。
    HTML化のベースは、jQTouch を使っている。
"""
import sys
import os
import csv
import pdb
import codecs
import ConfigParser
import time

def html_before(title):
    return """
<!doctype html>
<html manifest="fav.manifest">
    <head>
        <meta charset="UTF-8" />
        <title>%s</title>
        <style type="text/css" media="screen">@import "./jqtouch.min.css";</style>
        <style type="text/css" media="screen">@import "./theme.min.css";</style>
        <script src="./jquery.1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="./jqtouch.min.js" type="application/x-javascript" charset="utf-8"></script>
        <script src="./jqt.autotitles.js" type="application/x-javascript" charset="utf-8"></script>

        <script type="text/javascript" charset="utf-8">
            var jQT = new $.jQTouch({
                icon: 'jqtouch.png',
                addGlossToIcon: false,
                startupScreen: 'jqt_startup.png',
                statusBar: 'black',
                preloadImages: [
                    './img/back_button.png',
                    './img/back_button_clicked.png',
                    './img/button_clicked.png',
                    './img/grayButton.png',
                    './img/whiteButton.png',
                    './img/loading.gif'
                    ]
            });
        </script>
    </head>
    <body>""" % title

def html_after():
    return """
    </body>
</html>"""

def str_normal(s):
    if s.isalnum() == True:
        return s.replace(".", "_")
    else:
        ss = ""
        for c in s:
            ss += "%d" % ord(c)
        #print ss
        return ss

def get_ini_url(fpath):
    #print "ini file:%s" % fpath
    cfg = ConfigParser.ConfigParser()
    try:
        cfg.readfp(open(fpath))
        return cfg.get('InternetShortcut', 'URL')
    except:
        return "#"

def make_node(is_top, root, dirs, files):
    title = os.path.basename(root)
    
    back_node = ""
    if is_top == False: back_node = '<a href="#" class="back">back</a>'
    
    node = ""
    for s in dirs:
        node += "<li class='arrow'><a href='#%s'>%s</a><small class='counter'>%d</small></li>" % (str_normal(s), s, len(os.listdir(os.path.join(root, s))))
    node += "\n"
    for s in files:
        node += "<li class='forward'><a href='%s' target='_blank'>%s</a></li>" % (get_ini_url(os.path.join(root, s)), s.replace('.url', ''))
    html = """
<div id='%s'>
    <div class='toolbar'>
        %s
        <h1>%s</h1>
    </div>
    <ul class='rounded'>
    %s
    </ul>
</div>""" % (str_normal(title), back_node, title, node)
    return html

def make_manifest(dir):
    print >>f, "CACHE MANIFEST\n# %s\n" % time.asctime()
    print >>f, "CACHE:"
    for root, dirs, files in os.walk(dir):
        for s in files:
            ext = os.path.splitext(s)[1]
            if (ext == ".css") or (ext == ".js") or (ext == ".png") or (ext == ".gif"):
                fpath = os.path.join(root, s)
                print >>f, fpath.replace(dir, "").strip("\\").replace("\\", "/")

def main(f):
    fav_dir = r"C:\Documents and Settings\あなたの名前\Favorites"
    
    is_top = True
    html_body = ""
    for root, dirs, files in os.walk(fav_dir):
        #print root, dirs, files
        print ">", root
        html_body += make_node(is_top, root, dirs, files)
        is_top = False
    print >>f, html_body

if __name__ == "__main__":
    save_dir =  r"C:\配置したいフォルダパスを指定する"
    # 「お気に入り」から、HTMLページを生成(use jQtouch)
    save_path = os.path.join(save_dir, "_test.html")
    f = file(save_path, "w")
    print >>f, html_before("Favoraites List")
    main(f)
    print >>f, html_after()
    f.close()
    
    # fav.manifestを生成
    save_man = os.path.join(save_dir, "fav.manifest")
    f = file(save_man, "w")
    make_manifest(save_dir)
    f.close()
    
    # cp932からutf-8に変換
    save_path2 = os.path.join(save_dir, "index.html")
    f_i = file(save_path, "r")
    f_o = file(save_path2, "w")
    f_o.write(unicode(f_i.read(), "cp932").encode('utf-8'))
    f_i.close()
    f_o.close()

コードの流れとしては、大体以下の様です。

  1. お気に入りフォルダを os.walk で再起探索し、HTMLリスト化しています。
    サブフォルダ毎を1ページとして ulタグでくくり、後は .url ファイルの URLキー値を、liタグ内の aタグでジャンプ先になるようにしています。
    (jQTouchは、divタグでページを作り出している)
  2. ローカルWeb化する為に、jQTouch提供のライブラリ群を、fav.manifest ファイルで ローカルキャッシュ対象にしています。
    fav.manifestファイルは、htmlタグのmanifest属性キーでファイル指定できます。
  3. 1.でHTML化したファイルは、cp932(shift_jis)なので、utf-8に保存し直しています。
    (最初からutf-8でファイルを作ろうと思ったのですが、encodeメソッドを使うのが面倒になったので、後から変えてしまえ。と思った次第)

動かし方

1.パソコン内に、ローカルなWWWサーバーが走っている環境を構築します。
自分の環境は Apache ですが、最近なら WebMatrix でも良いかも知れません。


2.仮に、Apacheを動かしていたら、httpd.confに以下の行を追加します。

AddType text/cache-manifest .manifest


3.次に、ブックマークをHTML化したファイルを保存したいフォルダを決めます。
仮に、「c:\html\test」フォルダに配置すると仮定します。
上のソースの「save_dir」変数の値を、「c:\html\test」に設定してください。


4.jQTouchのライブラリをダウンロード(私のは、jqtouch-1.0-beta-2-r109.zip でした)して展開します。
で、jqtouchフォルダとthemes/jqtフォルダのファイルを、「c:\html\test」フォルダに ぶち込みます。


5.上のソースを Python で実行します。
すると、「c:\html\test」フォルダに、「index.html」ファイルが出来上がります。
(ChromeとかSafariなどのWebKit系ブラウザで、簡単な確認が出来ます)


6.作成したページのURLを、iPod touchから参照します。
例えば、↓こんな感じの画面になります。
fav_test
fav_test posted by (C)wacky


7.ローカルWeb化しているので、機内モード:ONにしてアクセスしても、「ページを開けません」と出ずに、ページが開きます。やったぁ。
(但し、ブックマークリスト先のURLを開こうとすると、当然ですが「ページが開けません」となります)

最後に

ローカルWebアプリって、結構簡単に作れるもんですね。
(.manifestファイルで、キャッシュ対象を指示すれば良いだけなんですもん)


Mac使いじゃない私には、iPhone/iPod touchなアプリは、永遠に作る事が出来ないのか。と半分諦めモードでしたが、少し元気が出てきました。


これなら、自分のPCのローカルWWWサーバーで、ちまちまWebアプリを作って、それをローカルWebアプリ化すれば良いのですから、かなり敷居が低くなった気がしてきました。
(しかも、ローカルWebアプリ化すれば、Wi-Fiの使えない所でも使えるし)

自作のプロキシサーバーを介して、iPodからのWebアクセス速度を上げてみる

iPod touchを使って、ごろ寝しながらWebサーフィン(死語)していたんですが、携帯に最適化されていないWebページを見ると、結構待たされるんですね。
しかも、3ペイン構造になっているのが多くて、ズームイン・ズームアウトを繰り返すのもアホらしい。


携帯(ガラケー)のWebアクセス機能は、アクセス速度こそ遅いものの、少しでもアクセス速度が上がるよう、携帯のキャリア側のサーバーで本来のHTMLを変換して最適化して表示してくれるので、小さい画面でも見やすいし、我慢ならんほど表示が遅い訳でも無い。


2つの良い所と悪い所が頭の中で、シェイクしている内、ふと思った訳です。

iPod touch(iPhoneでも可)で、携帯向けにHTMLを最適化するHTTPプロキシサーバーを介してWebアクセスすれば、サクサクWebサーフィンできるんじゃね?

情報収集

そのものズバリな「携帯向け最適化用HTTPプロキシサーバー」を提供している所は、ぱっと調べた限り見つかりませんでした。
多分、調べ方が悪いんでしょう。(誰か教えて〜)


代わりに、プロキシの代替になるような、ページを変換してくれるWebサービスを提供してくれている所が見つかりました。

まぁ、これだけでもOKかな?と、一瞬思ったのですが、一々見たいページのURLを入力するのも面倒なんですよ。
(ブックマークレットも紹介されているけど、結局ブックマークを開く操作が入るしね)


で、思いついたのが、
適当な自作プロキシサーバーを作り、iPod touchは、このプロキシサーバーにアクセスさせる。プロキシサーバーは、送られてきたURLを、上記の「Google Mobile Proxy」に転送して、携帯向けWebページに変換させてしまう
というものです。

自作プロキシを作ってみる

Pythonなら諸先輩方が、何かサンプルコードを作っているだろうと思って検索したら、結構引っかかりました。
日本語系だと、BaseHTTPServerクラスを使うものとTwistedを使うものに、大体2分するようです。


という訳で、今回はid:yattさんの口調変換プロキシが、URLの転送の実例に使えそうなので、ちと拝借して改造してみました。
以下に、早速コードを示します。

#! /bin/env python
# -*- coding: utf-8 -*-

import sys
import BaseHTTPServer
import urllib
import urllib2
import urlparse
import re

TMPL = """<html>
  <body>
    <h1>Proxy Server Error</h1>
    <div>response error to '%(path)s'</div>
    <div>ERROR: <b>%(error)s</b></div>
  </body>
</html>
"""
#urllib.FancyURLopener.version = "Mozilla/4.0" # 素のままだとgoogleやwikipediaに弾かれる
#urllib.FancyURLopener.version = "transApp" # 素のままだとgoogleやwikipediaに弾かれる

class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def error(self, e):
        return  TMPL % dict([("path",self.path), ("error",e)])
    
    def do_GET(self):
        KEY_URL = "http://www.google.co.jp/gwt/"
        print "request:",self.path
        try:
            url = self.path
            if url.find("/gwt/") > 0:
                url = KEY_URL + url[url.find("/gwt/") + 5:]
                print "  change url:", url
            elif url.find(KEY_URL) < 0:
                url = KEY_URL + "n?u=" + url
                print "  make url:", url
            pipe = urllib.urlopen(url)
            content = pipe.read()
        except Exception, e:
            content = self.error(e)
        self.wfile.write(content)
    def do_POST(self):
        BaseHTTPRequestHandler.do_POST()

if __name__ == '__main__':
    serv_addr = ("", 8080)
    print "server address:", serv_addr
    httpd = BaseHTTPServer.HTTPServer(serv_addr, ProxyHandler)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass


早速試してみましょう。
作業の流れは、以下のようなものです。

  1. スクリプトを動かすWindows PC(ここでは、普通にWebが見られる環境にあります)の、IPアドレスを調べておきます。
    ipconfig」をコマンドラインで打ち込めばIPアドレスはわかります。
  2. 上記のコードスクリプトを、PC上で動かします。
    例えば、「python proxy.py」という風に。
  3. iPod touchの設定で、プロキシのIPアドレスとポート欄に、PCの(1.で調べた)IPアドレスと8080番ポートに設定します。
  4. iPod touchのMobile Safariを起動し、http://www.yahoo.co.jp にアクセスします。

すると、以下の画面が出てきました。
2
2 posted by (C)wacky

ちなみに、iPod touchで、プロキシの設定をするには、以下のWeb siteの説明が参考になるでしょう。
(実は切り替えが面倒なので、Pixyというソフトを突っ込んでみたんだが、うまく動かなかったです。何ででしょうね。誰か教えて!)

余談:urllib.FancyURLopener.versionの設定による挙動の差

動き出した後、普通にWebブラウザから表示する場合と、画面の表示の仕方が違う事に気づきました。


↓PCのWebブラウザから表示。
1
1 posted by (C)wacky

iPod touchから自作プロキシを介して表示。
2
2 posted by (C)wacky


一番上に表示されるメッセージが英語だったり、画像が出てこなかったりですね。
何でだろうなぁ?と思って、元のコードを見ていたら、「urllib.FancyURLopener.version」の設定値の所で、「素のままだとgooglewikipediaに弾かれる」とあったのですね。


この、urllib.FancyURLopener.version って何の設定かいな?と思って調べたら、「User-Agent」の設定値らしい。
んじゃぁ、という訳で、試しにIEなどで使われる「Mozilla/4.0」を設定してみました。

transAppを指定 Mozilla/4.0を指定
2 3
4 5

おぉ、表示される内容に変化が現れました。
ただ、Yahooのページに関して言えば、urllib.FancyURLopener.version の設定値は、「Mozilla/4.0」にしない方が良いです。
「スポーツ」などのページにジャンプしようとすると、転送ページに飛ばされて、結局ジャンプできませんでした。
プロキシのスクリプトコードの改善が必要という事でしょう。

参考:速度比較

何ちゃってですが、自作プロキシを介する事により、どれぐらい早くなるのか、幾つか試してみました。

Webページ 直接 自作プロキシ
Yahoo! 7秒 3秒
Google 6秒 2秒
楽画喜堂 35秒 3秒
まいじゃー推進委員会 14秒 3秒

携帯に最適化していない+画像が一杯のサイトほど、かなりの効果が見込める事が判ります。
まぁ、いわゆるテキストブラウザで閲覧するようなものですからね。(一部画像が付きますが)

余談:プロキシのテスト中に

余談ですが、自作プロキシを導入してテスト中、iPod touchをスタンバイにして暫く放置していたら、以下のメッセージログが。

192.x.x.x - - [03/Jan/2011 22:00:40] code 501, message Unsupported method ('C
ONNECT')
192.x.x.x - - [03/Jan/2011 22:00:44] "CONNECT m.google.com:443 HTTP/1.1" 501
-
192.x.x.x - - [03/Jan/2011 22:01:52] code 501, message Unsupported method ('C
ONNECT')

Wi-Fiを有効にしているとスタンバイにしても、結構バッテリを消費するので、何でだろう?と思っていたんですが、なるほど、数分おきに動いている訳ですね。
そりゃ、バッテリを消費するわ。

まとめ

という訳で、恒例のまとめ というか感想。

  • 携帯用にHTMLデータを加工させる事で、Webサーフィン(死語)は、更にサクサクになります。
  • 何ちゃって自作プロキシサーバーでも、その効果は得られます。

というか、iPod touchのアプリで、携帯向けにHTMLデータを加工するローカル鯖アプリを提供してくれないですかね。
Safariのプロキシ設定をlocalhostに設定して、アプリがプロキシ処理をするの。
現状だと、Jailbreakしないと出来そうもないですなぁ。
(Webサービスでなく、本当のプロキシサーバーでも良いんですけど。一部広告が入るぐらいなら我慢するんだけど…)

2011/1/4追記:

後で気付いたんですが、Opera Mini Web Browserの、携帯向け表示モードって、今回のプロキシ的な動作を行っているんじゃないかな。

技術仕様のページで、Opera's compression serversを経由してインターネットにアクセスって、図が書いてあるし。
出た当時、アクセス速度が3倍速とか評判になってたけど、今回のプロキシ的な処理を施す事で実現しているって事なんでしょうね。

…だとすると、もうちょっと他でもサービス実現してくれるといいのに。ぶちぶち。

PukiWikiの添付ファイル使用時、ローカルファイルをドラッグ&ドロップしたい(少し挽回版)

先日の、

の、少し挽回版です。


ある一定のバージョン(確認した限りIE6。IE7もいけそう)なら、ローカルファイルをドラッグ&ドロップできるメドが付きました。

ドラッグ&ドロップできる条件

調べていて判ったのですが、ローカルファイルをドラッグ&ドロップできるか否かは、ファイル入力欄にクリップボードからの文字列貼り付けが出来るか否かに拠ります。


試しに、文字列を適当にコピーした後、ファイル入力欄で右クリックしてみて下さい。
3
3 posted by (C)wacky

このように、「貼り付け」とか「削除」項目が表示されていれば、ドラッグ&ドロップが出来るのでは無いか?と思います。

コードと動作

早速ですが、patchコードを示します。

--- attach.inc.php  2006-04-15 08:51:12.000000000 +0900
+++ attach.inc.php.new  2010-09-08 23:00:59.250000000 +0900
@@ -408,6 +408,44 @@
        $title = $_attach_messages[PLUGIN_ATTACH_UPLOAD_ADMIN_ONLY ? 'msg_adminpass' : 'msg_password'];
        $pass = '<br />' . $title . ': <input type="password" name="pass" size="8" />';
    }
+   
+   $file_box = '';
+   if( ereg( "MSIE", getenv("HTTP_USER_AGENT") ) ){
+       $file_box = <<<EOD
+<!-- IE only -->
+<object id="DnD_Box" classid="clsid:8E3867A3-8586-11D1-B16A-00C0F0283628" width="350" height="100">
+   <param name="OleDropMode" value="1">
+   <param name="Style" value="1">
+   <param name="SimpleText" value="In this area, please do Drag & Drop of a file.">
+   <param name="allowScriptAccess" value="always">
+</object>
+<script type="text/JScript">
+function Sleep(ms){
+   var st_tm = new Date().getTime();
+   while((new Date().getTime()) < (st_tm + ms));
+}
+function DnD_Box::OLEDragDrop(Data){
+   if(Data.GetFormat(15)){
+       var O = "";
+       var e = new Enumerator(Data.Files);
+       O = e.item();
+       //alert(O);
+       DnD_Box.SimpleText = O;
+       clipboardData.setData("Text", O);       // ファイルパスをクリップボードへ
+       var e = document.getElementById("_p_attach_file");
+       e.select();
+       e.focus();
+       var w = new ActiveXObject("WScript.Shell");
+       w.SendKeys("%{TAB}");
+       Sleep(500);
+       w.SendKeys("^v");
+       //document.getElementById("_p_attach_file").click();    // ファイルダイアログを開くだけ
+   }
+}
+</script>
+EOD;
+   }
+
    return <<<EOD
 <form enctype="multipart/form-data" action="$script" method="post">
  <div>
@@ -419,11 +457,12 @@
   <span class="small">
    $msg_maxsize
   </span><br />
-  <label for="_p_attach_file">{$_attach_messages['msg_file']}:</label> <input type="file" name="attach_file" id="_p_attach_file" />
+  <label for="_p_attach_file">{$_attach_messages['msg_file']}:</label> <input type="file" name="attach_file" id="_p_attach_file"/>
   $pass
   <input type="submit" value="{$_attach_messages['btn_upload']}" />
  </div>
 </form>
+$file_box
 EOD;
 }


これを適用すると、下図のようにローカルファイルをドロップ領域にドラッグ&ドロップすると、
1
1 posted by (C)wacky

ご覧の通り、ファイル入力欄にローカルファイルのパスが、自動的に入力されます。
2
2 posted by (C)wacky

後は、「アップロード」ボタンを押すだけ。楽になりました。

最後に

私のメイン環境はIE8でして、その環境だと右クリックメニューの「貼り付け」と「削除」が出来ないんですね。
う〜ん、最後の一押しが…。

PukiWikiの添付ファイル使用時、ローカルファイルをドラッグ&ドロップしたい(残念賞版)

突然ですが、WikiWikiって便利ですよね。
自分は、個人的なメモは PukiWikiにメモっています。今だったら、Google DocumentとかにEvernoteにメモるというのもアリなんでしょうけど、貧弱なマシンには ちとリッチ過ぎるし、アクセス管理も自分で管理できるので、よく使っています。


で、よく使うPukiWikiなんですが、時折「うぎゃ〜」と叫ぶ事があります。
それは、添付ファイルのパス指定の時なんです。
…何で、アップロードするファイルを、ファイル入力欄にドラッグ&ドロップさせてくれないの?
1
1 posted by (C)wacky


セキュリティとか言われたら、まぁ納得するしか無いんですが、Googleのウェブアルバムでは、ドロップエリアにファイルをドラッグ&ドロップして、アップロード出来るんですね。
2
2 posted by (C)wacky


そうだよ。これがやりたいんだよ!
一々ファイルダイアログを開いて、アップロードするファイルをヘコヘコ選択していくなんて、前時代的な真似は やりたくないんだよ。
ついでに言うと、今使っているブラウザで、それがやりたいんだよ。
HTML5なら〜とか、足切りは勘弁してちょーだい。
という訳で、どうやれば出来るか、奮闘してみました。

情報収集

余談ですが、このファイル入力欄へのドラッグ&ドロップですが、Chromeだとデフォルトで出来ちゃいます。
なので、今回のネタはIEオンリーです。(FireFoxも、最新版だと出来るみたいだし〜)


まずは、Googleのウェブアルバムですが、ソースを眺めてみると、どうやらドロップエリアは、ActiveXコントロールっぽいです。


他に方法が無いのか?と言うと、↓このWeb siteでの検証では、ActiveXFTPクリップボードぐらいしか無い模様。


FTPの代わりに、WebDAVを使う自作プラグインが、PukiWikiのオフィシャルサイトにあります。


他には、オフィシャルサイトに、複数ファイルをアーカイブしたファイルをアップロードする事で、サーバー先で展開して格納してくれる。という自作プラグインもあるようです。これは、考え方の勝利という奴ですね。

方針決定

先のオフィシャルサイトの自作プラグインを検討したのですが、やっぱり「ドラッグ&ドロップしたい」んじゃぁ。という事で、ActiveXを貼り付けて、そこからパスをゲットする方法を検討します。


暫くググると、そのものズバリなものが見つかりました。

コードのキモは、objectタグでActiveXコントロールを貼り付ける際、OleDropMode プロパティを有効に設定する事と、OLEDragDropイベントでキャッチしたら、パス情報を取得する所みたいです。

<object id="IAnimation" 
classid="clsid:B09DE715-87C1-11D1-8BE3-0000F8754DA1">
<param name="OleDropMode" value="1">
</object>

<script type="text/JScript">

function IAnimation::OLEDragDrop(Data){
if(Data.GetFormat(15)){
var O = "";
var e = new Enumerator(Data.Files);
while(!e.atEnd()){
O += e.item() + "\n";
e.moveNext();
}
output.value = O;
BackColor=0x80000003;}
}

↑上で使っているActiveXはAnimationコントロールのようです。が、調べていくと、最近のWindowsのパッチで、このコントロールがWeb上で使えないよう Kill Bit指定されているようです。


他に、OleDropModeできるようなActiveXが無いものか?と調べていくと、某掲示板でグッドな情報が記載されています。うまうま。

833 :デフォルトの名無しさん:2008/05/29(木) 23:01:44 
ああスマン 
Clipboard.HTAってウィンドウにD&Dしたファイルをクリップボードにコピーするスクリプトなんだけど 
その「D&Dしたファイルを拾う方法」以外に無いの? って事 

Clipboard.HTAから最低限必要な部分だけを切り取ると 
<object id="dd_1c" classid="CLSID:8E3867A3-8586-11D1-B16A-00C0F0283628" > 
<PARAM NAME="OLEDropMode" VALUE="1"><PARAM NAME="Style" VALUE="1"><PARAM NAME="Simpletext" VALUE="ここにD&D"> 
</object> 
function dd_1c::OLEDragDrop(Data,e){ 
//Data.Files.;DDしたファイルの数 
//Data.Files.Item(n);n=1〜でDDしたファイルのフルパス 
if(e==7){ 
//こっちはファイルです 
}else{ 
//こっちはファイル以外の何か 
} 
} 
で、objectにファイルをD&DするとData.Files.Item(n)にD&Dしたファイルのパスが取得出来るんだけど 
この方法、「オブジェクトにD&Dしたファイルを拾う」事しか出来ないから、 
「このテキストボックスにファイルをD&Dしたら…」って事は出来ないんだよ 
だから他の方法無いかなって 


上の情報を組み合わせてみると、ローカルファイルを、IEにドラッグ&ドロップしてファイルパスを得られる事が判りました。

越えられない壁

ファイルパスを得られたら、INPUTタグのファイル入力欄に、スクリプトで書き込めば完了だ。と思ったのですよ。
が、そうは問屋が卸さないようで、TYPE='FILE'指定のかかったものは、スクリプトで設定出来ない模様。
確かに、直接ファイル入力欄にキー入力しようとしても、入力すらさせてくれない…。


調べていくと、昔はクリップボード経由でINPUTタグに入れる事も出来たようですが、現在は それも適わない模様。
セキュリティ厳しいなぁ。…まぁ、当然か。


TYPE='TEXT'指定に変えて、逃げる手も考えたのですが、他に弄らないといけないコードが多い事に気付いて、もう八方塞りに陥りました。orz
(PHPの言語仕様知らないし〜)

挫折

という訳で、

  • IEにローカルファイルをドラッグ&ドロップすると、ファイルパスをクリップボードにコピーする
  • ファイルダイアログを、とりあえず開くだけ行う

までは、実現する事にし、その後は

  • 人間の手で貼り付け&閉じる&アップロードを行ってもらう。

という中途半端な対応となりました。


以下に、plugin/attach.inc.php プラグインに対する、パッチコードを示します。(1.4.7で確認済み)

--- F:\Wacky\Test\pukiwiki\attach.inc.php	2010-09-05 17:07:25.281250000 +0900
+++ F:\Wacky\Test\pukiwiki\attach.inc.php.new	2010-09-05 17:07:33.578125000 +0900
@@ -419,11 +419,32 @@
   <span class="small">
    $msg_maxsize
   </span><br />
-  <label for="_p_attach_file">{$_attach_messages['msg_file']}:</label> <input type="file" name="attach_file" id="_p_attach_file" />
+  <label for="_p_attach_file">{$_attach_messages['msg_file']}:</label> <input type="file" name="attach_file" id="_p_attach_file"/>
   $pass
   <input type="submit" value="{$_attach_messages['btn_upload']}" />
  </div>
 </form>
+<!-- IE only -->
+<object id="DnD_Box" classid="clsid:8E3867A3-8586-11D1-B16A-00C0F0283628" width="350" height="100">
+	<param name="OleDropMode" value="1">
+	<param name="Style" value="1">
+	<param name="SimpleText" value="In this area, please do Drag & Drop of a file.">
+	<param name="allowScriptAccess" value="always">
+</object>
+<script type="text/JScript">
+function DnD_Box::OLEDragDrop(Data){
+	if(Data.GetFormat(15)){
+		var O = "";
+		var e = new Enumerator(Data.Files);
+		O = e.item();
+		//alert(O);
+		DnD_Box.SimpleText = O;
+		clipboardData.setData("Text", O);		// ファイルパスをクリップボードへ
+		//document.getElementById("_p_attach_file").value = clipboardData.getData("Text");
+		document.getElementById("_p_attach_file").click();	// ファイルダイアログを開くだけ
+	}
+}
+</script>
 EOD;
 }

最後に

という訳で、今回は途中挫折という結果に陥りました。
もし、HTMLに詳しい方が居られましたら、完全版を作って頂けないかな?と思う次第であります。まる。

iPod touchで、バッテリ消費を抑えたい

暇つぶし+αに購入した iPod touchですが、OSバージョンが4になってから、(体感的に)バッテリ消費が激しくなった気がしていました。


1日30分も使わないのに、夜中 80%の状態から、朝起きたら 10%の状態に。
もう、2〜3日おきどころか、毎日充電しないとダメじゃん状態に。


画面を暗くした方が良いとか、メールをプッシュ式は止めるべきとか、色々アドバイスされているサイトを参考に幾つか試しました。
で、今の所、一番効いているのは、「機内モードをオンにする」でした。
多分、Wi-Fiがバッテリ消費の癌なんでしょうね。


ちなみに、「夜中 80%の状態から、朝起きたら 10%の状態」ってのは、上記機内モードに設定しても同じ。
という事は、バッテリがヘタっているか、iPodのバッテリ計測がアホの子(仕様とも言う)なんでしょうね。(まだ1年経ってないのに、ブツブツ)

メモ:Adobe Reader 9.3.4へのアップデート

Adobe Reader 9.3.4が出ていたので、重い腰を上げてアップデートしました。


最近のAdobe Readerは、Ver.9になってから、ファイルのオープンが早くなり(多分、常駐プロセスのおかげと思います)、軽量で有名なFoxit Readerより早いんじゃないか?という程になりました。
(余談ですが、Power PointをPDF化したものを閲覧する時、Foxit Readerだとページ送り中に少しずつ表示位置がズレたりするんですが、何ででしょうね)


アップデート前、現在のバージョンをチェックしたら、9.3.2でした。やべぇ。(大汗)


逆ギレっぽくて嫌なんですが、Adobe ReaderFlash Player って結構攻撃受けているんだから、マイクロソフトと提携して Windows Update に含めるなり、自動アップデートを強化すりゃ良いのに。と思っています。(常駐プロセスは何の為に存在しているのやら)


ちなみに手元の環境だと、自動更新にチェックを入れているにも関わらず、ちゃんと更新してくれない。
「ヘルプ」→「アップデートの有無をチェック」を選ぶと、

アップデートに失敗しました

ユーザーのシステムポリシーにより、アップデートが無効にされました。
エラー:1007

って出るんですね。


ちょっとぐぐって見たんですが、同じ状況に悩まされている人は、あまり居ないみたい。
Administratorで一旦入ってアップデートしろって事なのかなぁ。と思いつつ、更新ファイルをゲットしてアップデートする私。