var mutate = require('xtend/mutable') var expose = require('choo-expose') var css = require('sheetify') var html = require('bel') var choo = require('../') css('todomvc-common/base.css') css('todomvc-app-css/index.css') // we export this so tests can run if (module.parent) { exports.todoStore = todoStore } else { var app = choo() if (process.env.NODE_ENV !== 'production') { var persist = require('choo-persist') var logger = require('choo-log') app.use(persist()) app.use(logger()) } app.use(expose()) app.use(todoStore) app.route('/', mainView) app.route('#active', mainView) app.route('#completed', mainView) app.mount('body') } function mainView (state, emit) { emit('log:debug', 'Rendering main view') return html`
${Header(state, emit)} ${TodoList(state, emit)} ${Footer(state, emit)}
` } function todoStore (state, emitter) { if (!state.todos) { state.todos = {} state.todos.active = [] state.todos.done = [] state.todos.all = [] state.todos.input = '' state.todos.idCounter = 0 } emitter.on('DOMContentLoaded', function () { emitter.emit('log:debug', 'Loading todos model') // CRUD emitter.on('todos:create', create) emitter.on('todos:update', update) emitter.on('todos:delete', del) // Special emitter.on('todos:input', oninput) // Shorthand emitter.on('todos:edit', edit) emitter.on('todos:unedit', unedit) emitter.on('todos:toggle', toggle) emitter.on('todos:toggleAll', toggleAll) emitter.on('todos:deleteCompleted', deleteCompleted) }) function oninput (text) { state.todos.input = text } function create (name) { var item = { id: state.todos.idCounter, editing: false, done: false, name: name } state.todos.idCounter += 1 state.todos.active.push(item) state.todos.all.push(item) emitter.emit('render') } function edit (id) { state.todos.all.forEach(function (todo) { if (todo.id === id) todo.editing = true }) emitter.emit('render') } function unedit (id) { state.todos.all.forEach(function (todo) { if (todo.id === id) todo.editing = false }) emitter.emit('render') } function update (newTodo) { var todo = state.todos.all.filter(function (todo) { return todo.id === newTodo.id })[0] if (newTodo.done && todo.done === false) { state.todos.active.splice(state.todos.active.indexOf(todo), 1) state.todos.done.push(todo) } else if (newTodo.done === false && todo.done) { state.todos.done.splice(state.todos.done.indexOf(todo), 1) state.todos.active.push(todo) } mutate(todo, newTodo) emitter.emit('render') } function del (id) { var i = null var todo = null state.todos.all.forEach(function (_todo, j) { if (_todo.id === id) { i = j todo = _todo } }) state.todos.all.splice(i, 1) if (todo.done) { var done = state.todos.done var doneIndex done.forEach(function (_todo, j) { if (_todo.id === id) { doneIndex = j } }) done.splice(doneIndex, 1) } else { var active = state.todos.active var activeIndex active.forEach(function (_todo, j) { if (_todo.id === id) { activeIndex = j } }) active.splice(activeIndex, 1) } emitter.emit('render') } function deleteCompleted (data) { var done = state.todos.done done.forEach(function (todo) { var index = state.todos.all.indexOf(todo) state.todos.all.splice(index, 1) }) state.todos.done = [] emitter.emit('render') } function toggle (id) { var todo = state.todos.all.filter(function (todo) { return todo.id === id })[0] var done = todo.done todo.done = !done var arr = done ? state.todos.done : state.todos.active var target = done ? state.todos.active : state.todos.done var index = arr.indexOf(todo) arr.splice(index, 1) target.push(todo) emitter.emit('render') } function toggleAll (data) { var todos = state.todos.all var allDone = state.todos.all.length && state.todos.done.length === state.todos.all.length todos.forEach(function (todo) { todo.done = !allDone }) if (allDone) { state.todos.done = [] state.todos.active = state.todos.all } else { state.todos.done = state.todos.all state.todos.active = [] } emitter.emit('render') } } function Footer (state, emit) { var filter = window.location.hash.replace(/^#/, '') var activeCount = state.todos.active.length var hasDone = state.todos.done.length return html` ` function filterButton (name, filter, currentFilter, emit) { var filterClass = filter === currentFilter ? 'selected' : '' var uri = '#' + name.toLowerCase() if (uri === '#all') uri = '/' return html`
  • ${name}
  • ` } function deleteCompleted (emit) { return html` ` function deleteAllCompleted () { emit('todos:deleteCompleted') } } } function Header (state, emit) { return html`

    todos

    ` function createTodo (e) { var value = e.target.value if (!value) return if (e.keyCode === 13) { emit('todos:input', '') emit('todos:create', value) } else { emit('todos:input', value) } } } function TodoItem (todo, emit) { return html`
  • ` function toggle (e) { emit('todos:toggle', todo.id) } function edit (e) { emit('todos:edit', todo.id) } function destroy (e) { emit('todos:delete', todo.id) } function update (e) { emit('todos:update', { id: todo.id, editing: false, name: e.target.value }) } function handleEditKeydown (e) { if (e.keyCode === 13) update(e) // Enter else if (e.code === 27) emit('todos:unedit') // Escape } function classList (classes) { var str = '' var keys = Object.keys(classes) for (var i = 0, len = keys.length; i < len; i++) { var key = keys[i] var val = classes[key] if (val) str += (key + ' ') } return str } } function TodoList (state, emit) { var filter = window.location.hash.replace(/^#/, '') var items = filter === 'completed' ? state.todos.done : filter === 'active' ? state.todos.active : state.todos.all var allDone = state.todos.done.length === state.todos.all.length var nodes = items.map(function (todo) { return TodoItem(todo, emit) }) return html`
    ` function toggleAll () { emit('todos:toggleAll') } }