AutoPagerizeのように自動読み込みができるjQueryプラグイン

GreasemonkeyスクリプトAutoPagerizeFirefox add-onのAutoPagerのように、スクロールするだけで自動ページングできるjQueryプラグインを作ってみました。

※10/22 全面的に書き直したv1.0.0をリリースしました
詳しくはhttp://lagoscript.org/jquery/autopagerを参照してください。

サンプル

$.autopager({
    nextLink   : '#nav a:last', // 次ページリンクのセレクタ
    pageElement: '#res'         // ページコンテンツ部分のセレクタ 
});

jQuery.autopager(options)

サイトに次ページの自動読み込み機能を付け加えます。

options
  • nextLink: 次ページのリンクを持つ要素へのセレクタ
  • pageElement: ページコンテンツ部分のセレクタ
  • insertBefore(任意): ここで指定した要素の前に次ページのコンテンツが挿入される
  • pagingPoint(任意): 自動読み込みが行われる位置指定。ページ終端からの距離(ピクセル
  • load(任意): 次ページが読み込まれた時に実行されるコールバック関数。"this"は挿入するコンテンツとなる。第一引数に読み込まれたページ番号が渡される。
  • success(任意): 次ページの挿入まで成功した後に実行されるコールバック関数。"this"は挿入されたコンテンツとなる。第一引数に読み込まれたページ番号が渡される。

ソース

http://code.google.com/p/jquery-autopager/
http://jquery-autopager.googlecode.com/svn/trunk/jquery.autopager.js

(function($) {
    $.autopager = function(options) {
        var nextPageUrl;
        var requestedPages = [location.href];
        var loading = false;
        if ($.browser.msie) {
             var scrollTopElement = $('html');
             var heightElement = $(document);
        } else {
             var scrollTopElement = $(document);
             var heightElement = $('html');
        }

        options = $.extend({
            insertBefore: $(options.pageElement).next(),
            pagingPoint: 350,
            load: function() {},
            success: function() {}
        }, options);

        setNextPageUrl();

        $(window).scroll(function(){
            if (!loading && nextPageUrl && $.inArray(nextPageUrl, requestedPages) < 0 && scrollHeightRemain() < options.pagingPoint) {
                loading = true;
                requestedPages.push(nextPageUrl);
                $.get(nextPageUrl, function(data){
                    var nextPage = $('<div/>').html(data);
                    setNextPageUrl(nextPage);
                    insertPageElement(nextPage);
                    loading = false;
                });
            }
        });

        return this;

        function setNextPageUrl(context) {
            var args = [options.nextLink];
            if (context) args.push(context);
            nextPageUrl = $.apply(null, args).attr('href');
        }

        function insertPageElement(context) {
            var pageElement = $(options.pageElement, context); 
            if (pageElement) {
                options.load.call(pageElement, requestedPages.length);
                pageElement.insertBefore(options.insertBefore);
                options.success.call(pageElement, requestedPages.length);
            }
        }

        function scrollHeightRemain() {
            return heightElement.height() - scrollTopElement.scrollTop() - $(window).height();
        }
    };
})(jQuery);

参考

オプションの取り方などはAutoPagerizeを参考にさせてもらいました。

C言語で文字列のクイックソート

コマンドライン引数で受け取った文字列をstrcmpで比較してソートする。

#include <stdio.h>
#include <string.h>

void quicksort(char *array[], int left, int right) {
    if (left >= right) return;

    int l = left;
    int r = right;
    char *pivot = array[(left + right) / 2];  // 中央からピボットを選択
    char *buf;

    while (1) {
        while (strcmp(array[l], pivot) < 0) l++;
        while (strcmp(array[r], pivot) > 0) r--;

        if (l > r) break;

        // pivotより小さい値を左、大きい値を右に入れ替え
        buf = array[l];
        array[l] = array[r];
        array[r] = buf;

        l++, r--;
    }

    // pivotより小さい領域と大きい領域をそれぞれ再帰的にソート
    quicksort(array, left, r);
    quicksort(array, l, right);
}

int main(int argc, char *argv[]) {
    int i;

    if (argc < 2) {
        fprintf(stderr, "Usage; %s ...\n", argv[0]);
        return 1;
    }

    quicksort(argv, 1, argc - 1);

    for (i = 1; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    return 0;
}

C言語でクイックソート

Cだといちいちめんどくさい。

#include <stdio.h>

void quicksort(int array[], int left, int right) {
    int l = left;
    int r = right;
    int pivot = array[(left + right) / 2]; // 中央からピボットを選択
    int buf;

    while (1) {
        while (array[l] < pivot) l++;
        while (array[r] > pivot) r--;

        if (l > r) break;

        // pivotより小さい値を左、大きい値を右に入れ替え
        buf = array[l];
        array[l] = array[r];
        array[r] = buf;

        l++, r--;
    };

    // pivotより小さい領域と大きい領域をそれぞれ再帰的にソート
    if (left < r)  quicksort(array, left, r);
    if (l < right) quicksort(array, l, right);
}

int main(int argc, char *argv[]) {
    int i;
    int array[] = {
        6, 4, 3, 7, 8, 0, 5, 2, 9, 1,
    };
    int count = sizeof array / sizeof array[0];

    quicksort(array, 0, count - 1); // ソート

    // 結果表示
    for (i = 0; i < count; i++) {
        printf("%d\n", array[i]);
    }

    return 0;
}

PHPでクイックソートというものをやってみた

一般的に最も高速なソートのアルゴリズムらしい。

アルゴリズム

1. 適当な数(ピボットという)を選択する (この場合はデータの総数の中央値が望ましい)
2. ピボットより小さい数を前方、大きい数を後方に移動させる (分割)
3. 二分割された各々のデータを、それぞれソートする

実装

<?php
function quicksort($array) {
    if (count($array) <= 1) {
        return $array;
    }
    $pivot = array_shift($array); // ピボットの選択

    $left = $right = array();
    foreach ($array as $value) {
        if ($value < $pivot) {
            $left[]  = $value; // ピボットより小さい数は左
        } else {
            $right[] = $value;  // ピボットより大きい数は右
        }
    }
    // 左右のデータを再帰的にソートする
    return array_merge(quicksort($left), array($pivot), quicksort($right));
}

実行

<?php
$array = array(
    6, 4, 3, 7, 8, 5, 2, 9, 1,
);
$array = quicksort($array);
var_dump($array); // 1, 2, 3, 4, 5, 6, 7, 8, 9

Prototype.jsのbind()がすること

バージョン1.6.0.3。

Function.prototype.bind()

thisと引数の値を固定化した関数を返すメソッド。
第一引数にthisとなる値、第二引数以降に固定化する引数の値を指定する。
Function.prototypeを直接拡張している。

Object.extend(Function.prototype, {
 // 略

  bind: function() {
   // 引数の数が1以下で、第一引数がundefinedの場合は関数をそのまま返す
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this,       // bind実行時のthisは関数そのもの 
        args = $A(arguments),  // 固定化される引数
        object = args.shift(); // 関数実行時にthisとなるオブジェクト

    // thisと引数を固定化した関数を返す。固定化した以外の残りの引数は直接受け取ることができる
    return function() {
     // 元の関数をapplyメソッドで呼び出し、結果をreturn
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  // 略
});
  • Function.prototype.apply : 第一引数をthisに結びつけ、第二引数を引数の配列として関数を呼び出す。

Function.prototype.bindAsEventListener()

イベントのコールバック用にthisと引数の値を固定化した関数を返すメソッド。
引数の指定方法はbindと同じだが、返される関数は第一引数でeventオブジェクトを取る。
その他の引数を受け取ることはできない(コールバック関数なので必要がない)。

Object.extend(Function.prototype, {
 // 略

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    // 引数にeventオブジェクトをとる関数を返す。thisと残りの引数は固定化される。
    return function(event) {
      // eventオブジェクトを第一引数として関数を呼び出す
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  // 略
});

Prototype.jsの$()がすること

確認したバージョンは1.6.0.3。

$()

Prototype.jsで拡張したelementオブジェクト(の配列)を返す関数。
引数が複数の場合は、再帰的に自身を呼び出し配列にセット。その配列をreturn。
引数が一つで文字列であればidとみなしてelementオブジェクトを取得する。
Element.extendメソッドで拡張したelementをreturn。

function $(element) {
  if (arguments.length > 1) { // 引数が複数の場合
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i])); // 自身を呼び出した結果を配列に入れる
    return elements;
  }
  if (Object.isString(element)) // 文字列の場合はIdとみなす
    element = document.getElementById(element);
  return Element.extend(element); // メソッドを追加してreturn
}

Element.extend()

elementオブジェクトを拡張(メソッド追加)するメソッド。
タグごとにDOMオブジェクトが定義されているブラウザの場合は何もしない。
(あとでElement.addMethodsメソッドで直接DOMオブジェクトを拡張するため)
引数にとったelementがすでに拡張されている場合とElementノードではない場合とwindowオブジェクトの場合は何もしない。

Element.extend = (function() {
  // タグごとにDOMオブジェクトが定義されている場合
  if (Prototype.BrowserFeatures.SpecificElementExtensions) 
    return Prototype.K; // 引数をそのまま返すメソッドとする

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    // すでに拡張されている、elementノードでない、windowオブジェクトの場合...
    if (!element || element._extendedByPrototype || 
        element.nodeType != 1 || element == window) return element; //そのまま返す

    var methods = Object.clone(Methods), // 複製する
      tagName = element.tagName.toUpperCase(), property, value;

    // 特定のタグに対してのメソッド追加
    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    for (property in methods) {
      value = methods[property];
      // valueが関数で、elementにpropertyが定義されていない場合...
      if (Object.isFunction(value) && !(property in element))
        // 第一引数に自動でthisをとるメソッドにして返す
        // 実際にメソッドが呼ばれるときはthis==elementとなる
        element[property] = value.methodize(); 
    }

    element._extendedByPrototype = Prototype.emptyFunction; // 拡張済みのフラグをつける
    return element;

  }, {
    refresh: function() {
      // 全てのタグに対してメソッドを拡張する (Safariは必要なし)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

無名関数を作成してすぐ実行しているので、その戻り値がElement.extendにセットされる。
Methodsは追加されるメソッド群、ByTagはタグごとに追加されるメソッド群。
extend(ローカル変数)が、Element.extendの本体。
extend.refreshは全てのタグに対して追加するメソッドをMethodsに追加するメソッド。
extendとextend.refreshは共にMethodsを参照するクロージャのため、
Methodsを拡張することで追加されるメソッドを変更することができる。

  • Obejct.extend : 第一引数に第二引数のプロパティを追加して返すメソッド。
  • Object.clone : シャローコピーを作成して返すメソッド。
  • Prototype.K : 引数をそのまま返すメソッド。
  • Prototype.emptyFunction : 何もせず何も返さないメソッド。
  • Function.methodize : 第一引数にthisをとるメソッドにして返すメソッド。

Ubuntuでmanが使えない

Ubuntuのデフォルトでは日本語マニュアルがインストールされていないらしい

$ man man
No manual entry for man

マニュアルのインストール

$ sudo apt-get install manpages-ja manpages-ja-dev

manpages-ja-devは、システムコールとLibcのマニュアル