├── .gitignore ├── LICENCE ├── README.md ├── img └── 2019-12-17.png ├── index.html ├── resources ├── benchmark-runner.js ├── manager.js └── tests.js └── todomvc ├── elm17 ├── Todo.elm ├── bg.png ├── elm-package.json ├── elm.js ├── index.html └── style.css ├── mithril ├── LICENSE ├── README.md ├── bower.json ├── bower_components │ ├── mithril │ │ ├── .bower.json │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── Gruntfile.js │ │ ├── LICENSE │ │ ├── README.md │ │ ├── background.html │ │ ├── deploy │ │ │ └── cdnjs-package.json │ │ ├── mithril.d.ts │ │ ├── mithril.js │ │ └── package.json │ └── todomvc-common │ │ ├── .bower.json │ │ ├── base.css │ │ ├── base.js │ │ ├── bg.png │ │ ├── bower.json │ │ └── readme.md ├── css │ └── app.css ├── index.html └── js │ ├── app.js │ ├── controllers │ └── todo.js │ ├── models │ └── todo.js │ └── views │ ├── footer-view.js │ ├── main-view.js │ └── single-view.js ├── mogwai ├── index.html ├── pkg │ ├── README.md │ ├── mogwai_todo.d.ts │ ├── mogwai_todo.js │ ├── mogwai_todo_bg.d.ts │ ├── mogwai_todo_bg.wasm │ └── package.json └── todo.css ├── preact ├── bower_components │ └── todomvc-common │ │ ├── .bower.json │ │ ├── base.css │ │ ├── base.js │ │ ├── bg.png │ │ ├── bower.json │ │ └── readme.md ├── bundle.js ├── bundle.js.map ├── index.html ├── package.json └── src │ ├── app.js │ ├── footer.js │ ├── header.js │ ├── index.js │ ├── item.js │ ├── model.js │ └── util.js ├── react ├── .gitignore ├── index.html ├── js │ ├── app.js │ ├── app.jsx │ ├── footer.js │ ├── footer.jsx │ ├── todoItem.js │ ├── todoItem.jsx │ ├── todoModel.js │ └── utils.js ├── package.json ├── readme.md ├── todomvc-app-css │ ├── index.css │ ├── package.json │ └── readme.md └── todomvc-common │ ├── base.css │ └── base.js ├── sauron ├── index.html ├── pkg │ ├── package.json │ ├── todomvc.d.ts │ ├── todomvc.js │ ├── todomvc_bg.d.ts │ └── todomvc_bg.wasm └── style.css ├── seed ├── index.css ├── index.html ├── pkg │ ├── README.md │ ├── package.d.ts │ ├── package.js │ ├── package.json │ ├── package_bg.d.ts │ ├── package_bg.wasm │ ├── todomvc.d.ts │ ├── todomvc.js │ ├── todomvc_bg.d.ts │ └── todomvc_bg.wasm └── text-polyfill.min.js ├── vue ├── .gitignore ├── index.html ├── js │ ├── app.js │ ├── routes.js │ └── store.js ├── node_modules │ ├── director │ │ └── build │ │ │ └── director.js │ ├── todomvc-app-css │ │ └── index.css │ ├── todomvc-common │ │ ├── base.css │ │ └── base.js │ └── vue │ │ └── dist │ │ └── vue.js ├── package.json └── readme.md └── yew ├── index.html ├── todomvc.js └── todomvc.wasm /.gitignore: -------------------------------------------------------------------------------- 1 | *elm-stuff 2 | *.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Matt Esch. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # todomvc benchmark for rust frontend webframeworks relative to react,elm,vue 2 | 3 | Rust webframeworks: 4 | - [sauron](https://github.com/ivanceras/sauron) 5 | - [mogwai](https://github.com/schell/mogwai) 6 | - [seed](https://github.com/seed-rs/seed) 7 | - [yew](https://github.com/yewstack/yew) 8 | 9 | ![](https://raw.githubusercontent.com/ivanceras/sauron-perf/master/img/2019-12-17.png) 10 | 11 | **[Run benchmark](https://ivanceras.github.io/sauron-perf/)** 12 | 13 | ## Setup: 14 | ``` 15 | cargo install basic-http-server 16 | git clone https://github.com/ivanceras/sauron-perf 17 | cd sauron-perf 18 | basic-http-server 19 | ``` 20 | 21 | Open [http://localhost:4000](http://localhost:4000) 22 | -------------------------------------------------------------------------------- /img/2019-12-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/img/2019-12-17.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TodoMVC Benchmark 5 | 6 | 7 | 8 | 24 | 25 | 26 |
27 | 29 |

TodoMVC Benchmark v1.0.0

30 |

31 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /resources/benchmark-runner.js: -------------------------------------------------------------------------------- 1 | // FIXME: Use the real promise if available. 2 | // FIXME: Make sure this interface is compatible with the real Promise. 3 | function SimplePromise() { 4 | this._chainedPromise = null; 5 | this._callback = null; 6 | } 7 | 8 | SimplePromise.prototype.then = function (callback) { 9 | if (this._callback) 10 | throw "SimplePromise doesn't support multiple calls to then"; 11 | this._callback = callback; 12 | this._chainedPromise = new SimplePromise; 13 | 14 | if (this._resolved) 15 | this.resolve(this._resolvedValue); 16 | 17 | return this._chainedPromise; 18 | } 19 | 20 | SimplePromise.prototype.resolve = function (value) { 21 | if (!this._callback) { 22 | this._resolved = true; 23 | this._resolvedValue = value; 24 | return; 25 | } 26 | 27 | var result = this._callback(value); 28 | if (result instanceof SimplePromise) { 29 | var chainedPromise = this._chainedPromise; 30 | result.then(function (result) { chainedPromise.resolve(result); }); 31 | } else 32 | this._chainedPromise.resolve(result); 33 | } 34 | 35 | function BenchmarkTestStep(testName, testFunction) { 36 | this.name = testName; 37 | this.run = testFunction; 38 | } 39 | 40 | function BenchmarkRunner(suites, client) { 41 | this._suites = suites; 42 | this._prepareReturnValue = null; 43 | this._measuredValues = {}; 44 | this._client = client; 45 | } 46 | 47 | BenchmarkRunner.prototype.waitForElement = function (selector) { 48 | var promise = new SimplePromise; 49 | var contentDocument = this._frame.contentDocument; 50 | 51 | function resolveIfReady() { 52 | var element = contentDocument.querySelector(selector); 53 | if (element) 54 | return promise.resolve(element); 55 | setTimeout(resolveIfReady, 50); 56 | } 57 | 58 | resolveIfReady(); 59 | return promise; 60 | } 61 | 62 | BenchmarkRunner.prototype._removeFrame = function () { 63 | if (this._frame) { 64 | this._frame.parentNode.removeChild(this._frame); 65 | this._frame = null; 66 | } 67 | } 68 | 69 | BenchmarkRunner.prototype._appendFrame = function (src) { 70 | var frame = document.createElement('iframe'); 71 | frame.style.width = '800px'; 72 | frame.style.height = '600px' 73 | document.body.appendChild(frame); 74 | this._frame = frame; 75 | return frame; 76 | } 77 | 78 | BenchmarkRunner.prototype._waitAndWarmUp = function () { 79 | var startTime = Date.now(); 80 | 81 | function Fibonacci(n) { 82 | if (Date.now() - startTime > 100) 83 | return; 84 | if (n <= 0) 85 | return 0; 86 | else if (n == 1) 87 | return 1; 88 | return Fibonacci(n - 2) + Fibonacci(n - 1); 89 | } 90 | 91 | var promise = new SimplePromise; 92 | setTimeout(function () { 93 | Fibonacci(100); 94 | promise.resolve(); 95 | }, 200); 96 | return promise; 97 | } 98 | 99 | // This function ought be as simple as possible. Don't even use SimplePromise. 100 | BenchmarkRunner.prototype._runTest = function(suite, testFunction, prepareReturnValue, callback) 101 | { 102 | var now = window.performance && window.performance.now ? function () { return window.performance.now(); } : Date.now; 103 | 104 | var contentWindow = this._frame.contentWindow; 105 | var contentDocument = this._frame.contentDocument; 106 | 107 | var startTime = now(); 108 | testFunction(prepareReturnValue, contentWindow, contentDocument); 109 | var endTime = now(); 110 | var syncTime = endTime - startTime; 111 | 112 | var startTime = now(); 113 | setTimeout(function () { 114 | setTimeout(function () { 115 | var endTime = now(); 116 | callback(syncTime, endTime - startTime); 117 | }, 0) 118 | }, 0); 119 | } 120 | 121 | function BenchmarkState(suites) { 122 | this._suites = suites; 123 | this._suiteIndex = -1; 124 | this._testIndex = 0; 125 | this.next(); 126 | } 127 | 128 | BenchmarkState.prototype.currentSuite = function() { 129 | return this._suites[this._suiteIndex]; 130 | } 131 | 132 | BenchmarkState.prototype.currentTest = function () { 133 | var suite = this.currentSuite(); 134 | return suite ? suite.tests[this._testIndex] : null; 135 | } 136 | 137 | BenchmarkState.prototype.next = function () { 138 | this._testIndex++; 139 | 140 | var suite = this._suites[this._suiteIndex]; 141 | if (suite && this._testIndex < suite.tests.length) 142 | return this; 143 | 144 | this._testIndex = 0; 145 | do { 146 | this._suiteIndex++; 147 | } while (this._suiteIndex < this._suites.length && this._suites[this._suiteIndex].disabled); 148 | 149 | return this; 150 | } 151 | 152 | BenchmarkState.prototype.isFirstTest = function () { 153 | return !this._testIndex; 154 | } 155 | 156 | BenchmarkState.prototype.prepareCurrentSuite = function (runner, frame) { 157 | var suite = this.currentSuite(); 158 | var promise = new SimplePromise; 159 | frame.onload = function () { 160 | suite.prepare(runner, frame.contentWindow, frame.contentDocument).then(function (result) { promise.resolve(result); }); 161 | } 162 | frame.src = suite.url; 163 | return promise; 164 | } 165 | 166 | BenchmarkRunner.prototype.step = function (state) { 167 | if (!state) 168 | state = new BenchmarkState(this._suites); 169 | 170 | var suite = state.currentSuite(); 171 | if (!suite) { 172 | this._finalize(); 173 | var promise = new SimplePromise; 174 | promise.resolve(); 175 | return promise; 176 | } 177 | 178 | if (state.isFirstTest()) { 179 | this._masuredValuesForCurrentSuite = {}; 180 | var self = this; 181 | return state.prepareCurrentSuite(this, this._appendFrame()).then(function (prepareReturnValue) { 182 | self._prepareReturnValue = prepareReturnValue; 183 | return self._runTestAndRecordResults(state); 184 | }); 185 | } 186 | 187 | return this._runTestAndRecordResults(state); 188 | } 189 | 190 | BenchmarkRunner.prototype._runTestAndRecordResults = function (state) { 191 | var promise = new SimplePromise; 192 | var suite = state.currentSuite(); 193 | var test = state.currentTest(); 194 | 195 | if (this._client && this._client.willRunTest) 196 | this._client.willRunTest(suite, test); 197 | 198 | var self = this; 199 | setTimeout(function () { 200 | self._runTest(suite, test.run, self._prepareReturnValue, function (syncTime, asyncTime) { 201 | var suiteResults = self._measuredValues[suite.name] || {tests:{}, total: 0}; 202 | self._measuredValues[suite.name] = suiteResults; 203 | suiteResults.tests[test.name] = {'Sync': syncTime, 'Async': asyncTime}; 204 | suiteResults.total += syncTime + asyncTime; 205 | 206 | if (self._client && self._client.willRunTest) 207 | self._client.didRunTest(suite, test); 208 | 209 | state.next(); 210 | if (state.currentSuite() != suite) 211 | self._removeFrame(); 212 | promise.resolve(state); 213 | }); 214 | }, 0); 215 | return promise; 216 | } 217 | 218 | BenchmarkRunner.prototype._finalize = function () { 219 | this._removeFrame(); 220 | 221 | if (this._client && this._client.didRunSuites) 222 | this._client.didRunSuites(this._measuredValues); 223 | 224 | // FIXME: This should be done when we start running tests. 225 | this._measuredValues = {}; 226 | } 227 | -------------------------------------------------------------------------------- /resources/manager.js: -------------------------------------------------------------------------------- 1 | 2 | var runs = [], 3 | res = document.getElementById('results'), 4 | timesRan = 0, 5 | runButton 6 | 7 | function formatTestName(suiteName, testName) { 8 | return suiteName + (testName ? '/' + testName : ''); 9 | } 10 | 11 | function createUIForSuites(suites, onstep, onrun) { 12 | var control = document.createElement('nav'); 13 | var ol = document.createElement('ol'); 14 | var checkboxes = []; 15 | 16 | var button = document.createElement('button'); 17 | button.textContent = 'Step Tests'; 18 | button.onclick = onstep; 19 | control.appendChild(button); 20 | 21 | var button = runButton = document.createElement('button'); 22 | button.textContent = 'Run All'; 23 | button.onclick = onrun; 24 | control.appendChild(button); 25 | 26 | for (var suiteIndex = 0; suiteIndex < suites.length; suiteIndex++) { 27 | var suite = suites[suiteIndex]; 28 | var li = document.createElement('li'); 29 | var checkbox = document.createElement('input'); 30 | checkbox.id = suite.name; 31 | checkbox.type = 'checkbox'; 32 | checkbox.checked = true; 33 | checkbox.onchange = (function (suite, checkbox) { return function () { suite.disabled = !checkbox.checked; runs = []; } })(suite, checkbox); 34 | checkbox.onchange(); 35 | checkboxes.push(checkbox); 36 | 37 | li.appendChild(checkbox); 38 | var label = document.createElement('label'); 39 | label.appendChild(document.createTextNode(formatTestName(suite.name) + ' ' + suite.version)); 40 | li.appendChild(label); 41 | label.htmlFor = checkbox.id; 42 | 43 | var testList = document.createElement('ol'); 44 | for (var testIndex = 0; testIndex < suite.tests.length; testIndex++) { 45 | var testItem = document.createElement('li'); 46 | var test = suite.tests[testIndex]; 47 | var anchor = document.createElement('a'); 48 | anchor.id = suite.name + '-' + test.name; 49 | test.anchor = anchor; 50 | anchor.appendChild(document.createTextNode(formatTestName(suite.name, test.name))); 51 | testItem.appendChild(anchor); 52 | testList.appendChild(testItem); 53 | } 54 | li.appendChild(testList); 55 | 56 | ol.appendChild(li); 57 | } 58 | 59 | control.appendChild(ol); 60 | 61 | return control; 62 | } 63 | 64 | function startTest() { 65 | 66 | var match = window.location.search.match(/[\?&]r=(\d+)/), 67 | timesToRun = match ? +(match[1]) : 1 68 | 69 | var runner = new BenchmarkRunner(Suites, { 70 | willRunTest: function (suite, test) { 71 | if (!navigator.userAgent.match("MSIE 9.0")) test.anchor.classList.add('running'); 72 | }, 73 | didRunTest: function (suite, test) { 74 | var classList = test.anchor.classList; 75 | if (!navigator.userAgent.match("MSIE 9.0")) classList.remove('running'); 76 | if (!navigator.userAgent.match("MSIE 9.0")) classList.add('ran'); 77 | }, 78 | didRunSuites: function (measuredValues) { 79 | var results = ''; 80 | var total = 0; // FIXME: Compute the total properly. 81 | for (var suiteName in measuredValues) { 82 | var suiteResults = measuredValues[suiteName]; 83 | for (var testName in suiteResults.tests) { 84 | var testResults = suiteResults.tests[testName]; 85 | for (var subtestName in testResults) { 86 | results += suiteName + ' : ' + testName + ' : ' + subtestName 87 | + ': ' + testResults[subtestName] + ' ms\n'; 88 | } 89 | } 90 | results += suiteName + ' : ' + suiteResults.total + ' ms\n'; 91 | total += suiteResults.total; 92 | } 93 | results += 'Run ' + (runs.length + 1) +'/' + timesToRun + ' - Total : ' + total + ' ms\n'; 94 | 95 | if (!results) 96 | return; 97 | 98 | console.log(results) 99 | 100 | runs.push(measuredValues) 101 | timesRan++ 102 | if (timesRan >= timesToRun) { 103 | timesRan = 0 104 | reportFastest() 105 | shuffle(Suites); 106 | } else { 107 | setTimeout(function () { 108 | runButton.click() 109 | }, 0) 110 | } 111 | } 112 | }); 113 | 114 | var currentState = null; 115 | function callNextStep(state) { 116 | runner.step(state).then(function (newState) { 117 | currentState = newState; 118 | if (newState) 119 | callNextStep(newState); 120 | }); 121 | } 122 | 123 | // Don't call step while step is already executing. 124 | document.body.appendChild(createUIForSuites(Suites, 125 | function () { runner.step(currentState).then(function (state) { currentState = state; }); }, 126 | function () { 127 | var analysis = document.getElementById("analysis"); 128 | analysis.style.display = 'none'; 129 | localStorage.clear(); 130 | runs = []; 131 | callNextStep(currentState); 132 | })); 133 | 134 | function reportFastest () { 135 | var results = {} 136 | runs.forEach(function (runData) { 137 | for (var key in runData) { 138 | results[key] = Math.min(results[key] || Infinity, runData[key].total) 139 | } 140 | }); 141 | drawChart(results); 142 | } 143 | } 144 | 145 | google.load("visualization", "1", {packages:["corechart"]}); 146 | function drawChart(results) { 147 | var rawData = []; 148 | for (var key in results) { 149 | var color = colorify(key); 150 | rawData.push([ key, Math.round(results[key]), color ]); 151 | } 152 | rawData.sort(function(a, b){ return a[1] - b[1] }) 153 | rawData.unshift([ "Project" , "Time", { role: "style"} ]) 154 | var data = google.visualization.arrayToDataTable(rawData); 155 | 156 | var view = new google.visualization.DataView(data); 157 | view.setColumns([0, 1, 158 | { calc: "stringify", 159 | sourceColumn: 1, 160 | type: "string", 161 | role: "annotation" }, 162 | 2]); 163 | 164 | var runWord = "run" + (runs.length > 1 ? "s" : ""); 165 | var title = "Best time in milliseconds over " + runs.length + 166 | " " + runWord + " (lower is better)"; 167 | 168 | var options = { 169 | title: "TodoMVC Benchmark", 170 | width: 600, 171 | height: 500, 172 | legend: { position: "none" }, 173 | backgroundColor: 'transparent', 174 | hAxis: {title: title}, 175 | min:0, 176 | max:1500 177 | }; 178 | var analysis = document.getElementById("analysis"); 179 | analysis.style.display = 'block'; 180 | var barchart = document.getElementById("barchart_values"); 181 | var chart = new google.visualization.BarChart(barchart); 182 | chart.draw(view, options); 183 | } 184 | 185 | function shuffle ( ary ) { 186 | var i = ary.length; 187 | if ( i == 0 ) return false; 188 | while ( --i ) { 189 | var j = Math.floor( Math.random() * ( i + 1 ) ); 190 | var tempi = ary[i]; 191 | var tempj = ary[j]; 192 | ary[i] = tempj; 193 | ary[j] = tempi; 194 | } 195 | } 196 | 197 | function colorify(n){ 198 | var c = 'rgb(' + ( Math.max(0,(n.toLowerCase().charCodeAt(3 % n.length) - 97) / 26 * 255 | 0) ) + 199 | ", " + ( Math.max(0,(n.toLowerCase().charCodeAt(4 % n.length) - 97) / 26 * 255 | 0) ) + 200 | ", " + ( Math.max(0,(n.toLowerCase().charCodeAt(5 % n.length) - 97) / 26 * 255 | 0) ) + ")" 201 | return c 202 | } 203 | 204 | window.addEventListener('load', startTest); 205 | -------------------------------------------------------------------------------- /todomvc/elm17/Todo.elm: -------------------------------------------------------------------------------- 1 | port module Todo exposing (..) 2 | {-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering. 3 | 4 | This application is broken up into four distinct parts: 5 | 6 | 1. Model - a full definition of the application's state 7 | 2. Update - a way to step the application state forward 8 | 3. View - a way to visualize our application state with HTML 9 | 4. Inputs - the signals necessary to manage events 10 | 11 | This clean division of concerns is a core part of Elm. You can read more about 12 | this in the Pong tutorial: http://elm-lang.org/blog/making-pong 13 | 14 | This program is not particularly large, so definitely see the following 15 | for notes on structuring more complex GUIs with Elm: 16 | https://github.com/evancz/elm-architecture-tutorial/ 17 | -} 18 | 19 | import Html exposing (..) 20 | import Html.Attributes exposing (..) 21 | import Html.Events exposing (..) 22 | import Html.App 23 | import Html.Lazy exposing (lazy, lazy2) 24 | import Json.Decode as Json 25 | import String 26 | 27 | 28 | ---- MODEL ---- 29 | 30 | -- The full application state of our todo app. 31 | type alias Model = 32 | { tasks : List Task 33 | , field : String 34 | , uid : Int 35 | , visibility : String 36 | } 37 | 38 | 39 | type alias Task = 40 | { description : String 41 | , completed : Bool 42 | , editing : Bool 43 | , id : Int 44 | } 45 | 46 | 47 | newTask : String -> Int -> Task 48 | newTask desc id = 49 | { description = desc 50 | , completed = False 51 | , editing = False 52 | , id = id 53 | } 54 | 55 | 56 | emptyModel : Model 57 | emptyModel = 58 | { tasks = [] 59 | , visibility = "All" 60 | , field = "" 61 | , uid = 0 62 | } 63 | 64 | 65 | ---- UPDATE ---- 66 | 67 | -- A description of the kinds of actions that can be performed on the model of 68 | -- our application. See the following for more info on this pattern and 69 | -- some alternatives: https://github.com/evancz/elm-architecture-tutorial/ 70 | type Action 71 | = NoOp 72 | | UpdateField String 73 | | EditingTask Int Bool 74 | | UpdateTask Int String 75 | | Add 76 | | Delete Int 77 | | DeleteComplete 78 | | Check Int Bool 79 | | CheckAll Bool 80 | | ChangeVisibility String 81 | 82 | 83 | -- How we update our Model on a given Action? 84 | update : Action -> Model -> Model 85 | update action model = 86 | case action of 87 | NoOp -> model 88 | 89 | Add -> 90 | { model | 91 | uid = model.uid + 1, 92 | field = "", 93 | tasks = 94 | if String.isEmpty model.field 95 | then model.tasks 96 | else model.tasks ++ [newTask model.field model.uid] 97 | } 98 | 99 | UpdateField str -> 100 | { model | field = str } 101 | 102 | EditingTask id isEditing -> 103 | let updateTask t = if t.id == id then { t | editing = isEditing } else t 104 | in 105 | { model | tasks = List.map updateTask model.tasks } 106 | 107 | UpdateTask id task -> 108 | let updateTask t = if t.id == id then { t | description = task } else t 109 | in 110 | { model | tasks = List.map updateTask model.tasks } 111 | 112 | Delete id -> 113 | { model | tasks = List.filter (\t -> t.id /= id) model.tasks } 114 | 115 | DeleteComplete -> 116 | { model | tasks = List.filter (not << .completed) model.tasks } 117 | 118 | Check id isCompleted -> 119 | let updateTask t = if t.id == id then { t | completed = isCompleted } else t 120 | in 121 | { model | tasks = List.map updateTask model.tasks } 122 | 123 | CheckAll isCompleted -> 124 | let updateTask t = { t | completed = isCompleted } 125 | in 126 | { model | tasks = List.map updateTask model.tasks } 127 | 128 | ChangeVisibility visibility -> 129 | { model | visibility = visibility } 130 | 131 | 132 | ---- VIEW ---- 133 | 134 | view : Model -> Html Action 135 | view model = 136 | div 137 | [ class "todomvc-wrapper" 138 | , style [ ("visibility", "hidden") ] 139 | ] 140 | [ section 141 | [ id "todoapp" ] 142 | [ taskEntry model.field 143 | , taskList model.visibility model.tasks 144 | , controls model.visibility model.tasks 145 | ] 146 | , infoFooter 147 | ] 148 | 149 | 150 | onEnter : Action -> Attribute Action 151 | onEnter tagger = 152 | on "keydown" (Json.map (always tagger) (Json.customDecoder keyCode is13)) 153 | 154 | 155 | is13 : Int -> Result String () 156 | is13 code = 157 | if code == 13 then Ok () else Err "not the right key code" 158 | 159 | 160 | taskEntry : String -> Html Action 161 | taskEntry task = 162 | header 163 | [ id "header" ] 164 | [ h1 [] [ text "todos" ] 165 | , input 166 | [ id "new-todo" 167 | , placeholder "What needs to be done?" 168 | , autofocus True 169 | , value task 170 | , name "newTodo" 171 | , onInput UpdateField 172 | , onEnter Add 173 | ] 174 | [] 175 | ] 176 | 177 | 178 | taskList : String -> List Task -> Html Action 179 | taskList visibility tasks = 180 | let isVisible todo = 181 | case visibility of 182 | "Completed" -> todo.completed 183 | "Active" -> not todo.completed 184 | _ -> True 185 | 186 | allCompleted = List.all .completed tasks 187 | 188 | cssVisibility = if List.isEmpty tasks then "hidden" else "visible" 189 | in 190 | section 191 | [ id "main" 192 | , style [ ("visibility", cssVisibility) ] 193 | ] 194 | [ input 195 | [ id "toggle-all" 196 | , type' "checkbox" 197 | , name "toggle" 198 | , checked allCompleted 199 | , onClick (CheckAll (not allCompleted)) 200 | ] 201 | [] 202 | , label 203 | [ for "toggle-all" ] 204 | [ text "Mark all as complete" ] 205 | , ul 206 | [ id "todo-list" ] 207 | (List.map todoItem (List.filter isVisible tasks)) 208 | ] 209 | 210 | 211 | todoItem : Task -> Html Action 212 | todoItem todo = 213 | li 214 | [ classList [ ("completed", todo.completed), ("editing", todo.editing) ] ] 215 | [ div 216 | [ class "view" ] 217 | [ input 218 | [ class "toggle" 219 | , type' "checkbox" 220 | , checked todo.completed 221 | , onClick (Check todo.id (not todo.completed)) 222 | ] 223 | [] 224 | , label 225 | [ onDoubleClick (EditingTask todo.id True) ] 226 | [ text todo.description ] 227 | , button 228 | [ class "destroy" 229 | , onClick (Delete todo.id) 230 | ] 231 | [] 232 | ] 233 | , input 234 | [ class "edit" 235 | , value todo.description 236 | , name "title" 237 | , id ("todo-" ++ toString todo.id) 238 | , onInput (UpdateTask todo.id) 239 | , onBlur (EditingTask todo.id False) 240 | , onEnter (EditingTask todo.id False) 241 | ] 242 | [] 243 | ] 244 | 245 | 246 | controls : String -> List Task -> Html Action 247 | controls visibility tasks = 248 | let tasksCompleted = List.length (List.filter .completed tasks) 249 | tasksLeft = List.length tasks - tasksCompleted 250 | item_ = if tasksLeft == 1 then " item" else " items" 251 | in 252 | footer 253 | [ id "footer" 254 | , hidden (List.isEmpty tasks) 255 | ] 256 | [ span 257 | [ id "todo-count" ] 258 | [ strong [] [ text (toString tasksLeft) ] 259 | , text (item_ ++ " left") 260 | ] 261 | , ul 262 | [ id "filters" ] 263 | [ visibilitySwap "#/" "All" visibility 264 | , text " " 265 | , visibilitySwap "#/active" "Active" visibility 266 | , text " " 267 | , visibilitySwap "#/completed" "Completed" visibility 268 | ] 269 | , button 270 | [ class "clear-completed" 271 | , id "clear-completed" 272 | , hidden (tasksCompleted == 0) 273 | , onClick DeleteComplete 274 | ] 275 | [ text ("Clear completed (" ++ toString tasksCompleted ++ ")") ] 276 | ] 277 | 278 | 279 | visibilitySwap : String -> String -> String -> Html Action 280 | visibilitySwap uri visibility actualVisibility = 281 | li 282 | [ onClick (ChangeVisibility visibility) ] 283 | [ a [ href uri, classList [("selected", visibility == actualVisibility)] ] [ text visibility ] ] 284 | 285 | 286 | infoFooter : Html Action 287 | infoFooter = 288 | footer [ id "info" ] 289 | [ p [] [ text "Double-click to edit a todo" ] 290 | , p [] 291 | [ text "Written by " 292 | , a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ] 293 | ] 294 | , p [] 295 | [ text "Part of " 296 | , a [ href "http://todomvc.com" ] [ text "TodoMVC" ] 297 | ] 298 | ] 299 | 300 | 301 | ---- INPUTS ---- 302 | 303 | -- wire the entire application together 304 | main = 305 | Html.App.beginnerProgram 306 | { model = emptyModel 307 | , update = update 308 | , view = view 309 | } 310 | -------------------------------------------------------------------------------- /todomvc/elm17/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/elm17/bg.png -------------------------------------------------------------------------------- /todomvc/elm17/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "TodoMVC created with Elm and elm-html", 4 | "repository": "https://github.com/evancz/elm-todomvc.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "4.0.0 <= v < 5.0.0", 12 | "elm-lang/html": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "elm-version": "0.17.0 <= v < 0.18.0" 15 | } 16 | -------------------------------------------------------------------------------- /todomvc/elm17/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm • TodoMVC 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /todomvc/mithril/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jean-Philippe Monette 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /todomvc/mithril/README.md: -------------------------------------------------------------------------------- 1 | # Mithril TodoMVC Example 2 | 3 | > Mithril is a client-side Javascript MVC framework, i.e. it's a tool to make application code divided into a data layer (called "Model"), a UI layer (called View), and a glue layer (called Controller) 4 | 5 | > _[Mithril](http://lhorie.github.io/mithril/)_ 6 | 7 | 8 | ## Learning Mithril 9 | 10 | The [Mithril website](http://lhorie.github.io/mithril/) is a great resource for getting started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Getting Started](http://lhorie.github.io/mithril/getting-started.html) 15 | * [API Reference](http://lhorie.github.io/mithril/mithril.html) 16 | * [Mithril on Github](https://github.com/lhorie/mithril.js) 17 | 18 | _If you have other helpful links to share, or find any of the links above no longer work, please [let me know](https://github.com/jpmonette/todomvc-mithril/issues)._ 19 | 20 | ## Running 21 | 22 | 1. Clone the repo 23 | 2. Execute `bower install` in the repo (make sure yo install `npm`) 24 | 3. Open `index.html`! 25 | 26 | ## TODO 27 | 28 | * Proper routing support (fixed, waiting for latest Mithril version (v0.1.1)) 29 | * Select all 30 | * Fix items left behaviour 31 | 32 | ## Credit 33 | 34 | This TodoMVC application was created by [Jean-Philippe Monette](http://blogue.jpmonette.net/) 35 | 36 | ## License 37 | 38 | Copyright (C) 2013, Jean-Philippe Monette 39 | 40 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 45 | -------------------------------------------------------------------------------- /todomvc/mithril/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-mithril", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "mithril": "git://github.com/lhorie/mithril.js.git", 6 | "todomvc-common": "~0.1.4" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril", 3 | "homepage": "https://github.com/lhorie/mithril.js", 4 | "version": "0.1.19", 5 | "_release": "0.1.19", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v0.1.19", 9 | "commit": "c8e0d917f7962a6a2e1b78ce191f74cf1478a4d3" 10 | }, 11 | "_source": "git://github.com/lhorie/mithril.js.git", 12 | "_target": "*", 13 | "_originalSource": "git://github.com/lhorie/mithril.js.git" 14 | } -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | script: 6 | - grunt test 7 | - grunt teste2e 8 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | var version = "0.1.19" 4 | 5 | var inputFolder = "./docs" 6 | var tempFolder = "./temp" 7 | var archiveFolder = "./archive" 8 | var outputFolder = "../mithril" 9 | 10 | var guideLayout = "guide" 11 | var guide = [ 12 | "auto-redrawing", 13 | "benchmarks", 14 | "community", 15 | "compiling-templates", 16 | "comparison", 17 | "components", 18 | "getting-started", 19 | "installation", 20 | "integration", 21 | "practices", 22 | "refactoring", 23 | "routing", 24 | "tools", 25 | "web-services", 26 | ] 27 | var apiLayout = "api" 28 | var api = [ 29 | "change-log", 30 | "roadmap", 31 | "how-to-read-signatures", 32 | "mithril", 33 | "mithril.computation", 34 | "mithril.deferred", 35 | "mithril.module", 36 | "mithril.prop", 37 | "mithril.redraw", 38 | "mithril.render", 39 | "mithril.request", 40 | "mithril.route", 41 | "mithril.sync", 42 | "mithril.trust", 43 | "mithril.withAttr", 44 | "mithril.xhr" 45 | ] 46 | 47 | 48 | 49 | var md2htmlTasks = {} 50 | var makeTasks = function(layout, pages) { 51 | pages.map(function(name) { 52 | md2htmlTasks[name] = { 53 | options: {layout: inputFolder + "/layout/" + layout + ".html"}, 54 | files: [{src: [inputFolder + "/" + name + ".md"], dest: tempFolder + "/" + name + ".html"}] 55 | } 56 | }) 57 | } 58 | makeTasks("guide", guide) 59 | makeTasks("api", api) 60 | 61 | var currentVersionArchiveFolder = archiveFolder + "/v" + version 62 | grunt.initConfig({ 63 | md2html: md2htmlTasks, 64 | uglify: { 65 | options: {banner: "/*\nMithril v" + version + "\nhttp://github.com/lhorie/mithril.js\n(c) Leo Horie\nLicense: MIT\n*/", sourceMap: true}, 66 | mithril: {src: "mithril.js", dest: currentVersionArchiveFolder + "/mithril.min.js"} 67 | }, 68 | concat: { 69 | test: {src: ["mithril.js", "./tests/test.js", "./tests/mock.js", "./tests/mithril-tests.js"], dest: currentVersionArchiveFolder + "/mithril-tests.js"} 70 | }, 71 | zip: { 72 | distribution: { 73 | cwd: currentVersionArchiveFolder + "/", 74 | src: [currentVersionArchiveFolder + "/mithril.min.js", currentVersionArchiveFolder + "/mithril.min.map", currentVersionArchiveFolder + "/mithril.js"], 75 | dest: currentVersionArchiveFolder + "/mithril.min.zip" 76 | } 77 | }, 78 | replace: { 79 | options: {force: true, patterns: [{match: /\.md/g, replacement: ".html"}, {match: /\$version/g, replacement: version}]}, 80 | links: {expand: true, flatten: true, src: [tempFolder + "/**/*.html"], dest: currentVersionArchiveFolder + "/"}, 81 | index: {src: inputFolder + "/layout/index.html", dest: currentVersionArchiveFolder + "/index.html"}, 82 | commonjs: {expand: true, flatten: true, src: [inputFolder + "/layout/*.json"], dest: currentVersionArchiveFolder}, 83 | cdnjs: {src: "deploy/cdnjs-package.json", dest: "../cdnjs/ajax/libs/mithril/package.json"} 84 | }, 85 | copy: { 86 | style: {src: inputFolder + "/layout/style.css", dest: currentVersionArchiveFolder + "/style.css"}, 87 | pages: {src: inputFolder + "/layout/pages.json", dest: currentVersionArchiveFolder + "/pages.json"}, 88 | lib: {expand: true, cwd: inputFolder + "/layout/lib/", src: "./**", dest: currentVersionArchiveFolder + "/lib/"}, 89 | tools: {expand: true, cwd: inputFolder + "/layout/tools/", src: "./**", dest: currentVersionArchiveFolder + "/tools/"}, 90 | comparisons: {expand: true, cwd: inputFolder + "/layout/comparisons/", src: "./**", dest: currentVersionArchiveFolder + "/comparisons/"}, 91 | unminified: {src: "mithril.js", dest: currentVersionArchiveFolder + "/mithril.js"}, 92 | typescript: {src: "mithril.d.ts", dest: currentVersionArchiveFolder + "/mithril.d.ts"}, 93 | publish: {expand: true, cwd: currentVersionArchiveFolder, src: "./**", dest: outputFolder}, 94 | archive: {expand: true, cwd: currentVersionArchiveFolder, src: "./**", dest: outputFolder + "/archive/v" + version}, 95 | cdnjs1: {src: currentVersionArchiveFolder + "/mithril.js", dest: "../cdnjs/ajax/libs/mithril/" + version + "/mithril.js"}, 96 | cdnjs2: {src: currentVersionArchiveFolder + "/mithril.min.js", dest: "../cdnjs/ajax/libs/mithril/" + version + "/mithril.min.js"}, 97 | cdnjs3: {src: currentVersionArchiveFolder + "/mithril.min.map", dest: "../cdnjs/ajax/libs/mithril/" + version + "/mithril.min.map"}, 98 | jsdelivr1: {src: currentVersionArchiveFolder + "/mithril.js", dest: "../jsdelivr/files/mithril/" + version + "/mithril.js"}, 99 | jsdelivr2: {src: currentVersionArchiveFolder + "/mithril.min.js", dest: "../jsdelivr/files/mithril/" + version + "/mithril.min.js"}, 100 | jsdelivr3: {src: currentVersionArchiveFolder + "/mithril.min.map", dest: "../jsdelivr/files/mithril/" + version + "/mithril.min.map"} 101 | }, 102 | execute: { 103 | tests: {src: [currentVersionArchiveFolder + "/mithril-tests.js"]} 104 | }, 105 | qunit: { 106 | all: ['tests/e2e/**/*.html'] 107 | }, 108 | connect: { 109 | server: { 110 | options: { 111 | port: 8000, 112 | base: '.' 113 | } 114 | } 115 | }, 116 | clean: { 117 | options: {force: true}, 118 | generated: [tempFolder] 119 | } 120 | }); 121 | 122 | grunt.loadNpmTasks("grunt-contrib-clean"); 123 | grunt.loadNpmTasks('grunt-contrib-concat'); 124 | grunt.loadNpmTasks("grunt-contrib-copy"); 125 | grunt.loadNpmTasks("grunt-contrib-uglify"); 126 | grunt.loadNpmTasks('grunt-execute'); 127 | grunt.loadNpmTasks("grunt-md2html"); 128 | grunt.loadNpmTasks("grunt-replace"); 129 | grunt.loadNpmTasks('grunt-zip'); 130 | grunt.loadNpmTasks('grunt-contrib-qunit'); 131 | grunt.loadNpmTasks('grunt-contrib-connect'); 132 | 133 | grunt.registerTask("build", ["test", "uglify", "zip", "md2html", "replace", "copy", "clean"]); 134 | grunt.registerTask("test", ["concat", "execute"]); 135 | grunt.registerTask('teste2e', ['connect', 'qunit']); 136 | grunt.registerTask("default", ["build"]); 137 | 138 | }; 139 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Leo Horie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/lhorie/mithril.js.svg?branch=master)](https://travis-ci.org/lhorie/mithril.js) 2 | 3 | # Mithril 4 | 5 | A Javascript Framework for Building Brilliant Applications 6 | 7 | See the [website](http://lhorie.github.io/mithril) for documentation 8 | 9 | There's also a [blog](http://lhorie.github.io/mithril-blog) and a [mailing list](https://groups.google.com/forum/#!forum/mithriljs) 10 | 11 | --- 12 | 13 | ## What is Mithril? 14 | 15 | Mithril is a client-side MVC framework - a tool to organize code in a way that is easy to think about and to maintain. 16 | 17 | ### Light-weight 18 | 19 | - Only 4kb gzipped, no dependencies 20 | - Small API, small learning curve 21 | 22 | ### Robust 23 | 24 | - Safe-by-default templates 25 | - Hierarchical MVC via components 26 | 27 | ### Fast 28 | 29 | - Virtual DOM diffing and compilable templates 30 | - Intelligent auto-redrawing system 31 | 32 | --- 33 | 34 | ## Sample code 35 | 36 | ```javascript 37 | //namespace 38 | var app = {}; 39 | 40 | //model 41 | app.PageList = function() { 42 | return m.request({method: "GET", url: "pages.json"}); 43 | }; 44 | 45 | //controller 46 | app.controller = function() { 47 | this.pages = app.PageList(); 48 | 49 | this.rotate = function() { 50 | this.pages().push(this.pages().shift()) 51 | }.bind(this) 52 | }; 53 | 54 | //view 55 | app.view = function(ctrl) { 56 | return [ 57 | ctrl.pages().map(function(page) { 58 | return m("a", {href: page.url}, page.title); 59 | }), 60 | m("a", {onclick: ctrl.rotate}, "Rotate links") 61 | ]; 62 | }; 63 | 64 | //initialize 65 | m.module(document.getElementById("example"), app); 66 | ``` 67 | 68 | --- 69 | 70 | ### Learn more 71 | 72 | - [Tutorial](http://lhorie.github.io/mithril/getting-started.html) 73 | - [Differences from Other MVC Frameworks](http://lhorie.github.io/mithril/comparison.html) 74 | - [Benchmarks](http://lhorie.github.io/mithril/benchmarks.html) -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/background.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/deploy/cdnjs-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril", 3 | "npmName": "mithril", 4 | "version": "$version", 5 | "filename": "mithril.min.js", 6 | "description": "A Javascript Framework for building brilliant applications", 7 | "homepage": "http://lhorie.github.io/mithril", 8 | "license": "MIT", 9 | "main": "mithril", 10 | "keywords": [ 11 | "mvc", 12 | "browser" 13 | ], 14 | "author": "Leo Horie (http://lhorie.blogspot.com/)", 15 | "contributors": [ 16 | "Leo Horie (http://lhorie.blogspot.com/)" 17 | ], 18 | "bugs": "https://github.com/lhorie/mithril.js/issues", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/lhorie/mithril.js.git" 22 | }, 23 | "npmFileMap": [ 24 | { 25 | "basePath": "/", 26 | "files": [ 27 | "mithril.js", 28 | "mithril.min.js", 29 | "mithril.min.map" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/mithril.d.ts: -------------------------------------------------------------------------------- 1 | //Mithril type definitions for Typescript 2 | 3 | interface MithrilStatic { 4 | (selector: string, attributes: Object, children?: any): MithrilVirtualElement; 5 | (selector: string, children?: any): MithrilVirtualElement; 6 | prop(value?: any): (value?: any) => any; 7 | withAttr(property: string, callback: (value: any) => void): (e: Event) => any; 8 | module(rootElement: Element, module: MithrilModule): void; 9 | trust(html: string): String; 10 | render(rootElement: Element, children?: any): void; 11 | render(rootElement: HTMLDocument, children?: any): void; 12 | redraw(): void; 13 | route(rootElement: Element, defaultRoute: string, routes: { [key: string]: MithrilModule }): void; 14 | route(rootElement: HTMLDocument, defaultRoute: string, routes: { [key: string]: MithrilModule }): void; 15 | route(path: string, params?: any, shouldReplaceHistory?: boolean): void; 16 | route(): string; 17 | route(element: Element, isInitialized: boolean): void; 18 | request(options: MithrilXHROptions): MithrilPromise; 19 | deferred(): MithrilDeferred; 20 | sync(promises: MithrilPromise[]): MithrilPromise; 21 | startComputation(): void; 22 | endComputation(): void; 23 | } 24 | 25 | interface MithrilVirtualElement { 26 | tag: string; 27 | attrs: Object; 28 | children: any; 29 | } 30 | 31 | interface MithrilModule { 32 | controller: Function; 33 | view: Function; 34 | } 35 | 36 | interface MithrilDeferred { 37 | resolve(value?: any): void; 38 | reject(value?: any): void; 39 | promise: MithrilPromise; 40 | } 41 | 42 | interface MithrilPromise { 43 | (value?: any): any; 44 | then(successCallback?: (value: any) => any, errorCallback?: (value: any) => any): MithrilPromise; 45 | } 46 | 47 | interface MithrilXHROptions { 48 | method: string; 49 | url: string; 50 | user?: string; 51 | password?: string; 52 | data?: any; 53 | background?: boolean; 54 | unwrapSuccess?(data: any): any; 55 | unwrapError?(data: any): any; 56 | serialize?(dataToSerialize: any): string; 57 | deserialize?(dataToDeserialize: string): any; 58 | extract?(xhr: XMLHttpRequest, options: MithrilXHROptions); 59 | type?(data: Object): void; 60 | config?(xhr: XMLHttpRequest, options: MithrilXHROptions) 61 | } 62 | 63 | declare var Mithril: MithrilStatic; 64 | declare var m: MithrilStatic; -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/mithril/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "test": "grunt test" 6 | }, 7 | "devDependencies": { 8 | "grunt-cli": "*", 9 | "grunt-contrib-copy": "*", 10 | "grunt-contrib-uglify": "*", 11 | "grunt-contrib-clean": "*", 12 | "grunt-contrib-concat": "*", 13 | "grunt-contrib-watch": "*", 14 | "grunt-execute": "*", 15 | "grunt-md2html": "*", 16 | "grunt-replace": "*", 17 | "grunt-contrib-qunit": "*", 18 | "grunt-contrib-connect": "*", 19 | "grunt-zip": "*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9", 4 | "homepage": "https://github.com/tastejs/todomvc-common", 5 | "_release": "0.1.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v0.1.9", 9 | "commit": "7dd61b0ebf56c020e719a69444442cc7ae7242ff" 10 | }, 11 | "_source": "git://github.com/tastejs/todomvc-common.git", 12 | "_target": "~0.1.4", 13 | "_originalSource": "todomvc-common" 14 | } -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | color: inherit; 16 | -webkit-appearance: none; 17 | -ms-appearance: none; 18 | -o-appearance: none; 19 | appearance: none; 20 | } 21 | 22 | body { 23 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 24 | line-height: 1.4em; 25 | background: #eaeaea url('bg.png'); 26 | color: #4d4d4d; 27 | width: 550px; 28 | margin: 0 auto; 29 | -webkit-font-smoothing: antialiased; 30 | -moz-font-smoothing: antialiased; 31 | -ms-font-smoothing: antialiased; 32 | -o-font-smoothing: antialiased; 33 | font-smoothing: antialiased; 34 | } 35 | 36 | button, 37 | input[type="checkbox"] { 38 | outline: none; 39 | } 40 | 41 | #todoapp { 42 | background: #fff; 43 | background: rgba(255, 255, 255, 0.9); 44 | margin: 130px 0 40px 0; 45 | border: 1px solid #ccc; 46 | position: relative; 47 | border-top-left-radius: 2px; 48 | border-top-right-radius: 2px; 49 | box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), 50 | 0 25px 50px 0 rgba(0, 0, 0, 0.15); 51 | } 52 | 53 | #todoapp:before { 54 | content: ''; 55 | border-left: 1px solid #f5d6d6; 56 | border-right: 1px solid #f5d6d6; 57 | width: 2px; 58 | position: absolute; 59 | top: 0; 60 | left: 40px; 61 | height: 100%; 62 | } 63 | 64 | #todoapp input::-webkit-input-placeholder { 65 | font-style: italic; 66 | } 67 | 68 | #todoapp input::-moz-placeholder { 69 | font-style: italic; 70 | color: #a9a9a9; 71 | } 72 | 73 | #todoapp h1 { 74 | position: absolute; 75 | top: -120px; 76 | width: 100%; 77 | font-size: 70px; 78 | font-weight: bold; 79 | text-align: center; 80 | color: #b3b3b3; 81 | color: rgba(255, 255, 255, 0.3); 82 | text-shadow: -1px -1px rgba(0, 0, 0, 0.2); 83 | -webkit-text-rendering: optimizeLegibility; 84 | -moz-text-rendering: optimizeLegibility; 85 | -ms-text-rendering: optimizeLegibility; 86 | -o-text-rendering: optimizeLegibility; 87 | text-rendering: optimizeLegibility; 88 | } 89 | 90 | #header { 91 | padding-top: 15px; 92 | border-radius: inherit; 93 | } 94 | 95 | #header:before { 96 | content: ''; 97 | position: absolute; 98 | top: 0; 99 | right: 0; 100 | left: 0; 101 | height: 15px; 102 | z-index: 2; 103 | border-bottom: 1px solid #6c615c; 104 | background: #8d7d77; 105 | background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); 106 | background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 107 | background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 108 | filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); 109 | border-top-left-radius: 1px; 110 | border-top-right-radius: 1px; 111 | } 112 | 113 | #new-todo, 114 | .edit { 115 | position: relative; 116 | margin: 0; 117 | width: 100%; 118 | font-size: 24px; 119 | font-family: inherit; 120 | line-height: 1.4em; 121 | border: 0; 122 | outline: none; 123 | color: inherit; 124 | padding: 6px; 125 | border: 1px solid #999; 126 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 127 | -moz-box-sizing: border-box; 128 | -ms-box-sizing: border-box; 129 | -o-box-sizing: border-box; 130 | box-sizing: border-box; 131 | -webkit-font-smoothing: antialiased; 132 | -moz-font-smoothing: antialiased; 133 | -ms-font-smoothing: antialiased; 134 | -o-font-smoothing: antialiased; 135 | font-smoothing: antialiased; 136 | } 137 | 138 | #new-todo { 139 | padding: 16px 16px 16px 60px; 140 | border: none; 141 | background: rgba(0, 0, 0, 0.02); 142 | z-index: 2; 143 | box-shadow: none; 144 | } 145 | 146 | #main { 147 | position: relative; 148 | z-index: 2; 149 | border-top: 1px dotted #adadad; 150 | } 151 | 152 | label[for='toggle-all'] { 153 | display: none; 154 | } 155 | 156 | #toggle-all { 157 | position: absolute; 158 | top: -42px; 159 | left: -4px; 160 | width: 40px; 161 | text-align: center; 162 | /* Mobile Safari */ 163 | border: none; 164 | } 165 | 166 | #toggle-all:before { 167 | content: '»'; 168 | font-size: 28px; 169 | color: #d9d9d9; 170 | padding: 0 25px 7px; 171 | } 172 | 173 | #toggle-all:checked:before { 174 | color: #737373; 175 | } 176 | 177 | #todo-list { 178 | margin: 0; 179 | padding: 0; 180 | list-style: none; 181 | } 182 | 183 | #todo-list li { 184 | position: relative; 185 | font-size: 24px; 186 | border-bottom: 1px dotted #ccc; 187 | } 188 | 189 | #todo-list li:last-child { 190 | border-bottom: none; 191 | } 192 | 193 | #todo-list li.editing { 194 | border-bottom: none; 195 | padding: 0; 196 | } 197 | 198 | #todo-list li.editing .edit { 199 | display: block; 200 | width: 506px; 201 | padding: 13px 17px 12px 17px; 202 | margin: 0 0 0 43px; 203 | } 204 | 205 | #todo-list li.editing .view { 206 | display: none; 207 | } 208 | 209 | #todo-list li .toggle { 210 | text-align: center; 211 | width: 40px; 212 | /* auto, since non-WebKit browsers doesn't support input styling */ 213 | height: auto; 214 | position: absolute; 215 | top: 0; 216 | bottom: 0; 217 | margin: auto 0; 218 | /* Mobile Safari */ 219 | border: none; 220 | -webkit-appearance: none; 221 | -ms-appearance: none; 222 | -o-appearance: none; 223 | appearance: none; 224 | } 225 | 226 | #todo-list li .toggle:after { 227 | content: '✔'; 228 | /* 40 + a couple of pixels visual adjustment */ 229 | line-height: 43px; 230 | font-size: 20px; 231 | color: #d9d9d9; 232 | text-shadow: 0 -1px 0 #bfbfbf; 233 | } 234 | 235 | #todo-list li .toggle:checked:after { 236 | color: #85ada7; 237 | text-shadow: 0 1px 0 #669991; 238 | bottom: 1px; 239 | position: relative; 240 | } 241 | 242 | #todo-list li label { 243 | white-space: pre; 244 | word-break: break-word; 245 | padding: 15px 60px 15px 15px; 246 | margin-left: 45px; 247 | display: block; 248 | line-height: 1.2; 249 | -webkit-transition: color 0.4s; 250 | transition: color 0.4s; 251 | } 252 | 253 | #todo-list li.completed label { 254 | color: #a9a9a9; 255 | text-decoration: line-through; 256 | } 257 | 258 | #todo-list li .destroy { 259 | display: none; 260 | position: absolute; 261 | top: 0; 262 | right: 10px; 263 | bottom: 0; 264 | width: 40px; 265 | height: 40px; 266 | margin: auto 0; 267 | font-size: 22px; 268 | color: #a88a8a; 269 | -webkit-transition: all 0.2s; 270 | transition: all 0.2s; 271 | } 272 | 273 | #todo-list li .destroy:hover { 274 | text-shadow: 0 0 1px #000, 275 | 0 0 10px rgba(199, 107, 107, 0.8); 276 | -webkit-transform: scale(1.3); 277 | -ms-transform: scale(1.3); 278 | transform: scale(1.3); 279 | } 280 | 281 | #todo-list li .destroy:after { 282 | content: '✖'; 283 | } 284 | 285 | #todo-list li:hover .destroy { 286 | display: block; 287 | } 288 | 289 | #todo-list li .edit { 290 | display: none; 291 | } 292 | 293 | #todo-list li.editing:last-child { 294 | margin-bottom: -1px; 295 | } 296 | 297 | #footer { 298 | color: #777; 299 | padding: 0 15px; 300 | position: absolute; 301 | right: 0; 302 | bottom: -31px; 303 | left: 0; 304 | height: 20px; 305 | z-index: 1; 306 | text-align: center; 307 | } 308 | 309 | #footer:before { 310 | content: ''; 311 | position: absolute; 312 | right: 0; 313 | bottom: 31px; 314 | left: 0; 315 | height: 50px; 316 | z-index: -1; 317 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 318 | 0 6px 0 -3px rgba(255, 255, 255, 0.8), 319 | 0 7px 1px -3px rgba(0, 0, 0, 0.3), 320 | 0 43px 0 -6px rgba(255, 255, 255, 0.8), 321 | 0 44px 2px -6px rgba(0, 0, 0, 0.2); 322 | } 323 | 324 | #todo-count { 325 | float: left; 326 | text-align: left; 327 | } 328 | 329 | #filters { 330 | margin: 0; 331 | padding: 0; 332 | list-style: none; 333 | position: absolute; 334 | right: 0; 335 | left: 0; 336 | } 337 | 338 | #filters li { 339 | display: inline; 340 | } 341 | 342 | #filters li a { 343 | color: #83756f; 344 | margin: 2px; 345 | text-decoration: none; 346 | } 347 | 348 | #filters li a.selected { 349 | font-weight: bold; 350 | } 351 | 352 | #clear-completed { 353 | float: right; 354 | position: relative; 355 | line-height: 20px; 356 | text-decoration: none; 357 | background: rgba(0, 0, 0, 0.1); 358 | font-size: 11px; 359 | padding: 0 10px; 360 | border-radius: 3px; 361 | box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); 362 | } 363 | 364 | #clear-completed:hover { 365 | background: rgba(0, 0, 0, 0.15); 366 | box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); 367 | } 368 | 369 | #info { 370 | margin: 65px auto 0; 371 | color: #a6a6a6; 372 | font-size: 12px; 373 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); 374 | text-align: center; 375 | } 376 | 377 | #info a { 378 | color: inherit; 379 | } 380 | 381 | /* 382 | Hack to remove background from Mobile Safari. 383 | Can't use it globally since it destroys checkboxes in Firefox and Opera 384 | */ 385 | 386 | @media screen and (-webkit-min-device-pixel-ratio:0) { 387 | #toggle-all, 388 | #todo-list li .toggle { 389 | background: none; 390 | } 391 | 392 | #todo-list li .toggle { 393 | height: 40px; 394 | } 395 | 396 | #toggle-all { 397 | top: -56px; 398 | left: -15px; 399 | width: 65px; 400 | height: 41px; 401 | -webkit-transform: rotate(90deg); 402 | -ms-transform: rotate(90deg); 403 | transform: rotate(90deg); 404 | -webkit-appearance: none; 405 | appearance: none; 406 | } 407 | } 408 | 409 | .hidden { 410 | display: none; 411 | } 412 | 413 | hr { 414 | margin: 20px 0; 415 | border: 0; 416 | border-top: 1px dashed #C5C5C5; 417 | border-bottom: 1px dashed #F7F7F7; 418 | } 419 | 420 | .learn a { 421 | font-weight: normal; 422 | text-decoration: none; 423 | color: #b83f45; 424 | } 425 | 426 | .learn a:hover { 427 | text-decoration: underline; 428 | color: #787e7e; 429 | } 430 | 431 | .learn h3, 432 | .learn h4, 433 | .learn h5 { 434 | margin: 10px 0; 435 | font-weight: 500; 436 | line-height: 1.2; 437 | color: #000; 438 | } 439 | 440 | .learn h3 { 441 | font-size: 24px; 442 | } 443 | 444 | .learn h4 { 445 | font-size: 18px; 446 | } 447 | 448 | .learn h5 { 449 | margin-bottom: 0; 450 | font-size: 14px; 451 | } 452 | 453 | .learn ul { 454 | padding: 0; 455 | margin: 0 0 30px 25px; 456 | } 457 | 458 | .learn li { 459 | line-height: 20px; 460 | } 461 | 462 | .learn p { 463 | font-size: 15px; 464 | font-weight: 300; 465 | line-height: 1.3; 466 | margin-top: 0; 467 | margin-bottom: 0; 468 | } 469 | 470 | .quote { 471 | border: none; 472 | margin: 20px 0 60px 0; 473 | } 474 | 475 | .quote p { 476 | font-style: italic; 477 | } 478 | 479 | .quote p:before { 480 | content: '“'; 481 | font-size: 50px; 482 | opacity: .15; 483 | position: absolute; 484 | top: -20px; 485 | left: 3px; 486 | } 487 | 488 | .quote p:after { 489 | content: '”'; 490 | font-size: 50px; 491 | opacity: .15; 492 | position: absolute; 493 | bottom: -42px; 494 | right: 3px; 495 | } 496 | 497 | .quote footer { 498 | position: absolute; 499 | bottom: -40px; 500 | right: 0; 501 | } 502 | 503 | .quote footer img { 504 | border-radius: 3px; 505 | } 506 | 507 | .quote footer a { 508 | margin-left: 5px; 509 | vertical-align: middle; 510 | } 511 | 512 | .speech-bubble { 513 | position: relative; 514 | padding: 10px; 515 | background: rgba(0, 0, 0, .04); 516 | border-radius: 5px; 517 | } 518 | 519 | .speech-bubble:after { 520 | content: ''; 521 | position: absolute; 522 | top: 100%; 523 | right: 30px; 524 | border: 13px solid transparent; 525 | border-top-color: rgba(0, 0, 0, .04); 526 | } 527 | 528 | .learn-bar > .learn { 529 | position: absolute; 530 | width: 272px; 531 | top: 8px; 532 | left: -300px; 533 | padding: 10px; 534 | border-radius: 5px; 535 | background-color: rgba(255, 255, 255, .6); 536 | -webkit-transition-property: left; 537 | transition-property: left; 538 | -webkit-transition-duration: 500ms; 539 | transition-duration: 500ms; 540 | } 541 | 542 | @media (min-width: 899px) { 543 | .learn-bar { 544 | width: auto; 545 | margin: 0 0 0 300px; 546 | } 547 | 548 | .learn-bar > .learn { 549 | left: 8px; 550 | } 551 | 552 | .learn-bar #todoapp { 553 | width: 550px; 554 | margin: 130px auto 40px auto; 555 | } 556 | } 557 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/base.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // Underscore's Template Module 5 | // Courtesy of underscorejs.org 6 | var _ = (function (_) { 7 | _.defaults = function (object) { 8 | if (!object) { 9 | return object; 10 | } 11 | for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { 12 | var iterable = arguments[argsIndex]; 13 | if (iterable) { 14 | for (var key in iterable) { 15 | if (object[key] == null) { 16 | object[key] = iterable[key]; 17 | } 18 | } 19 | } 20 | } 21 | return object; 22 | } 23 | 24 | // By default, Underscore uses ERB-style template delimiters, change the 25 | // following template settings to use alternative delimiters. 26 | _.templateSettings = { 27 | evaluate : /<%([\s\S]+?)%>/g, 28 | interpolate : /<%=([\s\S]+?)%>/g, 29 | escape : /<%-([\s\S]+?)%>/g 30 | }; 31 | 32 | // When customizing `templateSettings`, if you don't want to define an 33 | // interpolation, evaluation or escaping regex, we need one that is 34 | // guaranteed not to match. 35 | var noMatch = /(.)^/; 36 | 37 | // Certain characters need to be escaped so that they can be put into a 38 | // string literal. 39 | var escapes = { 40 | "'": "'", 41 | '\\': '\\', 42 | '\r': 'r', 43 | '\n': 'n', 44 | '\t': 't', 45 | '\u2028': 'u2028', 46 | '\u2029': 'u2029' 47 | }; 48 | 49 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 50 | 51 | // JavaScript micro-templating, similar to John Resig's implementation. 52 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 53 | // and correctly escapes quotes within interpolated code. 54 | _.template = function(text, data, settings) { 55 | var render; 56 | settings = _.defaults({}, settings, _.templateSettings); 57 | 58 | // Combine delimiters into one regular expression via alternation. 59 | var matcher = new RegExp([ 60 | (settings.escape || noMatch).source, 61 | (settings.interpolate || noMatch).source, 62 | (settings.evaluate || noMatch).source 63 | ].join('|') + '|$', 'g'); 64 | 65 | // Compile the template source, escaping string literals appropriately. 66 | var index = 0; 67 | var source = "__p+='"; 68 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 69 | source += text.slice(index, offset) 70 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 71 | 72 | if (escape) { 73 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 74 | } 75 | if (interpolate) { 76 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 77 | } 78 | if (evaluate) { 79 | source += "';\n" + evaluate + "\n__p+='"; 80 | } 81 | index = offset + match.length; 82 | return match; 83 | }); 84 | source += "';\n"; 85 | 86 | // If a variable is not specified, place data values in local scope. 87 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 88 | 89 | source = "var __t,__p='',__j=Array.prototype.join," + 90 | "print=function(){__p+=__j.call(arguments,'');};\n" + 91 | source + "return __p;\n"; 92 | 93 | try { 94 | render = new Function(settings.variable || 'obj', '_', source); 95 | } catch (e) { 96 | e.source = source; 97 | throw e; 98 | } 99 | 100 | if (data) return render(data, _); 101 | var template = function(data) { 102 | return render.call(this, data, _); 103 | }; 104 | 105 | // Provide the compiled function source as a convenience for precompilation. 106 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 107 | 108 | return template; 109 | }; 110 | 111 | return _; 112 | })({}); 113 | 114 | if (location.hostname === 'todomvc.com') { 115 | window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); 116 | } 117 | 118 | function redirect() { 119 | if (location.hostname === 'tastejs.github.io') { 120 | location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); 121 | } 122 | } 123 | 124 | function findRoot() { 125 | var base; 126 | 127 | [/labs/, /\w*-examples/].forEach(function (href) { 128 | var match = location.href.match(href); 129 | 130 | if (!base && match) { 131 | base = location.href.indexOf(match); 132 | } 133 | }); 134 | 135 | return location.href.substr(0, base); 136 | } 137 | 138 | function getFile(file, callback) { 139 | if (!location.host) { 140 | return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); 141 | } 142 | 143 | var xhr = new XMLHttpRequest(); 144 | 145 | xhr.open('GET', findRoot() + file, true); 146 | xhr.send(); 147 | 148 | xhr.onload = function () { 149 | if (xhr.status === 200 && callback) { 150 | callback(xhr.responseText); 151 | } 152 | }; 153 | } 154 | 155 | function Learn(learnJSON, config) { 156 | if (!(this instanceof Learn)) { 157 | return new Learn(learnJSON, config); 158 | } 159 | 160 | var template, framework; 161 | 162 | if (typeof learnJSON !== 'object') { 163 | try { 164 | learnJSON = JSON.parse(learnJSON); 165 | } catch (e) { 166 | return; 167 | } 168 | } 169 | 170 | if (config) { 171 | template = config.template; 172 | framework = config.framework; 173 | } 174 | 175 | if (!template && learnJSON.templates) { 176 | template = learnJSON.templates.todomvc; 177 | } 178 | 179 | if (!framework && document.querySelector('[data-framework]')) { 180 | framework = document.querySelector('[data-framework]').getAttribute('data-framework'); 181 | } 182 | 183 | 184 | if (template && learnJSON[framework]) { 185 | this.frameworkJSON = learnJSON[framework]; 186 | this.template = template; 187 | 188 | this.append(); 189 | } 190 | } 191 | 192 | Learn.prototype.append = function () { 193 | var aside = document.createElement('aside'); 194 | aside.innerHTML = _.template(this.template, this.frameworkJSON); 195 | aside.className = 'learn'; 196 | 197 | // Localize demo links 198 | var demoLinks = aside.querySelectorAll('.demo-link'); 199 | Array.prototype.forEach.call(demoLinks, function (demoLink) { 200 | demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); 201 | }); 202 | 203 | document.body.className = (document.body.className + ' learn-bar').trim(); 204 | document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); 205 | }; 206 | 207 | redirect(); 208 | // getFile('learn.json', Learn); 209 | })(); 210 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/mithril/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9" 4 | } 5 | -------------------------------------------------------------------------------- /todomvc/mithril/bower_components/todomvc-common/readme.md: -------------------------------------------------------------------------------- 1 | # todomvc-common 2 | 3 | > Bower component for some common utilities we use in every app 4 | 5 | 6 | ## License 7 | 8 | MIT 9 | -------------------------------------------------------------------------------- /todomvc/mithril/css/app.css: -------------------------------------------------------------------------------- 1 | /* base.css overrides */ 2 | -------------------------------------------------------------------------------- /todomvc/mithril/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mithril • TodoMVC 6 | 7 | 8 | 9 | 10 |
11 |
12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /todomvc/mithril/js/app.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function( window ) { 4 | 'use strict'; 5 | 6 | app.ENTER_KEY = 13; 7 | app.ESC_KEY = 27; 8 | 9 | m.route(document.getElementById('todoapp'), '/', { 10 | '/': app, 11 | '/:filter': app 12 | }); 13 | 14 | })(window); 15 | -------------------------------------------------------------------------------- /todomvc/mithril/js/controllers/todo.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | app.controller = function() { 7 | 8 | this.list = new app.TodoList(); // Todo collection 9 | this.title = m.prop(''); // Temp title placeholder 10 | this.filter = m.prop(m.route.param('filter') || ''); // TodoList filter 11 | 12 | // Add a Todo 13 | this.add = function(title) { 14 | if(this.title()) { 15 | this.list.push(new app.Todo({title: title()})); 16 | this.title(''); 17 | } 18 | }; 19 | 20 | //check whether a todo is visible 21 | this.isVisible = function(todo) { 22 | if(this.filter() == '') 23 | return true; 24 | if (this.filter() == 'active') 25 | return !todo.completed(); 26 | if (this.filter() == 'completed') 27 | return todo.completed(); 28 | } 29 | 30 | this.clearTitle = function() { 31 | this.title('') 32 | } 33 | 34 | // Removing a Todo from the list 35 | this.remove = function(key) { 36 | this.list.splice(key, 1) 37 | } 38 | 39 | // Remove all Todos where Completed == true 40 | this.clearCompleted = function() { 41 | for(var i = 0; i < this.list.length; i++) { 42 | if(this.list[i].completed()) 43 | this.list.splice(i, 1) 44 | } 45 | } 46 | 47 | // Total amount of Todos completed 48 | this.amountCompleted = function() { 49 | var amount = 0; 50 | 51 | for(var i = 0; i < this.list.length; i++) 52 | if(this.list[i].completed()) 53 | amount++; 54 | 55 | return amount; 56 | } 57 | }; 58 | 59 | })(); 60 | -------------------------------------------------------------------------------- /todomvc/mithril/js/models/todo.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | // Todo Model 7 | app.Todo = function(data) { 8 | this.title = m.prop(data.title); 9 | this.completed = m.prop(false); 10 | }; 11 | 12 | // List of Todos 13 | var list = []; 14 | app.TodoList = function() { 15 | return list; 16 | }; 17 | 18 | })(); 19 | -------------------------------------------------------------------------------- /todomvc/mithril/js/views/footer-view.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | app.footer = function(ctrl) { 7 | return m('footer#footer', [ 8 | m('span#todo-count', [ 9 | m('strong', ctrl.list.length), ' item' + (ctrl.list.length > 1 ? 's' : '') + ' left' 10 | ]), 11 | m('ul#filters', [ 12 | m('li', [ 13 | m('a[href=/]', { 14 | config: m.route, 15 | class: ctrl.filter() == '' ? 'selected' : '' 16 | }, 'All') 17 | ]), 18 | m('li', [ 19 | m('a[href=/active]', { 20 | config: m.route, 21 | class: ctrl.filter() == 'active' ? 'selected' : '' 22 | }, 'Active') 23 | ]), 24 | m('li', [ 25 | m('a[href=/completed]', { 26 | config: m.route, 27 | class: ctrl.filter() == 'completed' ? 'selected' : '' 28 | }, 'Completed') 29 | ]) 30 | ]), 31 | ctrl.amountCompleted() == 0 ? '' : m('button#clear-completed', { 32 | onclick: ctrl.clearCompleted.bind(ctrl) 33 | }, 'Clear completed (' + ctrl.amountCompleted() + ')') 34 | ]); 35 | } 36 | 37 | })(); 38 | -------------------------------------------------------------------------------- /todomvc/mithril/js/views/main-view.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | //view utility 7 | app.watchInput = function(ontype, onenter, onescape) { 8 | return function(e) { 9 | ontype(e) 10 | if (e.keyCode == app.ENTER_KEY) onenter() 11 | if (e.keyCode == app.ESC_KEY) onescape() 12 | } 13 | }; 14 | 15 | 16 | 17 | app.view = function(ctrl) { 18 | return [ 19 | m('header#header', [ 20 | m('h1', 'todos'), 21 | m('input#new-todo[placeholder="What needs to be done?"]', { 22 | onkeypress: app.watchInput( 23 | m.withAttr('value', ctrl.title), 24 | ctrl.add.bind(ctrl, ctrl.title), 25 | ctrl.clearTitle.bind(ctrl) 26 | ), 27 | value: ctrl.title() 28 | }) 29 | ]), 30 | m('section#main', [ 31 | m('input#toggle-all[type=checkbox]'), 32 | m('ul#todo-list', [ 33 | ctrl.list.filter(ctrl.isVisible.bind(ctrl)).map(function(task, index) { 34 | return m('li', { class: task.completed() ? 'completed' : ''}, [ 35 | m('.view', [ 36 | m('input.toggle[type=checkbox]', { 37 | onclick: m.withAttr('checked', task.completed), 38 | checked: task.completed() 39 | }), 40 | m('label', task.title()), 41 | m('button.destroy', { onclick: ctrl.remove.bind(ctrl, index)}) 42 | ]), 43 | m('input.edit') 44 | ]) 45 | }) 46 | ]) 47 | ]), 48 | ctrl.list.length == 0 ? '' : app.footer(ctrl) 49 | ]; 50 | }; 51 | 52 | })(); 53 | -------------------------------------------------------------------------------- /todomvc/mithril/js/views/single-view.js: -------------------------------------------------------------------------------- 1 | var app = app || {}; 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | // Single todo view 7 | app.single = function(ctrl, task, index) { 8 | return m('li', { class: task.completed() ? 'completed' : ''}, [ 9 | m('.view', [ 10 | m('input.toggle[type=checkbox]', { 11 | onclick: m.withAttr('checked', task.completed), 12 | checked: task.completed() 13 | }), 14 | m('label', task.title()), 15 | m('button.destroy', { onclick: ctrl.remove.bind(ctrl, index)}) 16 | ]), 17 | m('input.edit') 18 | ]); 19 | }; 20 | 21 | })(); 22 | -------------------------------------------------------------------------------- /todomvc/mogwai/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mogwai • TodoMVC 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /todomvc/mogwai/pkg/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

wasm-pack-template

4 | 5 | A template for kick starting a Rust and WebAssembly project using wasm-pack. 6 | 7 |

8 | Build Status 9 |

10 | 11 |

12 | Tutorial 13 | | 14 | Chat 15 |

16 | 17 | Built with 🦀🕸 by The Rust and WebAssembly Working Group 18 |
19 | 20 | ## About 21 | 22 | [**📚 Read this template tutorial! 📚**][template-docs] 23 | 24 | This template is designed for compiling Rust libraries into WebAssembly and 25 | publishing the resulting package to NPM. 26 | 27 | Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other 28 | templates and usages of `wasm-pack`. 29 | 30 | [tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html 31 | [template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html 32 | 33 | ## 🚴 Usage 34 | 35 | ### 🐑 Use `cargo generate` to Clone this Template 36 | 37 | [Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) 38 | 39 | ``` 40 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project 41 | cd my-project 42 | ``` 43 | 44 | ### 🛠️ Build with `wasm-pack build` 45 | 46 | ``` 47 | wasm-pack build 48 | ``` 49 | 50 | ### 🔬 Test in Headless Browsers with `wasm-pack test` 51 | 52 | ``` 53 | wasm-pack test --headless --firefox 54 | ``` 55 | 56 | ### 🎁 Publish to NPM with `wasm-pack publish` 57 | 58 | ``` 59 | wasm-pack publish 60 | ``` 61 | 62 | ## 🔋 Batteries Included 63 | 64 | * [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating 65 | between WebAssembly and JavaScript. 66 | * [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) 67 | for logging panic messages to the developer console. 68 | * [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized 69 | for small code size. 70 | -------------------------------------------------------------------------------- /todomvc/mogwai/pkg/mogwai_todo.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /** 3 | */ 4 | export function main(): void; 5 | 6 | /** 7 | * If `module_or_path` is {RequestInfo}, makes a request and 8 | * for everything else, calls `WebAssembly.instantiate` directly. 9 | * 10 | * @param {RequestInfo | BufferSource | WebAssembly.Module} module_or_path 11 | * 12 | * @returns {Promise} 13 | */ 14 | export default function init (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise; 15 | -------------------------------------------------------------------------------- /todomvc/mogwai/pkg/mogwai_todo_bg.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export const memory: WebAssembly.Memory; 3 | export function main(): void; 4 | export function __wbindgen_exn_store(a: number): void; 5 | export function __wbindgen_malloc(a: number): number; 6 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | export const __wbg_function_table: WebAssembly.Table; 9 | -------------------------------------------------------------------------------- /todomvc/mogwai/pkg/mogwai_todo_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/mogwai/pkg/mogwai_todo_bg.wasm -------------------------------------------------------------------------------- /todomvc/mogwai/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mogwai-todo", 3 | "collaborators": [ 4 | "Schell Scivally " 5 | ], 6 | "version": "0.1.0", 7 | "files": [ 8 | "mogwai_todo_bg.wasm", 9 | "mogwai_todo.js", 10 | "mogwai_todo.d.ts" 11 | ], 12 | "browser": "mogwai_todo.js", 13 | "types": "mogwai_todo.d.ts" 14 | } -------------------------------------------------------------------------------- /todomvc/mogwai/todo.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | 143 | html, 144 | body { 145 | margin: 0; 146 | padding: 0; 147 | } 148 | 149 | button { 150 | margin: 0; 151 | padding: 0; 152 | border: 0; 153 | background: none; 154 | font-size: 100%; 155 | vertical-align: baseline; 156 | font-family: inherit; 157 | font-weight: inherit; 158 | color: inherit; 159 | -webkit-appearance: none; 160 | appearance: none; 161 | -webkit-font-smoothing: antialiased; 162 | -moz-osx-font-smoothing: grayscale; 163 | } 164 | 165 | body { 166 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 167 | line-height: 1.4em; 168 | background: #f5f5f5; 169 | color: #4d4d4d; 170 | min-width: 230px; 171 | max-width: 550px; 172 | margin: 0 auto; 173 | -webkit-font-smoothing: antialiased; 174 | -moz-osx-font-smoothing: grayscale; 175 | font-weight: 300; 176 | } 177 | 178 | :focus { 179 | outline: 0; 180 | } 181 | 182 | .hidden { 183 | display: none; 184 | } 185 | 186 | .todoapp { 187 | background: #fff; 188 | margin: 130px 0 40px 0; 189 | position: relative; 190 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 191 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 192 | } 193 | 194 | .todoapp input::-webkit-input-placeholder { 195 | font-style: italic; 196 | font-weight: 300; 197 | color: #e6e6e6; 198 | } 199 | 200 | .todoapp input::-moz-placeholder { 201 | font-style: italic; 202 | font-weight: 300; 203 | color: #e6e6e6; 204 | } 205 | 206 | .todoapp input::input-placeholder { 207 | font-style: italic; 208 | font-weight: 300; 209 | color: #e6e6e6; 210 | } 211 | 212 | .todoapp h1 { 213 | position: absolute; 214 | top: -155px; 215 | width: 100%; 216 | font-size: 100px; 217 | font-weight: 100; 218 | text-align: center; 219 | color: rgba(175, 47, 47, 0.15); 220 | -webkit-text-rendering: optimizeLegibility; 221 | -moz-text-rendering: optimizeLegibility; 222 | text-rendering: optimizeLegibility; 223 | } 224 | 225 | .new-todo, 226 | .edit { 227 | position: relative; 228 | margin: 0; 229 | width: 100%; 230 | font-size: 24px; 231 | font-family: inherit; 232 | font-weight: inherit; 233 | line-height: 1.4em; 234 | border: 0; 235 | color: inherit; 236 | padding: 6px; 237 | border: 1px solid #999; 238 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 239 | box-sizing: border-box; 240 | -webkit-font-smoothing: antialiased; 241 | -moz-osx-font-smoothing: grayscale; 242 | } 243 | 244 | .new-todo { 245 | padding: 16px 16px 16px 60px; 246 | border: none; 247 | background: rgba(0, 0, 0, 0.003); 248 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 249 | } 250 | 251 | .main { 252 | position: relative; 253 | z-index: 2; 254 | border-top: 1px solid #e6e6e6; 255 | } 256 | 257 | .toggle-all { 258 | text-align: center; 259 | border: none; /* Mobile Safari */ 260 | opacity: 0; 261 | position: absolute; 262 | } 263 | 264 | .toggle-all + label { 265 | width: 60px; 266 | height: 34px; 267 | font-size: 0; 268 | position: absolute; 269 | top: -52px; 270 | left: -13px; 271 | -webkit-transform: rotate(90deg); 272 | transform: rotate(90deg); 273 | } 274 | 275 | .toggle-all + label:before { 276 | content: '❯'; 277 | font-size: 22px; 278 | color: #e6e6e6; 279 | padding: 10px 27px 10px 27px; 280 | } 281 | 282 | .toggle-all:checked + label:before { 283 | color: #737373; 284 | } 285 | 286 | .todo-list { 287 | margin: 0; 288 | padding: 0; 289 | list-style: none; 290 | } 291 | 292 | .todo-list li { 293 | position: relative; 294 | font-size: 24px; 295 | border-bottom: 1px solid #ededed; 296 | } 297 | 298 | .todo-list li:last-child { 299 | border-bottom: none; 300 | } 301 | 302 | .todo-list li.editing { 303 | border-bottom: none; 304 | padding: 0; 305 | } 306 | 307 | .todo-list li.editing .edit { 308 | display: block; 309 | width: 506px; 310 | padding: 12px 16px; 311 | margin: 0 0 0 43px; 312 | } 313 | 314 | .todo-list li.editing .view { 315 | display: none; 316 | } 317 | 318 | .todo-list li .toggle { 319 | text-align: center; 320 | width: 40px; 321 | /* auto, since non-WebKit browsers doesn't support input styling */ 322 | height: auto; 323 | position: absolute; 324 | top: 0; 325 | bottom: 0; 326 | margin: auto 0; 327 | border: none; /* Mobile Safari */ 328 | -webkit-appearance: none; 329 | appearance: none; 330 | } 331 | 332 | .todo-list li .toggle { 333 | opacity: 0; 334 | } 335 | 336 | .todo-list li .toggle + label { 337 | /* 338 | Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 339 | IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ 340 | */ 341 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); 342 | background-repeat: no-repeat; 343 | background-position: center left; 344 | } 345 | 346 | .todo-list li .toggle:checked + label { 347 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); 348 | } 349 | 350 | .todo-list li label { 351 | word-break: break-all; 352 | padding: 15px 15px 15px 60px; 353 | display: block; 354 | line-height: 1.2; 355 | transition: color 0.4s; 356 | } 357 | 358 | .todo-list li.completed label { 359 | color: #d9d9d9; 360 | text-decoration: line-through; 361 | } 362 | 363 | .todo-list li .destroy { 364 | display: none; 365 | position: absolute; 366 | top: 0; 367 | right: 10px; 368 | bottom: 0; 369 | width: 40px; 370 | height: 40px; 371 | margin: auto 0; 372 | font-size: 30px; 373 | color: #cc9a9a; 374 | margin-bottom: 11px; 375 | transition: color 0.2s ease-out; 376 | } 377 | 378 | .todo-list li .destroy:hover { 379 | color: #af5b5e; 380 | } 381 | 382 | .todo-list li .destroy:after { 383 | content: '×'; 384 | } 385 | 386 | .todo-list li:hover .destroy { 387 | display: block; 388 | } 389 | 390 | .todo-list li .edit { 391 | display: none; 392 | } 393 | 394 | .todo-list li.editing:last-child { 395 | margin-bottom: -1px; 396 | } 397 | 398 | .footer { 399 | color: #777; 400 | padding: 10px 15px; 401 | height: 20px; 402 | text-align: center; 403 | border-top: 1px solid #e6e6e6; 404 | } 405 | 406 | .footer:before { 407 | content: ''; 408 | position: absolute; 409 | right: 0; 410 | bottom: 0; 411 | left: 0; 412 | height: 50px; 413 | overflow: hidden; 414 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 415 | 0 8px 0 -3px #f6f6f6, 416 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 417 | 0 16px 0 -6px #f6f6f6, 418 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 419 | } 420 | 421 | .todo-count { 422 | float: left; 423 | text-align: left; 424 | } 425 | 426 | .todo-count strong { 427 | font-weight: 300; 428 | } 429 | 430 | .filters { 431 | margin: 0; 432 | padding: 0; 433 | list-style: none; 434 | position: absolute; 435 | right: 0; 436 | left: 0; 437 | } 438 | 439 | .filters li { 440 | display: inline; 441 | } 442 | 443 | .filters li a { 444 | color: inherit; 445 | margin: 3px; 446 | padding: 3px 7px; 447 | text-decoration: none; 448 | border: 1px solid transparent; 449 | border-radius: 3px; 450 | } 451 | 452 | .filters li a:hover { 453 | border-color: rgba(175, 47, 47, 0.1); 454 | } 455 | 456 | .filters li a.selected { 457 | border-color: rgba(175, 47, 47, 0.2); 458 | } 459 | 460 | .clear-completed, 461 | html .clear-completed:active { 462 | float: right; 463 | position: relative; 464 | line-height: 20px; 465 | text-decoration: none; 466 | cursor: pointer; 467 | } 468 | 469 | .clear-completed:hover { 470 | text-decoration: underline; 471 | } 472 | 473 | .info { 474 | margin: 65px auto 0; 475 | color: #bfbfbf; 476 | font-size: 10px; 477 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 478 | text-align: center; 479 | } 480 | 481 | .info p { 482 | line-height: 1; 483 | } 484 | 485 | .info a { 486 | color: inherit; 487 | text-decoration: none; 488 | font-weight: 400; 489 | } 490 | 491 | .info a:hover { 492 | text-decoration: underline; 493 | } 494 | 495 | /* 496 | Hack to remove background from Mobile Safari. 497 | Can't use it globally since it destroys checkboxes in Firefox 498 | */ 499 | @media screen and (-webkit-min-device-pixel-ratio:0) { 500 | .toggle-all, 501 | .todo-list li .toggle { 502 | background: none; 503 | } 504 | 505 | .todo-list li .toggle { 506 | height: 40px; 507 | } 508 | } 509 | 510 | @media (max-width: 430px) { 511 | .footer { 512 | height: 50px; 513 | } 514 | 515 | .filters { 516 | bottom: 10px; 517 | } 518 | } 519 | -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9", 4 | "homepage": "https://github.com/tastejs/todomvc-common", 5 | "_release": "0.1.9", 6 | "_resolution": { 7 | "type": "version", 8 | "tag": "v0.1.9", 9 | "commit": "7dd61b0ebf56c020e719a69444442cc7ae7242ff" 10 | }, 11 | "_source": "git://github.com/tastejs/todomvc-common.git", 12 | "_target": "~0.1.4", 13 | "_originalSource": "todomvc-common" 14 | } -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/base.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // Underscore's Template Module 5 | // Courtesy of underscorejs.org 6 | var _ = (function (_) { 7 | _.defaults = function (object) { 8 | if (!object) { 9 | return object; 10 | } 11 | for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { 12 | var iterable = arguments[argsIndex]; 13 | if (iterable) { 14 | for (var key in iterable) { 15 | if (object[key] == null) { 16 | object[key] = iterable[key]; 17 | } 18 | } 19 | } 20 | } 21 | return object; 22 | } 23 | 24 | // By default, Underscore uses ERB-style template delimiters, change the 25 | // following template settings to use alternative delimiters. 26 | _.templateSettings = { 27 | evaluate : /<%([\s\S]+?)%>/g, 28 | interpolate : /<%=([\s\S]+?)%>/g, 29 | escape : /<%-([\s\S]+?)%>/g 30 | }; 31 | 32 | // When customizing `templateSettings`, if you don't want to define an 33 | // interpolation, evaluation or escaping regex, we need one that is 34 | // guaranteed not to match. 35 | var noMatch = /(.)^/; 36 | 37 | // Certain characters need to be escaped so that they can be put into a 38 | // string literal. 39 | var escapes = { 40 | "'": "'", 41 | '\\': '\\', 42 | '\r': 'r', 43 | '\n': 'n', 44 | '\t': 't', 45 | '\u2028': 'u2028', 46 | '\u2029': 'u2029' 47 | }; 48 | 49 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 50 | 51 | // JavaScript micro-templating, similar to John Resig's implementation. 52 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 53 | // and correctly escapes quotes within interpolated code. 54 | _.template = function(text, data, settings) { 55 | var render; 56 | settings = _.defaults({}, settings, _.templateSettings); 57 | 58 | // Combine delimiters into one regular expression via alternation. 59 | var matcher = new RegExp([ 60 | (settings.escape || noMatch).source, 61 | (settings.interpolate || noMatch).source, 62 | (settings.evaluate || noMatch).source 63 | ].join('|') + '|$', 'g'); 64 | 65 | // Compile the template source, escaping string literals appropriately. 66 | var index = 0; 67 | var source = "__p+='"; 68 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 69 | source += text.slice(index, offset) 70 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 71 | 72 | if (escape) { 73 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 74 | } 75 | if (interpolate) { 76 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 77 | } 78 | if (evaluate) { 79 | source += "';\n" + evaluate + "\n__p+='"; 80 | } 81 | index = offset + match.length; 82 | return match; 83 | }); 84 | source += "';\n"; 85 | 86 | // If a variable is not specified, place data values in local scope. 87 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 88 | 89 | source = "var __t,__p='',__j=Array.prototype.join," + 90 | "print=function(){__p+=__j.call(arguments,'');};\n" + 91 | source + "return __p;\n"; 92 | 93 | try { 94 | render = new Function(settings.variable || 'obj', '_', source); 95 | } catch (e) { 96 | e.source = source; 97 | throw e; 98 | } 99 | 100 | if (data) return render(data, _); 101 | var template = function(data) { 102 | return render.call(this, data, _); 103 | }; 104 | 105 | // Provide the compiled function source as a convenience for precompilation. 106 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 107 | 108 | return template; 109 | }; 110 | 111 | return _; 112 | })({}); 113 | 114 | if (location.hostname === 'todomvc.com') { 115 | window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); 116 | } 117 | 118 | function redirect() { 119 | if (location.hostname === 'tastejs.github.io') { 120 | location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); 121 | } 122 | } 123 | 124 | function findRoot() { 125 | var base; 126 | 127 | [/labs/, /\w*-examples/].forEach(function (href) { 128 | var match = location.href.match(href); 129 | 130 | if (!base && match) { 131 | base = location.href.indexOf(match); 132 | } 133 | }); 134 | 135 | return location.href.substr(0, base); 136 | } 137 | 138 | function getFile(file, callback) { 139 | if (!location.host) { 140 | return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); 141 | } 142 | 143 | var xhr = new XMLHttpRequest(); 144 | 145 | xhr.open('GET', findRoot() + file, true); 146 | xhr.send(); 147 | 148 | xhr.onload = function () { 149 | if (xhr.status === 200 && callback) { 150 | callback(xhr.responseText); 151 | } 152 | }; 153 | } 154 | 155 | function Learn(learnJSON, config) { 156 | if (!(this instanceof Learn)) { 157 | return new Learn(learnJSON, config); 158 | } 159 | 160 | var template, framework; 161 | 162 | if (typeof learnJSON !== 'object') { 163 | try { 164 | learnJSON = JSON.parse(learnJSON); 165 | } catch (e) { 166 | return; 167 | } 168 | } 169 | 170 | if (config) { 171 | template = config.template; 172 | framework = config.framework; 173 | } 174 | 175 | if (!template && learnJSON.templates) { 176 | template = learnJSON.templates.todomvc; 177 | } 178 | 179 | if (!framework && document.querySelector('[data-framework]')) { 180 | framework = document.querySelector('[data-framework]').getAttribute('data-framework'); 181 | } 182 | 183 | 184 | if (template && learnJSON[framework]) { 185 | this.frameworkJSON = learnJSON[framework]; 186 | this.template = template; 187 | 188 | this.append(); 189 | } 190 | } 191 | 192 | Learn.prototype.append = function () { 193 | var aside = document.createElement('aside'); 194 | aside.innerHTML = _.template(this.template, this.frameworkJSON); 195 | aside.className = 'learn'; 196 | 197 | // Localize demo links 198 | var demoLinks = aside.querySelectorAll('.demo-link'); 199 | Array.prototype.forEach.call(demoLinks, function (demoLink) { 200 | demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); 201 | }); 202 | 203 | document.body.className = (document.body.className + ' learn-bar').trim(); 204 | document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); 205 | }; 206 | 207 | redirect(); 208 | // getFile('learn.json', Learn); 209 | })(); 210 | -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/preact/bower_components/todomvc-common/bg.png -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc-common", 3 | "version": "0.1.9" 4 | } 5 | -------------------------------------------------------------------------------- /todomvc/preact/bower_components/todomvc-common/readme.md: -------------------------------------------------------------------------------- 1 | # todomvc-common 2 | 3 | > Bower component for some common utilities we use in every app 4 | 5 | 6 | ## License 7 | 8 | MIT 9 | -------------------------------------------------------------------------------- /todomvc/preact/index.html: -------------------------------------------------------------------------------- 1 | Preact • TodoMVC
-------------------------------------------------------------------------------- /todomvc/preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preact-todomvc", 3 | "version": "0.2.0", 4 | "scripts": { 5 | "build": "preact build --template index.html && cp -r build/{bundle.js,bundle.js.map,index.html} ./ && rm -rf build" 6 | }, 7 | "dependencies": { 8 | "linkstate": "^1.0.1", 9 | "preact": "^8.1.0", 10 | "preact-router": "^2.0.0" 11 | }, 12 | "devDependencies": { 13 | "preact-cli": "^1.3.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /todomvc/preact/src/app.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import linkState from 'linkstate'; 3 | import Router from 'preact-router'; 4 | import createTodoModel from './model'; 5 | import TodoHeader from './header'; 6 | import TodoFooter from './footer'; 7 | import TodoItem from './item'; 8 | 9 | const ALL_TODOS = 'all'; 10 | const ACTIVE_TODOS = 'active'; 11 | const COMPLETED_TODOS = 'completed'; 12 | 13 | const FILTERS = { 14 | [ALL_TODOS]: todo => true, 15 | [ACTIVE_TODOS]: todo => !todo.completed, 16 | [COMPLETED_TODOS]: todo => todo.completed 17 | }; 18 | 19 | export default class App extends Component { 20 | model = createTodoModel(); 21 | 22 | state = { 23 | todos: this.model.todos, 24 | nowShowing: ALL_TODOS 25 | }; 26 | 27 | handleRoute = ({ url }) => { 28 | let nowShowing = url.replace(/\/$/,'').split('/').pop(); 29 | if (!FILTERS[nowShowing]) { 30 | nowShowing = ALL_TODOS; 31 | } 32 | this.setState({ nowShowing }); 33 | }; 34 | 35 | toggleAll = e => { 36 | this.model.toggleAll(e.target.checked); 37 | }; 38 | 39 | save = (todo, text) => { 40 | this.model.save(todo, text); 41 | this.reset(); 42 | }; 43 | 44 | reset = () => { 45 | this.setState({ editing: null }); 46 | }; 47 | 48 | componentWillMount() { 49 | this.model.subscribe( state => { 50 | this.setState({ todos: state.todos }); 51 | }); 52 | } 53 | 54 | render({ }, { nowShowing=ALL_TODOS, todos, newTodo, editing }) { 55 | let shownTodos = todos.filter( FILTERS[nowShowing] ), 56 | activeTodoCount = todos.reduce( (a, todo) => a + (todo.completed ? 0 : 1), 0), 57 | completedCount = todos.length - activeTodoCount; 58 | 59 | return ( 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | { todos.length ? ( 68 |
69 | 75 |
    76 | { shownTodos.map( todo => ( 77 | 87 | )) } 88 |
89 |
90 | ) : null } 91 | 92 | { (activeTodoCount || completedCount) ? ( 93 | 99 | ) : null } 100 |
101 | ); 102 | } 103 | } 104 | 105 | 106 | // just a fake component we can feed to router. yay. 107 | const Noop = () => null; 108 | -------------------------------------------------------------------------------- /todomvc/preact/src/footer.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import { pluralize } from './util'; 3 | 4 | const ALL_TODOS = 'all'; 5 | const ACTIVE_TODOS = 'active'; 6 | const COMPLETED_TODOS = 'completed'; 7 | 8 | export default ({ nowShowing, count, completedCount, onClearCompleted }) => ( 9 |
10 | 11 | {count} {pluralize(count, 'item')} left 12 | 13 | 26 | { completedCount > 0 ? ( 27 | 30 | ) : null } 31 |
32 | ); 33 | -------------------------------------------------------------------------------- /todomvc/preact/src/header.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import linkState from 'linkstate'; 3 | 4 | const ENTER_KEY = 13; 5 | 6 | export default class TodoHeader extends Component { 7 | handleKey = e => { 8 | if (e.keyCode!==ENTER_KEY) return; 9 | e.preventDefault(); 10 | 11 | let text = this.state.text.trim(); 12 | if (text) { 13 | this.props.addTodo(text); 14 | this.setState({ text: '' }); 15 | } 16 | }; 17 | 18 | render({ }, { text }) { 19 | return ( 20 | 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /todomvc/preact/src/index.js: -------------------------------------------------------------------------------- 1 | import { h, render } from 'preact'; 2 | import App from './app'; 3 | 4 | render(, document.getElementById('todoapp')); 5 | -------------------------------------------------------------------------------- /todomvc/preact/src/item.js: -------------------------------------------------------------------------------- 1 | import { h, Component } from 'preact'; 2 | import linkState from 'linkstate'; 3 | 4 | const ESCAPE_KEY = 27; 5 | const ENTER_KEY = 13; 6 | 7 | export default class TodoItem extends Component { 8 | handleSubmit = () => { 9 | let val = this.state.editText.trim(), 10 | { todo, onSave, onDestroy } = this.props; 11 | if (val) { 12 | onSave(todo, val); 13 | this.setState({ editText: val }); 14 | } 15 | else { 16 | onDestroy(todo); 17 | } 18 | }; 19 | 20 | handleEdit = () => { 21 | let { todo, onEdit } = this.props; 22 | onEdit(todo); 23 | this.setState({ editText: todo.title }); 24 | }; 25 | 26 | toggle = e => { 27 | let { todo, onToggle } = this.props; 28 | onToggle(todo); 29 | e.preventDefault(); 30 | }; 31 | 32 | handleKeyDown = e => { 33 | let { todo, onCancel } = this.props; 34 | if (e.which===ESCAPE_KEY) { 35 | this.setState({ editText: todo.title }); 36 | onCancel(todo); 37 | } 38 | else if (e.which===ENTER_KEY) { 39 | this.handleSubmit(todo); 40 | } 41 | }; 42 | 43 | destroy = () => { 44 | let { todo, onDestroy } = this.props; 45 | onDestroy(todo); 46 | }; 47 | 48 | focus(c) { 49 | if (c) setTimeout(() => c.focus(), 1); 50 | } 51 | 52 | // componentDidUpdate({ editing }) { 53 | // let node = editing && this.base && this.base.querySelector('.edit'); 54 | // if (node) node.focus(); 55 | // } 56 | 57 | render({ todo:{ title, completed }, editing }, { editText }) { 58 | return ( 59 |
  • 60 |
    61 | 67 | 68 |
    70 | { editing && ( 71 | 79 | ) } 80 |
  • 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /todomvc/preact/src/model.js: -------------------------------------------------------------------------------- 1 | import { uuid, store } from './util'; 2 | 3 | // note: commented out localStorage persistence as it mucks up tests. 4 | 5 | export default () => { 6 | let onChanges = []; 7 | 8 | function inform() { 9 | for (let i=onChanges.length; i--; ) { 10 | onChanges[i](model); 11 | } 12 | } 13 | 14 | let model = { 15 | todos: [], 16 | 17 | onChanges: [], 18 | 19 | subscribe(fn) { 20 | onChanges.push(fn); 21 | }, 22 | 23 | addTodo(title) { 24 | model.todos = model.todos.concat({ 25 | id: uuid(), 26 | title, 27 | completed: false 28 | }); 29 | inform(); 30 | }, 31 | 32 | toggleAll(completed) { 33 | model.todos = model.todos.map( 34 | todo => ({ ...todo, completed }) 35 | ); 36 | inform(); 37 | }, 38 | 39 | toggle(todoToToggle) { 40 | model.todos = model.todos.map( todo => ( 41 | todo !== todoToToggle ? todo : ({ ...todo, completed: !todo.completed }) 42 | ) ); 43 | inform(); 44 | }, 45 | 46 | destroy(todo) { 47 | model.todos = model.todos.filter( t => t !== todo ); 48 | inform(); 49 | }, 50 | 51 | save(todoToSave, title) { 52 | model.todos = model.todos.map( todo => ( 53 | todo !== todoToSave ? todo : ({ ...todo, title }) 54 | )); 55 | inform(); 56 | }, 57 | 58 | clearCompleted() { 59 | model.todos = model.todos.filter( todo => !todo.completed ); 60 | inform(); 61 | } 62 | }; 63 | 64 | return model; 65 | }; 66 | -------------------------------------------------------------------------------- /todomvc/preact/src/util.js: -------------------------------------------------------------------------------- 1 | export function uuid() { 2 | let uuid = ''; 3 | for (let i=0; i<32; i++) { 4 | let random = Math.random() * 16 | 0; 5 | if (i === 8 || i === 12 || i === 16 || i === 20) { 6 | uuid += '-'; 7 | } 8 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16); 9 | } 10 | return uuid; 11 | } 12 | 13 | export function pluralize(count, word) { 14 | return count === 1 ? word : word + 's'; 15 | } 16 | 17 | export function store(namespace, data) { 18 | if (data) { 19 | return localStorage.setItem(namespace, JSON.stringify(data)); 20 | } 21 | 22 | let store = localStorage.getItem(namespace); 23 | return (store && JSON.parse(store)) || []; 24 | } 25 | -------------------------------------------------------------------------------- /todomvc/react/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/director 2 | !node_modules/director/build/director.js 3 | 4 | node_modules/react 5 | !node_modules/react/dist/react-with-addons.js 6 | !node_modules/react/dist/JSXTransformer.js 7 | node_modules/todomvc-app-css 8 | !node_modules/todomvc-app-css/index.css 9 | 10 | node_modules/todomvc-common 11 | !node_modules/todomvc-common/base.css 12 | !node_modules/todomvc-common/base.js 13 | -------------------------------------------------------------------------------- /todomvc/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React • TodoMVC 6 | 7 | 8 | 9 | 10 |
    11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /todomvc/react/js/app.js: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | /*global React, Router, ReactDOM*/ 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 = React.createFactory(app.TodoFooter); 15 | var TodoItem = React.createFactory(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 | newTodo: '' 25 | }; 26 | }, 27 | 28 | componentDidMount: function () { 29 | var setState = this.setState; 30 | var router = Router({ 31 | '/': setState.bind(this, {nowShowing: app.ALL_TODOS}), 32 | '/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}), 33 | '/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS}) 34 | }); 35 | router.init('/'); 36 | }, 37 | 38 | handleChange: function (event) { 39 | this.setState({newTodo: event.target.value}); 40 | }, 41 | 42 | handleNewTodoKeyDown: function (event) { 43 | if (event.keyCode !== ENTER_KEY) { 44 | return; 45 | } 46 | 47 | event.preventDefault(); 48 | 49 | var val = this.state.newTodo.trim(); 50 | 51 | if (val) { 52 | this.props.model.addTodo(val); 53 | this.setState({newTodo: ''}); 54 | } 55 | }, 56 | 57 | toggleAll: function (event) { 58 | var checked = event.target.checked; 59 | this.props.model.toggleAll(checked); 60 | }, 61 | 62 | toggle: function (todoToToggle) { 63 | this.props.model.toggle(todoToToggle); 64 | }, 65 | 66 | destroy: function (todo) { 67 | this.props.model.destroy(todo); 68 | }, 69 | 70 | edit: function (todo) { 71 | this.setState({editing: todo.id}); 72 | }, 73 | 74 | save: function (todoToSave, text) { 75 | this.props.model.save(todoToSave, text); 76 | this.setState({editing: null}); 77 | }, 78 | 79 | cancel: function () { 80 | this.setState({editing: null}); 81 | }, 82 | 83 | clearCompleted: function () { 84 | this.props.model.clearCompleted(); 85 | }, 86 | 87 | render: function () { 88 | var footer; 89 | var main; 90 | var todos = this.props.model.todos; 91 | 92 | var shownTodos = todos.filter(function (todo) { 93 | switch (this.state.nowShowing) { 94 | case app.ACTIVE_TODOS: 95 | return !todo.completed; 96 | case app.COMPLETED_TODOS: 97 | return todo.completed; 98 | default: 99 | return true; 100 | } 101 | }, this); 102 | 103 | var todoItems = shownTodos.map(function (todo) { 104 | return ( 105 | TodoItem({ 106 | key: todo.id, 107 | todo: todo, 108 | onToggle: this.toggle.bind(this, todo), 109 | onDestroy: this.destroy.bind(this, todo), 110 | onEdit: this.edit.bind(this, todo), 111 | editing: this.state.editing === todo.id, 112 | onSave: this.save.bind(this, todo), 113 | onCancel: this.cancel} 114 | ) 115 | ); 116 | }, this); 117 | 118 | var activeTodoCount = todos.reduce(function (accum, todo) { 119 | return todo.completed ? accum : accum + 1; 120 | }, 0); 121 | 122 | var completedCount = todos.length - activeTodoCount; 123 | 124 | if (activeTodoCount || completedCount) { 125 | footer = 126 | TodoFooter({ 127 | count: activeTodoCount, 128 | completedCount: completedCount, 129 | nowShowing: this.state.nowShowing, 130 | onClearCompleted: this.clearCompleted} 131 | ); 132 | } 133 | 134 | if (todos.length) { 135 | main = ( 136 | React.createElement('section', {className: "main"}, [ 137 | React.createElement('input', { 138 | className: "toggle-all", 139 | type: "checkbox", 140 | onChange: this.toggleAll, 141 | checked: activeTodoCount === 0} 142 | ), 143 | React.createElement('ul', {className: "todo-list"}, [ 144 | todoItems 145 | ]) 146 | ]) 147 | ); 148 | } 149 | 150 | return ( 151 | React.createElement('div', null, [ 152 | React.createElement('header', {className: "header"}, [ 153 | React.createElement('h1', null, ["todos"]), 154 | React.createElement('input', { 155 | className: "new-todo", 156 | placeholder: "What needs to be done?", 157 | value: this.state.newTodo, 158 | onKeyDown: this.handleNewTodoKeyDown, 159 | onChange: this.handleChange, 160 | autoFocus: true} 161 | ) 162 | ]), 163 | main, 164 | footer 165 | ]) 166 | ); 167 | } 168 | }); 169 | 170 | var model = new app.TodoModel('react-todos'); 171 | 172 | function render() { 173 | ReactDOM.render( 174 | React.createFactory(TodoApp)({model: model}), 175 | document.getElementsByClassName('todoapp')[0] 176 | ); 177 | } 178 | 179 | window.Utils = app.Utils 180 | model.subscribe(render); 181 | render(); 182 | })(); 183 | -------------------------------------------------------------------------------- /todomvc/react/js/app.jsx: -------------------------------------------------------------------------------- 1 | /*jshint quotmark:false */ 2 | /*jshint white:false */ 3 | /*jshint trailing:false */ 4 | /*jshint newcap:false */ 5 | /*global React, Router, ReactDOM*/ 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 | newTodo: '' 25 | }; 26 | }, 27 | 28 | componentDidMount: function () { 29 | var setState = this.setState; 30 | var router = Router({ 31 | '/': setState.bind(this, {nowShowing: app.ALL_TODOS}), 32 | '/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}), 33 | '/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS}) 34 | }); 35 | router.init('/'); 36 | }, 37 | 38 | handleChange: function (event) { 39 | this.setState({newTodo: event.target.value}); 40 | }, 41 | 42 | handleNewTodoKeyDown: function (event) { 43 | if (event.keyCode !== ENTER_KEY) { 44 | return; 45 | } 46 | 47 | event.preventDefault(); 48 | 49 | var val = this.state.newTodo.trim(); 50 | 51 | if (val) { 52 | this.props.model.addTodo(val); 53 | this.setState({newTodo: ''}); 54 | } 55 | }, 56 | 57 | toggleAll: function (event) { 58 | var checked = event.target.checked; 59 | this.props.model.toggleAll(checked); 60 | }, 61 | 62 | toggle: function (todoToToggle) { 63 | this.props.model.toggle(todoToToggle); 64 | }, 65 | 66 | destroy: function (todo) { 67 | this.props.model.destroy(todo); 68 | }, 69 | 70 | edit: function (todo) { 71 | this.setState({editing: todo.id}); 72 | }, 73 | 74 | save: function (todoToSave, text) { 75 | this.props.model.save(todoToSave, text); 76 | this.setState({editing: null}); 77 | }, 78 | 79 | cancel: function () { 80 | this.setState({editing: null}); 81 | }, 82 | 83 | clearCompleted: function () { 84 | this.props.model.clearCompleted(); 85 | }, 86 | 87 | render: function () { 88 | var footer; 89 | var main; 90 | var todos = this.props.model.todos; 91 | 92 | var shownTodos = todos.filter(function (todo) { 93 | switch (this.state.nowShowing) { 94 | case app.ACTIVE_TODOS: 95 | return !todo.completed; 96 | case app.COMPLETED_TODOS: 97 | return todo.completed; 98 | default: 99 | return true; 100 | } 101 | }, this); 102 | 103 | var todoItems = shownTodos.map(function (todo) { 104 | return ( 105 | 115 | ); 116 | }, this); 117 | 118 | var activeTodoCount = todos.reduce(function (accum, todo) { 119 | return todo.completed ? accum : accum + 1; 120 | }, 0); 121 | 122 | var completedCount = todos.length - activeTodoCount; 123 | 124 | if (activeTodoCount || completedCount) { 125 | footer = 126 | ; 132 | } 133 | 134 | if (todos.length) { 135 | main = ( 136 |
    137 | 143 |
      144 | {todoItems} 145 |
    146 |
    147 | ); 148 | } 149 | 150 | return ( 151 |
    152 |
    153 |

    todos

    154 | 162 |
    163 | {main} 164 | {footer} 165 |
    166 | ); 167 | } 168 | }); 169 | 170 | var model = new app.TodoModel('react-todos'); 171 | 172 | function render() { 173 | ReactDOM.render( 174 | , 175 | document.getElementsByClassName('todoapp')[0] 176 | ); 177 | } 178 | 179 | model.subscribe(render); 180 | render(); 181 | })(); 182 | -------------------------------------------------------------------------------- /todomvc/react/js/footer.js: -------------------------------------------------------------------------------- 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 | React.createElement('button', { 19 | className: "clear-completed", 20 | onClick: this.props.onClearCompleted}, [ 21 | "Clear completed" 22 | ]) 23 | ); 24 | } 25 | 26 | var nowShowing = this.props.nowShowing; 27 | return ( 28 | React.createElement('footer', {className: "footer"}, [ 29 | React.createElement('span', {className: "todo-count"}, [ 30 | React.createElement('strong', null, [this.props.count])," ", activeTodoWord, " left" 31 | ]), 32 | React.createElement('ul', {className: "filters"}, [ 33 | React.createElement('li', null, [ 34 | React.createElement('a', { 35 | href: "#/", 36 | className: classNames({selected: nowShowing === app.ALL_TODOS})}, [ 37 | "All" 38 | ]) 39 | ]), 40 | ' ', 41 | React.createElement('li', null, [ 42 | React.createElement('a', { 43 | href: "#/active", 44 | className: classNames({selected: nowShowing === app.ACTIVE_TODOS})}, [ 45 | "Active" 46 | ]) 47 | ]), 48 | ' ', 49 | React.createElement('li', null, [ 50 | React.createElement('a', { 51 | href: "#/completed", 52 | className: classNames({selected: nowShowing === app.COMPLETED_TODOS})}, [ 53 | "Completed" 54 | ]) 55 | ]) 56 | ]), 57 | clearButton 58 | ]) 59 | ); 60 | } 61 | }); 62 | })(); 63 | -------------------------------------------------------------------------------- /todomvc/react/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 | var nowShowing = this.props.nowShowing; 27 | return ( 28 | 59 | ); 60 | } 61 | }); 62 | })(); 63 | -------------------------------------------------------------------------------- /todomvc/react/js/todoItem.js: -------------------------------------------------------------------------------- 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 | if (this.props.editing) { 41 | this.setState({editText: event.target.value}); 42 | } 43 | }, 44 | 45 | getInitialState: function () { 46 | return {editText: this.props.todo.title}; 47 | }, 48 | 49 | /** 50 | * This is a completely optional performance enhancement that you can 51 | * implement on any React component. If you were to delete this method 52 | * the app would still work correctly (and still be very performant!), we 53 | * just use it as an example of how little code it takes to get an order 54 | * of magnitude performance improvement. 55 | */ 56 | shouldComponentUpdate: function (nextProps, nextState) { 57 | return ( 58 | nextProps.todo !== this.props.todo || 59 | nextProps.editing !== this.props.editing || 60 | nextState.editText !== this.state.editText 61 | ); 62 | }, 63 | 64 | /** 65 | * Safely manipulate the DOM after updating the state when invoking 66 | * `this.props.onEdit()` in the `handleEdit` method above. 67 | * For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate 68 | * and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate 69 | */ 70 | componentDidUpdate: function (prevProps) { 71 | if (!prevProps.editing && this.props.editing) { 72 | var node = React.findDOMNode(this.refs.editField); 73 | node.focus(); 74 | node.setSelectionRange(node.value.length, node.value.length); 75 | } 76 | }, 77 | 78 | render: function () { 79 | return ( 80 | React.createElement('li', {className: classNames({ 81 | completed: this.props.todo.completed, 82 | editing: this.props.editing 83 | })}, [ 84 | React.createElement('div', {className: "view"}, [ 85 | React.createElement('input', { 86 | className: "toggle", 87 | type: "checkbox", 88 | checked: this.props.todo.completed, 89 | onChange: this.props.onToggle} 90 | ), 91 | React.createElement('label', {onDoubleClick: this.handleEdit}, [ 92 | this.props.todo.title 93 | ]), 94 | React.createElement('button', {className: "destroy", onClick: this.props.onDestroy}) 95 | ]), 96 | React.createElement('input', { 97 | ref: "editField", 98 | className: "edit", 99 | value: this.state.editText, 100 | onBlur: this.handleSubmit, 101 | onChange: this.handleChange, 102 | onKeyDown: this.handleKeyDown} 103 | ) 104 | ]) 105 | ); 106 | } 107 | }); 108 | })(); 109 | -------------------------------------------------------------------------------- /todomvc/react/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 | if (this.props.editing) { 41 | this.setState({editText: event.target.value}); 42 | } 43 | }, 44 | 45 | getInitialState: function () { 46 | return {editText: this.props.todo.title}; 47 | }, 48 | 49 | /** 50 | * This is a completely optional performance enhancement that you can 51 | * implement on any React component. If you were to delete this method 52 | * the app would still work correctly (and still be very performant!), we 53 | * just use it as an example of how little code it takes to get an order 54 | * of magnitude performance improvement. 55 | */ 56 | shouldComponentUpdate: function (nextProps, nextState) { 57 | return ( 58 | nextProps.todo !== this.props.todo || 59 | nextProps.editing !== this.props.editing || 60 | nextState.editText !== this.state.editText 61 | ); 62 | }, 63 | 64 | /** 65 | * Safely manipulate the DOM after updating the state when invoking 66 | * `this.props.onEdit()` in the `handleEdit` method above. 67 | * For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate 68 | * and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate 69 | */ 70 | componentDidUpdate: function (prevProps) { 71 | if (!prevProps.editing && this.props.editing) { 72 | var node = React.findDOMNode(this.refs.editField); 73 | node.focus(); 74 | node.setSelectionRange(node.value.length, node.value.length); 75 | } 76 | }, 77 | 78 | render: function () { 79 | return ( 80 |
  • 84 |
    85 | 91 | 94 |
    96 | 104 |
  • 105 | ); 106 | } 107 | }); 108 | })(); 109 | -------------------------------------------------------------------------------- /todomvc/react/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 | -------------------------------------------------------------------------------- /todomvc/react/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 | -------------------------------------------------------------------------------- /todomvc/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "classnames": "^2.1.5", 5 | "director": "^1.2.0", 6 | "react": "^15.0.0", 7 | "react-dom": "^15.0.2", 8 | "todomvc-app-css": "^2.0.0", 9 | "todomvc-common": "^1.0.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /todomvc/react/readme.md: -------------------------------------------------------------------------------- 1 | # React TodoMVC Example 2 | 3 | > React is a JavaScript library for creating user interfaces. Its core principles are declarative code, efficiency, and flexibility. Simply specify what your component looks like and React will keep it up-to-date when the underlying data changes. 4 | 5 | > _[React - facebook.github.io/react](http://facebook.github.io/react)_ 6 | 7 | 8 | ## Learning React 9 | 10 | The [React getting started documentation](http://facebook.github.io/react/docs/getting-started.html) is a great way to get started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Documentation](http://facebook.github.io/react/docs/getting-started.html) 15 | * [API Reference](http://facebook.github.io/react/docs/reference.html) 16 | * [Blog](http://facebook.github.io/react/blog/) 17 | * [React on GitHub](https://github.com/facebook/react) 18 | * [Support](http://facebook.github.io/react/support.html) 19 | 20 | Articles and guides from the community: 21 | 22 | * [How is Facebook's React JavaScript library](http://www.quora.com/React-JS-Library/How-is-Facebooks-React-JavaScript-library) 23 | * [React: Under the hood](http://www.quora.com/Pete-Hunt/Posts/React-Under-the-Hood) 24 | 25 | Get help from other React users: 26 | 27 | * [React on StackOverflow](http://stackoverflow.com/questions/tagged/reactjs) 28 | * [Discussion Forum](https://discuss.reactjs.org/) 29 | 30 | _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ 31 | 32 | 33 | ## Running 34 | 35 | The app is built with [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) and compiled at runtime for a lighter and more fun code reading experience. As stated in the link, JSX is not mandatory. 36 | 37 | To run the app, spin up an HTTP server (e.g. `python -m SimpleHTTPServer`) and visit http://localhost/.../myexample/. 38 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-app-css/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #f5f5f5; 27 | color: #4d4d4d; 28 | min-width: 230px; 29 | max-width: 550px; 30 | margin: 0 auto; 31 | -webkit-font-smoothing: antialiased; 32 | -moz-osx-font-smoothing: grayscale; 33 | font-weight: 300; 34 | } 35 | 36 | button, 37 | input[type="checkbox"] { 38 | outline: none; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | .todoapp { 46 | background: #fff; 47 | margin: 130px 0 40px 0; 48 | position: relative; 49 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 50 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 51 | } 52 | 53 | .todoapp input::-webkit-input-placeholder { 54 | font-style: italic; 55 | font-weight: 300; 56 | color: #e6e6e6; 57 | } 58 | 59 | .todoapp input::-moz-placeholder { 60 | font-style: italic; 61 | font-weight: 300; 62 | color: #e6e6e6; 63 | } 64 | 65 | .todoapp input::input-placeholder { 66 | font-style: italic; 67 | font-weight: 300; 68 | color: #e6e6e6; 69 | } 70 | 71 | .todoapp h1 { 72 | position: absolute; 73 | top: -155px; 74 | width: 100%; 75 | font-size: 100px; 76 | font-weight: 100; 77 | text-align: center; 78 | color: rgba(175, 47, 47, 0.15); 79 | -webkit-text-rendering: optimizeLegibility; 80 | -moz-text-rendering: optimizeLegibility; 81 | text-rendering: optimizeLegibility; 82 | } 83 | 84 | .new-todo, 85 | .edit { 86 | position: relative; 87 | margin: 0; 88 | width: 100%; 89 | font-size: 24px; 90 | font-family: inherit; 91 | font-weight: inherit; 92 | line-height: 1.4em; 93 | border: 0; 94 | outline: none; 95 | color: inherit; 96 | padding: 6px; 97 | border: 1px solid #999; 98 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 99 | box-sizing: border-box; 100 | -webkit-font-smoothing: antialiased; 101 | -moz-osx-font-smoothing: grayscale; 102 | } 103 | 104 | .new-todo { 105 | padding: 16px 16px 16px 60px; 106 | border: none; 107 | background: rgba(0, 0, 0, 0.003); 108 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 109 | } 110 | 111 | .main { 112 | position: relative; 113 | z-index: 2; 114 | border-top: 1px solid #e6e6e6; 115 | } 116 | 117 | label[for='toggle-all'] { 118 | display: none; 119 | } 120 | 121 | .toggle-all { 122 | position: absolute; 123 | top: -55px; 124 | left: -12px; 125 | width: 60px; 126 | height: 34px; 127 | text-align: center; 128 | border: none; /* Mobile Safari */ 129 | } 130 | 131 | .toggle-all:before { 132 | content: '❯'; 133 | font-size: 22px; 134 | color: #e6e6e6; 135 | padding: 10px 27px 10px 27px; 136 | } 137 | 138 | .toggle-all:checked:before { 139 | color: #737373; 140 | } 141 | 142 | .todo-list { 143 | margin: 0; 144 | padding: 0; 145 | list-style: none; 146 | } 147 | 148 | .todo-list li { 149 | position: relative; 150 | font-size: 24px; 151 | border-bottom: 1px solid #ededed; 152 | } 153 | 154 | .todo-list li:last-child { 155 | border-bottom: none; 156 | } 157 | 158 | .todo-list li.editing { 159 | border-bottom: none; 160 | padding: 0; 161 | } 162 | 163 | .todo-list li.editing .edit { 164 | display: block; 165 | width: 506px; 166 | padding: 13px 17px 12px 17px; 167 | margin: 0 0 0 43px; 168 | } 169 | 170 | .todo-list li.editing .view { 171 | display: none; 172 | } 173 | 174 | .todo-list li .toggle { 175 | text-align: center; 176 | width: 40px; 177 | /* auto, since non-WebKit browsers doesn't support input styling */ 178 | height: auto; 179 | position: absolute; 180 | top: 0; 181 | bottom: 0; 182 | margin: auto 0; 183 | border: none; /* Mobile Safari */ 184 | -webkit-appearance: none; 185 | appearance: none; 186 | } 187 | 188 | .todo-list li .toggle:after { 189 | content: url('data:image/svg+xml;utf8,'); 190 | } 191 | 192 | .todo-list li .toggle:checked:after { 193 | content: url('data:image/svg+xml;utf8,'); 194 | } 195 | 196 | .todo-list li label { 197 | white-space: pre-line; 198 | word-break: break-all; 199 | padding: 15px 60px 15px 15px; 200 | margin-left: 45px; 201 | display: block; 202 | line-height: 1.2; 203 | transition: color 0.4s; 204 | } 205 | 206 | .todo-list li.completed label { 207 | color: #d9d9d9; 208 | text-decoration: line-through; 209 | } 210 | 211 | .todo-list li .destroy { 212 | display: none; 213 | position: absolute; 214 | top: 0; 215 | right: 10px; 216 | bottom: 0; 217 | width: 40px; 218 | height: 40px; 219 | margin: auto 0; 220 | font-size: 30px; 221 | color: #cc9a9a; 222 | margin-bottom: 11px; 223 | transition: color 0.2s ease-out; 224 | } 225 | 226 | .todo-list li .destroy:hover { 227 | color: #af5b5e; 228 | } 229 | 230 | .todo-list li .destroy:after { 231 | content: '×'; 232 | } 233 | 234 | .todo-list li:hover .destroy { 235 | display: block; 236 | } 237 | 238 | .todo-list li .edit { 239 | display: none; 240 | } 241 | 242 | .todo-list li.editing:last-child { 243 | margin-bottom: -1px; 244 | } 245 | 246 | .footer { 247 | color: #777; 248 | padding: 10px 15px; 249 | height: 20px; 250 | text-align: center; 251 | border-top: 1px solid #e6e6e6; 252 | } 253 | 254 | .footer:before { 255 | content: ''; 256 | position: absolute; 257 | right: 0; 258 | bottom: 0; 259 | left: 0; 260 | height: 50px; 261 | overflow: hidden; 262 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 263 | 0 8px 0 -3px #f6f6f6, 264 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 265 | 0 16px 0 -6px #f6f6f6, 266 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 267 | } 268 | 269 | .todo-count { 270 | float: left; 271 | text-align: left; 272 | } 273 | 274 | .todo-count strong { 275 | font-weight: 300; 276 | } 277 | 278 | .filters { 279 | margin: 0; 280 | padding: 0; 281 | list-style: none; 282 | position: absolute; 283 | right: 0; 284 | left: 0; 285 | } 286 | 287 | .filters li { 288 | display: inline; 289 | } 290 | 291 | .filters li a { 292 | color: inherit; 293 | margin: 3px; 294 | padding: 3px 7px; 295 | text-decoration: none; 296 | border: 1px solid transparent; 297 | border-radius: 3px; 298 | } 299 | 300 | .filters li a.selected, 301 | .filters li a:hover { 302 | border-color: rgba(175, 47, 47, 0.1); 303 | } 304 | 305 | .filters li a.selected { 306 | border-color: rgba(175, 47, 47, 0.2); 307 | } 308 | 309 | .clear-completed, 310 | html .clear-completed:active { 311 | float: right; 312 | position: relative; 313 | line-height: 20px; 314 | text-decoration: none; 315 | cursor: pointer; 316 | } 317 | 318 | .clear-completed:hover { 319 | text-decoration: underline; 320 | } 321 | 322 | .info { 323 | margin: 65px auto 0; 324 | color: #bfbfbf; 325 | font-size: 10px; 326 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 327 | text-align: center; 328 | } 329 | 330 | .info p { 331 | line-height: 1; 332 | } 333 | 334 | .info a { 335 | color: inherit; 336 | text-decoration: none; 337 | font-weight: 400; 338 | } 339 | 340 | .info a:hover { 341 | text-decoration: underline; 342 | } 343 | 344 | /* 345 | Hack to remove background from Mobile Safari. 346 | Can't use it globally since it destroys checkboxes in Firefox 347 | */ 348 | @media screen and (-webkit-min-device-pixel-ratio:0) { 349 | .toggle-all, 350 | .todo-list li .toggle { 351 | background: none; 352 | } 353 | 354 | .todo-list li .toggle { 355 | height: 40px; 356 | } 357 | 358 | .toggle-all { 359 | -webkit-transform: rotate(90deg); 360 | transform: rotate(90deg); 361 | -webkit-appearance: none; 362 | appearance: none; 363 | } 364 | } 365 | 366 | @media (max-width: 430px) { 367 | .footer { 368 | height: 50px; 369 | } 370 | 371 | .filters { 372 | bottom: 10px; 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-app-css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "todomvc-app-css@^2.0.0", 5 | "/Users/lorenzo/Projects/todomvc-perf-comparison/todomvc/react" 6 | ] 7 | ], 8 | "_from": "todomvc-app-css@>=2.0.0 <3.0.0", 9 | "_id": "todomvc-app-css@2.0.4", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/todomvc-app-css", 13 | "_nodeVersion": "4.2.4", 14 | "_npmUser": { 15 | "email": "sindresorhus@gmail.com", 16 | "name": "sindresorhus" 17 | }, 18 | "_npmVersion": "2.14.12", 19 | "_phantomChildren": {}, 20 | "_requested": { 21 | "name": "todomvc-app-css", 22 | "raw": "todomvc-app-css@^2.0.0", 23 | "rawSpec": "^2.0.0", 24 | "scope": null, 25 | "spec": ">=2.0.0 <3.0.0", 26 | "type": "range" 27 | }, 28 | "_requiredBy": [ 29 | "/" 30 | ], 31 | "_resolved": "https://registry.npmjs.org/todomvc-app-css/-/todomvc-app-css-2.0.4.tgz", 32 | "_shasum": "a1b62664d1e3ade62140b6c40a5bb92adea0f0ff", 33 | "_shrinkwrap": null, 34 | "_spec": "todomvc-app-css@^2.0.0", 35 | "_where": "/Users/lorenzo/Projects/todomvc-perf-comparison/todomvc/react", 36 | "author": { 37 | "email": "sindresorhus@gmail.com", 38 | "name": "Sindre Sorhus", 39 | "url": "sindresorhus.com" 40 | }, 41 | "bugs": { 42 | "url": "https://github.com/tastejs/todomvc-app-css/issues" 43 | }, 44 | "dependencies": {}, 45 | "description": "CSS for TodoMVC apps", 46 | "devDependencies": {}, 47 | "directories": {}, 48 | "dist": { 49 | "shasum": "a1b62664d1e3ade62140b6c40a5bb92adea0f0ff", 50 | "tarball": "https://registry.npmjs.org/todomvc-app-css/-/todomvc-app-css-2.0.4.tgz" 51 | }, 52 | "files": [ 53 | "index.css" 54 | ], 55 | "gitHead": "47337a0da5727cbca2672c3055411405bd43bde4", 56 | "homepage": "https://github.com/tastejs/todomvc-app-css", 57 | "keywords": [ 58 | "app", 59 | "css", 60 | "style", 61 | "stylesheet", 62 | "tastejs", 63 | "template", 64 | "todo", 65 | "todomvc" 66 | ], 67 | "license": "CC-BY-4.0", 68 | "maintainers": [ 69 | { 70 | "name": "sindresorhus", 71 | "email": "sindresorhus@gmail.com" 72 | }, 73 | { 74 | "name": "addyosmani", 75 | "email": "addyosmani@gmail.com" 76 | }, 77 | { 78 | "name": "passy", 79 | "email": "phartig@rdrei.net" 80 | }, 81 | { 82 | "name": "stephenplusplus", 83 | "email": "sawchuk@gmail.com" 84 | } 85 | ], 86 | "name": "todomvc-app-css", 87 | "optionalDependencies": {}, 88 | "readme": "ERROR: No README data found!", 89 | "repository": { 90 | "type": "git", 91 | "url": "git+https://github.com/tastejs/todomvc-app-css.git" 92 | }, 93 | "scripts": {}, 94 | "style": "index.css", 95 | "version": "2.0.4" 96 | } 97 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-app-css/readme.md: -------------------------------------------------------------------------------- 1 | # todomvc-app-css 2 | 3 | > CSS for TodoMVC apps 4 | 5 | ![](screenshot.png) 6 | 7 | 8 | ## Install 9 | 10 | 11 | ``` 12 | $ npm install --save todomvc-app-css 13 | ``` 14 | 15 | 16 | ## Getting started 17 | 18 | ```html 19 | 20 | ``` 21 | 22 | See the [TodoMVC app template](https://github.com/tastejs/todomvc-app-template). 23 | 24 | 25 | 26 | ## License 27 | 28 | Creative Commons License
    This work by Sindre Sorhus is licensed under a Creative Commons Attribution 4.0 International License. 29 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/react/todomvc-common/base.js: -------------------------------------------------------------------------------- 1 | /* global _ */ 2 | (function () { 3 | 'use strict'; 4 | 5 | /* jshint ignore:start */ 6 | // Underscore's Template Module 7 | // Courtesy of underscorejs.org 8 | var _ = (function (_) { 9 | _.defaults = function (object) { 10 | if (!object) { 11 | return object; 12 | } 13 | for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { 14 | var iterable = arguments[argsIndex]; 15 | if (iterable) { 16 | for (var key in iterable) { 17 | if (object[key] == null) { 18 | object[key] = iterable[key]; 19 | } 20 | } 21 | } 22 | } 23 | return object; 24 | } 25 | 26 | // By default, Underscore uses ERB-style template delimiters, change the 27 | // following template settings to use alternative delimiters. 28 | _.templateSettings = { 29 | evaluate : /<%([\s\S]+?)%>/g, 30 | interpolate : /<%=([\s\S]+?)%>/g, 31 | escape : /<%-([\s\S]+?)%>/g 32 | }; 33 | 34 | // When customizing `templateSettings`, if you don't want to define an 35 | // interpolation, evaluation or escaping regex, we need one that is 36 | // guaranteed not to match. 37 | var noMatch = /(.)^/; 38 | 39 | // Certain characters need to be escaped so that they can be put into a 40 | // string literal. 41 | var escapes = { 42 | "'": "'", 43 | '\\': '\\', 44 | '\r': 'r', 45 | '\n': 'n', 46 | '\t': 't', 47 | '\u2028': 'u2028', 48 | '\u2029': 'u2029' 49 | }; 50 | 51 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 52 | 53 | // JavaScript micro-templating, similar to John Resig's implementation. 54 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 55 | // and correctly escapes quotes within interpolated code. 56 | _.template = function(text, data, settings) { 57 | var render; 58 | settings = _.defaults({}, settings, _.templateSettings); 59 | 60 | // Combine delimiters into one regular expression via alternation. 61 | var matcher = new RegExp([ 62 | (settings.escape || noMatch).source, 63 | (settings.interpolate || noMatch).source, 64 | (settings.evaluate || noMatch).source 65 | ].join('|') + '|$', 'g'); 66 | 67 | // Compile the template source, escaping string literals appropriately. 68 | var index = 0; 69 | var source = "__p+='"; 70 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 71 | source += text.slice(index, offset) 72 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 73 | 74 | if (escape) { 75 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 76 | } 77 | if (interpolate) { 78 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 79 | } 80 | if (evaluate) { 81 | source += "';\n" + evaluate + "\n__p+='"; 82 | } 83 | index = offset + match.length; 84 | return match; 85 | }); 86 | source += "';\n"; 87 | 88 | // If a variable is not specified, place data values in local scope. 89 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 90 | 91 | source = "var __t,__p='',__j=Array.prototype.join," + 92 | "print=function(){__p+=__j.call(arguments,'');};\n" + 93 | source + "return __p;\n"; 94 | 95 | try { 96 | render = new Function(settings.variable || 'obj', '_', source); 97 | } catch (e) { 98 | e.source = source; 99 | throw e; 100 | } 101 | 102 | if (data) return render(data, _); 103 | var template = function(data) { 104 | return render.call(this, data, _); 105 | }; 106 | 107 | // Provide the compiled function source as a convenience for precompilation. 108 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 109 | 110 | return template; 111 | }; 112 | 113 | return _; 114 | })({}); 115 | 116 | if (location.hostname === 'todomvc.com') { 117 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 118 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 119 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 120 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 121 | ga('create', 'UA-31081062-1', 'auto'); 122 | ga('send', 'pageview'); 123 | } 124 | /* jshint ignore:end */ 125 | 126 | function redirect() { 127 | if (location.hostname === 'tastejs.github.io') { 128 | location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); 129 | } 130 | } 131 | 132 | function findRoot() { 133 | var base = location.href.indexOf('examples/'); 134 | return location.href.substr(0, base); 135 | } 136 | 137 | function getFile(file, callback) { 138 | if (!location.host) { 139 | return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); 140 | } 141 | 142 | var xhr = new XMLHttpRequest(); 143 | 144 | xhr.open('GET', findRoot() + file, true); 145 | xhr.send(); 146 | 147 | xhr.onload = function () { 148 | if (xhr.status === 200 && callback) { 149 | callback(xhr.responseText); 150 | } 151 | }; 152 | } 153 | 154 | function Learn(learnJSON, config) { 155 | if (!(this instanceof Learn)) { 156 | return new Learn(learnJSON, config); 157 | } 158 | 159 | var template, framework; 160 | 161 | if (typeof learnJSON !== 'object') { 162 | try { 163 | learnJSON = JSON.parse(learnJSON); 164 | } catch (e) { 165 | return; 166 | } 167 | } 168 | 169 | if (config) { 170 | template = config.template; 171 | framework = config.framework; 172 | } 173 | 174 | if (!template && learnJSON.templates) { 175 | template = learnJSON.templates.todomvc; 176 | } 177 | 178 | if (!framework && document.querySelector('[data-framework]')) { 179 | framework = document.querySelector('[data-framework]').dataset.framework; 180 | } 181 | 182 | this.template = template; 183 | 184 | if (learnJSON.backend) { 185 | this.frameworkJSON = learnJSON.backend; 186 | this.frameworkJSON.issueLabel = framework; 187 | this.append({ 188 | backend: true 189 | }); 190 | } else if (learnJSON[framework]) { 191 | this.frameworkJSON = learnJSON[framework]; 192 | this.frameworkJSON.issueLabel = framework; 193 | this.append(); 194 | } 195 | 196 | this.fetchIssueCount(); 197 | } 198 | 199 | Learn.prototype.append = function (opts) { 200 | var aside = document.createElement('aside'); 201 | aside.innerHTML = _.template(this.template, this.frameworkJSON); 202 | aside.className = 'learn'; 203 | 204 | if (opts && opts.backend) { 205 | // Remove demo link 206 | var sourceLinks = aside.querySelector('.source-links'); 207 | var heading = sourceLinks.firstElementChild; 208 | var sourceLink = sourceLinks.lastElementChild; 209 | // Correct link path 210 | var href = sourceLink.getAttribute('href'); 211 | sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); 212 | sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; 213 | } else { 214 | // Localize demo links 215 | var demoLinks = aside.querySelectorAll('.demo-link'); 216 | Array.prototype.forEach.call(demoLinks, function (demoLink) { 217 | if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { 218 | demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); 219 | } 220 | }); 221 | } 222 | 223 | document.body.className = (document.body.className + ' learn-bar').trim(); 224 | document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); 225 | }; 226 | 227 | Learn.prototype.fetchIssueCount = function () { 228 | var issueLink = document.getElementById('issue-count-link'); 229 | if (issueLink) { 230 | var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); 231 | var xhr = new XMLHttpRequest(); 232 | xhr.open('GET', url, true); 233 | xhr.onload = function (e) { 234 | var parsedResponse = JSON.parse(e.target.responseText); 235 | if (parsedResponse instanceof Array) { 236 | var count = parsedResponse.length; 237 | if (count !== 0) { 238 | issueLink.innerHTML = 'This app has ' + count + ' open issues'; 239 | document.getElementById('issue-count').style.display = 'inline'; 240 | } 241 | } 242 | }; 243 | xhr.send(); 244 | } 245 | }; 246 | 247 | redirect(); 248 | // getFile('learn.json', Learn); 249 | })(); 250 | -------------------------------------------------------------------------------- /todomvc/sauron/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sauron • TodoMVC 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /todomvc/sauron/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc", 3 | "collaborators": [ 4 | "Jovansonlee Cesar " 5 | ], 6 | "version": "0.1.0", 7 | "license": "MIT", 8 | "files": [ 9 | "todomvc_bg.wasm", 10 | "todomvc.js", 11 | "todomvc.d.ts" 12 | ], 13 | "browser": "todomvc.js", 14 | "types": "todomvc.d.ts" 15 | } -------------------------------------------------------------------------------- /todomvc/sauron/pkg/todomvc.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /** 3 | */ 4 | export function main(): void; 5 | 6 | /** 7 | * If `module_or_path` is {RequestInfo}, makes a request and 8 | * for everything else, calls `WebAssembly.instantiate` directly. 9 | * 10 | * @param {RequestInfo | BufferSource | WebAssembly.Module} module_or_path 11 | * 12 | * @returns {Promise} 13 | */ 14 | export default function init (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise; 15 | -------------------------------------------------------------------------------- /todomvc/sauron/pkg/todomvc_bg.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export const memory: WebAssembly.Memory; 3 | export function main(): void; 4 | export function __wbindgen_exn_store(a: number): void; 5 | export function __wbindgen_malloc(a: number): number; 6 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | export const __wbg_function_table: WebAssembly.Table; 9 | export function __wbindgen_start(): void; 10 | -------------------------------------------------------------------------------- /todomvc/sauron/pkg/todomvc_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/sauron/pkg/todomvc_bg.wasm -------------------------------------------------------------------------------- /todomvc/sauron/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #f5f5f5; 27 | color: #4d4d4d; 28 | min-width: 230px; 29 | max-width: 550px; 30 | margin: 0 auto; 31 | -webkit-font-smoothing: antialiased; 32 | -moz-osx-font-smoothing: grayscale; 33 | font-weight: 300; 34 | } 35 | 36 | :focus { 37 | outline: 0; 38 | } 39 | 40 | .hidden { 41 | display: none; 42 | } 43 | 44 | .todoapp { 45 | background: #fff; 46 | margin: 130px 0 40px 0; 47 | position: relative; 48 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 49 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 50 | } 51 | 52 | .todoapp input::-webkit-input-placeholder { 53 | font-style: italic; 54 | font-weight: 300; 55 | color: #e6e6e6; 56 | } 57 | 58 | .todoapp input::-moz-placeholder { 59 | font-style: italic; 60 | font-weight: 300; 61 | color: #e6e6e6; 62 | } 63 | 64 | .todoapp input::input-placeholder { 65 | font-style: italic; 66 | font-weight: 300; 67 | color: #e6e6e6; 68 | } 69 | 70 | .todoapp h1 { 71 | position: absolute; 72 | top: -155px; 73 | width: 100%; 74 | font-size: 100px; 75 | font-weight: 100; 76 | text-align: center; 77 | color: rgba(175, 47, 47, 0.15); 78 | -webkit-text-rendering: optimizeLegibility; 79 | -moz-text-rendering: optimizeLegibility; 80 | text-rendering: optimizeLegibility; 81 | } 82 | 83 | .new-todo, 84 | .edit { 85 | position: relative; 86 | margin: 0; 87 | width: 100%; 88 | font-size: 24px; 89 | font-family: inherit; 90 | font-weight: inherit; 91 | line-height: 1.4em; 92 | border: 0; 93 | color: inherit; 94 | padding: 6px; 95 | border: 1px solid #999; 96 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 97 | box-sizing: border-box; 98 | -webkit-font-smoothing: antialiased; 99 | -moz-osx-font-smoothing: grayscale; 100 | } 101 | 102 | .new-todo { 103 | padding: 16px 16px 16px 60px; 104 | border: none; 105 | background: rgba(0, 0, 0, 0.003); 106 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 107 | } 108 | 109 | .main { 110 | position: relative; 111 | z-index: 2; 112 | border-top: 1px solid #e6e6e6; 113 | } 114 | 115 | .toggle-all { 116 | width: 1px; 117 | height: 1px; 118 | border: none; /* Mobile Safari */ 119 | opacity: 0; 120 | position: absolute; 121 | right: 100%; 122 | bottom: 100%; 123 | } 124 | 125 | .toggle-all + label { 126 | width: 60px; 127 | height: 34px; 128 | font-size: 0; 129 | position: absolute; 130 | top: -52px; 131 | left: -13px; 132 | -webkit-transform: rotate(90deg); 133 | transform: rotate(90deg); 134 | } 135 | 136 | .toggle-all + label:before { 137 | content: '❯'; 138 | font-size: 22px; 139 | color: #e6e6e6; 140 | padding: 10px 27px 10px 27px; 141 | } 142 | 143 | .toggle-all:checked + label:before { 144 | color: #737373; 145 | } 146 | 147 | .todo-list { 148 | margin: 0; 149 | padding: 0; 150 | list-style: none; 151 | } 152 | 153 | .todo-list li { 154 | position: relative; 155 | font-size: 24px; 156 | border-bottom: 1px solid #ededed; 157 | } 158 | 159 | .todo-list li:last-child { 160 | border-bottom: none; 161 | } 162 | 163 | .todo-list li.editing { 164 | border-bottom: none; 165 | padding: 0; 166 | } 167 | 168 | .todo-list li.editing .edit { 169 | display: block; 170 | width: 506px; 171 | padding: 12px 16px; 172 | margin: 0 0 0 43px; 173 | } 174 | 175 | .todo-list li.editing .view { 176 | display: none; 177 | } 178 | 179 | .todo-list li .toggle { 180 | text-align: center; 181 | width: 40px; 182 | /* auto, since non-WebKit browsers doesn't support input styling */ 183 | height: auto; 184 | position: absolute; 185 | top: 0; 186 | bottom: 0; 187 | margin: auto 0; 188 | border: none; /* Mobile Safari */ 189 | -webkit-appearance: none; 190 | appearance: none; 191 | } 192 | 193 | .todo-list li .toggle { 194 | opacity: 0; 195 | } 196 | 197 | .todo-list li .toggle + label { 198 | /* 199 | Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 200 | IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ 201 | */ 202 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); 203 | background-repeat: no-repeat; 204 | background-position: center left; 205 | } 206 | 207 | .todo-list li .toggle:checked + label { 208 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); 209 | } 210 | 211 | .todo-list li label { 212 | word-break: break-all; 213 | padding: 15px 15px 15px 60px; 214 | display: block; 215 | line-height: 1.2; 216 | transition: color 0.4s; 217 | } 218 | 219 | .todo-list li.completed label { 220 | color: #d9d9d9; 221 | text-decoration: line-through; 222 | } 223 | 224 | .todo-list li .destroy { 225 | display: none; 226 | position: absolute; 227 | top: 0; 228 | right: 10px; 229 | bottom: 0; 230 | width: 40px; 231 | height: 40px; 232 | margin: auto 0; 233 | font-size: 30px; 234 | color: #cc9a9a; 235 | margin-bottom: 11px; 236 | transition: color 0.2s ease-out; 237 | } 238 | 239 | .todo-list li .destroy:hover { 240 | color: #af5b5e; 241 | } 242 | 243 | .todo-list li .destroy:after { 244 | content: '×'; 245 | } 246 | 247 | .todo-list li:hover .destroy { 248 | display: block; 249 | } 250 | 251 | .todo-list li .edit { 252 | display: none; 253 | } 254 | 255 | .todo-list li.editing:last-child { 256 | margin-bottom: -1px; 257 | } 258 | 259 | .footer { 260 | color: #777; 261 | padding: 10px 15px; 262 | height: 20px; 263 | text-align: center; 264 | border-top: 1px solid #e6e6e6; 265 | } 266 | 267 | .footer:before { 268 | content: ''; 269 | position: absolute; 270 | right: 0; 271 | bottom: 0; 272 | left: 0; 273 | height: 50px; 274 | overflow: hidden; 275 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 276 | 0 8px 0 -3px #f6f6f6, 277 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 278 | 0 16px 0 -6px #f6f6f6, 279 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 280 | } 281 | 282 | .todo-count { 283 | float: left; 284 | text-align: left; 285 | } 286 | 287 | .todo-count strong { 288 | font-weight: 300; 289 | } 290 | 291 | .filters { 292 | margin: 0; 293 | padding: 0; 294 | list-style: none; 295 | position: absolute; 296 | right: 0; 297 | left: 0; 298 | } 299 | 300 | .filters li { 301 | display: inline; 302 | } 303 | 304 | .filters li a { 305 | color: inherit; 306 | margin: 3px; 307 | padding: 3px 7px; 308 | text-decoration: none; 309 | border: 1px solid transparent; 310 | border-radius: 3px; 311 | } 312 | 313 | .filters li a:hover { 314 | border-color: rgba(175, 47, 47, 0.1); 315 | } 316 | 317 | .filters li a.selected { 318 | border-color: rgba(175, 47, 47, 0.2); 319 | } 320 | 321 | .clear-completed, 322 | html .clear-completed:active { 323 | float: right; 324 | position: relative; 325 | line-height: 20px; 326 | text-decoration: none; 327 | cursor: pointer; 328 | } 329 | 330 | .clear-completed:hover { 331 | text-decoration: underline; 332 | } 333 | 334 | .info { 335 | margin: 65px auto 0; 336 | color: #bfbfbf; 337 | font-size: 10px; 338 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 339 | text-align: center; 340 | } 341 | 342 | .info p { 343 | line-height: 1; 344 | } 345 | 346 | .info a { 347 | color: inherit; 348 | text-decoration: none; 349 | font-weight: 400; 350 | } 351 | 352 | .info a:hover { 353 | text-decoration: underline; 354 | } 355 | 356 | /* 357 | Hack to remove background from Mobile Safari. 358 | Can't use it globally since it destroys checkboxes in Firefox 359 | */ 360 | @media screen and (-webkit-min-device-pixel-ratio:0) { 361 | .toggle-all, 362 | .todo-list li .toggle { 363 | background: none; 364 | } 365 | 366 | .todo-list li .toggle { 367 | height: 40px; 368 | } 369 | } 370 | 371 | @media (max-width: 430px) { 372 | .footer { 373 | height: 50px; 374 | } 375 | 376 | .filters { 377 | bottom: 10px; 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /todomvc/seed/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #f5f5f5; 27 | color: #4d4d4d; 28 | min-width: 230px; 29 | max-width: 550px; 30 | margin: 0 auto; 31 | -webkit-font-smoothing: antialiased; 32 | -moz-osx-font-smoothing: grayscale; 33 | font-weight: 300; 34 | } 35 | 36 | :focus { 37 | outline: 0; 38 | } 39 | 40 | .hidden { 41 | display: none; 42 | } 43 | 44 | .todoapp { 45 | background: #fff; 46 | margin: 130px 0 40px 0; 47 | position: relative; 48 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 49 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 50 | } 51 | 52 | .todoapp input::-webkit-input-placeholder { 53 | font-style: italic; 54 | font-weight: 300; 55 | color: #e6e6e6; 56 | } 57 | 58 | .todoapp input::-moz-placeholder { 59 | font-style: italic; 60 | font-weight: 300; 61 | color: #e6e6e6; 62 | } 63 | 64 | .todoapp input::input-placeholder { 65 | font-style: italic; 66 | font-weight: 300; 67 | color: #e6e6e6; 68 | } 69 | 70 | .todoapp h1 { 71 | position: absolute; 72 | top: -155px; 73 | width: 100%; 74 | font-size: 100px; 75 | font-weight: 100; 76 | text-align: center; 77 | color: rgba(175, 47, 47, 0.15); 78 | -webkit-text-rendering: optimizeLegibility; 79 | -moz-text-rendering: optimizeLegibility; 80 | text-rendering: optimizeLegibility; 81 | } 82 | 83 | .new-todo, 84 | .edit { 85 | position: relative; 86 | margin: 0; 87 | width: 100%; 88 | font-size: 24px; 89 | font-family: inherit; 90 | font-weight: inherit; 91 | line-height: 1.4em; 92 | border: 0; 93 | color: inherit; 94 | padding: 6px; 95 | border: 1px solid #999; 96 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 97 | box-sizing: border-box; 98 | -webkit-font-smoothing: antialiased; 99 | -moz-osx-font-smoothing: grayscale; 100 | } 101 | 102 | .new-todo { 103 | padding: 16px 16px 16px 60px; 104 | border: none; 105 | background: rgba(0, 0, 0, 0.003); 106 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 107 | } 108 | 109 | .main { 110 | position: relative; 111 | z-index: 2; 112 | border-top: 1px solid #e6e6e6; 113 | } 114 | 115 | .toggle-all { 116 | text-align: center; 117 | border: none; /* Mobile Safari */ 118 | opacity: 0; 119 | position: absolute; 120 | } 121 | 122 | .toggle-all + label { 123 | width: 60px; 124 | height: 34px; 125 | font-size: 0; 126 | position: absolute; 127 | top: -52px; 128 | left: -13px; 129 | -webkit-transform: rotate(90deg); 130 | transform: rotate(90deg); 131 | } 132 | 133 | .toggle-all + label:before { 134 | content: '❯'; 135 | font-size: 22px; 136 | color: #e6e6e6; 137 | padding: 10px 27px 10px 27px; 138 | } 139 | 140 | .toggle-all:checked + label:before { 141 | color: #737373; 142 | } 143 | 144 | .todo-list { 145 | margin: 0; 146 | padding: 0; 147 | list-style: none; 148 | } 149 | 150 | .todo-list li { 151 | position: relative; 152 | font-size: 24px; 153 | border-bottom: 1px solid #ededed; 154 | } 155 | 156 | .todo-list li:last-child { 157 | border-bottom: none; 158 | } 159 | 160 | .todo-list li.editing { 161 | border-bottom: none; 162 | padding: 0; 163 | } 164 | 165 | .todo-list li.editing .edit { 166 | display: block; 167 | width: 506px; 168 | padding: 12px 16px; 169 | margin: 0 0 0 43px; 170 | } 171 | 172 | .todo-list li.editing .view { 173 | display: none; 174 | } 175 | 176 | .todo-list li .toggle { 177 | text-align: center; 178 | width: 40px; 179 | /* auto, since non-WebKit browsers doesn't support input styling */ 180 | height: auto; 181 | position: absolute; 182 | top: 0; 183 | bottom: 0; 184 | margin: auto 0; 185 | border: none; /* Mobile Safari */ 186 | -webkit-appearance: none; 187 | appearance: none; 188 | } 189 | 190 | .todo-list li .toggle { 191 | opacity: 0; 192 | } 193 | 194 | .todo-list li .toggle + label { 195 | /* 196 | Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 197 | IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ 198 | */ 199 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); 200 | background-repeat: no-repeat; 201 | background-position: center left; 202 | } 203 | 204 | .todo-list li .toggle:checked + label { 205 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); 206 | } 207 | 208 | .todo-list li label { 209 | word-break: break-all; 210 | padding: 15px 15px 15px 60px; 211 | display: block; 212 | line-height: 1.2; 213 | transition: color 0.4s; 214 | } 215 | 216 | .todo-list li.completed label { 217 | color: #d9d9d9; 218 | text-decoration: line-through; 219 | } 220 | 221 | .todo-list li .destroy { 222 | display: none; 223 | position: absolute; 224 | top: 0; 225 | right: 10px; 226 | bottom: 0; 227 | width: 40px; 228 | height: 40px; 229 | margin: auto 0; 230 | font-size: 30px; 231 | color: #cc9a9a; 232 | margin-bottom: 11px; 233 | transition: color 0.2s ease-out; 234 | } 235 | 236 | .todo-list li .destroy:hover { 237 | color: #af5b5e; 238 | } 239 | 240 | .todo-list li .destroy:after { 241 | content: '×'; 242 | } 243 | 244 | .todo-list li:hover .destroy { 245 | display: block; 246 | } 247 | 248 | .todo-list li .edit { 249 | display: none; 250 | } 251 | 252 | .todo-list li.editing:last-child { 253 | margin-bottom: -1px; 254 | } 255 | 256 | .footer { 257 | color: #777; 258 | padding: 10px 15px; 259 | height: 20px; 260 | text-align: center; 261 | border-top: 1px solid #e6e6e6; 262 | } 263 | 264 | .footer:before { 265 | content: ''; 266 | position: absolute; 267 | right: 0; 268 | bottom: 0; 269 | left: 0; 270 | height: 50px; 271 | overflow: hidden; 272 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 273 | 0 8px 0 -3px #f6f6f6, 274 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 275 | 0 16px 0 -6px #f6f6f6, 276 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 277 | } 278 | 279 | .todo-count { 280 | float: left; 281 | text-align: left; 282 | } 283 | 284 | .todo-count strong { 285 | font-weight: 300; 286 | } 287 | 288 | .filters { 289 | margin: 0; 290 | padding: 0; 291 | list-style: none; 292 | position: absolute; 293 | right: 0; 294 | left: 0; 295 | } 296 | 297 | .filters li { 298 | display: inline; 299 | } 300 | 301 | .filters li a { 302 | color: inherit; 303 | margin: 3px; 304 | padding: 3px 7px; 305 | text-decoration: none; 306 | border: 1px solid transparent; 307 | border-radius: 3px; 308 | } 309 | 310 | .filters li a:hover { 311 | border-color: rgba(175, 47, 47, 0.1); 312 | } 313 | 314 | .filters li a.selected { 315 | border-color: rgba(175, 47, 47, 0.2); 316 | } 317 | 318 | .clear-completed, 319 | html .clear-completed:active { 320 | float: right; 321 | position: relative; 322 | line-height: 20px; 323 | text-decoration: none; 324 | cursor: pointer; 325 | } 326 | 327 | .clear-completed:hover { 328 | text-decoration: underline; 329 | } 330 | 331 | .info { 332 | margin: 65px auto 0; 333 | color: #bfbfbf; 334 | font-size: 10px; 335 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 336 | text-align: center; 337 | } 338 | 339 | .info p { 340 | line-height: 1; 341 | } 342 | 343 | .info a { 344 | color: inherit; 345 | text-decoration: none; 346 | font-weight: 400; 347 | } 348 | 349 | .info a:hover { 350 | text-decoration: underline; 351 | } 352 | 353 | /* 354 | Hack to remove background from Mobile Safari. 355 | Can't use it globally since it destroys checkboxes in Firefox 356 | */ 357 | @media screen and (-webkit-min-device-pixel-ratio:0) { 358 | .toggle-all, 359 | .todo-list li .toggle { 360 | background: none; 361 | } 362 | 363 | .todo-list li .toggle { 364 | height: 40px; 365 | } 366 | } 367 | 368 | @media (max-width: 430px) { 369 | .footer { 370 | height: 50px; 371 | } 372 | 373 | .filters { 374 | bottom: 10px; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /todomvc/seed/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Seed • TodoMVC 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /todomvc/seed/pkg/README.md: -------------------------------------------------------------------------------- 1 | # Rust & Seed TodoMVC Example 2 | 3 | 4 | > Rust is a systems programming language with a focus on safety, 5 | especially safe concurrency. 6 | 7 | > _[Rust](https://www.rust-lang.org)_ 8 | 9 | > wasm-bindgen, and its web-sys package allow Rust to be used in web browsers via WASM. 10 | 11 | > [Wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/) 12 | 13 | > Seed is a high-level framework for building websites using these tools. 14 | 15 | > _[Seed](https://github.com/seed-rs/seed)_ 16 | 17 | ## Learning Rust 18 | 19 | The [Rust book](https://doc.rust-lang.org/book/index.html) is a great resource for getting started. 20 | 21 | Here are some links you may find helpful: 22 | 23 | * [Code Playground](https://play.rust-lang.org/) 24 | * [Rust Documentation](https://doc.rust-lang.org/) 25 | * [Rust Source Code](https://github.com/rust-lang/rust) 26 | * [wasm-bindgen Source Code](https://github.com/rustwasm/wasm-bindgen) 27 | * [Seed guide](https://github.com/seed-rs/seed) 28 | * [Seed quickstart repo](https://github.com/seed-rs/seed-quickstart) 29 | 30 | Get help from Rust users: 31 | 32 | * [Rust on StackOverflow](http://stackoverflow.com/questions/tagged/rust) 33 | * [Reddit](https://www.reddit.com/r/rust/) 34 | * [Gitter chat](https://gitter.im/rust-lang/rust) 35 | 36 | _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ 37 | 38 | 39 | ## Running 40 | 41 | #### Prerequisites 42 | 43 | - This framework requires you to first install [Rust](https://www.rust-lang.org/tools/install). 44 | - You'll need a recent version of Rust: `rustup update` 45 | - The wasm32 target: `rustup target add wasm32-unknown-unknown` 46 | - And cargo-make: `cargo install --force cargo-make` 47 | 48 | 49 | #### Build & Run 50 | ```bash 51 | cargo make start 52 | ``` 53 | 54 | Open [127.0.0.1:8000](http://127.0.0.1:8000) in your browser. 55 | 56 | --- 57 | 58 | ### [How to make this example standalone] 59 | - **`Makefile.toml`** 60 | - Replace tasks with aliases with their parents. 61 | - Remove root Makefile import. 62 | - **`Cargo.toml`** 63 | - replace Seed path with version number 64 | - This file 65 | - Remove this chapter 66 | -------------------------------------------------------------------------------- /todomvc/seed/pkg/package.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /** 3 | */ 4 | export function render(): void; 5 | 6 | /** 7 | * If `module_or_path` is {RequestInfo}, makes a request and 8 | * for everything else, calls `WebAssembly.instantiate` directly. 9 | * 10 | * @param {RequestInfo | BufferSource | WebAssembly.Module} module_or_path 11 | * 12 | * @returns {Promise} 13 | */ 14 | export default function init (module_or_path?: RequestInfo | BufferSource | WebAssembly.Module): Promise; 15 | -------------------------------------------------------------------------------- /todomvc/seed/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todomvc", 3 | "collaborators": [ 4 | "Your Name " 5 | ], 6 | "version": "0.1.0", 7 | "files": [ 8 | "package_bg.wasm", 9 | "package.js", 10 | "package.d.ts" 11 | ], 12 | "module": "package.js", 13 | "types": "package.d.ts", 14 | "sideEffects": "false" 15 | } -------------------------------------------------------------------------------- /todomvc/seed/pkg/package_bg.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export const memory: WebAssembly.Memory; 3 | export function render(): void; 4 | export function __wbindgen_exn_store(a: number): void; 5 | export function __wbindgen_malloc(a: number): number; 6 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | export const __wbg_function_table: WebAssembly.Table; 9 | export function __wbindgen_start(): void; 10 | -------------------------------------------------------------------------------- /todomvc/seed/pkg/package_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/seed/pkg/package_bg.wasm -------------------------------------------------------------------------------- /todomvc/seed/pkg/todomvc.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /** 3 | */ 4 | export function render(): void; 5 | 6 | /** 7 | * If `module_or_path` is {RequestInfo}, makes a request and 8 | * for everything else, calls `WebAssembly.instantiate` directly. 9 | * 10 | * @param {RequestInfo | BufferSource | WebAssembly.Module} module_or_path 11 | * 12 | * @returns {Promise} 13 | */ 14 | export default function init (module_or_path: RequestInfo | BufferSource | WebAssembly.Module): Promise; 15 | -------------------------------------------------------------------------------- /todomvc/seed/pkg/todomvc_bg.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export const memory: WebAssembly.Memory; 3 | export function render(): void; 4 | export function __wbindgen_exn_store(a: number): void; 5 | export function __wbindgen_malloc(a: number): number; 6 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | export const __wbg_function_table: WebAssembly.Table; 9 | export function __wbindgen_start(): void; 10 | -------------------------------------------------------------------------------- /todomvc/seed/pkg/todomvc_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/seed/pkg/todomvc_bg.wasm -------------------------------------------------------------------------------- /todomvc/seed/text-polyfill.min.js: -------------------------------------------------------------------------------- 1 | (function(l){function m(b){b=void 0===b?"utf-8":b;if("utf-8"!==b)throw new RangeError("Failed to construct 'TextEncoder': The encoding label provided ('"+b+"') is invalid.");}function k(b,a){b=void 0===b?"utf-8":b;a=void 0===a?{fatal:!1}:a;if("utf-8"!==b)throw new RangeError("Failed to construct 'TextDecoder': The encoding label provided ('"+b+"') is invalid.");if(a.fatal)throw Error("Failed to construct 'TextDecoder': the 'fatal' option is unsupported.");}if(l.TextEncoder&&l.TextDecoder)return!1; 2 | Object.defineProperty(m.prototype,"encoding",{value:"utf-8"});m.prototype.encode=function(b,a){a=void 0===a?{stream:!1}:a;if(a.stream)throw Error("Failed to encode: the 'stream' option is unsupported.");a=0;for(var h=b.length,f=0,c=Math.max(32,h+(h>>1)+7),e=new Uint8Array(c>>3<<3);a=d){if(a=d)continue}f+4>e.length&&(c+=8,c*=1+a/b.length*2,c=c>>3<<3, 3 | g=new Uint8Array(c),g.set(e),e=g);if(0===(d&4294967168))e[f++]=d;else{if(0===(d&4294965248))e[f++]=d>>6&31|192;else if(0===(d&4294901760))e[f++]=d>>12&15|224,e[f++]=d>>6&63|128;else if(0===(d&4292870144))e[f++]=d>>18&7|240,e[f++]=d>>12&63|128,e[f++]=d>>6&63|128;else continue;e[f++]=d&63|128}}return e.slice(0,f)};Object.defineProperty(k.prototype,"encoding",{value:"utf-8"});Object.defineProperty(k.prototype,"fatal",{value:!1});Object.defineProperty(k.prototype,"ignoreBOM",{value:!1});k.prototype.decode= 4 | function(b,a){a=void 0===a?{stream:!1}:a;if(a.stream)throw Error("Failed to decode: the 'stream' option is unsupported.");b=new Uint8Array(b);a=0;for(var h=b.length,f=[];a>>10&1023|55296),c=56320| 5 | c&1023);f.push(c)}}return String.fromCharCode.apply(null,f)};l.TextEncoder=m;l.TextDecoder=k})("undefined"!==typeof window?window:"undefined"!==typeof global?global:this); 6 | -------------------------------------------------------------------------------- /todomvc/vue/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/vue/.gitignore -------------------------------------------------------------------------------- /todomvc/vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js • TodoMVC 6 | 7 | 8 | 9 | 10 | 11 |
    12 |
    13 |

    todos

    14 | 15 |
    16 |
    17 | 18 |
      19 |
    • 20 |
      21 | 22 | 23 | 24 |
      25 | 26 |
    • 27 |
    28 |
    29 |
    30 | 31 | {{remaining | pluralize 'item'}} left 32 | 33 | 38 | 41 |
    42 |
    43 |
    44 |

    Double-click to edit a todo

    45 |

    Written by Evan You

    46 |

    Part of TodoMVC

    47 |
    48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /todomvc/vue/js/app.js: -------------------------------------------------------------------------------- 1 | /*global Vue, todoStorage */ 2 | 3 | (function (exports) { 4 | 5 | 'use strict'; 6 | 7 | var filters = { 8 | all: function (todos) { 9 | return todos; 10 | }, 11 | active: function (todos) { 12 | return todos.filter(function (todo) { 13 | return !todo.completed; 14 | }); 15 | }, 16 | completed: function (todos) { 17 | return todos.filter(function (todo) { 18 | return todo.completed; 19 | }); 20 | } 21 | }; 22 | 23 | exports.app = new Vue({ 24 | 25 | // the root element that will be compiled 26 | el: '.todoapp', 27 | 28 | // app initial state 29 | data: { 30 | todos: todoStorage.fetch(), 31 | newTodo: '', 32 | editedTodo: null, 33 | visibility: 'all' 34 | }, 35 | 36 | // watch todos change for localStorage persistence 37 | watch: { 38 | todos: { 39 | deep: true, 40 | handler: todoStorage.save 41 | } 42 | }, 43 | 44 | // computed properties 45 | // http://vuejs.org/guide/computed.html 46 | computed: { 47 | filteredTodos: function () { 48 | return filters[this.visibility](this.todos); 49 | }, 50 | remaining: function () { 51 | return filters.active(this.todos).length; 52 | }, 53 | allDone: { 54 | get: function () { 55 | return this.remaining === 0; 56 | }, 57 | set: function (value) { 58 | this.todos.forEach(function (todo) { 59 | todo.completed = value; 60 | }); 61 | } 62 | } 63 | }, 64 | 65 | // methods that implement data logic. 66 | // note there's no DOM manipulation here at all. 67 | methods: { 68 | 69 | addTodo: function () { 70 | var value = this.newTodo && this.newTodo.trim(); 71 | if (!value) { 72 | return; 73 | } 74 | this.todos.push({ title: value, completed: false }); 75 | this.newTodo = ''; 76 | }, 77 | 78 | removeTodo: function (todo) { 79 | this.todos.$remove(todo); 80 | }, 81 | 82 | editTodo: function (todo) { 83 | this.beforeEditCache = todo.title; 84 | this.editedTodo = todo; 85 | }, 86 | 87 | doneEdit: function (todo) { 88 | if (!this.editedTodo) { 89 | return; 90 | } 91 | this.editedTodo = null; 92 | todo.title = todo.title.trim(); 93 | if (!todo.title) { 94 | this.removeTodo(todo); 95 | } 96 | }, 97 | 98 | cancelEdit: function (todo) { 99 | this.editedTodo = null; 100 | todo.title = this.beforeEditCache; 101 | }, 102 | 103 | removeCompleted: function () { 104 | this.todos = filters.active(this.todos); 105 | } 106 | }, 107 | 108 | // a custom directive to wait for the DOM to be updated 109 | // before focusing on the input field. 110 | // http://vuejs.org/guide/custom-directive.html 111 | directives: { 112 | 'todo-focus': function (value) { 113 | if (!value) { 114 | return; 115 | } 116 | var el = this.el; 117 | Vue.nextTick(function () { 118 | el.focus(); 119 | }); 120 | } 121 | } 122 | }); 123 | 124 | })(window); 125 | -------------------------------------------------------------------------------- /todomvc/vue/js/routes.js: -------------------------------------------------------------------------------- 1 | /*global app, Router */ 2 | 3 | (function (app, Router) { 4 | 5 | 'use strict'; 6 | 7 | var router = new Router(); 8 | 9 | ['all', 'active', 'completed'].forEach(function (visibility) { 10 | router.on(visibility, function () { 11 | app.visibility = visibility; 12 | }); 13 | }); 14 | 15 | router.configure({ 16 | notfound: function () { 17 | window.location.hash = ''; 18 | app.visibility = 'all'; 19 | } 20 | }); 21 | 22 | router.init(); 23 | 24 | })(app, Router); 25 | -------------------------------------------------------------------------------- /todomvc/vue/js/store.js: -------------------------------------------------------------------------------- 1 | /*jshint unused:false */ 2 | 3 | (function (exports) { 4 | 5 | 'use strict'; 6 | 7 | var STORAGE_KEY = 'todos-vuejs'; 8 | 9 | exports.todoStorage = { 10 | fetch: function () { 11 | return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); 12 | }, 13 | save: function (todos) { 14 | localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)); 15 | } 16 | }; 17 | 18 | })(window); 19 | -------------------------------------------------------------------------------- /todomvc/vue/node_modules/todomvc-app-css/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #f5f5f5; 27 | color: #4d4d4d; 28 | min-width: 230px; 29 | max-width: 550px; 30 | margin: 0 auto; 31 | -webkit-font-smoothing: antialiased; 32 | -moz-osx-font-smoothing: grayscale; 33 | font-weight: 300; 34 | } 35 | 36 | :focus { 37 | outline: 0; 38 | } 39 | 40 | .hidden { 41 | display: none; 42 | } 43 | 44 | .todoapp { 45 | background: #fff; 46 | margin: 130px 0 40px 0; 47 | position: relative; 48 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 49 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 50 | } 51 | 52 | .todoapp input::-webkit-input-placeholder { 53 | font-style: italic; 54 | font-weight: 300; 55 | color: #e6e6e6; 56 | } 57 | 58 | .todoapp input::-moz-placeholder { 59 | font-style: italic; 60 | font-weight: 300; 61 | color: #e6e6e6; 62 | } 63 | 64 | .todoapp input::input-placeholder { 65 | font-style: italic; 66 | font-weight: 300; 67 | color: #e6e6e6; 68 | } 69 | 70 | .todoapp h1 { 71 | position: absolute; 72 | top: -155px; 73 | width: 100%; 74 | font-size: 100px; 75 | font-weight: 100; 76 | text-align: center; 77 | color: rgba(175, 47, 47, 0.15); 78 | -webkit-text-rendering: optimizeLegibility; 79 | -moz-text-rendering: optimizeLegibility; 80 | text-rendering: optimizeLegibility; 81 | } 82 | 83 | .new-todo, 84 | .edit { 85 | position: relative; 86 | margin: 0; 87 | width: 100%; 88 | font-size: 24px; 89 | font-family: inherit; 90 | font-weight: inherit; 91 | line-height: 1.4em; 92 | border: 0; 93 | color: inherit; 94 | padding: 6px; 95 | border: 1px solid #999; 96 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 97 | box-sizing: border-box; 98 | -webkit-font-smoothing: antialiased; 99 | -moz-osx-font-smoothing: grayscale; 100 | } 101 | 102 | .new-todo { 103 | padding: 16px 16px 16px 60px; 104 | border: none; 105 | background: rgba(0, 0, 0, 0.003); 106 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 107 | } 108 | 109 | .main { 110 | position: relative; 111 | z-index: 2; 112 | border-top: 1px solid #e6e6e6; 113 | } 114 | 115 | label[for='toggle-all'] { 116 | display: none; 117 | } 118 | 119 | .toggle-all { 120 | position: absolute; 121 | top: -55px; 122 | left: -12px; 123 | width: 60px; 124 | height: 34px; 125 | text-align: center; 126 | border: none; /* Mobile Safari */ 127 | } 128 | 129 | .toggle-all:before { 130 | content: '❯'; 131 | font-size: 22px; 132 | color: #e6e6e6; 133 | padding: 10px 27px 10px 27px; 134 | } 135 | 136 | .toggle-all:checked:before { 137 | color: #737373; 138 | } 139 | 140 | .todo-list { 141 | margin: 0; 142 | padding: 0; 143 | list-style: none; 144 | } 145 | 146 | .todo-list li { 147 | position: relative; 148 | font-size: 24px; 149 | border-bottom: 1px solid #ededed; 150 | } 151 | 152 | .todo-list li:last-child { 153 | border-bottom: none; 154 | } 155 | 156 | .todo-list li.editing { 157 | border-bottom: none; 158 | padding: 0; 159 | } 160 | 161 | .todo-list li.editing .edit { 162 | display: block; 163 | width: 506px; 164 | padding: 12px 16px; 165 | margin: 0 0 0 43px; 166 | } 167 | 168 | .todo-list li.editing .view { 169 | display: none; 170 | } 171 | 172 | .todo-list li .toggle { 173 | text-align: center; 174 | width: 40px; 175 | /* auto, since non-WebKit browsers doesn't support input styling */ 176 | height: auto; 177 | position: absolute; 178 | top: 0; 179 | bottom: 0; 180 | margin: auto 0; 181 | border: none; /* Mobile Safari */ 182 | -webkit-appearance: none; 183 | appearance: none; 184 | } 185 | 186 | .todo-list li .toggle:after { 187 | content: url('data:image/svg+xml;utf8,'); 188 | } 189 | 190 | .todo-list li .toggle:checked:after { 191 | content: url('data:image/svg+xml;utf8,'); 192 | } 193 | 194 | .todo-list li label { 195 | word-break: break-all; 196 | padding: 15px 60px 15px 15px; 197 | margin-left: 45px; 198 | display: block; 199 | line-height: 1.2; 200 | transition: color 0.4s; 201 | } 202 | 203 | .todo-list li.completed label { 204 | color: #d9d9d9; 205 | text-decoration: line-through; 206 | } 207 | 208 | .todo-list li .destroy { 209 | display: none; 210 | position: absolute; 211 | top: 0; 212 | right: 10px; 213 | bottom: 0; 214 | width: 40px; 215 | height: 40px; 216 | margin: auto 0; 217 | font-size: 30px; 218 | color: #cc9a9a; 219 | margin-bottom: 11px; 220 | transition: color 0.2s ease-out; 221 | } 222 | 223 | .todo-list li .destroy:hover { 224 | color: #af5b5e; 225 | } 226 | 227 | .todo-list li .destroy:after { 228 | content: '×'; 229 | } 230 | 231 | .todo-list li:hover .destroy { 232 | display: block; 233 | } 234 | 235 | .todo-list li .edit { 236 | display: none; 237 | } 238 | 239 | .todo-list li.editing:last-child { 240 | margin-bottom: -1px; 241 | } 242 | 243 | .footer { 244 | color: #777; 245 | padding: 10px 15px; 246 | height: 20px; 247 | text-align: center; 248 | border-top: 1px solid #e6e6e6; 249 | } 250 | 251 | .footer:before { 252 | content: ''; 253 | position: absolute; 254 | right: 0; 255 | bottom: 0; 256 | left: 0; 257 | height: 50px; 258 | overflow: hidden; 259 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 260 | 0 8px 0 -3px #f6f6f6, 261 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 262 | 0 16px 0 -6px #f6f6f6, 263 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 264 | } 265 | 266 | .todo-count { 267 | float: left; 268 | text-align: left; 269 | } 270 | 271 | .todo-count strong { 272 | font-weight: 300; 273 | } 274 | 275 | .filters { 276 | margin: 0; 277 | padding: 0; 278 | list-style: none; 279 | position: absolute; 280 | right: 0; 281 | left: 0; 282 | } 283 | 284 | .filters li { 285 | display: inline; 286 | } 287 | 288 | .filters li a { 289 | color: inherit; 290 | margin: 3px; 291 | padding: 3px 7px; 292 | text-decoration: none; 293 | border: 1px solid transparent; 294 | border-radius: 3px; 295 | } 296 | 297 | .filters li a:hover { 298 | border-color: rgba(175, 47, 47, 0.1); 299 | } 300 | 301 | .filters li a.selected { 302 | border-color: rgba(175, 47, 47, 0.2); 303 | } 304 | 305 | .clear-completed, 306 | html .clear-completed:active { 307 | float: right; 308 | position: relative; 309 | line-height: 20px; 310 | text-decoration: none; 311 | cursor: pointer; 312 | } 313 | 314 | .clear-completed:hover { 315 | text-decoration: underline; 316 | } 317 | 318 | .info { 319 | margin: 65px auto 0; 320 | color: #bfbfbf; 321 | font-size: 10px; 322 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 323 | text-align: center; 324 | } 325 | 326 | .info p { 327 | line-height: 1; 328 | } 329 | 330 | .info a { 331 | color: inherit; 332 | text-decoration: none; 333 | font-weight: 400; 334 | } 335 | 336 | .info a:hover { 337 | text-decoration: underline; 338 | } 339 | 340 | /* 341 | Hack to remove background from Mobile Safari. 342 | Can't use it globally since it destroys checkboxes in Firefox 343 | */ 344 | @media screen and (-webkit-min-device-pixel-ratio:0) { 345 | .toggle-all, 346 | .todo-list li .toggle { 347 | background: none; 348 | } 349 | 350 | .todo-list li .toggle { 351 | height: 40px; 352 | } 353 | 354 | .toggle-all { 355 | -webkit-transform: rotate(90deg); 356 | transform: rotate(90deg); 357 | -webkit-appearance: none; 358 | appearance: none; 359 | } 360 | } 361 | 362 | @media (max-width: 430px) { 363 | .footer { 364 | height: 50px; 365 | } 366 | 367 | .filters { 368 | bottom: 10px; 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /todomvc/vue/node_modules/todomvc-common/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /todomvc/vue/node_modules/todomvc-common/base.js: -------------------------------------------------------------------------------- 1 | /* global _ */ 2 | (function () { 3 | 'use strict'; 4 | 5 | /* jshint ignore:start */ 6 | // Underscore's Template Module 7 | // Courtesy of underscorejs.org 8 | var _ = (function (_) { 9 | _.defaults = function (object) { 10 | if (!object) { 11 | return object; 12 | } 13 | for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { 14 | var iterable = arguments[argsIndex]; 15 | if (iterable) { 16 | for (var key in iterable) { 17 | if (object[key] == null) { 18 | object[key] = iterable[key]; 19 | } 20 | } 21 | } 22 | } 23 | return object; 24 | } 25 | 26 | // By default, Underscore uses ERB-style template delimiters, change the 27 | // following template settings to use alternative delimiters. 28 | _.templateSettings = { 29 | evaluate : /<%([\s\S]+?)%>/g, 30 | interpolate : /<%=([\s\S]+?)%>/g, 31 | escape : /<%-([\s\S]+?)%>/g 32 | }; 33 | 34 | // When customizing `templateSettings`, if you don't want to define an 35 | // interpolation, evaluation or escaping regex, we need one that is 36 | // guaranteed not to match. 37 | var noMatch = /(.)^/; 38 | 39 | // Certain characters need to be escaped so that they can be put into a 40 | // string literal. 41 | var escapes = { 42 | "'": "'", 43 | '\\': '\\', 44 | '\r': 'r', 45 | '\n': 'n', 46 | '\t': 't', 47 | '\u2028': 'u2028', 48 | '\u2029': 'u2029' 49 | }; 50 | 51 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 52 | 53 | // JavaScript micro-templating, similar to John Resig's implementation. 54 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 55 | // and correctly escapes quotes within interpolated code. 56 | _.template = function(text, data, settings) { 57 | var render; 58 | settings = _.defaults({}, settings, _.templateSettings); 59 | 60 | // Combine delimiters into one regular expression via alternation. 61 | var matcher = new RegExp([ 62 | (settings.escape || noMatch).source, 63 | (settings.interpolate || noMatch).source, 64 | (settings.evaluate || noMatch).source 65 | ].join('|') + '|$', 'g'); 66 | 67 | // Compile the template source, escaping string literals appropriately. 68 | var index = 0; 69 | var source = "__p+='"; 70 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 71 | source += text.slice(index, offset) 72 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 73 | 74 | if (escape) { 75 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 76 | } 77 | if (interpolate) { 78 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 79 | } 80 | if (evaluate) { 81 | source += "';\n" + evaluate + "\n__p+='"; 82 | } 83 | index = offset + match.length; 84 | return match; 85 | }); 86 | source += "';\n"; 87 | 88 | // If a variable is not specified, place data values in local scope. 89 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 90 | 91 | source = "var __t,__p='',__j=Array.prototype.join," + 92 | "print=function(){__p+=__j.call(arguments,'');};\n" + 93 | source + "return __p;\n"; 94 | 95 | try { 96 | render = new Function(settings.variable || 'obj', '_', source); 97 | } catch (e) { 98 | e.source = source; 99 | throw e; 100 | } 101 | 102 | if (data) return render(data, _); 103 | var template = function(data) { 104 | return render.call(this, data, _); 105 | }; 106 | 107 | // Provide the compiled function source as a convenience for precompilation. 108 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 109 | 110 | return template; 111 | }; 112 | 113 | return _; 114 | })({}); 115 | 116 | if (location.hostname === 'todomvc.com') { 117 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 118 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 119 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 120 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 121 | ga('create', 'UA-31081062-1', 'auto'); 122 | ga('send', 'pageview'); 123 | } 124 | /* jshint ignore:end */ 125 | 126 | function redirect() { 127 | if (location.hostname === 'tastejs.github.io') { 128 | location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); 129 | } 130 | } 131 | 132 | function findRoot() { 133 | var base = location.href.indexOf('examples/'); 134 | return location.href.substr(0, base); 135 | } 136 | 137 | function getFile(file, callback) { 138 | if (!location.host) { 139 | return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); 140 | } 141 | 142 | var xhr = new XMLHttpRequest(); 143 | 144 | xhr.open('GET', findRoot() + file, true); 145 | xhr.send(); 146 | 147 | xhr.onload = function () { 148 | if (xhr.status === 200 && callback) { 149 | callback(xhr.responseText); 150 | } 151 | }; 152 | } 153 | 154 | function Learn(learnJSON, config) { 155 | if (!(this instanceof Learn)) { 156 | return new Learn(learnJSON, config); 157 | } 158 | 159 | var template, framework; 160 | 161 | if (typeof learnJSON !== 'object') { 162 | try { 163 | learnJSON = JSON.parse(learnJSON); 164 | } catch (e) { 165 | return; 166 | } 167 | } 168 | 169 | if (config) { 170 | template = config.template; 171 | framework = config.framework; 172 | } 173 | 174 | if (!template && learnJSON.templates) { 175 | template = learnJSON.templates.todomvc; 176 | } 177 | 178 | if (!framework && document.querySelector('[data-framework]')) { 179 | framework = document.querySelector('[data-framework]').dataset.framework; 180 | } 181 | 182 | this.template = template; 183 | 184 | if (learnJSON.backend) { 185 | this.frameworkJSON = learnJSON.backend; 186 | this.frameworkJSON.issueLabel = framework; 187 | this.append({ 188 | backend: true 189 | }); 190 | } else if (learnJSON[framework]) { 191 | this.frameworkJSON = learnJSON[framework]; 192 | this.frameworkJSON.issueLabel = framework; 193 | this.append(); 194 | } 195 | 196 | this.fetchIssueCount(); 197 | } 198 | 199 | Learn.prototype.append = function (opts) { 200 | var aside = document.createElement('aside'); 201 | aside.innerHTML = _.template(this.template, this.frameworkJSON); 202 | aside.className = 'learn'; 203 | 204 | if (opts && opts.backend) { 205 | // Remove demo link 206 | var sourceLinks = aside.querySelector('.source-links'); 207 | var heading = sourceLinks.firstElementChild; 208 | var sourceLink = sourceLinks.lastElementChild; 209 | // Correct link path 210 | var href = sourceLink.getAttribute('href'); 211 | sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); 212 | sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; 213 | } else { 214 | // Localize demo links 215 | var demoLinks = aside.querySelectorAll('.demo-link'); 216 | Array.prototype.forEach.call(demoLinks, function (demoLink) { 217 | if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { 218 | demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); 219 | } 220 | }); 221 | } 222 | 223 | document.body.className = (document.body.className + ' learn-bar').trim(); 224 | document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); 225 | }; 226 | 227 | Learn.prototype.fetchIssueCount = function () { 228 | var issueLink = document.getElementById('issue-count-link'); 229 | if (issueLink) { 230 | var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); 231 | var xhr = new XMLHttpRequest(); 232 | xhr.open('GET', url, true); 233 | xhr.onload = function (e) { 234 | var parsedResponse = JSON.parse(e.target.responseText); 235 | if (parsedResponse instanceof Array) { 236 | var count = parsedResponse.length; 237 | if (count !== 0) { 238 | issueLink.innerHTML = 'This app has ' + count + ' open issues'; 239 | document.getElementById('issue-count').style.display = 'inline'; 240 | } 241 | } 242 | }; 243 | xhr.send(); 244 | } 245 | }; 246 | 247 | redirect(); 248 | // getFile('learn.json', Learn); 249 | })(); 250 | -------------------------------------------------------------------------------- /todomvc/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "director": "^1.2.8", 5 | "vue": "^1.0.24", 6 | "todomvc-common": "^1.0.2", 7 | "todomvc-app-css": "^2.0.6" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /todomvc/vue/readme.md: -------------------------------------------------------------------------------- 1 | # Vue.js TodoMVC Example 2 | 3 | > Vue.js is a library for building interactive web interfaces. 4 | It provides data-driven, nestable view components with a simple and flexible API. 5 | 6 | > _[Vue.js - vuejs.org](http://vuejs.org)_ 7 | 8 | ## Learning Vue.js 9 | 10 | The [Vue.js website](http://vuejs.org/) is a great resource to get started. 11 | 12 | Here are some links you may find helpful: 13 | 14 | * [Official Guide](http://vuejs.org/guide/) 15 | * [API Reference](http://vuejs.org/api/) 16 | * [Examples](http://vuejs.org/examples/) 17 | * [Building Larger Apps with Vue.js](http://vuejs.org/guide/application.html) 18 | 19 | Get help from other Vue.js users: 20 | 21 | * [Vue.js on Twitter](https://twitter.com/vuejs) 22 | * [Vue.js on Gitter](https://gitter.im/vuejs/vue) 23 | * [Vue.js Forum](http://forum.vuejs.org) 24 | 25 | _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ 26 | 27 | ## Credit 28 | 29 | This TodoMVC application was created by [Evan You](http://evanyou.me). 30 | -------------------------------------------------------------------------------- /todomvc/yew/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yew • TodoMVC 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /todomvc/yew/todomvc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanceras/sauron-perf/fec748b89e00ca85ef9bd12cee6431979151f76b/todomvc/yew/todomvc.wasm --------------------------------------------------------------------------------