diff --git a/perf/todomvc-benchmark/results.json b/perf/todomvc-benchmark/results.json index d52ec17929..0d5e6090bf 100644 --- a/perf/todomvc-benchmark/results.json +++ b/perf/todomvc-benchmark/results.json @@ -6,40 +6,40 @@ "Chrome": { "version": 32, "data": { - "Vue": 513.4351000189781, - "Backbone": 905.215000017779, - "Knockout": 479.85350001836196, - "Ember": 2041.034799994668, - "Angular": 2284.198399982415, - "React": 2159.7387000045273, - "Om": 531.0522000072524, - "Ractive": 1299.3730999995023 + "Vue": 517.1309999423102, + "Backbone": 766.0240000113845, + "Knockout": 466.5719998884015, + "Ember": 1470.551999984309, + "Angular": 1421.3290000916459, + "React": 1366.7290000012144, + "Om": 422.92199993971735, + "Ractive": 1009.0529999579303 } }, "Firefox": { "version": 26, "data": { - "Vue": 563.2124097999965, - "Backbone": 804.2158809000063, - "Knockout": 498.9543262000112, - "Ember": 2303.997436800003, - "Angular": 2189.604799899995, - "React": 1334.863771099995, - "Om": 576.9800275999982, - "Ractive": 1776.5675642999904 + "Vue": 604.8002719999986, + "Backbone": 1057.144711, + "Knockout": 560.318631000001, + "Ember": 2023.1656529999982, + "Angular": 1153.135662000006, + "React": 1062.1933359999966, + "Om": 520.8095430000012, + "Ractive": 1845.2588180000002 } }, "Safari": { "version": 7, "data": { - "Vue": 263.5, - "Backbone": 906.3, - "Knockout": 544.5, - "Ember": 1576.1, - "Angular": 1956, - "React": 1022.5, - "Om": 545.5, - "Ractive": 1115.6 + "Vue": 351, + "Backbone": 894, + "Knockout": 666, + "Ember": 1933, + "Angular": 3095, + "React": 1308, + "Om": 648, + "Ractive": 2557 } } }, diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/index.html b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/index.html index 65384148fe..7551380601 100644 --- a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/index.html +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/index.html @@ -11,13 +11,14 @@
- - - + + + + - - - - + + + + diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/0319583b4090f6e45363b5ea23e2253c547195d3.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/0319583b4090f6e45363b5ea23e2253c547195d3.js new file mode 100644 index 0000000000..4002a1d7c1 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/0319583b4090f6e45363b5ea23e2253c547195d3.js @@ -0,0 +1,212 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark:false */ +/*jshint white:false */ +/*jshint trailing:false */ +/*jshint newcap:false */ +/*global Utils, ALL_TODOS, ACTIVE_TODOS, COMPLETED_TODOS, + TodoItem, TodoFooter, React, Router*/ + +(function (window, React) { + 'use strict'; + + window.ALL_TODOS = 'all'; + window.ACTIVE_TODOS = 'active'; + window.COMPLETED_TODOS = 'completed'; + + var ENTER_KEY = 13; + + var TodoApp = React.createClass({displayName: 'TodoApp', + getInitialState: function () { + var todos = Utils.store('react-todos'); + return { + todos: todos, + nowShowing: ALL_TODOS, + editing: null + }; + }, + + componentDidMount: function () { + var router = Router({ + '/': this.setState.bind(this, {nowShowing: ALL_TODOS}), + '/active': this.setState.bind(this, {nowShowing: ACTIVE_TODOS}), + '/completed': this.setState.bind(this, {nowShowing: COMPLETED_TODOS}) + }); + router.init(); + this.refs.newField.getDOMNode().focus(); + }, + + handleNewTodoKeyDown: function (event) { + if (event.which !== ENTER_KEY) { + return; + } + + var val = this.refs.newField.getDOMNode().value.trim(); + var newTodo; + + if (val) { + newTodo = { + id: Utils.uuid(), + title: val, + completed: false + }; + this.setState({todos: this.state.todos.concat([newTodo])}); + this.refs.newField.getDOMNode().value = ''; + } + + return false; + }, + + toggleAll: function (event) { + var checked = event.target.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. + var newTodos = this.state.todos.map(function (todo) { + return Utils.extend({}, todo, {completed: checked}); + }); + + this.setState({todos: newTodos}); + }, + + toggle: function (todoToToggle) { + var newTodos = this.state.todos.map(function (todo) { + return todo !== todoToToggle ? todo : Utils.extend({}, todo, {completed: !todo.completed}); + }); + + this.setState({todos: newTodos}); + }, + + destroy: function (todo) { + var newTodos = this.state.todos.filter(function (candidate) { + return candidate.id !== todo.id; + }); + + this.setState({todos: newTodos}); + }, + + edit: function (todo, callback) { + // refer to todoItem.js `handleEdit` for the reasoning behind the + // callback + this.setState({editing: todo.id}, function () { + callback(); + }); + }, + + save: function (todoToSave, text) { + var newTodos = this.state.todos.map(function (todo) { + return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text}); + }); + + this.setState({todos: newTodos, editing: null}); + }, + + cancel: function () { + this.setState({editing: null}); + }, + + clearCompleted: function () { + var newTodos = this.state.todos.filter(function (todo) { + return !todo.completed; + }); + + this.setState({todos: newTodos}); + }, + + componentDidUpdate: function () { + Utils.store('react-todos', this.state.todos); + }, + + render: function () { + var footer = null; + var main = null; + + var shownTodos = this.state.todos.filter(function (todo) { + switch (this.state.nowShowing) { + case ACTIVE_TODOS: + return !todo.completed; + case 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 = this.state.todos.reduce(function(accum, todo) { + return todo.completed ? accum : accum + 1; + }, 0); + + var completedCount = this.state.todos.length - activeTodoCount; + + if (activeTodoCount || completedCount) { + footer = + TodoFooter( + {count:activeTodoCount, + completedCount:completedCount, + nowShowing:this.state.nowShowing, + onClearCompleted:this.clearCompleted} + ); + } + + if (this.state.todos.length) { + main = ( + React.DOM.section( {id:"main"}, + React.DOM.input( + {id:"toggle-all", + type:"checkbox", + onChange:this.toggleAll, + checked:activeTodoCount === 0} + ), + React.DOM.ul( {id:"todo-list"}, + todoItems + ) + ) + ); + } + + return ( + React.DOM.div(null, + React.DOM.header( {id:"header"}, + React.DOM.h1(null, "todos"), + React.DOM.input( + {ref:"newField", + id:"new-todo", + placeholder:"What needs to be done?", + onKeyDown:this.handleNewTodoKeyDown} + ) + ), + main, + footer + ) + ); + } + }); + + React.renderComponent(TodoApp(null ), document.getElementById('todoapp')); + React.renderComponent( + React.DOM.div(null, + React.DOM.p(null, "Double-click to edit a todo"), + React.DOM.p(null, "Created by",' ', + React.DOM.a( {href:"http://github.com/petehunt/"}, "petehunt") + ), + React.DOM.p(null, "Part of",' ',React.DOM.a( {href:"http://todomvc.com"}, "TodoMVC")) + ), + document.getElementById('info')); +})(window, React); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/utils.jsx b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/3ab25da3ba5ea1e3e4727e8fe70bbd6e784d428c.js similarity index 100% rename from perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/utils.jsx rename to perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/3ab25da3ba5ea1e3e4727e8fe70bbd6e784d428c.js diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/7914a33ab617df6f400f1745b753613631dddf23.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/7914a33ab617df6f400f1745b753613631dddf23.js new file mode 100644 index 0000000000..62ddc95949 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/7914a33ab617df6f400f1745b753613631dddf23.js @@ -0,0 +1,102 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark: false */ +/*jshint white: false */ +/*jshint trailing: false */ +/*jshint newcap: false */ +/*global React, Utils */ +(function (window) { + 'use strict'; + + var ESCAPE_KEY = 27; + var ENTER_KEY = 13; + + window.TodoItem = React.createClass({displayName: 'TodoItem', + handleSubmit: function () { + var val = this.state.editText.trim(); + if (val) { + this.props.onSave(val); + this.setState({editText: val}); + } else { + this.props.onDestroy(); + } + return false; + }, + + handleEdit: function () { + // react optimizes renders by batching them. This means you can't call + // parent's `onEdit` (which in this case triggeres a re-render), and + // immediately manipulate the DOM as if the rendering's over. Put it as a + // callback. Refer to app.js' `edit` method + this.props.onEdit(function () { + var node = this.refs.editField.getDOMNode(); + node.focus(); + node.setSelectionRange(node.value.length, node.value.length); + }.bind(this)); + this.setState({editText: this.props.todo.title}); + }, + + handleKeyDown: function (event) { + if (event.keyCode === ESCAPE_KEY) { + this.setState({editText: this.props.todo.title}); + this.props.onCancel(); + } else if (event.keyCode === ENTER_KEY) { + this.handleSubmit(); + } + }, + + handleChange: function (event) { + 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.id !== this.props.todo.id || + nextProps.todo !== this.props.todo || + nextProps.editing !== this.props.editing || + nextState.editText !== this.state.editText + ); + }, + + render: function () { + return ( + React.DOM.li( {className:React.addons.classSet({ + completed: this.props.todo.completed, + editing: this.props.editing + })}, + React.DOM.div( {className:"view"}, + React.DOM.input( + {className:"toggle", + type:"checkbox", + checked:this.props.todo.completed ? 'checked' : null, + onChange:this.props.onToggle} + ), + React.DOM.label( {onDoubleClick:this.handleEdit}, + this.props.todo.title + ), + React.DOM.button( {className:"destroy", onClick:this.props.onDestroy} ) + ), + React.DOM.input( + {ref:"editField", + className:"edit", + value:this.state.editText, + onBlur:this.handleSubmit, + onChange:this.handleChange, + onKeyDown:this.handleKeyDown} + ) + ) + ); + } + }); +})(window); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.js new file mode 100644 index 0000000000..5ed8ae7ae6 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.js @@ -0,0 +1,58 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark:false */ +/*jshint white:false */ +/*jshint trailing:false */ +/*jshint newcap:false */ +/*global React, ALL_TODOS, ACTIVE_TODOS, Utils, COMPLETED_TODOS */ +(function (window) { + 'use strict'; + + window.TodoFooter = React.createClass({displayName: 'TodoFooter', + render: function () { + var activeTodoWord = Utils.pluralize(this.props.count, 'item'); + var clearButton = null; + + if (this.props.completedCount > 0) { + clearButton = ( + React.DOM.button( + {id:"clear-completed", + onClick:this.props.onClearCompleted}, + '',"Clear completed (",this.props.completedCount,")",'' + ) + ); + } + + var show = { + ALL_TODOS: '', + ACTIVE_TODOS: '', + COMPLETED_TODOS: '' + }; + show[this.props.nowShowing] = 'selected'; + + return ( + React.DOM.footer( {id:"footer"}, + React.DOM.span( {id:"todo-count"}, + React.DOM.strong(null, this.props.count), + ' ',activeTodoWord,' ',"left",'' + ), + React.DOM.ul( {id:"filters"}, + React.DOM.li(null, + React.DOM.a( {href:"#/", className:show[ALL_TODOS]}, "All") + ), + ' ', + React.DOM.li(null, + React.DOM.a( {href:"#/active", className:show[ACTIVE_TODOS]}, "Active") + ), + ' ', + React.DOM.li(null, + React.DOM.a( {href:"#/completed", className:show[COMPLETED_TODOS]}, "Completed") + ) + ), + clearButton + ) + ); + } + }); +})(window); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/0319583b4090f6e45363b5ea23e2253c547195d3.json b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/0319583b4090f6e45363b5ea23e2253c547195d3.json new file mode 100644 index 0000000000..9d070cbd80 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/0319583b4090f6e45363b5ea23e2253c547195d3.json @@ -0,0 +1 @@ +{".js":"0319583b4090f6e45363b5ea23e2253c547195d3.js"} \ No newline at end of file diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/3ab25da3ba5ea1e3e4727e8fe70bbd6e784d428c.json b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/3ab25da3ba5ea1e3e4727e8fe70bbd6e784d428c.json new file mode 100644 index 0000000000..911ddb3dbc --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/3ab25da3ba5ea1e3e4727e8fe70bbd6e784d428c.json @@ -0,0 +1 @@ +{".js":"3ab25da3ba5ea1e3e4727e8fe70bbd6e784d428c.js"} \ No newline at end of file diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/7914a33ab617df6f400f1745b753613631dddf23.json b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/7914a33ab617df6f400f1745b753613631dddf23.json new file mode 100644 index 0000000000..a2c348c8fb --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/7914a33ab617df6f400f1745b753613631dddf23.json @@ -0,0 +1 @@ +{".js":"7914a33ab617df6f400f1745b753613631dddf23.js"} \ No newline at end of file diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.json b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.json new file mode 100644 index 0000000000..c8e3427e9b --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/.module-cache/manifest/8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.json @@ -0,0 +1 @@ +{".js":"8b48ef8f1783d4b70c3ffb3354b5161a1e960e8c.js"} \ No newline at end of file diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/app.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/app.js new file mode 100644 index 0000000000..4002a1d7c1 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/app.js @@ -0,0 +1,212 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark:false */ +/*jshint white:false */ +/*jshint trailing:false */ +/*jshint newcap:false */ +/*global Utils, ALL_TODOS, ACTIVE_TODOS, COMPLETED_TODOS, + TodoItem, TodoFooter, React, Router*/ + +(function (window, React) { + 'use strict'; + + window.ALL_TODOS = 'all'; + window.ACTIVE_TODOS = 'active'; + window.COMPLETED_TODOS = 'completed'; + + var ENTER_KEY = 13; + + var TodoApp = React.createClass({displayName: 'TodoApp', + getInitialState: function () { + var todos = Utils.store('react-todos'); + return { + todos: todos, + nowShowing: ALL_TODOS, + editing: null + }; + }, + + componentDidMount: function () { + var router = Router({ + '/': this.setState.bind(this, {nowShowing: ALL_TODOS}), + '/active': this.setState.bind(this, {nowShowing: ACTIVE_TODOS}), + '/completed': this.setState.bind(this, {nowShowing: COMPLETED_TODOS}) + }); + router.init(); + this.refs.newField.getDOMNode().focus(); + }, + + handleNewTodoKeyDown: function (event) { + if (event.which !== ENTER_KEY) { + return; + } + + var val = this.refs.newField.getDOMNode().value.trim(); + var newTodo; + + if (val) { + newTodo = { + id: Utils.uuid(), + title: val, + completed: false + }; + this.setState({todos: this.state.todos.concat([newTodo])}); + this.refs.newField.getDOMNode().value = ''; + } + + return false; + }, + + toggleAll: function (event) { + var checked = event.target.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. + var newTodos = this.state.todos.map(function (todo) { + return Utils.extend({}, todo, {completed: checked}); + }); + + this.setState({todos: newTodos}); + }, + + toggle: function (todoToToggle) { + var newTodos = this.state.todos.map(function (todo) { + return todo !== todoToToggle ? todo : Utils.extend({}, todo, {completed: !todo.completed}); + }); + + this.setState({todos: newTodos}); + }, + + destroy: function (todo) { + var newTodos = this.state.todos.filter(function (candidate) { + return candidate.id !== todo.id; + }); + + this.setState({todos: newTodos}); + }, + + edit: function (todo, callback) { + // refer to todoItem.js `handleEdit` for the reasoning behind the + // callback + this.setState({editing: todo.id}, function () { + callback(); + }); + }, + + save: function (todoToSave, text) { + var newTodos = this.state.todos.map(function (todo) { + return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text}); + }); + + this.setState({todos: newTodos, editing: null}); + }, + + cancel: function () { + this.setState({editing: null}); + }, + + clearCompleted: function () { + var newTodos = this.state.todos.filter(function (todo) { + return !todo.completed; + }); + + this.setState({todos: newTodos}); + }, + + componentDidUpdate: function () { + Utils.store('react-todos', this.state.todos); + }, + + render: function () { + var footer = null; + var main = null; + + var shownTodos = this.state.todos.filter(function (todo) { + switch (this.state.nowShowing) { + case ACTIVE_TODOS: + return !todo.completed; + case 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 = this.state.todos.reduce(function(accum, todo) { + return todo.completed ? accum : accum + 1; + }, 0); + + var completedCount = this.state.todos.length - activeTodoCount; + + if (activeTodoCount || completedCount) { + footer = + TodoFooter( + {count:activeTodoCount, + completedCount:completedCount, + nowShowing:this.state.nowShowing, + onClearCompleted:this.clearCompleted} + ); + } + + if (this.state.todos.length) { + main = ( + React.DOM.section( {id:"main"}, + React.DOM.input( + {id:"toggle-all", + type:"checkbox", + onChange:this.toggleAll, + checked:activeTodoCount === 0} + ), + React.DOM.ul( {id:"todo-list"}, + todoItems + ) + ) + ); + } + + return ( + React.DOM.div(null, + React.DOM.header( {id:"header"}, + React.DOM.h1(null, "todos"), + React.DOM.input( + {ref:"newField", + id:"new-todo", + placeholder:"What needs to be done?", + onKeyDown:this.handleNewTodoKeyDown} + ) + ), + main, + footer + ) + ); + } + }); + + React.renderComponent(TodoApp(null ), document.getElementById('todoapp')); + React.renderComponent( + React.DOM.div(null, + React.DOM.p(null, "Double-click to edit a todo"), + React.DOM.p(null, "Created by",' ', + React.DOM.a( {href:"http://github.com/petehunt/"}, "petehunt") + ), + React.DOM.p(null, "Part of",' ',React.DOM.a( {href:"http://todomvc.com"}, "TodoMVC")) + ), + document.getElementById('info')); +})(window, React); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/footer.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/footer.js new file mode 100644 index 0000000000..5ed8ae7ae6 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/footer.js @@ -0,0 +1,58 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark:false */ +/*jshint white:false */ +/*jshint trailing:false */ +/*jshint newcap:false */ +/*global React, ALL_TODOS, ACTIVE_TODOS, Utils, COMPLETED_TODOS */ +(function (window) { + 'use strict'; + + window.TodoFooter = React.createClass({displayName: 'TodoFooter', + render: function () { + var activeTodoWord = Utils.pluralize(this.props.count, 'item'); + var clearButton = null; + + if (this.props.completedCount > 0) { + clearButton = ( + React.DOM.button( + {id:"clear-completed", + onClick:this.props.onClearCompleted}, + '',"Clear completed (",this.props.completedCount,")",'' + ) + ); + } + + var show = { + ALL_TODOS: '', + ACTIVE_TODOS: '', + COMPLETED_TODOS: '' + }; + show[this.props.nowShowing] = 'selected'; + + return ( + React.DOM.footer( {id:"footer"}, + React.DOM.span( {id:"todo-count"}, + React.DOM.strong(null, this.props.count), + ' ',activeTodoWord,' ',"left",'' + ), + React.DOM.ul( {id:"filters"}, + React.DOM.li(null, + React.DOM.a( {href:"#/", className:show[ALL_TODOS]}, "All") + ), + ' ', + React.DOM.li(null, + React.DOM.a( {href:"#/active", className:show[ACTIVE_TODOS]}, "Active") + ), + ' ', + React.DOM.li(null, + React.DOM.a( {href:"#/completed", className:show[COMPLETED_TODOS]}, "Completed") + ) + ), + clearButton + ) + ); + } + }); +})(window); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/todoItem.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/todoItem.js new file mode 100644 index 0000000000..62ddc95949 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/todoItem.js @@ -0,0 +1,102 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark: false */ +/*jshint white: false */ +/*jshint trailing: false */ +/*jshint newcap: false */ +/*global React, Utils */ +(function (window) { + 'use strict'; + + var ESCAPE_KEY = 27; + var ENTER_KEY = 13; + + window.TodoItem = React.createClass({displayName: 'TodoItem', + handleSubmit: function () { + var val = this.state.editText.trim(); + if (val) { + this.props.onSave(val); + this.setState({editText: val}); + } else { + this.props.onDestroy(); + } + return false; + }, + + handleEdit: function () { + // react optimizes renders by batching them. This means you can't call + // parent's `onEdit` (which in this case triggeres a re-render), and + // immediately manipulate the DOM as if the rendering's over. Put it as a + // callback. Refer to app.js' `edit` method + this.props.onEdit(function () { + var node = this.refs.editField.getDOMNode(); + node.focus(); + node.setSelectionRange(node.value.length, node.value.length); + }.bind(this)); + this.setState({editText: this.props.todo.title}); + }, + + handleKeyDown: function (event) { + if (event.keyCode === ESCAPE_KEY) { + this.setState({editText: this.props.todo.title}); + this.props.onCancel(); + } else if (event.keyCode === ENTER_KEY) { + this.handleSubmit(); + } + }, + + handleChange: function (event) { + 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.id !== this.props.todo.id || + nextProps.todo !== this.props.todo || + nextProps.editing !== this.props.editing || + nextState.editText !== this.state.editText + ); + }, + + render: function () { + return ( + React.DOM.li( {className:React.addons.classSet({ + completed: this.props.todo.completed, + editing: this.props.editing + })}, + React.DOM.div( {className:"view"}, + React.DOM.input( + {className:"toggle", + type:"checkbox", + checked:this.props.todo.completed ? 'checked' : null, + onChange:this.props.onToggle} + ), + React.DOM.label( {onDoubleClick:this.handleEdit}, + this.props.todo.title + ), + React.DOM.button( {className:"destroy", onClick:this.props.onDestroy} ) + ), + React.DOM.input( + {ref:"editField", + className:"edit", + value:this.state.editText, + onBlur:this.handleSubmit, + onChange:this.handleChange, + onKeyDown:this.handleKeyDown} + ) + ) + ); + } + }); +})(window); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/utils.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/utils.js new file mode 100644 index 0000000000..dd929a2963 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/utils.js @@ -0,0 +1,49 @@ +(function (window) { + 'use strict'; + + window.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'; + }, + + store: function (namespace, data) { + if (data) { + return localStorage.setItem(namespace, JSON.stringify(data)); + } + + var store = localStorage.getItem(namespace); + return (store && JSON.parse(store)) || []; + }, + + 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; + } + }; + +})(window); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/app.jsx b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/app.jsx similarity index 100% rename from perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/app.jsx rename to perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/app.jsx diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/footer.jsx b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/footer.jsx similarity index 100% rename from perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/footer.jsx rename to perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/footer.jsx diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/todoItem.jsx b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/todoItem.jsx similarity index 100% rename from perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/js/todoItem.jsx rename to perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/todoItem.jsx diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/utils.jsx b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/utils.jsx new file mode 100644 index 0000000000..dd929a2963 --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/jsx/utils.jsx @@ -0,0 +1,49 @@ +(function (window) { + 'use strict'; + + window.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'; + }, + + store: function (namespace, data) { + if (data) { + return localStorage.setItem(namespace, JSON.stringify(data)); + } + + var store = localStorage.getItem(namespace); + return (store && JSON.parse(store)) || []; + }, + + 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; + } + }; + +})(window); diff --git a/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/production/director.min.js b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/production/director.min.js new file mode 100644 index 0000000000..dc4760660b --- /dev/null +++ b/perf/todomvc-benchmark/todomvc/labs/architecture-examples/react/production/director.min.js @@ -0,0 +1,5 @@ +// +// Generated on Fri Dec 27 2013 12:02:11 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon). +// Version 1.2.2 +// +(function(a){function k(a,b,c,d){var e=0,f=0,g=0,c=(c||"(").toString(),d=(d||")").toString(),h;for(h=0;hi.indexOf(d,e)||~i.indexOf(c,e)&&!~i.indexOf(d,e)||!~i.indexOf(c,e)&&~i.indexOf(d,e)){f=i.indexOf(c,e),g=i.indexOf(d,e);if(~f&&!~g||!~f&&~g){var j=a.slice(0,(h||1)+1).join(b);a=[j].concat(a.slice((h||1)+1))}e=(g>f?g:f)+1,h=0}else e=0}return a}function j(a,b){var c,d=0,e="";while(c=a.substr(d).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/))d=c.index+c[0].length,c[0]=c[0].replace(/^\*/,"([_.()!\\ %@&a-zA-Z0-9-]+)"),e+=a.substr(0,c.index)+c[0];a=e+=a.substr(d);var f=a.match(/:([^\/]+)/ig),g,h;if(f){h=f.length;for(var j=0;j7))this.history===!0?setTimeout(function(){window.onpopstate=d},500):window.onhashchange=d,this.mode="modern";else{var f=document.createElement("iframe");f.id="state-frame",f.style.display="none",document.body.appendChild(f),this.writeFrame(""),"onpropertychange"in document&&"attachEvent"in document&&document.attachEvent("onpropertychange",function(){event.propertyName==="location"&&c.check()}),window.setInterval(function(){c.check()},50),this.onHashChanged=d,this.mode="legacy"}e.listeners.push(a);return this.mode},destroy:function(a){if(!!e&&!!e.listeners){var b=e.listeners;for(var c=b.length-1;c>=0;c--)b[c]===a&&b.splice(c,1)}},setHash:function(a){this.mode==="legacy"&&this.writeFrame(a),this.history===!0?(window.history.pushState({},document.title,a),this.fire()):b.hash=a[0]==="/"?a:"/"+a;return this},writeFrame:function(a){var b=document.getElementById("state-frame"),c=b.contentDocument||b.contentWindow.document;c.open(),c.write("