chromium/third_party/blink/perf_tests/speedometer20/resources/todomvc/vanilla-examples/es2015/src/view.js

'use strict';


// import {qs, qsa, $on, $parent, $delegate} from './helpers';

let _itemId = element => parseInt($parent(element, 'li').dataset.id, 10);

let _setFilter = currentPage => {
    qs('.filters .selected').className = '';
    qs(`.filters [href="#/${currentPage}"]`).className = 'selected';
};

let _elementComplete = (id, completed) => {
    let listItem = qs(`[data-id="${id}"]`);

    if (!listItem) {
        return;
    }

    listItem.className = completed ? 'completed' : '';

    // In case it was toggled from an event and not by clicking the checkbox
    qs('input', listItem).checked = completed;
};

let _editItem = (id, title) => {
    let listItem = qs(`[data-id="${id}"]`);

    if (!listItem) {
        return;
    }

    listItem.className += ' editing';

    let input = document.createElement('input');
    input.className = 'edit';

    listItem.appendChild(input);
    input.focus();
    input.value = title;
};

/**
 * View that abstracts away the browser's DOM completely.
 * It has two simple entry points:
 *
 *   - bind(eventName, handler)
 *     Takes a todo application event and registers the handler
 *   - render(command, parameterObject)
 *     Renders the given command with the options
 */
class View {
    constructor(template) {
        this.template = template;

        this.ENTER_KEY = 13;
        this.ESCAPE_KEY = 27;

        this.$todoList = qs('.todo-list');
        this.$todoItemCounter = qs('.todo-count');
        this.$clearCompleted = qs('.clear-completed');
        this.$main = qs('.main');
        this.$footer = qs('.footer');
        this.$toggleAll = qs('.toggle-all');
        this.$newTodo = qs('.new-todo');

        this.viewCommands = {
            showEntries: parameter => this.$todoList.innerHTML = this.template.show(parameter),
            removeItem: parameter => this._removeItem(parameter),
            updateElementCount: parameter => this.$todoItemCounter.innerHTML = this.template.itemCounter(parameter),
            clearCompletedButton: parameter => this._clearCompletedButton(parameter.completed, parameter.visible),
            contentBlockVisibility: parameter => this.$main.style.display = this.$footer.style.display = parameter.visible ? 'block' : 'none',
            toggleAll: parameter => this.$toggleAll.checked = parameter.checked,
            setFilter: parameter => _setFilter(parameter),
            clearNewTodo: parameter => this.$newTodo.value = '',
            elementComplete: parameter => _elementComplete(parameter.id, parameter.completed),
            editItem: parameter => _editItem(parameter.id, parameter.title),
            editItemDone: parameter => this._editItemDone(parameter.id, parameter.title),
        };
    }

    _removeItem(id) {
        let elem = qs(`[data-id="${id}"]`);

        if (elem) {
            this.$todoList.removeChild(elem);
        }
    }

    _clearCompletedButton(completedCount, visible) {
        this.$clearCompleted.innerHTML = this.template.clearCompletedButton(completedCount);
        this.$clearCompleted.style.display = visible ? 'block' : 'none';
    }

    _editItemDone(id, title) {
        let listItem = qs(`[data-id="${id}"]`);

        if (!listItem) {
            return;
        }

        let input = qs('input.edit', listItem);
        listItem.removeChild(input);

        listItem.className = listItem.className.replace(' editing', '');

        qsa('label', listItem).forEach(label => label.textContent = title);
    }

    render(viewCmd, parameter) {
        this.viewCommands[viewCmd](parameter);
    }

    _bindItemEditDone(handler) {
        let self = this;

        $delegate(self.$todoList, 'li .edit', 'blur', function () {
            if (!this.dataset.iscanceled) {
                handler({
                    id: _itemId(this),
                    title: this.value
                });
            }
        });

        // Remove the cursor from the input when you hit enter just like if it were a real form
        $delegate(self.$todoList, 'li .edit', 'keypress', function (event) {
            if (event.keyCode === self.ENTER_KEY) {
                this.blur();
            }
        });
    }

    _bindItemEditCancel(handler) {
        let self = this;

        $delegate(self.$todoList, 'li .edit', 'keyup', function (event) {
            if (event.keyCode === self.ESCAPE_KEY) {
                let id = _itemId(this);
                this.dataset.iscanceled = true;
                this.blur();

                handler({ id });
            }
        });
    }

    bind(event, handler) {
        if (event === 'newTodo') {
            $on(this.$newTodo, 'change', () => handler(this.$newTodo.value));
        } else if (event === 'removeCompleted') {
            $on(this.$clearCompleted, 'click', handler);
        } else if (event === 'toggleAll') {
            $on(this.$toggleAll, 'click', function(){
                handler({completed: this.checked});
            });
        } else if (event === 'itemEdit') {
            $delegate(this.$todoList, 'li label', 'dblclick', function(){
                handler({id: _itemId(this)});
            });
        } else if (event === 'itemRemove') {
            $delegate(this.$todoList, '.destroy', 'click', function(){
                handler({id: _itemId(this)});
            });
        } else if (event === 'itemToggle') {
            $delegate(this.$todoList, '.toggle', 'click', function(){
                handler({
                    id: _itemId(this),
                    completed: this.checked
                });
            });
        } else if (event === 'itemEditDone') {
            this._bindItemEditDone(handler);
        } else if (event === 'itemEditCancel') {
            this._bindItemEditCancel(handler);
        }
    }
}