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

'use strict';

class Controller {
    /**
     * Take a model & view, then act as controller between them
     * @param  {object} model The model instance
     * @param  {object} view  The view instance
     */
    constructor(model, view) {
        this.model = model;
        this.view = view;

        this.view.bind('newTodo', title => this.addItem(title));
        this.view.bind('itemEdit', item => this.editItem(item.id));
        this.view.bind('itemEditDone', item => this.editItemSave(item.id, item.title));
        this.view.bind('itemEditCancel', item => this.editItemCancel(item.id));
        this.view.bind('itemRemove', item => this.removeItem(item.id));
        this.view.bind('itemToggle', item => this.toggleComplete(item.id, item.completed));
        this.view.bind('removeCompleted', () => this.removeCompletedItems());
        this.view.bind('toggleAll', status => this.toggleAll(status.completed));
    }

    /**
     * Load & Initialize the view
     * @param {string}  '' | 'active' | 'completed'
     */
    setView(hash){
        let route = hash.split('/')[1];
        let page = route || '';
        this._updateFilter(page);
    }

    /**
     * Event fires on load. Gets all items & displays them
     */
    showAll(){
        this.model.read(data => this.view.render('showEntries', data));
    }

    /**
     * Renders all active tasks
     */
    showActive(){
        this.model.read({completed: false}, data => this.view.render('showEntries', data));
    }

    /**
     * Renders all completed tasks
     */
    showCompleted(){
        this.model.read({completed: true}, data => this.view.render('showEntries', data));
    }

    /**
     * An event to fire whenever you want to add an item. Simply pass in the event
     * object and it'll handle the DOM insertion and saving of the new item.
     */
    addItem(title){
        if (title.trim() === '') {
            return;
        }

        this.model.create(title, () => {
            this.view.render('clearNewTodo');
            this._filter(true);
        });
    }

    /*
     * Triggers the item editing mode.
     */
    editItem(id){
        this.model.read(id, data => {
            let title = data[0].title;
            this.view.render('editItem', {id, title});
        });
    }

    /*
     * Finishes the item editing mode successfully.
     */
    editItemSave(id, title){
        title = title.trim();

        if (title.length !== 0) {
            this.model.update(id, {title}, () => {
                this.view.render('editItemDone', {id, title});
            });
        } else {
            this.removeItem(id);
        }
    }

    /*
     * Cancels the item editing mode.
     */
    editItemCancel(id){
        this.model.read(id, data => {
            let title = data[0].title;
            this.view.render('editItemDone', {id, title});
        });
    }

    /**
     * Find the DOM element with given ID,
     * Then remove it from DOM & Storage
     */
    removeItem(id){
        this.model.remove(id, () => this.view.render('removeItem', id));
        this._filter();
    }

    /**
     * Will remove all completed items from the DOM and storage.
     */
    removeCompletedItems(){
        this.model.read({completed: true}, data => {
            for (let item of data) {
                this.removeItem(item.id);
            }
        });

        this._filter();
    }

    /**
     * Give it an ID of a model and a checkbox and it will update the item
     * in storage based on the checkbox's state.
     *
     * @param {number} id The ID of the element to complete or uncomplete
     * @param {object} checkbox The checkbox to check the state of complete
     *                          or not
     * @param {boolean|undefined} silent Prevent re-filtering the todo items
     */
    toggleComplete(id, completed, silent){
        this.model.update(id, {completed}, () => {
            this.view.render('elementComplete', {id, completed});
        });

        if (!silent) {
            this._filter();
        }
    }

    /**
     * Will toggle ALL checkboxes' on/off state and completeness of models.
     * Just pass in the event object.
     */
    toggleAll(completed){
        this.model.read({completed: !completed}, data => {
            for (let item of data) {
                this.toggleComplete(item.id, completed, true);
            }
        });

        this._filter();
    }

    /**
     * Updates the pieces of the page which change depending on the remaining
     * number of todos.
     */
    _updateCount(){
        this.model.getCount(todos => {
            const completed = todos.completed;
            const visible = completed > 0;
            const checked = completed === todos.total;

            this.view.render('updateElementCount', todos.active);
            this.view.render('clearCompletedButton', {completed, visible});

            this.view.render('toggleAll', {checked});
            this.view.render('contentBlockVisibility', {visible: todos.total > 0});
        });
    }

    /**
     * Re-filters the todo items, based on the active route.
     * @param {boolean|undefined} force  forces a re-painting of todo items.
     */
    _filter(force){
        let active = this._activeRoute;
        const activeRoute = active.charAt(0).toUpperCase() + active.substr(1);

        // Update the elements on the page, which change with each completed todo
        this._updateCount();

        // If the last active route isn't "All", or we're switching routes, we
        // re-create the todo item elements, calling:
        //   this.show[All|Active|Completed]()
        if (force || this._lastActiveRoute !== 'All' || this._lastActiveRoute !== activeRoute) {
            this['show' + activeRoute]();
        }

        this._lastActiveRoute = activeRoute;
    }

    /**
     * Simply updates the filter nav's selected states
     */
    _updateFilter(currentPage){
        // Store a reference to the active route, allowing us to re-filter todo
        // items as they are marked complete or incomplete.
        this._activeRoute = currentPage;

        if (currentPage === '') {
            this._activeRoute = 'All';
        }

        this._filter();

        this.view.render('setFilter', currentPage);
    }
}