paginate

 連続する各ページへのリンクを作るプラグインです。連載記事なんかで使えるでしょう。

使い方

{{paginate(AGirlOfElm,1,2,3,4,5,6,7,8,9,10,11,12,13,14,後書|Afterwords)}}

は、Hiki記法で次のように書いたのと同様になります。

# [[1|AGirlOfElm1]]
# [[2|AGirlOfElm2]]
# [[3|AGirlOfElm3]]
# [[4|AGirlOfElm4]]
# [[5|AGirlOfElm5]]
# [[6|AGirlOfElm6]]
# [[7|AGirlOfElm7]]
# [[8|AGirlOfElm8]]
# [[9|AGirlOfElm9]]
# [[10|AGirlOfElm10]]
# [[11|AGirlOfElm11]]
# [[12|AGirlOfElm12]]
# [[13|AGirlOfElm13]]
# [[14|AGirlOfElm14]]
# [[後書|AGirlOfElmAfterwords]]

 最初の文字(AGirlOfElm)がページ名の共通部分で、以下、その後に付く文字(1〜14と「後書|Afterwords」)です。「|」(半角)を使うと、表示には(複数ある場合は最後の)「|」の左側(後書)、実際のページ名には右側(Afterwords)が使われます。無ければ表示にはそのまま、ページ名には共通部分+文字が使われます。

 但し、現在のページの所はリンクにならないで強調になり、前や次のページへのリンクも付きます。例えば「シーン3:影の部族の集落で宴会 - 楡の少女」というページで上のようにプラグインを呼ぶと次のようになります。

* [[<|AGirlOfElm2]]
# [[1|AGirlOfElm1]]
# [[2|AGirlOfElm2]]
# ''3''
# [[4|AGirlOfElm4]]
# [[5|AGirlOfElm5]]
# [[6|AGirlOfElm6]]
# [[7|AGirlOfElm7]]
# [[8|AGirlOfElm8]]
# [[9|AGirlOfElm9]]
# [[10|AGirlOfElm10]]
# [[11|AGirlOfElm11]]
# [[12|AGirlOfElm12]]
# [[13|AGirlOfElm13]]
# [[14|AGirlOfElm14]]
# [[後書|AGirlOfElmAfterwords]]
* [[>|AGirlOfElm4]]

 サイドバーならそのままでいいと思いますが、実際にはスタイルシートでインライン要素表示するのがいいでしょう(「display: inline」を使う)。

 十数ページの連載記事全部に{ {paginate(SeriesTitle,1,2,3,4,5,6,7,8)}}といったソースをコピーするのはナンセンスなので*1、どれか一ページにこれを書いておいて、実際の連載記事ではquote_pageプラグインでそのページを読み込むのがいいでしょう。

 ページ内で一度paginateプラグインを呼び出した後は、prev_page、next_pageプラグインが使えるようになります。それぞれ「前のページ」「次のページ」へのリンクを作ります。表示するテキストは管理画面から設定できますし、呼び出す時々で変えることもできます。

 例えばこのサイトでは、next_pageのデフォルトの表示を「次のシーンに進む」にしているので{ {next_page}}と書くと「次のシーンに進む」というリンクができますが、次のページがシーンではなくて後書きの場合だけ{ {next_page(後書きに進む)}}としています。こうすると「後書きに進む」というリンクができます。詳しくは管理画面(ページネート)を見てください。

 あと、地味だけど、head要素内に前や次のページへのlink要素が入ります。Operaの次のページへ移動するマウスジェスチャー*2を使ってページを進めるので便利かも。

ソース

misc/plugin/paginate.rb

module Paginate
  class Page
    attr_accessor :page, :text
    
    # Page.parse('1|First', 'AGirlOfElm').to_s # => '[[AGirlOfElm1|First]]'
    # Page.parse('3', 'AnEagleEye').to_s # => '[[AnEagleEye3]]'
    # Page.parse('Parrot').to_s # => '[[Parrot]]'
    def self.parse(frag, page_prefix = '')
      frag = frag.to_s
      idx = frag.rindex '|'
      if idx
        text = frag.slice(0 ... idx)
        num = frag.slice(idx+1 .. -1)
      else
        text = num = frag
      end
      
      new(page_prefix + num, text)
    end
    
    def initialize(page, text = nil)
      @page, @text = page, text
    end
    
    def to_s
      ret = "[["
      ret << "#{@text}|" if @text
      ret << "#{@page}]]"
    end
  end
end

def paginate(prefix = '', *pages)
  parser = @conf.parser.new(@conf)
  
  anchors = []
  current_page = nil
  pages.each_with_index do |page, i|
    anchors << p = Paginate::Page.parse(page, prefix)
    current_page = i if p.page == @page
  end
  if current_page
    anchors[current_page] = "''" + anchors[current_page].text + "''"
    if current_page != anchors.length - 1
      @next_page_name = anchors[current_page + 1].page
      next_page_anchor = "[[>|#{@next_page_name}]]"
    end
    if current_page != 0
      @prev_page_name = anchors[current_page - 1].page
      prev_page_anchor = "[[<|#{@prev_page_name}]]"
    end
  end
  
  add_header_proc do
    ret = ''
    ret << %Q|\n  <link rel="prev" href="?#{@prev_page_name}">| if @prev_page_name
    ret << %Q|\n  <link rel="next" href="?#{@next_page_name}">| if @next_page_name
  end
  
  src_ary = anchors.collect { |a| '# ' + a.to_s }
  src_ary.unshift('* ' + prev_page_anchor) if prev_page_anchor
  src_ary.push('* ' + next_page_anchor) if next_page_anchor
  src = src_ary.join("\n")
  html = parser.parse(src)
  @conf.formatter.new(html, @db, self, @conf).to_s
end

def prev_page(label = @conf['paginate.default_prev_page_label'].to_s)
  if @prev_page_name
    %Q|<a href="?#{@prev_page_name}" rel="prev">#{h label}</a>|
  else
    ''
  end
end

def next_page(label = @conf['paginate.default_next_page_label'].to_s)
  if @next_page_name
    %Q|<a href="?#{@next_page_name}" rel="next">#{h label}</a>|
  else
    ''
  end
end

def paginate_conf_template
<<EOT
<p>#{paginate_desc}</p>
<ul>
  <li>#{paginate_prev_page_label}<input type="text" name="paginate.default_prev_page_label" value="#{h @conf['paginate.default_prev_page_label'].to_s}"></li>
  <li>#{paginate_next_page_label}<input type="text" name="paginate.default_next_page_label" value="#{h @conf['paginate.default_next_page_label'].to_s}"></li>
</ul>
EOT
end

def paginate_saveconf
  @conf['paginate.default_prev_page_label'] = @cgi.params['paginate.default_prev_page_label'][0]
  @conf['paginate.default_next_page_label'] = @cgi.params['paginate.default_next_page_label'][0]
end

add_conf_proc('paginate', paginate_label) do
  paginate_saveconf if @mode == 'saveconf'
  paginate_conf_template
end

export_plugin_methods :paginate, :prev_page, :next_page

misc/plugin/en/paginate.rb

def paginate_label; 'Paginate'; end
def paginate_desc; 'Set text used by prev_page and next_page plugins. These are used when plugins are called without text like {{prev_page}}. When called with text like {{next_page("Go to next page")}}, the text("Go to next page") is used.'; end
def paginate_prev_page_label; 'Default text for prev_page plugin: '; end
def paginate_next_page_label; 'Default text for next_page plugin: '; end

misc/plugin/ja/paginate.rb

# encoding: euc-jp

def paginate_label; 'ページネート'; end
def paginate_desc; 'prev_page、next_pageプラグインで使われるデフォルトのテキストを設定します。テキストで「{{prev_page}}」などと呼び出した場合に、ここで設定したテキストが使われます。「{{next_page(次のページに進む)}}」というように明確にテキストを設定してプラグインを呼び出した場合には、そちら(「次のページに進む」)が優先されます。'; end
def paginate_prev_page_label; 'prev_pageプラグインのデフォルトのテキスト:'; end
def paginate_next_page_label; 'next_pageプラグインのデフォルトのテキスト:'; end

 ……うーん、汚いなあ。pagenateが長いなあ。FiberとかEnumeratorとか使えればもう少し綺麗になりそうだけど、いかんせんここのサーバーはRuby 1.8.6だからな。Enumeratorは使えるようなので、後日これで作り直してみます。あと、実はクラスじゃなくてラムダ(とクロージャー?)を使ってもいいんじゃないかと思っているところなので、それも併せて。

 最初はインスタンス変数を使わないでprev_pageとnext_pageを作ろうと色々やっていたのですが、結局できなくて諦めました。

 Paginate::Paginationクラスでも作って、クラスメソッドとして機能を実装して、それをpaginateメソッドから呼ぶようにしたらいいのかな。それならpaginateメソッドはきれいになるしprev_pageとnext_pageも簡単そうだ。Paginationクラスのインスタンスを一つ@pluginオブジェクトのインスタンス変数に入れてしまえばいい。それってクラスでプラグインを作るってことだからHikiプラグインの思想(tDiaryプラグインの思想?)とは相容れないかもしれない。

 一ページに複数種類のページネーションを作った時、prev_pageとnext_pageが一方の物しか使えないという難点があります。複数のprev_page等を使い分けたい時は、もう直接[[]]を使って書いちゃってください。

*1 ページの増減があった時に、全ページのpaginate部分を直さなくてはいけない。

*2 左クリックを押しっぱなしで右クリック、や、右クリックを押しっぱなしでマウスを右に動かして離す。