セクション番号を自動で振ってくれる JavaScript

JavaScript の勉強のつもりで作ってみました.

表示例

     <h1>ABC</h1>
     <h2>foo</h2>
     <h2>bar</h2>
     <h3>hoge</h3>
     <h3>fuga</h3>
     <h1>XYZ</h1>
     <h3>piyo</h3>

f:id:kei10in:20100519223036p:image

こんな感じのものが,

f:id:kei10in:20100519224733p:image

こんな風になります.

コード

var HeaderEnumerator = function() {
    this.enumeratingTags = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
    this.sectionLevel = new SectionLevel();
};

HeaderEnumerator.prototype = {
    startElement: function(element) {
        var newSectionLevel = this.calcSectionLevel(element);
        if (newSectionLevel < 0) {
            return;
        }
        this.setSectionLevel(newSectionLevel);
        this.preppendSectionNumber(element);
    },
    endElement: function(element) {
    },
    calcSectionLevel: function(element) {
        return this.enumeratingTags.indexOf(element.nodeName);
    },
    preppendSectionNumber: function(element) {
        var numElem = document.createElement('span');
        numElem.className = 'section-number';
        var numText = document.createTextNode(this.formatSectionNumber());
        numElem.appendChild(numText);
        element.insertBefore(numElem, element.firstChild);
    },
    setSectionLevel: function(newSectionLevel) {
        this.sectionLevel.setLevel(newSectionLevel);
    },
    formatSectionNumber: function() {
        return this.sectionLevel.format();
    }
};


var SectionLevel = function() {
    this.numbers = [new SectionNumber()];
};

SectionLevel.prototype = {
    indent: function() {
        this.numbers.push(new SectionNumber());
    },
    unindent: function() {
        this.numbers.pop();
    },
    increment: function() {
        this.getCurrentNumber().increment();
    },
    setLevel: function(level) {
        if (level < 0) {
            return;
        }
        if (level < this.getLevel()) {
            var n = this.getLevel() - level;
            for (var i = 0; i < n; i++) {
                this.unindent();
            }
        } else if (this.getLevel() < level) {
            var n = level - this.getLevel();
            for (var i = 0; i < n; i++) {
                this.indent();
            }
        }
        this.increment();
    },
    getLevel: function() {
        return this.numbers.length - 1;
    },
    getCurrentNumber: function() {
        return this.numbers[this.getLevel()];
    },
    getNumber: function(i) {
        return this.numbers[i];
    },
    format: function() {
        var numstr = '';
        for (var i = 0; i < this.numbers.length; i++) {
            numstr += this.getNumber(i).get() + '.';
        }
        return numstr + ' ';
    }
};


var SectionNumber = function() {
    this.sectionNumber = 0;
};

SectionNumber.prototype = {
    increment: function() {
        this.sectionNumber++;
    },
    get: function() {
        return this.sectionNumber;
    }
};



function walk(element, obj) {
    obj.startElement(element);
    var len = element.childNodes.length;
    for (var i = 0; i < len; i++) {
        walk(element.childNodes[i], obj);
    }
    obj.endElement(element);
}

window.addEventListener("load",
                        function () {
                            walk(document.body, new HeaderEnumerator());
                        },
                        false);

全然 JavaScript っぽくないような気がしますが…

感想

基本的な文法はだいたいわかった気がします.

あと,見やすいライブラリ リファレンスも手に入れました.

Core JavaScript 1.5 ガイド - MDN


JavaScript を面白いと感じるには,JavaScript っぽい書き方をしてこそだと思う (Python は内包表現が面白いよね) のだけど,JavaScript っぽく書けた気がしません...