プレゼンフレームワーク reveal.js にやっつけ chapter 機能をつける

# 概要
プレゼンするのに [reveal.js](https://github.com/hakimel/reveal.js/) を使ってみてよかったんですが、プレゼンの構成として、chapter があって、それに各スライドが所属している、って形にしたくて、やっつけで chapter 機能をつけてみた、という話です。

# ここでいうところの chapter 機能
次の動画における、左上ににゅるっと出てくる「麦・大豆＆野菜」とか「生物多様性」とかの部分。各スライドは chapter に所属していて、複数個の chapter でプレゼン資料が成り立っている、という構成です。

![reveal_chapter3.gif](https://qiita-image-store.s3.amazonaws.com/0/64927/d26d5a80-a533-0a7f-10b4-78b818d124dd.gif)

# 実装方針

- 水呑百姓がきれいなコードを書いている暇はないのでやっつけ
  - reveal.js に手は入れない
  - とりあえず動けばよくて、プラグインとして整備したりしない
  - プレゼン資料を作るごとにコードに手を入れるのも持さない
- 出先でのプレゼンなのでローカル WEB サーバは動かさない前提で
 
# コード

## javascript

ほんと、やっつけです。きれいに書き直し、自由に使ってください。
グローバル汚染上等。ES6 って何？　おいしいの？

```javascript:chapter.js
const Chapterion = {
  current: {
    c: 0
  },
  chapters: [],
};

Chapterion.Chapter = function(section, idx, elm) {
  this.section = section;
  section.dataset.chapterIndex = idx;
  this.elm = elm;
  elm.dataset.chapterIndex = idx;
  this.index = idx;
  let text = section.dataset.chapter;
  if (text) {
    elm.innerHTML = text;
    this.activity = true;
  } else {
    this.activity = false;
  }
};

Chapterion.init = function() {
  const self = this;
  const header = document.querySelector("header");
  
  document.querySelectorAll("div.reveal section").forEach(function(section, i) \
{
    let elm = document.createElement("div");
    self.chapters.push(new Chapterion.Chapter(section, i, elm));
    header.appendChild(elm);
  });
};

Chapterion.flip = function(event) {
  let newChapterIndex = event.currentSlide.dataset.chapterIndex;
  if (! newChapterIndex) {
    newChapterIndex = event.currentSlide.offsetParent.dataset.chapterIndex;
  }
  if (newChapterIndex == this.current.c) return;
  this.chapters.forEach(function(chapter) {
    const section = chapter.section;
    if (chapter.activity && chapter.index == newChapterIndex) {
      console.log('ADD', newChapterIndex, chapter.index);
      chapter.elm.classList.add('shown');
    } else {
      console.log('REM', newChapterIndex, chapter.index);
      chapter.elm.classList.remove('shown');
    }
  });
  this.current.c = newChapterIndex;
};
```

## スタイルシート
スタイルはこんな感じで。チャプターは HTML 的には `header` 要素です。
 
```css:chapter.css
header {
  clear: both;
  
  position: absolute;
  top: 5px;
  left: 5px;

  z-index: 20;
}

header div {
  text-align: center;
  
  color: #fff;
  
  transition: all 300ms 0s ease-in;
  
  font-family: "Source Sans Pro", Helvetica, sans-serif;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: normal;
  text-transform: uppercase;
  text-shadow: none;

  word-wrap: break-word;
  
  display: block;
  
  font-size: 0em;
}

header div.shown {
  font-size: 2.5em;
  
  margin: 0 auto;
  padding: 10px;

  box-shadow: 1px 2px 3px #000;
  border-radius: 15px;
  background-color: rgba(0, 255, 0, 0.3);
}
```

## Markup/Markdown
プレゼンの html はこんな感じで。
chapter にしたい文字列を `section` の `data-chapter` 属性として書いときます。
この `section` 内では Markdown で複数のスライドを書きます。

```html:index.html

    <section data-chapter="麦・大豆＆野菜"
             data-markdown
             data-separator="^\n---\n$"
             data-separator-vertical="^\n>>>\n$">
```

あ、上記 css をリンクして、上記 js を読み込んでおくのは、もちろん忘れずに。
`Chapterion.init();` で初期化して、あとは `slidechagenged` イベントを拾って `Chapterion.flip()` を呼ぶだけです。

```html:index.html
 <script src="js/reveal.js"></script>
  <script src="js/echi5c.js"></script>

  <script>
   // More info about config & dependencies:
   // - https://github.com/hakimel/reveal.js#configuration
   // - https://github.com/hakimel/reveal.js#dependencies
   Reveal.initialize({
    dependencies: [
     { src: 'plugin/markdown/marked.js' },
     { src: 'plugin/markdown/markdown.js' },
     { src: 'plugin/notes/notes.js', async: true },
     { src: 'plugin/zoom-js/zoom.js', async: true },
    ],
    parallaxBackgroundImage: "mugi.jpg",
    parallaxBackgroundHorizontal: 220,
    parallaxBackgroundVertical: 100
   });

   Chapterion.init();

   Reveal.addEventListener('slidechanged', function(event) {
     Chapterion.flip(event);
   });
  </script>
```