summaryrefslogtreecommitdiffstats
path: root/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/react/js/app.jsx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xthird_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/react/js/app.jsx442
1 files changed, 442 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/react/js/app.jsx b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/react/js/app.jsx
new file mode 100755
index 0000000000..fd895e5105
--- /dev/null
+++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/react/js/app.jsx
@@ -0,0 +1,442 @@
+/*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};
+ },
+
+ /**
+ * This is a completely optional performance enhancement that you can
+ * implement on any React component. If you were to delete this method
+ * the app would still work correctly (and still be very performant!), we
+ * just use it as an example of how little code it takes to get an order
+ * of magnitude performance improvement.
+ */
+ shouldComponentUpdate: function (nextProps, nextState) {
+ return (
+ nextProps.todo !== this.props.todo ||
+ nextProps.editing !== this.props.editing ||
+ nextState.editText !== this.state.editText
+ );
+ },
+
+ /**
+ * 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
+ key={todo.id}
+ 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();
+})();