├── .giignore
├── README.md
├── demo
├── index.html
├── js
│ ├── app.jsx
│ ├── footer.jsx
│ ├── todoItem.jsx
│ ├── todoModel.js
│ └── utils.js
├── node_modules
│ ├── director
│ │ └── build
│ │ │ └── director.js
│ ├── react
│ │ └── dist
│ │ │ ├── JSXTransformer.js
│ │ │ └── react-with-addons.js
│ ├── todomvc-app-css
│ │ └── index.css
│ └── todomvc-common
│ │ ├── base.css
│ │ └── base.js
├── package.json
└── readme.md
├── package.json
└── src
└── react-gl.jsx
/.giignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React-GL
2 | Render React components in WebGL for 60 FPS animations
3 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React • TodoMVC
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/demo/js/app.jsx:
--------------------------------------------------------------------------------
1 | /*jshint quotmark:false */
2 | /*jshint white:false */
3 | /*jshint trailing:false */
4 | /*jshint newcap:false */
5 | /*global React, Router*/
6 | var app = app || {};
7 |
8 | (function () {
9 | 'use strict';
10 |
11 | app.ALL_TODOS = 'all';
12 | app.ACTIVE_TODOS = 'active';
13 | app.COMPLETED_TODOS = 'completed';
14 | var TodoFooter = app.TodoFooter;
15 | var TodoItem = app.TodoItem;
16 |
17 | var ENTER_KEY = 13;
18 |
19 | var TodoApp = React.createClass({
20 | getInitialState: function () {
21 | return {
22 | nowShowing: app.ALL_TODOS,
23 | editing: null
24 | };
25 | },
26 |
27 | componentDidMount: function () {
28 | var setState = this.setState;
29 | var router = Router({
30 | '/': setState.bind(this, {nowShowing: app.ALL_TODOS}),
31 | '/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}),
32 | '/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS})
33 | });
34 | router.init('/');
35 | },
36 |
37 | handleNewTodoKeyDown: function (event) {
38 | if (event.keyCode !== ENTER_KEY) {
39 | return;
40 | }
41 |
42 | event.preventDefault();
43 |
44 | var val = React.findDOMNode(this.refs.newField).value.trim();
45 |
46 | if (val) {
47 | this.props.model.addTodo(val);
48 | React.findDOMNode(this.refs.newField).value = '';
49 | }
50 | },
51 |
52 | toggleAll: function (event) {
53 | var checked = event.target.checked;
54 | this.props.model.toggleAll(checked);
55 | },
56 |
57 | toggle: function (todoToToggle) {
58 | this.props.model.toggle(todoToToggle);
59 | },
60 |
61 | destroy: function (todo) {
62 | this.props.model.destroy(todo);
63 | },
64 |
65 | edit: function (todo) {
66 | this.setState({editing: todo.id});
67 | },
68 |
69 | save: function (todoToSave, text) {
70 | this.props.model.save(todoToSave, text);
71 | this.setState({editing: null});
72 | },
73 |
74 | cancel: function () {
75 | this.setState({editing: null});
76 | },
77 |
78 | clearCompleted: function () {
79 | this.props.model.clearCompleted();
80 | },
81 |
82 | render: function () {
83 | var footer;
84 | var main;
85 | var todos = this.props.model.todos;
86 |
87 | var shownTodos = todos.filter(function (todo) {
88 | switch (this.state.nowShowing) {
89 | case app.ACTIVE_TODOS:
90 | return !todo.completed;
91 | case app.COMPLETED_TODOS:
92 | return todo.completed;
93 | default:
94 | return true;
95 | }
96 | }, this);
97 |
98 | var todoItems = shownTodos.map(function (todo) {
99 | return (
100 |
110 | );
111 | }, this);
112 |
113 | var activeTodoCount = todos.reduce(function (accum, todo) {
114 | return todo.completed ? accum : accum + 1;
115 | }, 0);
116 |
117 | var completedCount = todos.length - activeTodoCount;
118 |
119 | if (activeTodoCount || completedCount) {
120 | footer =
121 | ;
127 | }
128 |
129 | if (todos.length) {
130 | main = (
131 |
142 | );
143 | }
144 |
145 | return (
146 |
147 |
157 | {main}
158 | {footer}
159 |
160 | );
161 | }
162 | });
163 |
164 | var model = new app.TodoModel('react-todos');
165 |
166 | function render() {
167 | React.render(
168 | ,
169 | document.getElementsByClassName('todoapp')[0]
170 | );
171 | }
172 |
173 | model.subscribe(render);
174 | render();
175 | })();
176 |
--------------------------------------------------------------------------------
/demo/js/footer.jsx:
--------------------------------------------------------------------------------
1 | /*jshint quotmark:false */
2 | /*jshint white:false */
3 | /*jshint trailing:false */
4 | /*jshint newcap:false */
5 | /*global React */
6 | var app = app || {};
7 |
8 | (function () {
9 | 'use strict';
10 |
11 | app.TodoFooter = React.createClass({
12 | render: function () {
13 | var activeTodoWord = app.Utils.pluralize(this.props.count, 'item');
14 | var clearButton = null;
15 |
16 | if (this.props.completedCount > 0) {
17 | clearButton = (
18 |
23 | );
24 | }
25 |
26 | // React idiom for shortcutting to `classSet` since it'll be used often
27 | var cx = React.addons.classSet;
28 | var nowShowing = this.props.nowShowing;
29 | return (
30 |
31 |
63 |
64 | );
65 | }
66 | });
67 | })();
68 |
--------------------------------------------------------------------------------
/demo/js/todoItem.jsx:
--------------------------------------------------------------------------------
1 | /*jshint quotmark: false */
2 | /*jshint white: false */
3 | /*jshint trailing: false */
4 | /*jshint newcap: false */
5 | /*global React */
6 | var app = app || {};
7 |
8 | (function () {
9 | 'use strict';
10 |
11 | var ESCAPE_KEY = 27;
12 | var ENTER_KEY = 13;
13 |
14 | app.TodoItem = React.createClass({
15 | handleSubmit: function (event) {
16 | var val = this.state.editText.trim();
17 | if (val) {
18 | this.props.onSave(val);
19 | this.setState({editText: val});
20 | } else {
21 | this.props.onDestroy();
22 | }
23 | },
24 |
25 | handleEdit: function () {
26 | this.props.onEdit();
27 | this.setState({editText: this.props.todo.title});
28 | },
29 |
30 | handleKeyDown: function (event) {
31 | if (event.which === ESCAPE_KEY) {
32 | this.setState({editText: this.props.todo.title});
33 | this.props.onCancel(event);
34 | } else if (event.which === ENTER_KEY) {
35 | this.handleSubmit(event);
36 | }
37 | },
38 |
39 | handleChange: function (event) {
40 | this.setState({editText: event.target.value});
41 | },
42 |
43 | getInitialState: function () {
44 | return {editText: this.props.todo.title};
45 | },
46 |
47 | /**
48 | * This is a completely optional performance enhancement that you can
49 | * implement on any React component. If you were to delete this method
50 | * the app would still work correctly (and still be very performant!), we
51 | * just use it as an example of how little code it takes to get an order
52 | * of magnitude performance improvement.
53 | */
54 | shouldComponentUpdate: function (nextProps, nextState) {
55 | return (
56 | nextProps.todo !== this.props.todo ||
57 | nextProps.editing !== this.props.editing ||
58 | nextState.editText !== this.state.editText
59 | );
60 | },
61 |
62 | /**
63 | * Safely manipulate the DOM after updating the state when invoking
64 | * `this.props.onEdit()` in the `handleEdit` method above.
65 | * For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate
66 | * and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate
67 | */
68 | componentDidUpdate: function (prevProps) {
69 | if (!prevProps.editing && this.props.editing) {
70 | var node = React.findDOMNode(this.refs.editField);
71 | node.focus();
72 | node.setSelectionRange(node.value.length, node.value.length);
73 | }
74 | },
75 |
76 | render: function () {
77 | return (
78 |
82 |
83 |
89 |
92 |
93 |
94 |
102 |
103 | );
104 | }
105 | });
106 | })();
107 |
--------------------------------------------------------------------------------
/demo/js/todoModel.js:
--------------------------------------------------------------------------------
1 | /*jshint quotmark:false */
2 | /*jshint white:false */
3 | /*jshint trailing:false */
4 | /*jshint newcap:false */
5 | var app = app || {};
6 |
7 | (function () {
8 | 'use strict';
9 |
10 | var Utils = app.Utils;
11 | // Generic "model" object. You can use whatever
12 | // framework you want. For this application it
13 | // may not even be worth separating this logic
14 | // out, but we do this to demonstrate one way to
15 | // separate out parts of your application.
16 | app.TodoModel = function (key) {
17 | this.key = key;
18 | this.todos = Utils.store(key);
19 | this.onChanges = [];
20 | };
21 |
22 | app.TodoModel.prototype.subscribe = function (onChange) {
23 | this.onChanges.push(onChange);
24 | };
25 |
26 | app.TodoModel.prototype.inform = function () {
27 | Utils.store(this.key, this.todos);
28 | this.onChanges.forEach(function (cb) { cb(); });
29 | };
30 |
31 | app.TodoModel.prototype.addTodo = function (title) {
32 | this.todos = this.todos.concat({
33 | id: Utils.uuid(),
34 | title: title,
35 | completed: false
36 | });
37 |
38 | this.inform();
39 | };
40 |
41 | app.TodoModel.prototype.toggleAll = function (checked) {
42 | // Note: it's usually better to use immutable data structures since they're
43 | // easier to reason about and React works very well with them. That's why
44 | // we use map() and filter() everywhere instead of mutating the array or
45 | // todo items themselves.
46 | this.todos = this.todos.map(function (todo) {
47 | return Utils.extend({}, todo, {completed: checked});
48 | });
49 |
50 | this.inform();
51 | };
52 |
53 | app.TodoModel.prototype.toggle = function (todoToToggle) {
54 | this.todos = this.todos.map(function (todo) {
55 | return todo !== todoToToggle ?
56 | todo :
57 | Utils.extend({}, todo, {completed: !todo.completed});
58 | });
59 |
60 | this.inform();
61 | };
62 |
63 | app.TodoModel.prototype.destroy = function (todo) {
64 | this.todos = this.todos.filter(function (candidate) {
65 | return candidate !== todo;
66 | });
67 |
68 | this.inform();
69 | };
70 |
71 | app.TodoModel.prototype.save = function (todoToSave, text) {
72 | this.todos = this.todos.map(function (todo) {
73 | return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text});
74 | });
75 |
76 | this.inform();
77 | };
78 |
79 | app.TodoModel.prototype.clearCompleted = function () {
80 | this.todos = this.todos.filter(function (todo) {
81 | return !todo.completed;
82 | });
83 |
84 | this.inform();
85 | };
86 |
87 | })();
88 |
--------------------------------------------------------------------------------
/demo/js/utils.js:
--------------------------------------------------------------------------------
1 | var app = app || {};
2 |
3 | (function () {
4 | 'use strict';
5 |
6 | app.Utils = {
7 | uuid: function () {
8 | /*jshint bitwise:false */
9 | var i, random;
10 | var uuid = '';
11 |
12 | for (i = 0; i < 32; i++) {
13 | random = Math.random() * 16 | 0;
14 | if (i === 8 || i === 12 || i === 16 || i === 20) {
15 | uuid += '-';
16 | }
17 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
18 | .toString(16);
19 | }
20 |
21 | return uuid;
22 | },
23 |
24 | pluralize: function (count, word) {
25 | return count === 1 ? word : word + 's';
26 | },
27 |
28 | store: function (namespace, data) {
29 | if (data) {
30 | return localStorage.setItem(namespace, JSON.stringify(data));
31 | }
32 |
33 | var store = localStorage.getItem(namespace);
34 | return (store && JSON.parse(store)) || [];
35 | },
36 |
37 | extend: function () {
38 | var newObj = {};
39 | for (var i = 0; i < arguments.length; i++) {
40 | var obj = arguments[i];
41 | for (var key in obj) {
42 | if (obj.hasOwnProperty(key)) {
43 | newObj[key] = obj[key];
44 | }
45 | }
46 | }
47 | return newObj;
48 | }
49 | };
50 | })();
51 |
--------------------------------------------------------------------------------
/demo/node_modules/director/build/director.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | //
4 | // Generated on Tue Dec 16 2014 12:13:47 GMT+0100 (CET) by Charlie Robbins, Paolo Fragomeni & the Contributors (Using Codesurgeon).
5 | // Version 1.2.6
6 | //
7 |
8 | (function (exports) {
9 |
10 | /*
11 | * browser.js: Browser specific functionality for director.
12 | *
13 | * (C) 2011, Charlie Robbins, Paolo Fragomeni, & the Contributors.
14 | * MIT LICENSE
15 | *
16 | */
17 |
18 | var dloc = document.location;
19 |
20 | function dlocHashEmpty() {
21 | // Non-IE browsers return '' when the address bar shows '#'; Director's logic
22 | // assumes both mean empty.
23 | return dloc.hash === '' || dloc.hash === '#';
24 | }
25 |
26 | var listener = {
27 | mode: 'modern',
28 | hash: dloc.hash,
29 | history: false,
30 |
31 | check: function () {
32 | var h = dloc.hash;
33 | if (h != this.hash) {
34 | this.hash = h;
35 | this.onHashChanged();
36 | }
37 | },
38 |
39 | fire: function () {
40 | if (this.mode === 'modern') {
41 | this.history === true ? window.onpopstate() : window.onhashchange();
42 | }
43 | else {
44 | this.onHashChanged();
45 | }
46 | },
47 |
48 | init: function (fn, history) {
49 | var self = this;
50 | this.history = history;
51 |
52 | if (!Router.listeners) {
53 | Router.listeners = [];
54 | }
55 |
56 | function onchange(onChangeEvent) {
57 | for (var i = 0, l = Router.listeners.length; i < l; i++) {
58 | Router.listeners[i](onChangeEvent);
59 | }
60 | }
61 |
62 | //note IE8 is being counted as 'modern' because it has the hashchange event
63 | if ('onhashchange' in window && (document.documentMode === undefined
64 | || document.documentMode > 7)) {
65 | // At least for now HTML5 history is available for 'modern' browsers only
66 | if (this.history === true) {
67 | // There is an old bug in Chrome that causes onpopstate to fire even
68 | // upon initial page load. Since the handler is run manually in init(),
69 | // this would cause Chrome to run it twise. Currently the only
70 | // workaround seems to be to set the handler after the initial page load
71 | // http://code.google.com/p/chromium/issues/detail?id=63040
72 | setTimeout(function() {
73 | window.onpopstate = onchange;
74 | }, 500);
75 | }
76 | else {
77 | window.onhashchange = onchange;
78 | }
79 | this.mode = 'modern';
80 | }
81 | else {
82 | //
83 | // IE support, based on a concept by Erik Arvidson ...
84 | //
85 | var frame = document.createElement('iframe');
86 | frame.id = 'state-frame';
87 | frame.style.display = 'none';
88 | document.body.appendChild(frame);
89 | this.writeFrame('');
90 |
91 | if ('onpropertychange' in document && 'attachEvent' in document) {
92 | document.attachEvent('onpropertychange', function () {
93 | if (event.propertyName === 'location') {
94 | self.check();
95 | }
96 | });
97 | }
98 |
99 | window.setInterval(function () { self.check(); }, 50);
100 |
101 | this.onHashChanged = onchange;
102 | this.mode = 'legacy';
103 | }
104 |
105 | Router.listeners.push(fn);
106 |
107 | return this.mode;
108 | },
109 |
110 | destroy: function (fn) {
111 | if (!Router || !Router.listeners) {
112 | return;
113 | }
114 |
115 | var listeners = Router.listeners;
116 |
117 | for (var i = listeners.length - 1; i >= 0; i--) {
118 | if (listeners[i] === fn) {
119 | listeners.splice(i, 1);
120 | }
121 | }
122 | },
123 |
124 | setHash: function (s) {
125 | // Mozilla always adds an entry to the history
126 | if (this.mode === 'legacy') {
127 | this.writeFrame(s);
128 | }
129 |
130 | if (this.history === true) {
131 | window.history.pushState({}, document.title, s);
132 | // Fire an onpopstate event manually since pushing does not obviously
133 | // trigger the pop event.
134 | this.fire();
135 | } else {
136 | dloc.hash = (s[0] === '/') ? s : '/' + s;
137 | }
138 | return this;
139 | },
140 |
141 | writeFrame: function (s) {
142 | // IE support...
143 | var f = document.getElementById('state-frame');
144 | var d = f.contentDocument || f.contentWindow.document;
145 | d.open();
146 | d.write("