frog.raindrop.jp.knowledge > www

December 24, 2012

ブログ記事にイメージを埋め込み、さらに拡大表示時に前後のイメージに移動することのできるスクリプト

写真をいっぱい埋め込んだブログ記事を書くのが面倒になってきて、省力化しようとスクリプトを書いてみました。
ライブラリフリー。innerHTML でなくて、普通に DOM 操作しているので処理速度は微妙かも。
まだちゃんと CSS ファイルを作成していなくて、スクリプト内部でいっぱい style アトリビュートを設定していますがご笑覧ください。
実際に diary の記事で使ってます。2個目以降のイメージをクリックすると拡大表示になり、拡大表示したイメージの上にマウスポインタを乗せると、前後のイメージを表示するための「<」と「>」が表示されます。

/** photo.js
 *
 * @author Kuwabara Miyuki (ba)
 * @version 1.0.0
 */
var jp;
if (!jp) jp = {};
if (!jp.raindrop) jp.raindrop = {};
if (!jp.raindrop.frog) jp.raindrop.frog = {};
if (!jp.raindrop.frog.photo) (function () {

    var _albums = [];

    var _getElementsByTagName = function (node, namespaceURI, tagName, prefix) {
//        var elements = node.getElementsByTagNameNS (namespaceURI, tagName);
//        if (!elements.length)
//            elements = node.getElementsByTagName ([prefix, tagName].join (':'));
        var elements = node.getElementsByTagName ([prefix, tagName].join (':'));
        if (!elements.length)
            elements = node.getElementsByTagName (tagName);
        return elements;
    };

    var _getContainedAlbum = function (id) {
        for (var i = 0; i < _albums.length; i ++)
            if (_albums [i].isExists (id))
                return _albums [i];
        return null;
    };

    var _load = function (targetDocument) {
        _loadAlbums (targetDocument);
        _loadThumbnails (targetDocument);
    };

    var _loadAlbums = function (targetDocument) {
        _albums = [];
        var albumElements = _getElementsByTagName (targetDocument, 'http://frog.raindrop.jp/ns', 'photo-album', 'frog');
        for (var i = 0; i < albumElements.length; i ++) {
            var itemElements = _getElementsByTagName (albumElements [i], 'http://frog.raindrop.jp/ns', 'photo-item', 'frog');
            if (itemElements.length) {
                var album = new jp.raindrop.frog.photo.__album ();
                for (var j = 0; j < itemElements.length; j ++) {
                    var id        = itemElements [j].getAttribute ('id');
                    var src        = itemElements [j].getAttribute ('src');
                    var title    = itemElements [j].getAttribute ('title');
                    album.add (id, src, title);
                }
                _albums.push (album);
            }
        }
    };

    var _loadThumbnails = function (targetDocument) {
        var elements = _getElementsByTagName (targetDocument, 'http://frog.raindrop.jp/ns', 'photo-thumbnail', 'frog');
        for (var i = 0; i < elements.length; i ++) {
            var caption    = (elements [i].getAttribute ('caption').match (/^yes$/i))? true: false;
            var link    = (elements [i].getAttribute ('link').match (/^yes$/i))? true: false;
            var content    = elements [i].getAttribute ('content');
            
            var album    = _getContainedAlbum (content);
            if (album) {
                var item = album.get (content);
                var thumbnail = item.getThumbnail (targetDocument, caption, link);
                elements [i].parentNode.insertBefore (thumbnail, elements [i]);
            }
        }
        for (var i = 0; i < elements.length; i ++) {
            elements [i].parentNode.removeChild (elements [i]);
        }
    };

    jp.raindrop.frog.photo = {
        "__album": function () {
            this._catalog = {};
        },
        "__item": function (id, src, title) {
            this.id        = id;
            this.src    = src;
            this.title    = title;
        },
        "__iterator": function (catalog, first, key) {
            var _keys = [];
            for (var k in catalog)
                _keys.push (k);
            
            var _index = first? 0: (_keys.length - 1);
            if (arguments.length > 2) {
                var index = 0;
                var length = _keys.length;
                
                _keys.push (key);
                while (_keys [index] != key)
                    index ++;
                _keys.length = length;
                if (index < length)
                    _index = index;
            }
            var _move = function (i) {
                if ((i < 0) || (i >= _keys.length))
                    return null;
                _index = i;
                return catalog[_keys[i]];
            };

            return {
                item: function () {
                    return _move (_index);
                },
                next: function () {
                    return _move (_index + 1);
                },
                previous: function () {
                    return _move (_index - 1);
                }
            };
        },
        '__albumWindow': function (iterator) {
            this._current    = iterator;
            var _this = this;
            var newWindow = window.open('', 'album', 'width=300,height=300,toolbar=no,directories=no,menubar=no,status=no,left=0,top=0');
            var newDocument = newWindow.document;
            newDocument.body.setAttribute ('style', [
                    'position: relative',
                    'background: black',
                    'color: #ccc',
                    'margin: 0',
                    'padding: 0',
                    'text-align: center',
                    ''
                ].join (';'));

            var baseLocation = newDocument.createElement ('base');
            baseLocation.setAttribute ('href', newWindow.opener.location.href);
            var head = newDocument.getElementsByTagName ('head');
            if (head.length) {
                head [0].insertBefore (baseLocation, head [0].firstChild);
            }
            else {
                head = newDocument.createElement ('head');
                head.appendChild (baseLocation);
                newDocument.body.parentNode.insertBefore (head, newDocument.body);
            }
            var container = newDocument.createElement ('div');
            container.setAttribute ('id', 'photo-container');
            container.setAttribute ('style', [
                'margin: 0',
                'padding: 0',
                ''
            ].join (';'));
            newDocument.body.appendChild (container);
            var caption = newDocument.createElement ('div');
            caption.setAttribute ('id', 'photo-caption');
            caption.setAttribute ('style', [
                'position: absolute',
                'visibility: hidden',
                'padding: 5px',
                'left: 0',
                'top: 0',
                'width: 100%',
                'text-align: center',
                'font: bold 25px/1.1 sans-serif',
                'color: white',
                ''
            ].join (';'));
            newDocument.body.appendChild (caption);
            var navigation = newDocument.createElement ('div');
            navigation.setAttribute ('id', 'album-navigation');
            navigation.setAttribute ('style', [
                'position: absolute',
                'visibility: hidden',
                'left: 0',
                'top: 0',
                'margin: 0',
                'padding: 0',
                'width: 100%',
                'vertical-align: middle',
                'font: bold 40px/1.1 sans-serif',
                'color: white',
                ''
            ].join (';'));
            var previous = newDocument.createElement ('div');
            previous.setAttribute ('id', 'album-navigation-previous');
            previous.setAttribute ('style', [
                'position: absolute',
                'left: 0',
                'top: 0',
                'margin: 0',
                'padding: 5px',
                'cursor: pointer',
                ''
            ].join (';'));
            previous.onclick = function (event) {
                _this.movePrevious ();
            };
            previous.innerHTML = '&lt;';
            navigation.appendChild (previous);
            var next = newDocument.createElement ('div');
            next.setAttribute ('id', 'album-navigation-next');
            next.setAttribute ('style', [
                'position: absolute',
                'right: 0',
                'top: 0',
                'margin: 0',
                'padding: 5px',
                'cursor: pointer',
                ''
            ].join (';'));
            next.onclick = function (event) {
                _this.moveNext ();
            };
            next.innerHTML = '&gt;'
            navigation.appendChild (next);
            newDocument.body.appendChild (navigation);

            newDocument.body.onmouseover = function (event) {
                caption.style.visibility = 'visible';
                navigation.style.visibility = 'visible';
            };
            newDocument.body.onmouseout = function (event) {
                caption.style.visibility = 'hidden';
                navigation.style.visibility = 'hidden';
            };

            this._update = function (item) {
                var image = item.getImageElement (newDocument);
                image.onload = function (event) {
                    newWindow.resizeTo (image.width, image.height + 20);
                    navigation.style.top = (image.height - 50) / 2;
                };
                if (container.firstChild)
                    container.replaceChild (image, container.firstChild);
                else
                    container.appendChild (image);
                caption.innerHTML = item.title;
                newDocument.title = item.title;
            };
            this._update (iterator.item ());
        },
        load: function (targetDocument) {
            return _load (targetDocument);
        }
    };
    
    jp.raindrop.frog.photo.__album.prototype = {
        add: function (id, src, title) {
            return this.append (new jp.raindrop.frog.photo.__item (id, src, title));
        },
        append: function (item) {
            item._setAlbum (this);
            this._catalog [item.id] = item;
            return item;
        },
        getCount: function () {
            return this._catalog.length;
        },
        get: function (id) {
            return this._catalog [id];
        },
        isExists: function (id) {
            return (id in this._catalog);
        },
        begin: function () {
            return new jp.raindrop.frog.photo.__iterator (this._catalog, true);
        },
        end: function () {
            return new jp.raindrop.frog.photo.__iterator (this._catalog, false);
        },
        iterator: function (id) {
            return new jp.raindrop.frog.photo.__iterator (this._catalog, false, id);
        }
    };

    jp.raindrop.frog.photo.__item.prototype = {
        getAlbum: function () {
            return this._album;
        },
        getImageElement: function (targetDocument) {
            var image = targetDocument.createElement ('img');
            image.setAttribute ('src', this.src);
            image.setAttribute ('alt', this.title);
            image.setAttribute ('title', this.title);
            return image;
        },
        getThumbnailURI: function () {
            var m = this.src.match (/^(.+)\.([^\.]+)$/);
            if (m)
                return [m [1], '-thumb.', m [2].toLowerCase ()].join ('');
            else
                return [this.src, '-thumb'].join ('');
        },
        getThumbnail: function (targetDocument, captionFlag, linkFlag) {
            var container = targetDocument.createElement ('div');
            container.setAttribute ('class', 'album-thumbnail');
            container.setAttribute ('style', 'display: inline-block; margin: 2px; vertical-align: top;');    // ★
            var thumbnail = targetDocument.createElement ('img');
            thumbnail.setAttribute ('title', this.title);
            thumbnail.setAttribute ('alt', this.title);
            thumbnail.setAttribute ('src', this.getThumbnailURI ());
            if (linkFlag) {
                var ancher = targetDocument.createElement ('a');
                var current = this._album.iterator (this.id);
                ancher.setAttribute ('href', this.src);
                ancher.setAttribute ('title', this.title);
                ancher.onclick = function (event) {
                    new jp.raindrop.frog.photo.__albumWindow (current);
                    return false;
                };
                ancher.appendChild (thumbnail);
                container.appendChild (ancher);
            }
            if (captionFlag) {
                var caption = targetDocument.createElement ('div');
                caption.setAttribute ('class', 'album-thumbnail-caption');
                caption.setAttribute ('style', 'margin: 0 0 0.5em 0;');    // ★
                thumbnail.onload = function (event) {
                    caption.setAttribute ('style', 'margin: 0 0 0.5em 0; width: ' + thumbnail.width + 'px;')
                };
                caption.appendChild (targetDocument.createTextNode (this.title));
                container.appendChild (caption);
            }
            return container;
        },
        '_setAlbum': function (album) {
            this._album = album;
        }
    };

    jp.raindrop.frog.photo.__albumWindow.prototype = {
        movePrevious: function () {
            var item = this._current.previous ();
            if (item)
                this._update (item);
        },
        moveNext: function () {
            var item = this._current.next ();
            if (item)
                this._update (item);
        }
    };

} ());
続きを読む...

September 30, 2010

隣接兄弟セレクタ

CSS2 から導入された隣接兄弟セレクタだけど、IE でも 7 以降はサポートされてるみたいです。例えばこんな。

ul#menu > li { display: inline; margin: 0; padding: .5em 1em; }
ul#menu > li + li { border-left: 1px solid black; }

id="menu" の <ul> の子要素である <li> に隣接している <li> にのみ左ボーダーを指定しています。これを以下のような HTML に適用した結果を示します。

<ul id="menu">
    <li>トップページ</li>
    <li>事業内容</li>
    <li>会社概要</li>
    <li>お問い合わせ</li>
    <li>サイトマップ</li>
</ul>

結果 (Firefox 3.6.10 で表示したもの)

adjacent_sibling_selectors.png

1 番目の <li> は <li> に隣接していないので、左ボーダーがありません。2 番目以降にのみ左ボーダーが適用されています。

サンプル

September 29, 2010

Google AJAX Feed API を使ってみる

Google AJAX Feed API ってライブラリがあるみたいです。これを使えば公開されているフィードに Javascript でアクセスすることができ、他ドメインのフィードも取得できちゃいます。

ためしにはてなアンテナのフィードを取得して表示してみました。

<script type="text/javascript" src="http://frog.raindrop.jp/scripts/xmlight.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://www.google.com/jsapi?key=YOUR_AJAX_API_KEY"></script>
<script type="text/javascript">
//<![CDATA[
// Google AJAX FEED API Version 1 を読み込む
google.load ("feeds", "1");
// AJAX Feed API のサンプルでは google.setOnLoadCallback でコールバックをバインドしてるけど
// jQuery と一緒に使用する場合は document.ready で実行したらよいと思う
$(function ()
{
    // RSS フィードの URI を指定して google.feeds.Feed を生成
    var feed = new google.feeds.Feed ("http://a.hatena.ne.jp/ba-raindrop/rss");
    // 読み込み件数を指定
    feed.setNumEntries (10);
    // フィード取得時に呼び出すコールバックを指定して load を呼び出す
    feed.load (function (result)
        {
            // 処理結果を判断する
            if (result.error)
                return;
            // 表示用の HTML の構築には自作の簡易 XML ビルダ/パーサを使用しています
            var list = jp.raindrop.frog.xmlight.create ("dl");
            // result.feed.entries を列挙
            for (var i = 0; i < result.feed.entries.length; i ++)
            {
                var entry = result.feed.entries[i];
                list.addChild ('dt').addText (entry.title);
                var memberList = list.addChild ('dd').addChild ('dl');
                // entries の各要素のメンバを列挙
                for (var member in entry)
                {
                    if (member === 'categories')
                        continue;    // categories は配列なので後で列挙
                    memberList.addChild ('dt').addText (member);
                    memberList.addChild ('dd').addText (entry[member]);
                }
                // categories を列挙
                memberList.addChild ('dt').addText ('categories');
                var categoryList = memberList.addChild ('dd').addChild ('ul');
                for (var j = 0; j < entry.categories.length; j ++)
                    categoryList.addChild ('li').addText (entry.categories [j]);
            }
            $(document.body).append (list.toString ());
        });
});
//]]>
</script>

サンプルを見る

September 22, 2010

最後の要素の後のカンマの件

識別子、文字列または数がありません。 で、ECMA でも新しい仕様では最後のカンマを許容することになってるらしいって結論になったけど、じゃあ JSON はどうよ ? と思って調べてみた。

object
{}
{ members }
members
pair
pair , members
pair
string : value
array
[]
[ elements ]
elements
value
value , elements
value
string
number
object
array
true
false
null

JSON では、最後の要素の後ろにカンマがあってはダメみたいです。配列でも同じ。
Standard ECMA-262 3rd Edition のサブセットってなってるから当然かな。

続きを読む...

September 21, 2010

識別子、文字列または数がありません。

Firefox や Chrome では正常に動作する Javascript が、IE では「識別子、文字列または数がありません。」というエラーになることが。さっぱり意味が分かんなくて調べてみたんですけど、結構あちこちのブログで取り上げられています。IE ではオブジェクトリテラルの最後のメンバの後ろにカンマがある場合に出るみたいです。

var person = {
    lastName: '鈴木',
    firstName: '一郎',
    age: 25,  // ←ここにカンマがあるとエラー
    };

C 言語などで、配列を中括弧で初期化する場合に最後の要素の後ろにもカンマを付けることが認められているので、ついついこのケースでもカンマを入れたくなってしまうんですが、意識して付けないようにした方がよさそうです。

ちなみに、WSH で実行するときも同じエラーになります。当然か。

ECMA の仕様ではどうなってるのかな…。

続きを読む...

September 15, 2010

簡易 XML ビルダ/パーサ

ブログのデザイン変更に伴い、jQuery で DOM を生成する部分が増えました。引数に渡す HTML ソースを作成するのに、文字列を + 演算子で連結していくと何が何やら…。ソースを見やすくしたくて、簡易的な XML ビルダ を作成してみました。"簡易" ってつけたら何を公開してもいいのか!という声が聞こえてきそうですが、でも実際このブログのあちこちでこのモジュールを活用しています。

一応、xml を渡すとパースもできますが、引用符のネストなんかにうまく対応できていません。誤りを検出するようには作ってないので、少々間違った xml を渡しても適当に解析してしまいます。なんちゃってです。

var jp;
if (!jp) jp = {};
if (!jp.raindrop) jp.raindrop = {};
if (!jp.raindrop.frog) jp.raindrop.frog = {};
jp.raindrop.frog.xmlight || (function () {

    function Element (name)
    {
        var _name = name;
        this.name = function () { return _name; };
        this.children = new Elements ();
        this.attributes = {};
        this.attr = function ()
            {
                if (!arguments.length)
                    return this.attributes;
                if (arguments.length == 1)
                    return this.attributes[arguments[0]];
                this.attributes[arguments[0]] = arguments[1];
                return this;
            }
        this.wrap = function (name) { var elem = new Element (name); elem.children.append (this); return elem; };
        this.toString = function ()
            {
                var s = '<' + _name;
                for (var key in this.attributes)
                    s += ' ' + key + '="' + this.attributes[key] + '"';
                var c = this.children.toString ();
                if (c.length)
                    s += '>' + c + '</' + _name + '>';
                else
                    s += ' />';
                return s;
            }
    };

    function TextNode (text) { this.toString = function () { return text; }; };

    function Elements ()
    {
        var _inner = [];
        this.add = function (name) { return this.append (new Element (name)); };
        this.append = function (elem) { _inner.push (elem); return elem; };
        this.addText = function (text) { return this.append (new TextNode (text)); };
        this.remove = function (elem)
            {
                var index = -1;
                for (var i = 0; i < _inner.length; i++)
                {
                    if (elem == _inner [i])
                    {
                        index = i;
                        break;
                    }
                }

                if (index < 0)
                    return;

                _inner.splice (index, 1);
            };
        this.insert = function (index, elem) { _inner.splice (index, 0, elem); return elem; };
        this.insertText = function (index, text) { return this.insert (index, new TextNode (text)); }
        this.item = function (index) { return _inner[index]; };
        this.wrap = function (name)
            {
                var elem = new Element (name);
                for (var i = 0; i < _inner.length; i ++)
                    elem.children.append (_inner[i]); 
                return elem;
            };
        this.length = function () { return _inner.length; };
        this.toString = function ()
            {
                var s = "";
                for (var i = 0; i < _inner.length; i ++)
                    s += _inner[i].toString ();
                return s;
            };
    }

    var _parse = function (str)
        {
            var elements = new Elements ();
            _parseElements (str, elements);
            return elements;
        };

    var _parseElements = function (str, elements)
        {
            var regex = /<((?:(\w+):)?(\w+))([^>]*)(?:\/|>(.*)<\/\1)>/g;
            var match = [];
            while (match = regex.exec (str))
            {
                var element = elements.add (match[1]);
                _parseAttributes (match[4], element);
                if (match.length > 5)
                    _parseElements (match[5], element.children);
            }
        };

    var _parseAttributes = function (str, element)
        {
            var regex = /((?:(\w+):)?(\w+))=(["'])(.*?)(\4)/g;
            var match = [];
            while (match = regex.exec (str))
            {
                element.attributes [match[1]] = match[5];
            }
        };

    jp.raindrop.frog.xmlight = {
        create: function (name) { return new Element (name); },
        parse: function (str) { var c = new Elements (); _parseElements (str, c); return c; }
        };
}());
続きを読む...

December 22, 2009

目次フレームを自動生成する

Javascript で HTML 文書中の h1 ~ h6 を抜き出して、目次を作成するスクリプトを作成しました。見出し要素に id が振られていなければ自動生成します。自動生成した場合にやたらと "-0" が付くのはご愛嬌…。


2010.01.06 10:40 追記
自動生成した id の階層がおかしかったのを修正したものはこちら

とりあえず、Google Chrome 3.0.195.38 と IE 8 で動作確認しました。あんまりきれいじゃないです。

さらに、CSS で目次部分を左側のフレームっぽく表示させるようにしています。なんちゃって擬似フレーム。これも動作確認環境は同じ。


2009.12.22 22:50 追記
Firefox 3.5 で確認したところ、目次が全部 "undefined" ってなっちゃってました。
Firefox は Element.innerText って使えないんでしたな… とりあえず、Element.textContent を優先的に使うように修正してみました。

サンプルの HTML / toc.js / toc.css

HTML 側では、toc.js と toc.css を読込むだけです。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
    <head>
        <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8"/>
        <meta http-equiv="content-script-type" content="text/javascript"/>
        <meta http-equiv="content-style-type" content="text/css"/>
        <link href="toc.css" rel="stylesheet" type="text/css"/> 
        <script type="text/javascript" src="toc.js"></script>
        <!-- 省略… -->
    </head>
    <body>
        <h1>1章</h1>
        <h2>1章-1節</h2>
        <h3>1章-1節-1項</h3>
        <!-- 省略… -->
    </body>
</html>

自動生成する内容は、若干カスタマイズできます。toc.js を読込んだより下の行で以下のように書けば。

<script type="text/javascript">//<![CDATA[
toc.listtag = "ol";    // 番号つきリストに
toc.tocId = "tableOfContents";    // 目次部分の id を別の値に
toc.defaultIdPrefix = "section";    // 見出し要素の自動生成 id のプリフィックス
//]]>
</script>
続きを読む...

December 8, 2009

CSS で章番号を生成する

Internet Explorer でも :before 疑似要素や counter 関連のプロパティが使えるようになったみたいです。具体的にどのバージョンから何がサポートされたのか調べてないですが。

counter 関連のプロパティについてはさまざまなサイトで説明されてるんですが、紹介されているサンプルをコピーペーストしても、多くの場合、最上位のカウンターがインクリメントされないことが多いです。

続きを読む...

December 11, 2008

メモ: HTTP/1.1 Reason phrase

RFC 2616 の 6.1.1 Status Code and Reason Phrase がオリジナルということになるのかしら ?

Status-Code =
  "100"  ; Section 10.1.1: Continue
| "101"  ; Section 10.1.2: Switching Protocols
| "200"  ; Section 10.2.1: OK
| "201"  ; Section 10.2.2: Created
| "202"  ; Section 10.2.3: Accepted
| "203"  ; Section 10.2.4: Non-Authoritative Information
| "204"  ; Section 10.2.5: No Content
| "205"  ; Section 10.2.6: Reset Content
| "206"  ; Section 10.2.7: Partial Content
| "300"  ; Section 10.3.1: Multiple Choices
| "301"  ; Section 10.3.2: Moved Permanently
| "302"  ; Section 10.3.3: Found
| "303"  ; Section 10.3.4: See Other
| "304"  ; Section 10.3.5: Not Modified
| "305"  ; Section 10.3.6: Use Proxy
| "307"  ; Section 10.3.8: Temporary Redirect
| "400"  ; Section 10.4.1: Bad Request
| "401"  ; Section 10.4.2: Unauthorized
| "402"  ; Section 10.4.3: Payment Required
| "403"  ; Section 10.4.4: Forbidden
| "404"  ; Section 10.4.5: Not Found
| "405"  ; Section 10.4.6: Method Not Allowed
| "406"  ; Section 10.4.7: Not Acceptable
| "407"  ; Section 10.4.8: Proxy Authentication Required
| "408"  ; Section 10.4.9: Request Time-out
| "409"  ; Section 10.4.10: Conflict
| "410"  ; Section 10.4.11: Gone
| "411"  ; Section 10.4.12: Length Required
| "412"  ; Section 10.4.13: Precondition Failed
| "413"  ; Section 10.4.14: Request Entity Too Large
| "414"  ; Section 10.4.15: Request-URI Too Large
| "415"  ; Section 10.4.16: Unsupported Media Type
| "416"  ; Section 10.4.17: Requested range not satisfiable
| "417"  ; Section 10.4.18: Expectation Failed
| "500"  ; Section 10.5.1: Internal Server Error
| "501"  ; Section 10.5.2: Not Implemented
| "502"  ; Section 10.5.3: Bad Gateway
| "503"  ; Section 10.5.4: Service Unavailable
| "504"  ; Section 10.5.5: Gateway Time-out
| "505"  ; Section 10.5.6: HTTP Version not supported
| extension-code
extension-code
= 3DIGIT
Reason-Phrase
= *<TEXT, excluding CR, LF>

May 11, 2004

Microsoft 単語帳 用ブックマークレット

Web 上の IT用語辞典 、みたいな物ってよく使うのですが、Microsoft単語帳 が、ちっちゃいウインドウで開くコンパクト版があって、ひそかにお気に入りだったりします。ただし、コンパクト版の、ブックマークに登録するリンクは用意されていないんですよね。単語帳のホームページには「単語帳 コンパクト版の起動」というリンクがあるのですが、ページの JScript関数 を呼び出すようになっているのみです。そこで、ブックマーク登録用に、JavaScript URLにしてみました。

javascript:window.open( %22http://www.microsoft.com/japan/Terminology/query.asp?ui=S%22, %22compact%22, %22width=300,height=360,titlebar=no,scrollbars=no,menubar=no,toolbar=no,location=no,status=no,resizable=no%22 ).focus();

右クリックして、お気に入りorブックマークに追加することができます。

March 5, 2004

新しいウインドウで開く

Firefoxもそうだけど、IEはリンクをShift + クリックすると、リンク先を新しいウインドウで開くことができる。
実はこれはHTMLヘルプでも可能なことを発見。ただしIEで開く。

December 10, 2003

このページにはセキュリティ保護された項目と保護されていない項目が含まれています。

DBの検索条件を指定して、検索結果を表示する、こんなフレームページを作成しました

<!-- フレームドキュメントのフレーム定義部分 -->
<frameset rows="*,60">
	<!-- 検索結果を表示するフレーム -->
	<frame id="frame_main" name="frame_main" frameborder="0" />
	<!-- 検索条件のフォーム -->
	<frame id="frame_menu" name="frame_form" frameborder="0" src="form.asp" />
	<noframes>
		(省略)
	</noframes>
</frameset>
<!-- 
実際にはこんな感じ
┏━━━━━━━┓
┃frame_main    ┃
┃              ┃
┃              ┃
┠───────┨
┃frame_form    ┃
┗━━━━━━━┛
-->

ページの初期表示に検索結果を表示するため、form.aspのbody.onloadにフォームをサブミットするコードを書いておいたのですが、httpsでアクセスすると、表示時に、'このページにはセキュリティ保護された項目と保護されていない項目が含まれています。 保護されていない項目を表示しますか?'が表示されてしまいます。frame_mainに表示するものがないため、UAが'about:blank'を表示しようとして起こるみたいです。ダミーのドキュメントを作成して、frameタグのsrcに指定しておくと解決します。

December 4, 2003

クライアントスクリプトで複数ページで同じ初期化処理を行う

複数ページの<body onload="....">に同じクライアントスクリプトの初期化処理を記述するのはメンテナンスが大変、というあなたに朗報です!

...というほどのものではないのですが、まあ、こんな感じで、このスクリプトを読み込むすべてのページで、同じ初期化処理を行うことができます。

// init.js
window.onload	= function () {
	// 初期化処理を記述します...
	return true;
}

そして、それぞれのページのhead要素内に

<script type="text/javascript" src="./init.js"></script>

と記述します。

ここでは関数を代入していますが、

// init.js
window.onload	= 'init(this);'

などと、スクリプトを文字列で指定しても動きそうな気もします。(確認していません)

また、もちろんonload以外でも、たとえば

window.onblur	= function () {
	window.focus();
}

なども動きます。

November 25, 2003

Refreshヘッダ

一定秒後に指定したページにジャンプさせるためのメタタグはこのように書く。

<meta http-equiv="Refresh" content="0; url=http://frog.raindrop.jp" />
同じように、HTTPヘッダでREFRESHを吐き出すと、同じ動作をさせることができる。
<%
Call Response.AddHeader("REFRESH", "0; url=http://frog.raindrop.jp")
%>
ただし、若干UAによって動作が違うらしい。
また、ヘッダとして出力する場合は、当然、コンテンツの出力の前に出力する必要がある。
(メタタグはbody要素の後ろにまたhead要素を作って、その中に書いてもちゃんと効くみたい。私はASPで「実行中...」などと画面に出しておき、処理完了後にメタタグで画面遷移、なんてことをしている。)
あと、REFRESHヘッダはRFCにはない。

November 21, 2003

ブラウザのキャッシュを無効にする

お決まりのヘッダです。わたしは、これをたとえば"nocache.asp"などとして保存しておき、

Call Server.Execute("./nocache.asp")

などとしています。

<%@ Language=VBScript %><%
	'@@ キャッシュを無効にする
	With Response
		Call .AddHeader("Pragma", "no-cache")
		Call .AddHeader("Cache-control", "no-cache")
		.Expires = 0
		.CacheControl = "Private"
	End With
%>

November 19, 2003

ASPからコールするCOMコンポーネント

VBでASPからコールするActiveX DLLやActiveX EXEを作成する場合のポイントらしきものです。

  1. データ型

    ASPで使用するスクリプト言語は型がありません。したがって

    • 引数はByValで
    • ByRefで渡す引数はVariantに
    と言う感じにします。
  2. プロジェクトのプロパティ
    • 「対話型インターフェースの抑制」にチェック
    • 「メモリに保持」にチェック
    • スレッドモデル
      • スコープがApplication---シングル スレッド
      • スコープがSession-------アパートメントスレッド
      で作成します。このあたり、VC++で作成すると「Both」が選択できた気がしますが、VBでそれに当たるのはないのかな?
  3. ASP組み込みオブジェクトへのアクセス

    以下の2つに参照設定を行って、ASPからと同じように、Response.Writeでクライアントへの出力を行ったり、Request.ServerVariablesで環境変数を取得したりすることができます。

    • COM+ Services Type Library(Win2K以降の場合。9xやNTは)
    • Microsoft Active Server Pages Object Library

    ASP組み込みオブジェクトにアクセスするには、GetObjectContextメソッドを使います。Responseオブジェクトを例に取ると

    Dim aspResponse         As ASPTypeLibrary.Response
    
        Set aspResponse = GetObjectContext("Response")
        Call aspResponse.Write("Hello ASP!")
    		

    と言うような感じです。このとき、クラスのMTSTransactionModeプロパティが、"NotAnMTSObject"ではうまくいきませんでした。"Uses Transaction"にするとうまくいきましたが、どういう値であるべきかまでは調べていません。

    また、この処理を、Class_Initiarizeプロシージャに記述したオブジェクトを、Global.asa内で生成したり、アプリケーションスコープのobjectタグで宣言すると、うまく動作しないようです。

(04/01/24 追記)

Applicationスコープにするには、スレッドモデルがBothまたはNeutralのコンポーネントを作成する必要があります。つまり、VBで作成することはできません。

November 17, 2003

Server.Execute()

「006~ASP 0231~Server.Execute エラー~無効な形式の URL または完全認証されている絶対 URL が使用されました。相対 URL を使用してください。」に悩まされること半日、Server.Executeメソッドで、パスパラメータにクエリ文字列が使えるってのはウソ!ってな情報 [Microsoft Windows 2000 Serverドキュメント]を得ました。
正確にはこうらしいです

Server.Execute を呼び出す .asp ファイルで利用できる QueryString は実行 .asp ファイルで利用できるので、Server.Execute のパス パラメータに含まれる QueryString を渡す必要はありません。ただし、Path パラメータに含まれる別のクエリー文字列を渡すことはできません。

迷惑な・・・。

October 28, 2003

任意のファイル名でダウンロードさせる

たとえば、ASPやCGIからCSVを動的に生成して出力するような場合です。
ためしにこんなコードを書いてみました。

<%
' sample.asp
Response.ContentType = "application/x-csv"
For i = 0 to 10
    For j = 0 to 10
        %><%=i*j%>,<%
    Next
    %>日本語フィールド<%=vbNewLine%><%
Next
%>

このaspスクリプトへのリンクを別のhtmlに貼っておき、クリックするとCSVがダウンロードされる、用にしたかったのですが、これではダウンロードされるファイル名がsample.aspになってしまいます。中身はカンマ区切りのテキストなのですが。
ここで関係してくるのがRFC2183[HTMLコーディングチェック]に定義されているContent-Dispositionヘッダです。

続きを読む...

October 27, 2003

IIS + OpenSSL

OpenSSLを使って、httpsを使用するために必要になる証明書を作成する方法です。
パラメータは結構適当です。大まかな手順のみです。以下のものを作成します。

  • CAの秘密鍵
  • CAの証明書(ルート証明書)
  • Webサーバの証明書

参考にしたサイト:IIS+OpenSSLでhttpsを[Yonetani A.'s Home Page !]

続きを読む...

CSS

Cascading Style Sheets Reference