Add todoapp benchmark
This commit is contained in:
parent
479db8c2cc
commit
02e7b40c07
218 changed files with 63168 additions and 0 deletions
426
bench/implementations/react-15.3.1/js/app.jsx
Executable file
426
bench/implementations/react-15.3.1/js/app.jsx
Executable file
|
|
@ -0,0 +1,426 @@
|
|||
/*jshint quotmark:false */
|
||||
/*jshint white:false */
|
||||
/*jshint trailing:false */
|
||||
/*jshint newcap:false */
|
||||
/*global React, Router*/
|
||||
var app = app || {};
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
app.ALL_TODOS = 'all';
|
||||
app.ACTIVE_TODOS = 'active';
|
||||
app.COMPLETED_TODOS = 'completed';
|
||||
|
||||
app.Utils = {
|
||||
uuid: function () {
|
||||
/*jshint bitwise:false */
|
||||
var i, random;
|
||||
var uuid = '';
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
random = Math.random() * 16 | 0;
|
||||
if (i === 8 || i === 12 || i === 16 || i === 20) {
|
||||
uuid += '-';
|
||||
}
|
||||
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
|
||||
.toString(16);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
},
|
||||
|
||||
pluralize: function (count, word) {
|
||||
return count === 1 ? word : word + 's';
|
||||
},
|
||||
|
||||
extend: function () {
|
||||
var newObj = {};
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var obj = arguments[i];
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
};
|
||||
|
||||
var Utils = app.Utils;
|
||||
// Generic "model" object. You can use whatever
|
||||
// framework you want. For this application it
|
||||
// may not even be worth separating this logic
|
||||
// out, but we do this to demonstrate one way to
|
||||
// separate out parts of your application.
|
||||
app.TodoModel = function (key) {
|
||||
this.key = key;
|
||||
this.todos = [];
|
||||
this.onChanges = [];
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.subscribe = function (onChange) {
|
||||
this.onChanges.push(onChange);
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.inform = function () {
|
||||
this.onChanges.forEach(function (cb) { cb(); });
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.addTodo = function (title) {
|
||||
this.todos = this.todos.concat({
|
||||
id: Utils.uuid(),
|
||||
title: title,
|
||||
completed: false
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.toggleAll = function (checked) {
|
||||
// Note: it's usually better to use immutable data structures since they're
|
||||
// easier to reason about and React works very well with them. That's why
|
||||
// we use map() and filter() everywhere instead of mutating the array or
|
||||
// todo items themselves.
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return Utils.extend({}, todo, {completed: checked});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.toggle = function (todoToToggle) {
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return todo !== todoToToggle ?
|
||||
todo :
|
||||
Utils.extend({}, todo, {completed: !todo.completed});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.destroy = function (todo) {
|
||||
this.todos = this.todos.filter(function (candidate) {
|
||||
return candidate !== todo;
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.save = function (todoToSave, text) {
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.clearCompleted = function () {
|
||||
this.todos = this.todos.filter(function (todo) {
|
||||
return !todo.completed;
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
|
||||
var TodoFooter = React.createClass({
|
||||
render: function () {
|
||||
var activeTodoWord = app.Utils.pluralize(this.props.count, 'item');
|
||||
var clearButton = null;
|
||||
|
||||
if (this.props.completedCount > 0) {
|
||||
clearButton = (
|
||||
<button
|
||||
className="clear-completed"
|
||||
onClick={this.props.onClearCompleted}>
|
||||
Clear completed
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
var nowShowing = this.props.nowShowing;
|
||||
return (
|
||||
<footer className="footer">
|
||||
<span className="todo-count">
|
||||
<strong>{this.props.count}</strong> {activeTodoWord} left
|
||||
</span>
|
||||
<ul className="filters">
|
||||
<li>
|
||||
<a
|
||||
href="#/"
|
||||
className={classNames({selected: nowShowing === app.ALL_TODOS})}>
|
||||
All
|
||||
</a>
|
||||
</li>
|
||||
{' '}
|
||||
<li>
|
||||
<a
|
||||
href="#/active"
|
||||
className={classNames({selected: nowShowing === app.ACTIVE_TODOS})}>
|
||||
Active
|
||||
</a>
|
||||
</li>
|
||||
{' '}
|
||||
<li>
|
||||
<a
|
||||
href="#/completed"
|
||||
className={classNames({selected: nowShowing === app.COMPLETED_TODOS})}>
|
||||
Completed
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{clearButton}
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ESCAPE_KEY = 27;
|
||||
var ENTER_KEY = 13;
|
||||
|
||||
var TodoItem = React.createClass({
|
||||
handleSubmit: function (event) {
|
||||
var val = this.state.editText.trim();
|
||||
if (val) {
|
||||
this.props.onSave(val);
|
||||
this.setState({editText: val});
|
||||
} else {
|
||||
this.props.onDestroy();
|
||||
}
|
||||
},
|
||||
|
||||
handleEdit: function () {
|
||||
this.props.onEdit();
|
||||
this.setState({editText: this.props.todo.title});
|
||||
},
|
||||
|
||||
handleKeyDown: function (event) {
|
||||
if (event.which === ESCAPE_KEY) {
|
||||
this.setState({editText: this.props.todo.title});
|
||||
this.props.onCancel(event);
|
||||
} else if (event.which === ENTER_KEY) {
|
||||
this.handleSubmit(event);
|
||||
}
|
||||
},
|
||||
|
||||
handleChange: function (event) {
|
||||
if (this.props.editing) {
|
||||
this.setState({editText: event.target.value});
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {editText: this.props.todo.title};
|
||||
},
|
||||
|
||||
/**
|
||||
* Safely manipulate the DOM after updating the state when invoking
|
||||
* `this.props.onEdit()` in the `handleEdit` method above.
|
||||
* For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate
|
||||
* and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate
|
||||
*/
|
||||
componentDidUpdate: function (prevProps) {
|
||||
if (!prevProps.editing && this.props.editing) {
|
||||
var node = React.findDOMNode(this.refs.editField);
|
||||
node.focus();
|
||||
node.setSelectionRange(node.value.length, node.value.length);
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return (
|
||||
<li className={classNames({
|
||||
completed: this.props.todo.completed,
|
||||
editing: this.props.editing
|
||||
})}>
|
||||
<div className="view">
|
||||
<input
|
||||
className="toggle"
|
||||
type="checkbox"
|
||||
checked={this.props.todo.completed}
|
||||
onChange={this.props.onToggle}
|
||||
/>
|
||||
<label onDoubleClick={this.handleEdit}>
|
||||
{this.props.todo.title}
|
||||
</label>
|
||||
<button className="destroy" onClick={this.props.onDestroy} />
|
||||
</div>
|
||||
<input
|
||||
ref="editField"
|
||||
className="edit"
|
||||
value={this.state.editText}
|
||||
onBlur={this.handleSubmit}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ENTER_KEY = 13;
|
||||
|
||||
var TodoApp = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
nowShowing: app.ALL_TODOS,
|
||||
editing: null,
|
||||
newTodo: ''
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
var setState = this.setState;
|
||||
var router = Router({
|
||||
'/': setState.bind(this, {nowShowing: app.ALL_TODOS}),
|
||||
'/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}),
|
||||
'/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS})
|
||||
});
|
||||
router.init('/');
|
||||
},
|
||||
|
||||
handleChange: function (event) {
|
||||
this.setState({newTodo: event.target.value});
|
||||
},
|
||||
|
||||
handleNewTodoKeyDown: function (event) {
|
||||
if (event.keyCode !== ENTER_KEY) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var val = this.state.newTodo.trim();
|
||||
|
||||
if (val) {
|
||||
this.props.model.addTodo(val);
|
||||
this.setState({newTodo: ''});
|
||||
}
|
||||
},
|
||||
|
||||
toggleAll: function (event) {
|
||||
var checked = event.target.checked;
|
||||
this.props.model.toggleAll(checked);
|
||||
},
|
||||
|
||||
toggle: function (todoToToggle) {
|
||||
this.props.model.toggle(todoToToggle);
|
||||
},
|
||||
|
||||
destroy: function (todo) {
|
||||
this.props.model.destroy(todo);
|
||||
},
|
||||
|
||||
edit: function (todo) {
|
||||
this.setState({editing: todo.id});
|
||||
},
|
||||
|
||||
save: function (todoToSave, text) {
|
||||
this.props.model.save(todoToSave, text);
|
||||
this.setState({editing: null});
|
||||
},
|
||||
|
||||
cancel: function () {
|
||||
this.setState({editing: null});
|
||||
},
|
||||
|
||||
clearCompleted: function () {
|
||||
this.props.model.clearCompleted();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var footer;
|
||||
var main;
|
||||
var todos = this.props.model.todos;
|
||||
|
||||
var shownTodos = todos.filter(function (todo) {
|
||||
switch (this.state.nowShowing) {
|
||||
case app.ACTIVE_TODOS:
|
||||
return !todo.completed;
|
||||
case app.COMPLETED_TODOS:
|
||||
return todo.completed;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
var todoItems = shownTodos.map(function (todo) {
|
||||
return (
|
||||
<TodoItem
|
||||
todo={todo}
|
||||
onToggle={this.toggle.bind(this, todo)}
|
||||
onDestroy={this.destroy.bind(this, todo)}
|
||||
onEdit={this.edit.bind(this, todo)}
|
||||
editing={this.state.editing === todo.id}
|
||||
onSave={this.save.bind(this, todo)}
|
||||
onCancel={this.cancel}
|
||||
/>
|
||||
);
|
||||
}, this);
|
||||
|
||||
var activeTodoCount = todos.reduce(function (accum, todo) {
|
||||
return todo.completed ? accum : accum + 1;
|
||||
}, 0);
|
||||
|
||||
var completedCount = todos.length - activeTodoCount;
|
||||
|
||||
if (activeTodoCount || completedCount) {
|
||||
footer =
|
||||
<TodoFooter
|
||||
count={activeTodoCount}
|
||||
completedCount={completedCount}
|
||||
nowShowing={this.state.nowShowing}
|
||||
onClearCompleted={this.clearCompleted}
|
||||
/>;
|
||||
}
|
||||
|
||||
if (todos.length) {
|
||||
main = (
|
||||
<section className="main">
|
||||
<input
|
||||
className="toggle-all"
|
||||
type="checkbox"
|
||||
onChange={this.toggleAll}
|
||||
checked={activeTodoCount === 0}
|
||||
/>
|
||||
<ul className="todo-list">
|
||||
{todoItems}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<header className="header">
|
||||
<h1>todos</h1>
|
||||
<input
|
||||
className="new-todo"
|
||||
placeholder="What needs to be done?"
|
||||
value={this.state.newTodo}
|
||||
onKeyDown={this.handleNewTodoKeyDown}
|
||||
onChange={this.handleChange}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</header>
|
||||
{main}
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var model = new app.TodoModel('react-todos');
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<TodoApp model={model}/>,
|
||||
document.getElementsByClassName('todoapp')[0]
|
||||
);
|
||||
}
|
||||
|
||||
model.subscribe(render);
|
||||
render();
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue