count up characters, words, lines at docs.google.com/document

  • /*
     * @title count up characters, words, lines at docs.google.com/document
     * @description count up characters, words, lines at docs.google.com/document for for http://q.hatena.ne.jp/1445242570
     * @include http://docs.google.com/document/*
     * @license MIT License
     */
    /*
    NOTE:
        ・単語のカウント
        単語は、空白 /\S/ で分割できるものとしてるので、ちょっと手抜き。
        日本語は一文で一単語とカウントされるし、ハイフン、コロンなどでつながってても一語とカウントしちゃう。
    
        ・キーイベント
        keyup や keydown が拾えないので、MutationObserver で監視してる。
        Text ノードの変更は type=childList で検知できる。
        Google の方が useCapture = true で listen しているのか?
        でも、あれは DOM tree の上下であって、同じターゲットには関係なさそうな...
        cancelBubble は、同じターゲットにも影響する?
        preventDefault は、同じターゲットにも影響する?
        よく分からない。
    
        ・文字修飾
        文字修飾をすると、その前後に Zero Width SPACE U+200B が挿入される。
        空白を削除する正規表現 /\s/gm では対象にされず、文字カウントの対象になっちゃう。
        正規表現 \s には \u200b が含まれるはずなのに。
        https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp#ref_equivalent_s
    
    */
    
    (function() {
        if (location.hostname != "docs.google.com") return;
    
        function countup_text(page) {
    
            var txt = page.textContent;
    
            function round_number(n) {
                return Math.round(n * 100) / 100;
            }
    
            var t, ret = []
    
            // count char
            t = txt.replace(/[\s\u200b]/gm, '');
            ret.n_char = t.length;
    
            // count line
            var lines = page.querySelectorAll("div.kix-paragraphrenderer");
            ret.n_line = lines.length;
    
            // count char / line
            ret.n_char_line = ret.n_line != 0 ? round_number(parseFloat(ret.n_char) / ret.n_line) : 0
    
            // count word
            t = txt.replace(/\S/gm, '|@|');
            t = t.replace(/\s+/gm, ' ');
            t = t.replace(/\|@\|/gm, '');
            ret.n_word = t.length;
    
            // count word / line
            ret.n_word_line = ret.n_line != 0 ? round_number(parseFloat(ret.n_word) / ret.n_line) : 0
    
            return ret;
        }
    
    
        function analyze_text(page) {
    
            var result = countup_text(page);
    
            var output = window.o1445242570;
            output.n_char     .innerHTML = result.n_char;
            output.n_char_line.innerHTML = result.n_char_line;
            output.n_line     .innerHTML = result.n_line;
            output.n_word     .innerHTML = result.n_word;
            output.n_word_line.innerHTML = result.n_word_line;
    
            console.log("------------------------------------------------------------------------");
            console.log("char:        " + result.n_char);
            console.log("char / line: " + result.n_char_line);
            console.log("line:        " + result.n_line);
            console.log("word:        " + result.n_word);
            console.log("word / line: " + result.n_word_line);
        }
    
        function initialize() {
            if (! window.o1445242570) {
                var copy_attr = function(dest, src) {
                    for (var i in src) {
                        dest[i] = src[i];
                    }
                }
    
                var output = [];
                window.o1445242570 = output;
    
                // MutationObserver
                var observeTarget = document.querySelector("div.kix-page-content-wrapper");
                var MutationObserver = window.MutationObserver || window.WebkitMutationObserver;
                new MutationObserver(function (records) {
                    records.forEach(function (record) {
                        var txt = observeTarget.textContent;
                        if (output.prev_text != txt) {
                            analyze_text(observeTarget);
                            output.prev_text = txt;
                        }
                    });
                }).observe(observeTarget, { childList: true, subtree: true, characterData: true });
    
                // Element
                var tbl = document.createElement("table");
                copy_attr(tbl.style, {
                    "borderSpacing" : 0,
                    "borderCollapse" : 'collapse',
                    "position" : 'fixed',
                    "top" : "120px",
                    "right" : "20px",
                    "backgroundColor" : "white",
                    "zIndex" : 9999,
                    });
    
                function create_row(tbl, label) {
                    var cell_style = {
                            "border" : "1px solid black",
                            "padding" : 3,
                        };
                    var tr, td;
                    tr = document.createElement("tr");
                    // label
                    td = document.createElement("td");
                    td.innerHTML = label;
                    copy_attr(td.style, cell_style);
                    tr.appendChild(td);
                    // value
                    td = document.createElement("td");
                    copy_attr(td.style, cell_style);
                    copy_attr(td.style, {
                        "textAlign" : "right",
                        "minWidth" : "3em",
                        });
                    tr.appendChild(td);
                    tbl.appendChild(tr);
                    return td;
                }
    
                output.n_char      = create_row(tbl, "文字数");                         // 文字数      
                output.n_char_line = create_row(tbl, "文字数/行");         // 文字数/行  
                output.n_line      = create_row(tbl, "行数");                                 // 行数        
                output.n_word      = create_row(tbl, "英単語数");                 // 英単語数    
                output.n_word_line = create_row(tbl, "英単語数/行"); // 英単語数/行
    
                document.body.appendChild(tbl);
            }
        }
    
    
        initialize();
    
        var e = document.querySelector("div.kix-page-content-wrapper");
        analyze_text(e);
        window.o1445242570.prev_text = e.textContent;
    
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2015/10/25 09:55:57 - 2015-10-25
  2. 2015/10/24 14:31:09 - 2015-10-24