Newer
Older
solitude / src / js / src / tracker / app.js
@sylow-castle sylow-castle on 24 Mar 2023 8 KB [add] domain layer begin to grow.
main(window);

function main(global) {
    const tracker = {};
    const domain = createDomain({},q("#record .col-memo").textContent);

    const app = tracker;
    global.addEventListener("load", initialize);
    global.addEventListener("keydown", checkShortKeys);

    return;

    function q(element, selector) {
        // 引数一つはdocumentを起点に検索する
        if (arguments.length <= 1)
            return q(global.document, element);

        /*
         * 任意のデータをクエリと解釈する。
         * 不適切なクエリの検索結果は空配列とした。
         * エラーのほうが適切なのかな……?
         */
        var result = []
        if (typeof selector == 'string') {
            result = element.querySelectorAll(selector);
            if(result.length == 1) {
                result = result[0];
            }
        }
        return  result;
    }

    function createDomain(domain, firstmemo) {
        domain.log = {
            start: startLog,
            end: endLog,
            getLatest: getLatestLog, 
            private: {
                latest: false,
                initialize: initializeLog,
            },
        };
        domain.latest= Date.now();
        domain.log.private.initialize(firstmemo);
        q("#record .col-memo").textContent
        return domain;

        /*
         * 関数宣言
         */

        // Log操作
        function initializeLog(memo) {
            const log = domain.log;
            if(log.private.latest) {
                return;
            }

            log.private.latest = {
                memo: memo,
                start: new Date(),
                end: "",
                interval: "",
            }
        }

        function getLatestLog() {            
            return domain.log.private.latest;
        }

        // 新しい記録を始める。古い記録は終了される。
        function startLog(memo, start) {
            const log = domain.log;

            const secondary = log.private.latest;
            // 実質Logオブジェクトを生成している箇所
            const primary = {
                memo: memo,
                start: start,
                end: "",
                interval: "",
            };

            secondary.end = start;
            // ここが開始-終了の引き算とラップタイムが合わない理由
            secondary.interval = Date.now() - domain.latest;
            domain.latest = Date.now();

            log.private.latest = primary;

            return new Promise((resolve, reject) => {
                resolve([primary,secondary]);
            });

            //return primary;

        }

        function endLog(end) {

        }
    }

    function initialize() {
        /*
         * アプリケーションオブジェクトの設定
         * 後でdefinePropertiesでゲッターを設定する
         */
        const ui = {
            now:  q("#now"),
            accesskey: document.forms.one_event.elements.accesskey,
            command:  document.forms.one_event.elements.command_line,
            memo:  document.forms.one_event.elements.event_memo,
            time:  document.forms.one_event.elements.event_time,
            addEvent: document.forms.one_event.elements.event_add,
            initRec: q("#initial_record"),
            get active() {
                return document.activeElement;
            },
            eventLogs: q("#event-logs"),
            tmpl: { 
                eventRec: q("#event_record"),
            }
        }

        const command = {
            readyCommand: readyCommand,
            readyMemo: readyMemo,
            addEvent: addEvent,
            getNow: getNow,
        }

        const data = {
            domain: domain,

            control: {
                get accesskey() {
                    return tracker.ui.accesskey.checked;
                }
            },
        }

        const key = {
            //特殊文字
            enter: "Enter",
            shift: "Shift",

            //通常文字
            c : "C",
        }
        Object.defineProperties(tracker, {
            ui: {value: ui},
            command: {value: command},
            key: {value: key},
            data: {value: data},
            domain:{value: domain},
        });
    
        // 初期行の変更
        const start = new Date();
        const time = start.toLocaleTimeString();
        q(tracker.ui.initRec, "td.col-position-start").textContent = time;

        // 現在時刻の初回呼び出し、後は自動で1秒ごとに更新。
        updateNow(1000);

        /*
         * コントローラの設定
         */
        tracker.ui.addEvent.addEventListener('click', (event) => {
            addEvent(tracker.ui.memo.value, tracker.ui.time.value);
        })

    }

    function addEvent(memo, datetime) {
        //ISOString形式の例: 14:02:12T00:12:34.000Z"
        const today = (new Date(Date.now())).toISOString().substr(0, 10);
        let finishTime = ""
        if(datetime) {
            finishTime = new Date(today + "T" + datetime);
        } else {
            finishTime = new Date();
        }

        const newMemo = memo.toString();
        domain.log.start(newMemo, finishTime)
            .then(value => {
                const first = value[0];
                const second = value[1];
                __addEvent(first, second);
            })

        function __addEvent(firstLog, secondLog) {
            const newTime = firstLog.start.toLocaleTimeString('ja-JP');
            const newMemo = firstLog.memo;

            const newEvent = q(tracker.ui.tmpl.eventRec.content.cloneNode(true), "tr");
            let tr = q(tracker.ui.eventLogs, "tbody tr ");

            q(newEvent, "td.col-position-start").textContent = newTime;
            q(newEvent, "td.col-memo").textContent = newMemo;
            if (tr instanceof Element) {  // i.e. 行数1行
                insertNewEvent(newEvent, tr, newTime);
            } else if (tr.length > 0) {  // i.e. 行数2行以上
                insertNewEvent(newEvent, tr[0], newTime);
            } else {  // i.e. 行数0行
                q(tracker.ui.eventLogs, "tbody").appendChild(newEvent);
            }

            function insertNewEvent(first, second, endTime) {
                // second行の開始列は作成時に入っている。
                const startTime = q(second, "td.col-position-start").textContent;
                q(second, "td.col-position-end").textContent = endTime;
                q(second, "td.col-interval").textContent = getInterval(startTime, endTime);

                q(tracker.ui.eventLogs, "tbody").insertBefore(first, second);
            }


            function getInterval(start, end) {
                const old = tracker.data.domain.latest
                tracker.data.domain.latest = Date.now();
                let interval = tracker.data.domain.latest - old;
                //秒単位にする。ミリ秒以下の数字を先に0にすることで割り切れるようにしてる。
                interval = (interval - (interval % 1000)) / 1000
                /* 
                const date = (new Date()).toLocaleDateString("ja-JP");
                const before = new Date(date.replaceAll('/', '-') + "T" + start);
                const after = new Date(date.replaceAll('/', '-') + "T" + end);
                // Dateの起算が1970/1/1/ 00:00:00 (UTC)のため、時刻として時間を求められる。
                const interval = before.getTime() - after.getTime();
                */
                return interval;
            }
        }
    }

    function getNow() {
        return tracker.ui.now.textContent.replace(/^.* /, "");
    }

    function readyCommand() {
        readyOf(tracker.ui.command);
    }

    function readyMemo() {
        readyOf(tracker.ui.memo);
    }

    function readyOf(ui) {
        if(tracker.ui.active == ui) {
            console.log("do nothing");
            return;
        }

        neutral();
        ui.focus();
    }

    function neutral() {
        for ( elem in tracker.ui) {
            if((elem instanceof Element) && (elem.hasFocus())) {
                elem.blur();
            }
        }            
    }

    function checkShortKeys(event) {
        if((!event.shiftKey) || (!tracker.data.control.accesskey)) {
            return;
        }
        const binds = tracker.key

        switch (event.key) {
            case binds.c:
                if( tracker.ui.command !== tracker.ui.active ) {
                    tracker.command.readyCommand();
                } else {
                    console.log("do nothing");
                }
                break;

            case binds.enter:
                tracker.command.readyMemo();
                break;

        }

    }


    function updateNow(interval) {
        const start = new Date();
        const time = start.toLocaleDateString() + " " + start.toLocaleTimeString();
        tracker.ui.now.textContent = time;

        setTimeout(updateNow, interval, interval);
    }

}