WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-171306-20170511173727.patch (text/plain), 93.48 KB, created by
Addy Osmani
on 2017-05-11 17:37:28 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Addy Osmani
Created:
2017-05-11 17:37:28 PDT
Size:
93.48 KB
patch
obsolete
>Subversion Revision: 215683 >diff --git a/PerformanceTests/ChangeLog b/PerformanceTests/ChangeLog >index ec46bf3e8aa2b2162217ec142b0c39d0a99daefc..93652e032c6ff2e1752c313893359f904eb0b5b4 100644 >--- a/PerformanceTests/ChangeLog >+++ b/PerformanceTests/ChangeLog >@@ -1,3 +1,29 @@ >+2017-05-11 Addy Osmani <addyosmani@gmail.com> >+ >+ Speedometer: Update the vanilla JavaScript TodoMVC implem to a more recent version >+ https://bugs.webkit.org/show_bug.cgi?id=171306 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Update vanilla JS TodoMVC implementation to the latest. >+ >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower.json: Removed. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.css: Removed. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.js: Removed. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/bg.png: Removed. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/index.html: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/app.js: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/controller.js: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/helpers.js: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/model.js: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/store.js: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/template.js: Added. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/view.js: >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-app-css/index.css: Added. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.css: Added. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.js: Added. >+ * Speedometer/resources/todomvc/vanilla-examples/vanillajs/package.json: Added. >+ > 2017-04-21 Zalan Bujtas <zalan@apple.com> > > Simple line layout: Add performance test to measure mid-word line breaking with long text. >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower.json b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower.json >deleted file mode 100644 >index 08e3bcee0c6ee06cfeeffd49fde3c80772a97179..0000000000000000000000000000000000000000 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower.json >+++ /dev/null >@@ -1,7 +0,0 @@ >-{ >- "name": "todomvc-vanillajs", >- "version": "0.0.0", >- "dependencies": { >- "todomvc-common": "~0.1.4" >- } >-} >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.css b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.css >deleted file mode 100644 >index 737cd9cc8c7e07e4691d4ee1baa702e4993ca21a..0000000000000000000000000000000000000000 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.css >+++ /dev/null >@@ -1,554 +0,0 @@ >-html, >-body { >- margin: 0; >- padding: 0; >-} >- >-button { >- margin: 0; >- padding: 0; >- border: 0; >- background: none; >- font-size: 100%; >- vertical-align: baseline; >- font-family: inherit; >- color: inherit; >- -webkit-appearance: none; >- /*-moz-appearance: none;*/ >- -ms-appearance: none; >- -o-appearance: none; >- appearance: none; >-} >- >-body { >- font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; >- line-height: 1.4em; >- background: #eaeaea url('bg.png'); >- color: #4d4d4d; >- width: 550px; >- margin: 0 auto; >- -webkit-font-smoothing: antialiased; >- -moz-font-smoothing: antialiased; >- -ms-font-smoothing: antialiased; >- -o-font-smoothing: antialiased; >- font-smoothing: antialiased; >-} >- >-#todoapp { >- background: #fff; >- background: rgba(255, 255, 255, 0.9); >- margin: 130px 0 40px 0; >- border: 1px solid #ccc; >- position: relative; >- border-top-left-radius: 2px; >- border-top-right-radius: 2px; >- box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), >- 0 25px 50px 0 rgba(0, 0, 0, 0.15); >-} >- >-#todoapp:before { >- content: ''; >- border-left: 1px solid #f5d6d6; >- border-right: 1px solid #f5d6d6; >- width: 2px; >- position: absolute; >- top: 0; >- left: 40px; >- height: 100%; >-} >- >-#todoapp input::-webkit-input-placeholder { >- font-style: italic; >-} >- >-#todoapp input::-moz-placeholder { >- font-style: italic; >- color: #a9a9a9; >-} >- >-#todoapp h1 { >- position: absolute; >- top: -120px; >- width: 100%; >- font-size: 70px; >- font-weight: bold; >- text-align: center; >- color: #b3b3b3; >- color: rgba(255, 255, 255, 0.3); >- text-shadow: -1px -1px rgba(0, 0, 0, 0.2); >- -webkit-text-rendering: optimizeLegibility; >- -moz-text-rendering: optimizeLegibility; >- -ms-text-rendering: optimizeLegibility; >- -o-text-rendering: optimizeLegibility; >- text-rendering: optimizeLegibility; >-} >- >-#header { >- padding-top: 15px; >- border-radius: inherit; >-} >- >-#header:before { >- content: ''; >- position: absolute; >- top: 0; >- right: 0; >- left: 0; >- height: 15px; >- z-index: 2; >- border-bottom: 1px solid #6c615c; >- background: #8d7d77; >- background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); >- background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); >- background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); >- background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); >- background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); >- background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); >- border-top-left-radius: 1px; >- border-top-right-radius: 1px; >-} >- >-#new-todo, >-.edit { >- position: relative; >- margin: 0; >- width: 100%; >- font-size: 24px; >- font-family: inherit; >- line-height: 1.4em; >- border: 0; >- outline: none; >- color: inherit; >- padding: 6px; >- border: 1px solid #999; >- box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); >- -webkit-box-sizing: border-box; >- -moz-box-sizing: border-box; >- -ms-box-sizing: border-box; >- -o-box-sizing: border-box; >- box-sizing: border-box; >- -webkit-font-smoothing: antialiased; >- -moz-font-smoothing: antialiased; >- -ms-font-smoothing: antialiased; >- -o-font-smoothing: antialiased; >- font-smoothing: antialiased; >-} >- >-#new-todo { >- padding: 16px 16px 16px 60px; >- border: none; >- background: rgba(0, 0, 0, 0.02); >- z-index: 2; >- box-shadow: none; >-} >- >-#main { >- position: relative; >- z-index: 2; >- border-top: 1px dotted #adadad; >-} >- >-label[for='toggle-all'] { >- display: none; >-} >- >-#toggle-all { >- position: absolute; >- top: -42px; >- left: -4px; >- width: 40px; >- text-align: center; >- border: none; /* Mobile Safari */ >-} >- >-#toggle-all:before { >- content: '»'; >- font-size: 28px; >- color: #d9d9d9; >- padding: 0 25px 7px; >-} >- >-#toggle-all:checked:before { >- color: #737373; >-} >- >-#todo-list { >- margin: 0; >- padding: 0; >- list-style: none; >-} >- >-#todo-list li { >- position: relative; >- font-size: 24px; >- border-bottom: 1px dotted #ccc; >-} >- >-#todo-list li:last-child { >- border-bottom: none; >-} >- >-#todo-list li.editing { >- border-bottom: none; >- padding: 0; >-} >- >-#todo-list li.editing .edit { >- display: block; >- width: 506px; >- padding: 13px 17px 12px 17px; >- margin: 0 0 0 43px; >-} >- >-#todo-list li.editing .view { >- display: none; >-} >- >-#todo-list li .toggle { >- text-align: center; >- width: 40px; >- /* auto, since non-WebKit browsers doesn't support input styling */ >- height: auto; >- position: absolute; >- top: 0; >- bottom: 0; >- margin: auto 0; >- border: none; /* Mobile Safari */ >- -webkit-appearance: none; >- /*-moz-appearance: none;*/ >- -ms-appearance: none; >- -o-appearance: none; >- appearance: none; >-} >- >-#todo-list li .toggle:after { >- content: 'â'; >- line-height: 43px; /* 40 + a couple of pixels visual adjustment */ >- font-size: 20px; >- color: #d9d9d9; >- text-shadow: 0 -1px 0 #bfbfbf; >-} >- >-#todo-list li .toggle:checked:after { >- color: #85ada7; >- text-shadow: 0 1px 0 #669991; >- bottom: 1px; >- position: relative; >-} >- >-#todo-list li label { >- word-break: break-word; >- padding: 15px; >- margin-left: 45px; >- display: block; >- line-height: 1.2; >- -webkit-transition: color 0.4s; >- -moz-transition: color 0.4s; >- -ms-transition: color 0.4s; >- -o-transition: color 0.4s; >- transition: color 0.4s; >-} >- >-#todo-list li.completed label { >- color: #a9a9a9; >- text-decoration: line-through; >-} >- >-#todo-list li .destroy { >- display: none; >- position: absolute; >- top: 0; >- right: 10px; >- bottom: 0; >- width: 40px; >- height: 40px; >- margin: auto 0; >- font-size: 22px; >- color: #a88a8a; >- -webkit-transition: all 0.2s; >- -moz-transition: all 0.2s; >- -ms-transition: all 0.2s; >- -o-transition: all 0.2s; >- transition: all 0.2s; >-} >- >-#todo-list li .destroy:hover { >- text-shadow: 0 0 1px #000, >- 0 0 10px rgba(199, 107, 107, 0.8); >- -webkit-transform: scale(1.3); >- -moz-transform: scale(1.3); >- -ms-transform: scale(1.3); >- -o-transform: scale(1.3); >- transform: scale(1.3); >-} >- >-#todo-list li .destroy:after { >- content: 'â'; >-} >- >-#todo-list li:hover .destroy { >- display: block; >-} >- >-#todo-list li .edit { >- display: none; >-} >- >-#todo-list li.editing:last-child { >- margin-bottom: -1px; >-} >- >-#footer { >- color: #777; >- padding: 0 15px; >- position: absolute; >- right: 0; >- bottom: -31px; >- left: 0; >- height: 20px; >- z-index: 1; >- text-align: center; >-} >- >-#footer:before { >- content: ''; >- position: absolute; >- right: 0; >- bottom: 31px; >- left: 0; >- height: 50px; >- z-index: -1; >- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), >- 0 6px 0 -3px rgba(255, 255, 255, 0.8), >- 0 7px 1px -3px rgba(0, 0, 0, 0.3), >- 0 43px 0 -6px rgba(255, 255, 255, 0.8), >- 0 44px 2px -6px rgba(0, 0, 0, 0.2); >-} >- >-#todo-count { >- float: left; >- text-align: left; >-} >- >-#filters { >- margin: 0; >- padding: 0; >- list-style: none; >- position: absolute; >- right: 0; >- left: 0; >-} >- >-#filters li { >- display: inline; >-} >- >-#filters li a { >- color: #83756f; >- margin: 2px; >- text-decoration: none; >-} >- >-#filters li a.selected { >- font-weight: bold; >-} >- >-#clear-completed { >- float: right; >- position: relative; >- line-height: 20px; >- text-decoration: none; >- background: rgba(0, 0, 0, 0.1); >- font-size: 11px; >- padding: 0 10px; >- border-radius: 3px; >- box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); >-} >- >-#clear-completed:hover { >- background: rgba(0, 0, 0, 0.15); >- box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); >-} >- >-#info { >- margin: 65px auto 0; >- color: #a6a6a6; >- font-size: 12px; >- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); >- text-align: center; >-} >- >-#info a { >- color: inherit; >-} >- >-/* >- Hack to remove background from Mobile Safari. >- Can't use it globally since it destroys checkboxes in Firefox and Opera >-*/ >-@media screen and (-webkit-min-device-pixel-ratio:0) { >- #toggle-all, >- #todo-list li .toggle { >- background: none; >- } >- >- #todo-list li .toggle { >- height: 40px; >- } >- >- #toggle-all { >- top: -56px; >- left: -15px; >- width: 65px; >- height: 41px; >- -webkit-transform: rotate(90deg); >- transform: rotate(90deg); >- -webkit-appearance: none; >- appearance: none; >- } >-} >- >-.hidden{ >- display:none; >-} >- >-hr { >- margin: 20px 0; >- border: 0; >- border-top: 1px dashed #C5C5C5; >- border-bottom: 1px dashed #F7F7F7; >-} >- >-.learn a { >- font-weight: normal; >- text-decoration: none; >- color: #b83f45; >-} >- >-.learn a:hover { >- text-decoration: underline; >- color: #787e7e; >-} >- >-.learn h3, >-.learn h4, >-.learn h5 { >- margin: 10px 0; >- font-weight: 500; >- line-height: 1.2; >- color: #000; >-} >- >-.learn h3 { >- font-size: 24px; >-} >- >-.learn h4 { >- font-size: 18px; >-} >- >-.learn h5 { >- margin-bottom: 0; >- font-size: 14px; >-} >- >-.learn ul { >- padding: 0; >- margin: 0 0 30px 25px; >-} >- >-.learn li { >- line-height: 20px; >-} >- >-.learn p { >- font-size: 15px; >- font-weight: 300; >- line-height: 1.3; >- margin-top: 0; >- margin-bottom: 0; >-} >- >-.quote { >- border: none; >- margin: 20px 0 60px 0; >-} >- >-.quote p { >- font-style: italic; >-} >- >-.quote p:before { >- content: 'â'; >- font-size: 50px; >- opacity: .15; >- position: absolute; >- top: -20px; >- left: 3px; >-} >- >-.quote p:after { >- content: 'â'; >- font-size: 50px; >- opacity: .15; >- position: absolute; >- bottom: -42px; >- right: 3px; >-} >- >-.quote footer { >- position: absolute; >- bottom: -40px; >- right: 0; >-} >- >-.quote footer img { >- border-radius: 3px; >-} >- >-.quote footer a { >- margin-left: 5px; >- vertical-align: middle; >-} >- >-.speech-bubble { >- position: relative; >- padding: 10px; >- background: rgba(0, 0, 0, .04); >- border-radius: 5px; >-} >- >-.speech-bubble:after { >- content: ''; >- position: absolute; >- top: 100%; >- right: 30px; >- border: 13px solid transparent; >- border-top-color: rgba(0, 0, 0, .04); >-} >- >-/**body*/.learn-bar > .learn { >- position: absolute; >- width: 272px; >- top: 8px; >- left: -300px; >- padding: 10px; >- border-radius: 5px; >- background-color: rgba(255, 255, 255, .6); >- transition-property: left; >- transition-duration: 500ms; >-} >- >-@media (min-width: 899px) { >- /**body*/.learn-bar { >- width: auto; >- margin: 0 0 0 300px; >- } >- /**body*/.learn-bar > .learn { >- left: 8px; >- } >- /**body*/.learn-bar #todoapp { >- width: 550px; >- margin: 130px auto 40px auto; >- } >-} >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.js >deleted file mode 100644 >index f9bf4240fad9a9c1194c81586dc2916bec1cca78..0000000000000000000000000000000000000000 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/base.js >+++ /dev/null >@@ -1,209 +0,0 @@ >-(function () { >- 'use strict'; >- >- // Underscore's Template Module >- // Courtesy of underscorejs.org >- var _ = (function (_) { >- _.defaults = function (object) { >- if (!object) { >- return object; >- } >- for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { >- var iterable = arguments[argsIndex]; >- if (iterable) { >- for (var key in iterable) { >- if (object[key] == null) { >- object[key] = iterable[key]; >- } >- } >- } >- } >- return object; >- } >- >- // By default, Underscore uses ERB-style template delimiters, change the >- // following template settings to use alternative delimiters. >- _.templateSettings = { >- evaluate : /<%([\s\S]+?)%>/g, >- interpolate : /<%=([\s\S]+?)%>/g, >- escape : /<%-([\s\S]+?)%>/g >- }; >- >- // When customizing `templateSettings`, if you don't want to define an >- // interpolation, evaluation or escaping regex, we need one that is >- // guaranteed not to match. >- var noMatch = /(.)^/; >- >- // Certain characters need to be escaped so that they can be put into a >- // string literal. >- var escapes = { >- "'": "'", >- '\\': '\\', >- '\r': 'r', >- '\n': 'n', >- '\t': 't', >- '\u2028': 'u2028', >- '\u2029': 'u2029' >- }; >- >- var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; >- >- // JavaScript micro-templating, similar to John Resig's implementation. >- // Underscore templating handles arbitrary delimiters, preserves whitespace, >- // and correctly escapes quotes within interpolated code. >- _.template = function(text, data, settings) { >- var render; >- settings = _.defaults({}, settings, _.templateSettings); >- >- // Combine delimiters into one regular expression via alternation. >- var matcher = new RegExp([ >- (settings.escape || noMatch).source, >- (settings.interpolate || noMatch).source, >- (settings.evaluate || noMatch).source >- ].join('|') + '|$', 'g'); >- >- // Compile the template source, escaping string literals appropriately. >- var index = 0; >- var source = "__p+='"; >- text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { >- source += text.slice(index, offset) >- .replace(escaper, function(match) { return '\\' + escapes[match]; }); >- >- if (escape) { >- source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; >- } >- if (interpolate) { >- source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; >- } >- if (evaluate) { >- source += "';\n" + evaluate + "\n__p+='"; >- } >- index = offset + match.length; >- return match; >- }); >- source += "';\n"; >- >- // If a variable is not specified, place data values in local scope. >- if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; >- >- source = "var __t,__p='',__j=Array.prototype.join," + >- "print=function(){__p+=__j.call(arguments,'');};\n" + >- source + "return __p;\n"; >- >- try { >- render = new Function(settings.variable || 'obj', '_', source); >- } catch (e) { >- e.source = source; >- throw e; >- } >- >- if (data) return render(data, _); >- var template = function(data) { >- return render.call(this, data, _); >- }; >- >- // Provide the compiled function source as a convenience for precompilation. >- template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; >- >- return template; >- }; >- >- return _; >- })({}); >- >- if (location.hostname === 'todomvc.com') { >- 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')); >- } >- >- function redirect() { >- if (location.hostname === 'tastejs.github.io') { >- location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); >- } >- } >- >- function findRoot() { >- var base; >- >- [/labs/, /\w*-examples/].forEach(function (href) { >- var match = location.href.match(href); >- >- if (!base && match) { >- base = location.href.indexOf(match); >- } >- }); >- >- return location.href.substr(0, base); >- } >- >- function getFile(file, callback) { >- if (!location.host) { >- return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); >- } >- >- var xhr = new XMLHttpRequest(); >- >- xhr.open('GET', findRoot() + file, true); >- xhr.send(); >- >- xhr.onload = function () { >- if (xhr.status === 200 && callback) { >- callback(xhr.responseText); >- } >- }; >- } >- >- function Learn(learnJSON, config) { >- if (!(this instanceof Learn)) { >- return new Learn(learnJSON, config); >- } >- >- var template, framework; >- >- if (typeof learnJSON !== 'object') { >- try { >- learnJSON = JSON.parse(learnJSON); >- } catch (e) { >- return; >- } >- } >- >- if (config) { >- template = config.template; >- framework = config.framework; >- } >- >- if (!template && learnJSON.templates) { >- template = learnJSON.templates.todomvc; >- } >- >- if (!framework && document.querySelector('[data-framework]')) { >- framework = document.querySelector('[data-framework]').getAttribute('data-framework'); >- } >- >- >- if (template && learnJSON[framework]) { >- this.frameworkJSON = learnJSON[framework]; >- this.template = template; >- >- this.append(); >- } >- } >- >- Learn.prototype.append = function () { >- var aside = document.createElement('aside'); >- aside.innerHTML = _.template(this.template, this.frameworkJSON); >- aside.className = 'learn'; >- >- // Localize demo links >- var demoLinks = aside.querySelectorAll('.demo-link'); >- Array.prototype.forEach.call(demoLinks, function (demoLink) { >- demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); >- }); >- >- document.body.className = (document.body.className + ' learn-bar').trim(); >- document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); >- }; >- >- redirect(); >- getFile('learn.json', Learn); >-})(); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/bg.png b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/bower_components/todomvc-common/bg.png >deleted file mode 100644 >index b2a7600825ee11f21849ed475499c7d1ecbcc870..0000000000000000000000000000000000000000 >GIT binary patch >literal 0 >HcmV?d00001 > >literal 2126 >zcmV-U2(kBxP)<h;3K|Lk000e1NJLTq003kF003kH0{{R34UB!q0000CP)t-s?(XjE >z>+9y`=HK7nt=~3t000O5Nkl<ZcwTju(Q4v~7KWEW#Ym?lK_v;Tm|!Fc)@NwEnmtsh >zx!O~z81JT3pFsm>m=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV >z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ >zjlJf(x8cj<PGcjn*J+a{&H5p*n5$5e3?<KCc?o06mAsNSbgPWO?Pz#Czn<!={>{VS >zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 >zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J >zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz >zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq<PU36C;5b=M^F`=p1dI >zk<CQOPNa7>!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# >z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C >zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo >zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%<!p-bvssplYY2}P;!v4CYj9kmF >zb0bN_m4V*f^YZMob2(j3pKj~7v#Ztks=Io+{su%WiukdJH$b?fZkiA=PY_3OhDBko >z(LTQHY&F^^2hG-Y`*Z93vb~c?*(JchDjTfCs?zRm*`*N*V-$=?QqthnSr*?OS3sjK >zo<_U^f{~ag60ul>jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA >z%SIVgt58EQxLJ+OiFq<yu4eOD|9Lt3dNsefxOuq)D&$_%j0DgqCy1gzc|tTD3*mee >z{=%|<``}_HyphOu{;)xkRg+QnHCC3Tf+kspqV6*|QK-RcqD}4>KkBuwquyZLUp|gr >zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ >z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA<SgcKcKOpmA;=HBZS@OFT@_XOvxq >z*N|3KLytgzH6GU&&Uoc4Zzk4lum3P{9?fpI4+QGPJ+v6`1lfRq3LcS*(M*WK+|b%O >ze$z>0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT >zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ >z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c >z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ >zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^<MC7@+xbwxsGgTvJIALjP{68L >z8AYuSGS0GUgk6W`v4W;Ry*uoETnulPi|573%lv*Zxw#v6yUQ^MOfcpT#V8RIDV##N >zD1`+AemsOh)wbWDExVcfpKz)jyV=<~&ZXPg6c|ZDE7U4bk<D^>-(^XJX4YzoiNRvL >zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ >zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6<twH86ID>g*mj&E!A))%gXJ=x6u#l >zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI >z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<bAZjz~9ek >z_Diar+AfJFP_9@ymUo$KW><-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b >z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56<O19 >zL)8r!KM()if0`@?W4(Jdc)7WoPG^&=$<zUsM+HCWakC^3tZxk+PjQG}B>Z5<Z6Vxw >zB@HrV$&l~@Taj70pce{iSuRLsUczDY_5TnDq8Py_Q3wh`D8!4jK;vFdRP+Q0=zJy* >zzuWd%!ai@D{|*J<I;OD&i%l{Jp-8*Pu3VGLWiyk6v#XmQqw`-+%e$|CJw*@`5zb3< >zE}z#;kRhL&q5PDG6@mm*$>Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh >zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 >z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< >zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ >Ef(>0N!2kdN > >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/index.html b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/index.html >index 477b9bf580d92d69a064736cd65221e9675cb8ce..6d944c4f886fad3480fddf3f2a0adf6ceb2d1923 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/index.html >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/index.html >@@ -1,53 +1,51 @@ > <!doctype html> > <html lang="en" data-framework="javascript"> > <head> >- <meta charset="utf-8"> >- <meta http-equiv="X-UA-Compatible" content="IE=edge"> >- <title>VanillaJS ⢠TodoMVC</title> >- <link rel="stylesheet" href="bower_components/todomvc-common/base.css"> >+ <meta charset="utf-8"> >+ <title>VanillaJS ⢠TodoMVC</title> >+ <link rel="stylesheet" href="node_modules/todomvc-common/base.css"> >+ <link rel="stylesheet" href="node_modules/todomvc-app-css/index.css"> > </head> > <body> >- <section id="todoapp"> >- <header id="header"> >- <h1>todos</h1> >- <input id="new-todo" placeholder="What needs to be done?" autofocus> >- </header> >- <section id="main"> >- <input id="toggle-all" type="checkbox"> >- <label for="toggle-all">Mark all as complete</label> >- <ul id="todo-list"></ul> >- </section> >- <footer id="footer"> >- <span id="todo-count"></span> >- <ul id="filters"> >- <li> >- <a href="#/">All</a> >- </li> >- <li> >- <a href="#/active">Active</a> >- </li> >- <li> >- <a href="#/completed">Completed</a> >- </li> >- </ul> >- <button id="clear-completed">Clear completed</button> >- </footer> >- </section> >- <footer id="info"> >- <p>Double-click to edit a todo</p> >- <p>Created by <a href="http://twitter.com/oscargodson">Oscar Godson</a></p> >- <p>Part of <a href="http://todomvc.com">TodoMVC</a></p> >- </footer> >- <script src="bower_components/todomvc-common/base.js"></script> >- <script> >- // Bootstrap app data >- window.app = {}; >- </script> >- <script src="js/helpers.js"></script> >- <script src="js/store.js"></script> >- <script src="js/model.js"></script> >- <script src="js/view.js"></script> >- <script src="js/controller.js"></script> >- <script src="js/app.js"></script> >+ <section class="todoapp"> >+ <header class="header"> >+ <h1>todos</h1> >+ <input class="new-todo" placeholder="What needs to be done?" autofocus> >+ </header> >+ <section class="main"> >+ <input class="toggle-all" type="checkbox"> >+ <label for="toggle-all">Mark all as complete</label> >+ <ul class="todo-list"></ul> >+ </section> >+ <footer class="footer"> >+ <span class="todo-count"></span> >+ <ul class="filters"> >+ <li> >+ <a href="#/" class="selected">All</a> >+ </li> >+ <li> >+ <a href="#/active">Active</a> >+ </li> >+ <li> >+ <a href="#/completed">Completed</a> >+ </li> >+ </ul> >+ <button class="clear-completed">Clear completed</button> >+ </footer> >+ </section> >+ <footer class="info"> >+ <p>Double-click to edit a todo</p> >+ <p>Speedometer version by <a href="http://twitter.com/addyosmani">Addy Osmani</p> >+ <p>Parts by <a href="http://twitter.com/oscargodson">Oscar Godson</a></p> >+ <p>Refactored by <a href="https://github.com/cburgmer">Christoph Burgmer</a></p> >+ <p>Part of <a href="http://todomvc.com">TodoMVC</a></p> >+ </footer> >+ <script src="js/helpers.js"></script> >+ <script src="js/store.js"></script> >+ <script src="js/model.js"></script> >+ <script src="js/template.js"></script> >+ <script src="js/view.js"></script> >+ <script src="js/controller.js"></script> >+ <script src="js/app.js"></script> > </body> > </html> >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/app.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/app.js >index 288e59bd8e07dfee9c904e40575f97bd29f07fb5..301cf9feae48552e67d8078ae9fa37174d4c38d4 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/app.js >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/app.js >@@ -1,4 +1,4 @@ >-/*global Store, Model, View, Controller, $$ */ >+/*global app, $on */ > (function () { > 'use strict'; > >@@ -8,67 +8,18 @@ > * @param {string} name The name of your new to do list. > */ > function Todo(name) { >- this.storage = new app.Store(name); >- this.model = new app.Model(this.storage); >- this.view = new app.View(); >- this.controller = new app.Controller(this.model, this.view); >+ this.storage = new app.Store(name); >+ this.model = new app.Model(this.storage); >+ this.template = new app.Template(); >+ this.view = new app.View(this.template); >+ this.controller = new app.Controller(this.model, this.view); > } > > var todo = new Todo('todos-vanillajs'); > >- window.todo = todo; // Benchmark modification; Expose todo so that we can call methods on it. >- >- /** >- * Finds the model ID of the clicked DOM element >- * >- * @param {object} target The starting point in the DOM for it to try to find >- * the ID of the model. >- */ >- function lookupId(target) { >- var lookup = target; >- >- while (lookup.nodeName !== 'LI') { >- lookup = lookup.parentNode; >+ function setView() { >+ todo.controller.setView(document.location.hash); > } >- >- return lookup.dataset.id; >- } >- >- // When the enter key is pressed fire the addItem method. >- $$('#new-todo').addEventListener('keypress', function (e) { >- todo.controller.addItem(e); >- }); >- >- // A delegation event. Will check what item was clicked whenever you click on any >- // part of a list item. >- $$('#todo-list').addEventListener('click', function (e) { >- var target = e.target; >- >- // If you click a destroy button >- if (target.className.indexOf('destroy') > -1) { >- todo.controller.removeItem(lookupId(target)); >- } >- >- // If you click the checkmark >- if (target.className.indexOf('toggle') > -1) { >- todo.controller.toggleComplete(lookupId(target), target); >- } >- >- }); >- >- $$('#todo-list').addEventListener('dblclick', function (e) { >- var target = e.target; >- >- if (target.nodeName === 'LABEL') { >- todo.controller.editItem(lookupId(target), target); >- } >- }); >- >- $$('#toggle-all').addEventListener('click', function (e) { >- todo.controller.toggleAll(e); >- }); >- >- $$('#clear-completed').addEventListener('click', function () { >- todo.controller.removeCompletedItems(); >- }); >+ $on(window, 'load', setView); >+ $on(window, 'hashchange', setView); > })(); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/controller.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/controller.js >index e3a0d6c81e02027deaf57b47674dffe9c203d0c8..0fbc62a89e65eb7b81407bcbd17cfb027b4ce687 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/controller.js >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/controller.js >@@ -1,4 +1,3 @@ >-/*global $$, $ */ > (function (window) { > 'use strict'; > >@@ -6,152 +5,140 @@ > * Takes a model and view and acts as the controller between them > * > * @constructor >- * @param {object} model The model constructor >- * @param {object} view The view constructor >+ * @param {object} model The model instance >+ * @param {object} view The view instance > */ > function Controller(model, view) { >- this.model = model; >- this.view = view; >- >- this.ENTER_KEY = 13; >- this.ESCAPE_KEY = 27; >- >- this.$main = $$('#main'); >- this.$toggleAll = $$('#toggle-all'); >- this.$todoList = $$('#todo-list'); >- this.$todoItemCounter = $$('#todo-count'); >- this.$clearCompleted = $$('#clear-completed'); >- this.$footer = $$('#footer'); >- >- window.addEventListener('load', function () { >- this._updateFilterState(); >- }.bind(this)); >- >- window.addEventListener('hashchange', function () { >- this._updateFilterState(); >- }.bind(this)); >+ var self = this; >+ self.model = model; >+ self.view = view; >+ >+ self.view.bind('newTodo', function (title) { >+ self.addItem(title); >+ }); >+ >+ self.view.bind('itemEdit', function (item) { >+ self.editItem(item.id); >+ }); >+ >+ self.view.bind('itemEditDone', function (item) { >+ self.editItemSave(item.id, item.title); >+ }); >+ >+ self.view.bind('itemEditCancel', function (item) { >+ self.editItemCancel(item.id); >+ }); >+ >+ self.view.bind('itemRemove', function (item) { >+ self.removeItem(item.id); >+ }); >+ >+ self.view.bind('itemToggle', function (item) { >+ self.toggleComplete(item.id, item.completed); >+ }); >+ >+ self.view.bind('removeCompleted', function () { >+ self.removeCompletedItems(); >+ }); >+ >+ self.view.bind('toggleAll', function (status) { >+ self.toggleAll(status.completed); >+ }); > } > > /** >+ * Loads and initialises the view >+ * >+ * @param {string} '' | 'active' | 'completed' >+ */ >+ Controller.prototype.setView = function (locationHash) { >+ var route = locationHash.split('/')[1]; >+ var page = route || ''; >+ this._updateFilterState(page); >+ }; >+ >+ /** > * An event to fire on load. Will get all items and display them in the > * todo-list > */ > Controller.prototype.showAll = function () { >- this.model.read(function (data) { >- this.$todoList.innerHTML = this.view.show(data); >- }.bind(this)); >+ var self = this; >+ self.model.read(function (data) { >+ self.view.render('showEntries', data); >+ }); > }; > > /** > * Renders all active tasks > */ > Controller.prototype.showActive = function () { >- this.model.read({ completed: 0 }, function (data) { >- this.$todoList.innerHTML = this.view.show(data); >- }.bind(this)); >+ var self = this; >+ self.model.read({ completed: false }, function (data) { >+ self.view.render('showEntries', data); >+ }); > }; > > /** > * Renders all completed tasks > */ > Controller.prototype.showCompleted = function () { >- this.model.read({ completed: 1 }, function (data) { >- this.$todoList.innerHTML = this.view.show(data); >- }.bind(this)); >+ var self = this; >+ self.model.read({ completed: true }, function (data) { >+ self.view.render('showEntries', data); >+ }); > }; > > /** > * An event to fire whenever you want to add an item. Simply pass in the event > * object and it'll handle the DOM insertion and saving of the new item. >- * >- * @param {object} e The event object > */ >- Controller.prototype.addItem = function (e) { >- var input = $$('#new-todo'); >- var title = title || ''; >+ Controller.prototype.addItem = function (title) { >+ var self = this; > >- if (e.keyCode === this.ENTER_KEY) { >- if (e.target.value.trim() === '') { >- return; >- } >- >- this.model.create(e.target.value, function (data) { >- input.value = ''; >- this._filter(true); >- }.bind(this)); >- } >+ if (title.trim() === '') { >+ return; >+ } > >+ self.model.create(title, function () { >+ self.view.render('clearNewTodo'); >+ self._filter(true); >+ }); > }; > >- /** >- * Hides the label text and creates an input to edit the title of the item. >- * When you hit enter or blur out of the input it saves it and updates the UI >- * with the new name. >- * >- * @param {number} id The id of the item to edit >- * @param {object} label The label you want to edit the text of >+ /* >+ * Triggers the item editing mode. > */ >- Controller.prototype.editItem = function (id, label) { >- var li = label; >- >- // This finds the <label>'s parent <li> >- while (li.nodeName !== 'LI') { >- li = li.parentNode; >- } >- >- var onSaveHandler = function () { >- var value = input.value.trim(); >- var discarding = input.dataset.discard; >- >- if (value.length && !discarding) { >- this.model.update(id, { title: input.value }); >- >- // Instead of re-rendering the whole view just update >- // this piece of it >- label.innerHTML = value; >- } else if (value.length === 0) { >- // No value was entered in the input. We'll remove the todo item. >- this.removeItem(id); >- } >- >- // Remove the input since we no longer need it >- // Less DOM means faster rendering >- li.removeChild(input); >- >- // Remove the editing class >- li.className = li.className.replace('editing', ''); >- }.bind(this); >- >- // Append the editing class >- li.className = li.className + ' editing'; >- >- var input = document.createElement('input'); >- input.className = 'edit'; >- >- // Get the innerHTML of the label instead of requesting the data from the >- // ORM. If this were a real DB this would save a lot of time and would avoid >- // a spinner gif. >- input.value = label.innerHTML; >- >- li.appendChild(input); >- >- input.addEventListener('blur', onSaveHandler); >- >- input.addEventListener('keypress', function (e) { >- if (e.keyCode === this.ENTER_KEY) { >- // Remove the cursor from the input when you hit enter just like if it >- // were a real form >- input.blur(); >- } >+ Controller.prototype.editItem = function (id) { >+ var self = this; >+ self.model.read(id, function (data) { >+ self.view.render('editItem', {id: id, title: data[0].title}); >+ }); >+ }; > >- if (e.keyCode === this.ESCAPE_KEY) { >- // Discard the changes >- input.dataset.discard = true; >- input.blur(); >- } >- }.bind(this)); >+ /* >+ * Finishes the item editing mode successfully. >+ */ >+ Controller.prototype.editItemSave = function (id, title) { >+ var self = this; >+ title = title.trim(); >+ >+ if (title.length !== 0) { >+ self.model.update(id, {title: title}, function () { >+ self.view.render('editItemDone', {id: id, title: title}); >+ }); >+ } else { >+ self.removeItem(id); >+ } >+ }; > >- input.focus(); >+ /* >+ * Cancels the item editing mode. >+ */ >+ Controller.prototype.editItemCancel = function (id) { >+ var self = this; >+ self.model.read(id, function (data) { >+ self.view.render('editItemDone', {id: id, title: data[0].title}); >+ }); > }; > > /** >@@ -162,24 +149,26 @@ > * storage > */ > Controller.prototype.removeItem = function (id) { >- this.model.remove(id, function () { >- this.$todoList.removeChild($$('[data-id="' + id + '"]')); >- }.bind(this)); >+ var self = this; >+ self.model.remove(id, function () { >+ self.view.render('removeItem', id); >+ }); > >- this._filter(); >+ self._filter(); > }; > > /** > * Will remove all completed items from the DOM and storage. > */ > Controller.prototype.removeCompletedItems = function () { >- this.model.read({ completed: 1 }, function (data) { >- data.forEach(function (item) { >- this.removeItem(item.id); >- }.bind(this)); >- }.bind(this)); >- >- this._filter(); >+ var self = this; >+ self.model.read({ completed: true }, function (data) { >+ data.forEach(function (item) { >+ self.removeItem(item.id); >+ }); >+ }); >+ >+ self._filter(); > }; > > /** >@@ -191,48 +180,33 @@ > * or not > * @param {boolean|undefined} silent Prevent re-filtering the todo items > */ >- Controller.prototype.toggleComplete = function (id, checkbox, silent) { >- var completed = checkbox.checked ? 1 : 0; >- >- this.model.update(id, { completed: completed }, function () { >- var listItem = $$('[data-id="' + id + '"]'); >- >- if (!listItem) { >- return; >- } >- >- listItem.className = completed ? 'completed' : ''; >- >- // In case it was toggled from an event and not by clicking the checkbox >- listItem.querySelector('input').checked = completed; >- }); >- >- if (!silent) { >- this._filter(); >- } >+ Controller.prototype.toggleComplete = function (id, completed, silent) { >+ var self = this; >+ self.model.update(id, { completed: completed }, function () { >+ self.view.render('elementComplete', { >+ id: id, >+ completed: completed >+ }); >+ }); >+ >+ if (!silent) { >+ self._filter(); >+ } > }; > > /** >- * Will toggle ALL checkboxe's on/off state and completeness of models. >+ * Will toggle ALL checkboxes' on/off state and completeness of models. > * Just pass in the event object. >- * >- * @param {object} e The event object > */ >- Controller.prototype.toggleAll = function (e) { >- var completed = e.target.checked ? 1 : 0; >- var query = 0; >- >- if (completed === 0) { >- query = 1; >- } >- >- this.model.read({ completed: query }, function (data) { >- data.forEach(function (item) { >- this.toggleComplete(item.id, e.target, true); >- }.bind(this)); >- }.bind(this)); >- >- this._filter(); >+ Controller.prototype.toggleAll = function (completed) { >+ var self = this; >+ self.model.read({ completed: !completed }, function (data) { >+ data.forEach(function (item) { >+ self.toggleComplete(item.id, completed, true); >+ }); >+ }); >+ >+ self._filter(); > }; > > /** >@@ -240,37 +214,17 @@ > * number of todos. > */ > Controller.prototype._updateCount = function () { >- var todos = this.model.getCount(); >- >- this.$todoItemCounter.innerHTML = this.view.itemCounter(todos.active); >- >- this.$clearCompleted.innerHTML = this.view.clearCompletedButton(todos.completed); >- this.$clearCompleted.style.display = todos.completed > 0 ? 'block' : 'none'; >- >- this.$toggleAll.checked = todos.completed === todos.total; >- >- this._toggleFrame(todos); >- }; >- >- /** >- * The main body and footer elements should not be visible when there are no >- * todos left. >- * >- * @param {object} todos Contains a count of all todos, and their statuses. >- */ >- Controller.prototype._toggleFrame = function (todos) { >- var frameDisplay = this.$main.style.display; >- var frameVisible = frameDisplay === 'block' || frameDisplay === ''; >- >- if (todos.total === 0 && frameVisible) { >- this.$main.style.display = 'none'; >- this.$footer.style.display = 'none'; >- } >- >- if (todos.total > 0 && !frameVisible) { >- this.$main.style.display = 'block'; >- this.$footer.style.display = 'block'; >- } >+ var self = this; >+ self.model.getCount(function (todos) { >+ self.view.render('updateElementCount', todos.active); >+ self.view.render('clearCompletedButton', { >+ completed: todos.completed, >+ visible: todos.completed > 0 >+ }); >+ >+ self.view.render('toggleAll', {checked: todos.completed === todos.total}); >+ self.view.render('contentBlockVisibility', {visible: todos.total > 0}); >+ }); > }; > > /** >@@ -278,53 +232,39 @@ > * @param {boolean|undefined} force forces a re-painting of todo items. > */ > Controller.prototype._filter = function (force) { >- var activeRoute = this._activeRoute.charAt(0).toUpperCase() + this._activeRoute.substr(1); >+ var activeRoute = this._activeRoute.charAt(0).toUpperCase() + this._activeRoute.substr(1); > >- // Update the elements on the page, which change with each completed todo >- this._updateCount(); >+ // Update the elements on the page, which change with each completed todo >+ this._updateCount(); > >- // If the last active route isn't "All", or we're switching routes, we >- // re-create the todo item elements, calling: >- // this.show[All|Active|Completed](); >- if (force || this._lastActiveRoute !== 'All' || this._lastActiveRoute !== activeRoute) { >- this['show' + activeRoute](); >- } >+ // If the last active route isn't "All", or we're switching routes, we >+ // re-create the todo item elements, calling: >+ // this.show[All|Active|Completed](); >+ if (force || this._lastActiveRoute !== 'All' || this._lastActiveRoute !== activeRoute) { >+ this['show' + activeRoute](); >+ } > >- this._lastActiveRoute = activeRoute; >+ this._lastActiveRoute = activeRoute; > }; > > /** > * Simply updates the filter nav's selected states > */ >- Controller.prototype._updateFilterState = function () { >- var currentPage = this._getCurrentPage() || ''; >- >- // Store a reference to the active route, allowing us to re-filter todo >- // items as they are marked complete or incomplete. >- this._activeRoute = currentPage; >- >- if (currentPage === '') { >- this._activeRoute = 'All'; >- } >+ Controller.prototype._updateFilterState = function (currentPage) { >+ // Store a reference to the active route, allowing us to re-filter todo >+ // items as they are marked complete or incomplete. >+ this._activeRoute = currentPage; > >- this._filter(); >+ if (currentPage === '') { >+ this._activeRoute = 'All'; >+ } > >- // Remove all other selected states. We loop through all of them in case the >- // UI gets in a funky state with two selected. >- $('#filters .selected').each(function (item) { >- item.className = ''; >- }); >+ this._filter(); > >- $$('#filters [href="#/' + currentPage + '"]').className = 'selected'; >- }; >- >- /** >- * A getter for getting the current page >- */ >- Controller.prototype._getCurrentPage = function () { >- return document.location.hash.split('/')[1]; >+ this.view.render('setFilter', currentPage); > }; > > // Export to window >+ window.app = window.app || {}; > window.app.Controller = Controller; > })(window); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/helpers.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/helpers.js >index 58a0e9fe32578a96857fe9b94dae3adf3f6f5e96..2af956f064d8818dfc5f1591dc667c1ba86fd10c 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/helpers.js >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/helpers.js >@@ -1,17 +1,52 @@ >+/*global NodeList */ > (function (window) { > 'use strict'; > >- // Cache the querySelector/All for easier and faster reuse >- window.$ = document.querySelectorAll.bind(document); >- window.$$ = document.querySelector.bind(document); >- >- // Allow for looping on Objects by chaining: >- // $('.foo').each(function () {}) >- Object.prototype.each = function (callback) { >- for (var x in this) { >- if (this.hasOwnProperty(x)) { >- callback.call(this, this[x]); >- } >- } >+ // Get element(s) by CSS selector: >+ window.qs = function (selector, scope) { >+ return (scope || document).querySelector(selector); > }; >+ window.qsa = function (selector, scope) { >+ return (scope || document).querySelectorAll(selector); >+ }; >+ >+ // addEventListener wrapper: >+ window.$on = function (target, type, callback, useCapture) { >+ target.addEventListener(type, callback, !!useCapture); >+ }; >+ >+ // Attach a handler to event for all elements that match the selector, >+ // now or in the future, based on a root element >+ window.$delegate = function (target, selector, type, handler) { >+ function dispatchEvent(event) { >+ var targetElement = event.target; >+ var potentialElements = window.qsa(selector, target); >+ var hasMatch = Array.prototype.indexOf.call(potentialElements, targetElement) >= 0; >+ >+ if (hasMatch) { >+ handler.call(targetElement, event); >+ } >+ } >+ >+ // https://developer.mozilla.org/en-US/docs/Web/Events/blur >+ var useCapture = type === 'blur' || type === 'focus'; >+ >+ window.$on(target, type, dispatchEvent, useCapture); >+ }; >+ >+ // Find the element's parent with the given tag name: >+ // $parent(qs('a'), 'div'); >+ window.$parent = function (element, tagName) { >+ if (!element.parentNode) { >+ return; >+ } >+ if (element.parentNode.tagName.toLowerCase() === tagName.toLowerCase()) { >+ return element.parentNode; >+ } >+ return window.$parent(element.parentNode, tagName); >+ }; >+ >+ // Allow for looping on nodes by chaining: >+ // qsa('.foo').forEach(function () {}) >+ NodeList.prototype.forEach = Array.prototype.forEach; > })(window); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/model.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/model.js >index e01467465e1699bb354c93e1f20860e08551561b..d8a73191d2c2df5b23723aaa41339db8dba79b4f 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/model.js >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/model.js >@@ -8,7 +8,7 @@ > * @param {object} storage A reference to the client side storage class > */ > function Model(storage) { >- this.storage = storage; >+ this.storage = storage; > } > > /** >@@ -18,15 +18,15 @@ > * @param {function} [callback] The callback to fire after the model is created > */ > Model.prototype.create = function (title, callback) { >- title = title || ''; >- callback = callback || function () {}; >+ title = title || ''; >+ callback = callback || function () {}; > >- var newItem = { >- title: title.trim(), >- completed: 0 >- }; >+ var newItem = { >+ title: title.trim(), >+ completed: false >+ }; > >- this.storage.save(newItem, callback); >+ this.storage.save(newItem, callback); > }; > > /** >@@ -45,17 +45,18 @@ > * model.read({ foo: 'bar', hello: 'world' }); > */ > Model.prototype.read = function (query, callback) { >- var queryType = typeof query; >- callback = callback || function () {}; >+ var queryType = typeof query; >+ callback = callback || function () {}; > >- if (queryType === 'function') { >- callback = query; >- return this.storage.findAll(callback); >- } else if (queryType === 'string' || queryType === 'number') { >- this.storage.find({ id: query }, callback); >- } else { >- this.storage.find(query, callback); >- } >+ if (queryType === 'function') { >+ callback = query; >+ return this.storage.findAll(callback); >+ } else if (queryType === 'string' || queryType === 'number') { >+ query = parseInt(query, 10); >+ this.storage.find({ id: query }, callback); >+ } else { >+ this.storage.find(query, callback); >+ } > }; > > /** >@@ -67,7 +68,7 @@ > * @param {function} callback The callback to fire when the update is complete. > */ > Model.prototype.update = function (id, data, callback) { >- this.storage.save(id, data, callback); >+ this.storage.save(data, callback, id); > }; > > /** >@@ -77,7 +78,7 @@ > * @param {function} callback The callback to fire when the removal is complete. > */ > Model.prototype.remove = function (id, callback) { >- this.storage.remove(id, callback); >+ this.storage.remove(id, callback); > }; > > /** >@@ -86,34 +87,34 @@ > * @param {function} callback The callback to fire when the storage is wiped. > */ > Model.prototype.removeAll = function (callback) { >- this.storage.drop(callback); >+ this.storage.drop(callback); > }; > > /** > * Returns a count of all todos > */ >- Model.prototype.getCount = function () { >- var todos = { >- active: 0, >- completed: 0, >- total: 0 >- }; >- >- this.storage.findAll(function (data) { >- data.each(function (todo) { >- if (todo.completed === 1) { >- todos.completed++; >- } else { >- todos.active++; >- } >+ Model.prototype.getCount = function (callback) { >+ var todos = { >+ active: 0, >+ completed: 0, >+ total: 0 >+ }; > >- todos.total++; >- }); >- }); >+ this.storage.findAll(function (data) { >+ data.forEach(function (todo) { >+ if (todo.completed) { >+ todos.completed++; >+ } else { >+ todos.active++; >+ } > >- return todos; >+ todos.total++; >+ }); >+ callback(todos); >+ }); > }; > > // Export to window >+ window.app = window.app || {}; > window.app.Model = Model; > })(window); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/store.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/store.js >index 9a7da0d9bb77aa41a3d8e9fe8bc7636d670ab1a7..fe682c88f8a4e754d086773c51dd59c32bb3f378 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/store.js >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/store.js >@@ -2,8 +2,7 @@ > (function (window) { > 'use strict'; > >- var localStorage = {}; // Benchmark modification: Avoid disk access. >- >+ var MemoryStorage = {}; > /** > * Creates a new client side storage object and will create an empty > * collection if no collection already exists. >@@ -13,22 +12,19 @@ > * real life you probably would be making AJAX calls > */ > function Store(name, callback) { >- var data; >- var dbName; >- >- callback = callback || function () {}; >+ callback = callback || function () {}; > >- dbName = this._dbName = name; >+ this._dbName = name; > >- if (!localStorage[dbName]) { >- data = { >- todos: [] >- }; >+ if (!MemoryStorage[name]) { >+ var data = { >+ todos: [] >+ }; > >- localStorage[dbName] = JSON.stringify(data); >- } >+ MemoryStorage[name] = JSON.stringify(data); >+ } > >- callback.call(this, JSON.parse(localStorage[dbName])); >+ callback.call(this, JSON.parse(MemoryStorage[name])); > } > > /** >@@ -45,17 +41,20 @@ > * }); > */ > Store.prototype.find = function (query, callback) { >- if (!callback) { >- return; >- } >- >- var todos = JSON.parse(localStorage[this._dbName]).todos; >- >- callback.call(this, todos.filter(function (todo) { >- for (var q in query) { >- return query[q] === todo[q]; >- } >- })); >+ if (!callback) { >+ return; >+ } >+ >+ var todos = JSON.parse(MemoryStorage[this._dbName]).todos; >+ >+ callback.call(this, todos.filter(function (todo) { >+ for (var q in query) { >+ if (query[q] !== todo[q]) { >+ return false; >+ } >+ } >+ return true; >+ })); > }; > > /** >@@ -64,48 +63,45 @@ > * @param {function} callback The callback to fire upon retrieving data > */ > Store.prototype.findAll = function (callback) { >- callback = callback || function () {}; >- callback.call(this, JSON.parse(localStorage[this._dbName]).todos); >+ callback = callback || function () {}; >+ callback.call(this, JSON.parse(MemoryStorage[this._dbName]).todos); > }; > > /** > * Will save the given data to the DB. If no item exists it will create a new > * item, otherwise it'll simply update an existing item's properties > * >- * @param {number} id An optional param to enter an ID of an item to update >- * @param {object} data The data to save back into the DB >+ * @param {object} updateData The data to save back into the DB > * @param {function} callback The callback to fire after saving >+ * @param {number} id An optional param to enter an ID of an item to update > */ >- Store.prototype.save = function (id, updateData, callback) { >- var data = JSON.parse(localStorage[this._dbName]); >- var todos = data.todos; >- >- callback = callback || function () {}; >- >- // If an ID was actually given, find the item and update each property >- if (typeof id !== 'object') { >- for (var i = 0; i < todos.length; i++) { >- if (todos[i].id == id) { >- for (var x in updateData) { >- todos[i][x] = updateData[x]; >- } >- } >- } >- >- localStorage[this._dbName] = JSON.stringify(data); >- callback.call(this, JSON.parse(localStorage[this._dbName]).todos); >- } else { >- callback = updateData; >- >- updateData = id; >- >- // Generate an ID >- updateData.id = new Date().getTime(); >- >- todos.push(updateData); >- localStorage[this._dbName] = JSON.stringify(data); >- callback.call(this, [updateData]); >- } >+ Store.prototype.save = function (updateData, callback, id) { >+ var data = JSON.parse(MemoryStorage[this._dbName]); >+ var todos = data.todos; >+ >+ callback = callback || function () {}; >+ >+ // If an ID was actually given, find the item and update each property >+ if (id) { >+ for (var i = 0; i < todos.length; i++) { >+ if (todos[i].id === id) { >+ for (var key in updateData) { >+ todos[i][key] = updateData[key]; >+ } >+ break; >+ } >+ } >+ >+ MemoryStorage[this._dbName] = JSON.stringify(data); >+ callback.call(this, todos); >+ } else { >+ // Generate an ID >+ updateData.id = new Date().getTime(); >+ >+ todos.push(updateData); >+ MemoryStorage[this._dbName] = JSON.stringify(data); >+ callback.call(this, [updateData]); >+ } > }; > > /** >@@ -115,18 +111,18 @@ > * @param {function} callback The callback to fire after saving > */ > Store.prototype.remove = function (id, callback) { >- var data = JSON.parse(localStorage[this._dbName]); >- var todos = data.todos; >- >- for (var i = 0; i < todos.length; i++) { >- if (todos[i].id == id) { >- todos.splice(i, 1); >- break; >- } >- } >- >- localStorage[this._dbName] = JSON.stringify(data); >- callback.call(this, JSON.parse(localStorage[this._dbName]).todos); >+ var data = JSON.parse(MemoryStorage[this._dbName]); >+ var todos = data.todos; >+ >+ for (var i = 0; i < todos.length; i++) { >+ if (todos[i].id == id) { >+ todos.splice(i, 1); >+ break; >+ } >+ } >+ >+ MemoryStorage[this._dbName] = JSON.stringify(data); >+ callback.call(this, todos); > }; > > /** >@@ -135,10 +131,12 @@ > * @param {function} callback The callback to fire after dropping the data > */ > Store.prototype.drop = function (callback) { >- localStorage[this._dbName] = JSON.stringify({todos: []}); >- callback.call(this, JSON.parse(localStorage[this._dbName]).todos); >+ var data = {todos: []}; >+ MemoryStorage[this._dbName] = JSON.stringify(data); >+ callback.call(this, data.todos); > }; > > // Export to window >+ window.app = window.app || {}; > window.app.Store = Store; > })(window); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/template.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/template.js >new file mode 100644 >index 0000000000000000000000000000000000000000..43724c652952b557bb621ca0ba0dba05fd5f9800 >--- /dev/null >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/template.js >@@ -0,0 +1,114 @@ >+/*jshint laxbreak:true */ >+(function (window) { >+ 'use strict'; >+ >+ var htmlEscapes = { >+ '&': '&', >+ '<': '<', >+ '>': '>', >+ '"': '"', >+ '\'': ''', >+ '`': '`' >+ }; >+ >+ var escapeHtmlChar = function (chr) { >+ return htmlEscapes[chr]; >+ }; >+ >+ var reUnescapedHtml = /[&<>"'`]/g; >+ var reHasUnescapedHtml = new RegExp(reUnescapedHtml.source); >+ >+ var escape = function (string) { >+ return (string && reHasUnescapedHtml.test(string)) >+ ? string.replace(reUnescapedHtml, escapeHtmlChar) >+ : string; >+ }; >+ >+ /** >+ * Sets up defaults for all the Template methods such as a default template >+ * >+ * @constructor >+ */ >+ function Template() { >+ this.defaultTemplate >+ = '<li data-id="{{id}}" class="{{completed}}">' >+ + '<div class="view">' >+ + '<input class="toggle" type="checkbox" {{checked}}>' >+ + '<label>{{title}}</label>' >+ + '<button class="destroy"></button>' >+ + '</div>' >+ + '</li>'; >+ } >+ >+ /** >+ * Creates an <li> HTML string and returns it for placement in your app. >+ * >+ * NOTE: In real life you should be using a templating engine such as Mustache >+ * or Handlebars, however, this is a vanilla JS example. >+ * >+ * @param {object} data The object containing keys you want to find in the >+ * template to replace. >+ * @returns {string} HTML String of an <li> element >+ * >+ * @example >+ * view.show({ >+ * id: 1, >+ * title: "Hello World", >+ * completed: 0, >+ * }); >+ */ >+ Template.prototype.show = function (data) { >+ var i, l; >+ var view = ''; >+ >+ for (i = 0, l = data.length; i < l; i++) { >+ var template = this.defaultTemplate; >+ var completed = ''; >+ var checked = ''; >+ >+ if (data[i].completed) { >+ completed = 'completed'; >+ checked = 'checked'; >+ } >+ >+ template = template.replace('{{id}}', data[i].id); >+ template = template.replace('{{title}}', escape(data[i].title)); >+ template = template.replace('{{completed}}', completed); >+ template = template.replace('{{checked}}', checked); >+ >+ view = view + template; >+ } >+ >+ return view; >+ }; >+ >+ /** >+ * Displays a counter of how many to dos are left to complete >+ * >+ * @param {number} activeTodos The number of active todos. >+ * @returns {string} String containing the count >+ */ >+ Template.prototype.itemCounter = function (activeTodos) { >+ var plural = activeTodos === 1 ? '' : 's'; >+ >+ return '<strong>' + activeTodos + '</strong> item' + plural + ' left'; >+ }; >+ >+ /** >+ * Updates the text within the "Clear completed" button >+ * >+ * @param {[type]} completedTodos The number of completed todos. >+ * @returns {string} String containing the count >+ */ >+ Template.prototype.clearCompletedButton = function (completedTodos) { >+ if (completedTodos > 0) { >+ return 'Clear completed'; >+ } else { >+ return ''; >+ } >+ }; >+ >+ // Export to window >+ window.app = window.app || {}; >+ window.app.Template = Template; >+})(window); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/view.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/view.js >index d82b09827ff443a06468e535b2a4c3cbb33431a2..68de8305136d18c90bd60a67dbb5f5435a7112db 100644 >--- a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/view.js >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/js/view.js >@@ -1,91 +1,219 @@ >-/*jshint laxbreak:true */ >+/*global qs, qsa, $on, $parent, $delegate */ >+ > (function (window) { > 'use strict'; > > /** >- * Sets up defaults for all the View methods such as a default template >- * >- * @constructor >- */ >- function View() { >- this.defaultTemplate >- = '<li data-id="{{id}}" class="{{completed}}">' >- + '<div class="view">' >- + '<input class="toggle" type="checkbox" {{checked}}>' >- + '<label>{{title}}</label>' >- + '<button class="destroy"></button>' >- + '</div>' >- + '</li>'; >- } >+ * View that abstracts away the browser's DOM completely. >+ * It has two simple entry points: >+ * >+ * - bind(eventName, handler) >+ * Takes a todo application event and registers the handler >+ * - render(command, parameterObject) >+ * Renders the given command with the options >+ */ >+ function View(template) { >+ this.template = template; > >- /** >- * Creates an <li> HTML string and returns it for placement in your app. >- * >- * NOTE: In real life you should be using a templating engine such as Mustache >- * or Handlebars, however, this is a vanilla JS example. >- * >- * @param {object} data The object containing keys you want to find in the >- * template to replace. >- * @returns {string} HTML String of an <li> element >- * >- * @example >- * view.show({ >- * id: 1, >- * title: "Hello World", >- * completed: 0, >- * }); >- */ >- View.prototype.show = function (data) { >- var i, l; >- var view = ''; >- >- for (i = 0, l = data.length; i < l; i++) { >- var template = this.defaultTemplate; >- var completed = ''; >- var checked = ''; >- >- if (data[i].completed === 1) { >- completed = 'completed'; >- checked = 'checked'; >+ this.ENTER_KEY = 13; >+ this.ESCAPE_KEY = 27; >+ >+ this.$todoList = qs('.todo-list'); >+ this.$todoItemCounter = qs('.todo-count'); >+ this.$clearCompleted = qs('.clear-completed'); >+ this.$main = qs('.main'); >+ this.$footer = qs('.footer'); >+ this.$toggleAll = qs('.toggle-all'); >+ this.$newTodo = qs('.new-todo'); > } > >- template = template.replace('{{id}}', data[i].id); >- template = template.replace('{{title}}', data[i].title); >- template = template.replace('{{completed}}', completed); >- template = template.replace('{{checked}}', checked); >+ View.prototype._removeItem = function (id) { >+ var elem = qs('[data-id="' + id + '"]'); > >- view = view + template; >- } >+ if (elem) { >+ this.$todoList.removeChild(elem); >+ } >+ }; > >- return view; >+ View.prototype._clearCompletedButton = function (completedCount, visible) { >+ this.$clearCompleted.innerHTML = this.template.clearCompletedButton(completedCount); >+ this.$clearCompleted.style.display = visible ? 'block' : 'none'; > }; > >- /** >- * Displays a counter of how many to dos are left to complete >- * >- * @param {number} activeTodos The number of active todos. >- * @returns {string} String containing the count >- */ >- View.prototype.itemCounter = function (activeTodos) { >- var plural = activeTodos === 1 ? '' : 's'; >- >- return '<strong>' + activeTodos + '</strong> item' + plural + ' left'; >+ View.prototype._setFilter = function (currentPage) { >+ qs('.filters .selected').className = ''; >+ qs('.filters [href="#/' + currentPage + '"]').className = 'selected'; > }; > >- /** >- * Updates the text within the "Clear completed" button >- * >- * @param {[type]} completedTodos The number of completed todos. >- * @returns {string} String containing the count >- */ >- View.prototype.clearCompletedButton = function (completedTodos) { >- if (completedTodos > 0) { >- return 'Clear completed (' + completedTodos + ')'; >- } else { >- return ''; >- } >+ View.prototype._elementComplete = function (id, completed) { >+ var listItem = qs('[data-id="' + id + '"]'); >+ >+ if (!listItem) { >+ return; >+ } >+ >+ listItem.className = completed ? 'completed' : ''; >+ >+ // In case it was toggled from an event and not by clicking the checkbox >+ qs('input', listItem).checked = completed; >+ }; >+ >+ View.prototype._editItem = function (id, title) { >+ var listItem = qs('[data-id="' + id + '"]'); >+ >+ if (!listItem) { >+ return; >+ } >+ >+ listItem.className = listItem.className + ' editing'; >+ >+ var input = document.createElement('input'); >+ input.className = 'edit'; >+ >+ listItem.appendChild(input); >+ input.focus(); >+ input.value = title; >+ }; >+ >+ View.prototype._editItemDone = function (id, title) { >+ var listItem = qs('[data-id="' + id + '"]'); >+ >+ if (!listItem) { >+ return; >+ } >+ >+ var input = qs('input.edit', listItem); >+ listItem.removeChild(input); >+ >+ listItem.className = listItem.className.replace('editing', ''); >+ >+ qsa('label', listItem).forEach(function (label) { >+ label.textContent = title; >+ }); >+ }; >+ >+ View.prototype.render = function (viewCmd, parameter) { >+ var self = this; >+ var viewCommands = { >+ showEntries: function () { >+ self.$todoList.innerHTML = self.template.show(parameter); >+ }, >+ removeItem: function () { >+ self._removeItem(parameter); >+ }, >+ updateElementCount: function () { >+ self.$todoItemCounter.innerHTML = self.template.itemCounter(parameter); >+ }, >+ clearCompletedButton: function () { >+ self._clearCompletedButton(parameter.completed, parameter.visible); >+ }, >+ contentBlockVisibility: function () { >+ self.$main.style.display = self.$footer.style.display = parameter.visible ? 'block' : 'none'; >+ }, >+ toggleAll: function () { >+ self.$toggleAll.checked = parameter.checked; >+ }, >+ setFilter: function () { >+ self._setFilter(parameter); >+ }, >+ clearNewTodo: function () { >+ self.$newTodo.value = ''; >+ }, >+ elementComplete: function () { >+ self._elementComplete(parameter.id, parameter.completed); >+ }, >+ editItem: function () { >+ self._editItem(parameter.id, parameter.title); >+ }, >+ editItemDone: function () { >+ self._editItemDone(parameter.id, parameter.title); >+ } >+ }; >+ >+ viewCommands[viewCmd](); >+ }; >+ >+ View.prototype._itemId = function (element) { >+ var li = $parent(element, 'li'); >+ return parseInt(li.dataset.id, 10); >+ }; >+ >+ View.prototype._bindItemEditDone = function (handler) { >+ var self = this; >+ $delegate(self.$todoList, 'li .edit', 'blur', function () { >+ if (!this.dataset.iscanceled) { >+ handler({ >+ id: self._itemId(this), >+ title: this.value >+ }); >+ } >+ }); >+ >+ $delegate(self.$todoList, 'li .edit', 'keypress', function (event) { >+ if (event.keyCode === self.ENTER_KEY) { >+ // Remove the cursor from the input when you hit enter just like if it >+ // were a real form >+ this.blur(); >+ } >+ }); >+ }; >+ >+ View.prototype._bindItemEditCancel = function (handler) { >+ var self = this; >+ $delegate(self.$todoList, 'li .edit', 'keyup', function (event) { >+ if (event.keyCode === self.ESCAPE_KEY) { >+ this.dataset.iscanceled = true; >+ this.blur(); >+ >+ handler({id: self._itemId(this)}); >+ } >+ }); >+ }; >+ >+ View.prototype.bind = function (event, handler) { >+ var self = this; >+ if (event === 'newTodo') { >+ $on(self.$newTodo, 'change', function () { >+ handler(self.$newTodo.value); >+ }); >+ >+ } else if (event === 'removeCompleted') { >+ $on(self.$clearCompleted, 'click', function () { >+ handler(); >+ }); >+ >+ } else if (event === 'toggleAll') { >+ $on(self.$toggleAll, 'click', function () { >+ handler({completed: this.checked}); >+ }); >+ >+ } else if (event === 'itemEdit') { >+ $delegate(self.$todoList, 'li label', 'dblclick', function () { >+ handler({id: self._itemId(this)}); >+ }); >+ >+ } else if (event === 'itemRemove') { >+ $delegate(self.$todoList, '.destroy', 'click', function () { >+ handler({id: self._itemId(this)}); >+ }); >+ >+ } else if (event === 'itemToggle') { >+ $delegate(self.$todoList, '.toggle', 'click', function () { >+ handler({ >+ id: self._itemId(this), >+ completed: this.checked >+ }); >+ }); >+ >+ } else if (event === 'itemEditDone') { >+ self._bindItemEditDone(handler); >+ >+ } else if (event === 'itemEditCancel') { >+ self._bindItemEditCancel(handler); >+ } > }; > > // Export to window >+ window.app = window.app || {}; > window.app.View = View; >-})(window); >+}(window)); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-app-css/index.css b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-app-css/index.css >new file mode 100644 >index 0000000000000000000000000000000000000000..22ab9e2cad76966deb8be42e977c430241c43477 >--- /dev/null >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-app-css/index.css >@@ -0,0 +1,378 @@ >+html, >+body { >+ margin: 0; >+ padding: 0; >+} >+ >+button { >+ margin: 0; >+ padding: 0; >+ border: 0; >+ background: none; >+ font-size: 100%; >+ vertical-align: baseline; >+ font-family: inherit; >+ font-weight: inherit; >+ color: inherit; >+ -webkit-appearance: none; >+ appearance: none; >+ -webkit-font-smoothing: antialiased; >+ -moz-font-smoothing: antialiased; >+ font-smoothing: antialiased; >+} >+ >+body { >+ font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; >+ line-height: 1.4em; >+ background: #f5f5f5; >+ color: #4d4d4d; >+ min-width: 230px; >+ max-width: 550px; >+ margin: 0 auto; >+ -webkit-font-smoothing: antialiased; >+ -moz-font-smoothing: antialiased; >+ font-smoothing: antialiased; >+ font-weight: 300; >+} >+ >+button, >+input[type="checkbox"] { >+ outline: none; >+} >+ >+.hidden { >+ display: none; >+} >+ >+.todoapp { >+ background: #fff; >+ margin: 130px 0 40px 0; >+ position: relative; >+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), >+ 0 25px 50px 0 rgba(0, 0, 0, 0.1); >+} >+ >+.todoapp input::-webkit-input-placeholder { >+ font-style: italic; >+ font-weight: 300; >+ color: #e6e6e6; >+} >+ >+.todoapp input::-moz-placeholder { >+ font-style: italic; >+ font-weight: 300; >+ color: #e6e6e6; >+} >+ >+.todoapp input::input-placeholder { >+ font-style: italic; >+ font-weight: 300; >+ color: #e6e6e6; >+} >+ >+.todoapp h1 { >+ position: absolute; >+ top: -155px; >+ width: 100%; >+ font-size: 100px; >+ font-weight: 100; >+ text-align: center; >+ color: rgba(175, 47, 47, 0.15); >+ -webkit-text-rendering: optimizeLegibility; >+ -moz-text-rendering: optimizeLegibility; >+ text-rendering: optimizeLegibility; >+} >+ >+.new-todo, >+.edit { >+ position: relative; >+ margin: 0; >+ width: 100%; >+ font-size: 24px; >+ font-family: inherit; >+ font-weight: inherit; >+ line-height: 1.4em; >+ border: 0; >+ outline: none; >+ color: inherit; >+ padding: 6px; >+ border: 1px solid #999; >+ box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); >+ box-sizing: border-box; >+ -webkit-font-smoothing: antialiased; >+ -moz-font-smoothing: antialiased; >+ font-smoothing: antialiased; >+} >+ >+.new-todo { >+ padding: 16px 16px 16px 60px; >+ border: none; >+ background: rgba(0, 0, 0, 0.003); >+ box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); >+} >+ >+.main { >+ position: relative; >+ z-index: 2; >+ border-top: 1px solid #e6e6e6; >+} >+ >+label[for='toggle-all'] { >+ display: none; >+} >+ >+.toggle-all { >+ position: absolute; >+ top: -55px; >+ left: -12px; >+ width: 60px; >+ height: 34px; >+ text-align: center; >+ border: none; /* Mobile Safari */ >+} >+ >+.toggle-all:before { >+ content: 'â¯'; >+ font-size: 22px; >+ color: #e6e6e6; >+ padding: 10px 27px 10px 27px; >+} >+ >+.toggle-all:checked:before { >+ color: #737373; >+} >+ >+.todo-list { >+ margin: 0; >+ padding: 0; >+ list-style: none; >+} >+ >+.todo-list li { >+ position: relative; >+ font-size: 24px; >+ border-bottom: 1px solid #ededed; >+} >+ >+.todo-list li:last-child { >+ border-bottom: none; >+} >+ >+.todo-list li.editing { >+ border-bottom: none; >+ padding: 0; >+} >+ >+.todo-list li.editing .edit { >+ display: block; >+ width: 506px; >+ padding: 13px 17px 12px 17px; >+ margin: 0 0 0 43px; >+} >+ >+.todo-list li.editing .view { >+ display: none; >+} >+ >+.todo-list li .toggle { >+ text-align: center; >+ width: 40px; >+ /* auto, since non-WebKit browsers doesn't support input styling */ >+ height: auto; >+ position: absolute; >+ top: 0; >+ bottom: 0; >+ margin: auto 0; >+ border: none; /* Mobile Safari */ >+ -webkit-appearance: none; >+ appearance: none; >+} >+ >+.todo-list li .toggle:after { >+ content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>'); >+} >+ >+.todo-list li .toggle:checked:after { >+ content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>'); >+} >+ >+.todo-list li label { >+ white-space: pre-line; >+ word-break: break-all; >+ padding: 15px 60px 15px 15px; >+ margin-left: 45px; >+ display: block; >+ line-height: 1.2; >+ transition: color 0.4s; >+} >+ >+.todo-list li.completed label { >+ color: #d9d9d9; >+ text-decoration: line-through; >+} >+ >+.todo-list li .destroy { >+ display: none; >+ position: absolute; >+ top: 0; >+ right: 10px; >+ bottom: 0; >+ width: 40px; >+ height: 40px; >+ margin: auto 0; >+ font-size: 30px; >+ color: #cc9a9a; >+ margin-bottom: 11px; >+ transition: color 0.2s ease-out; >+} >+ >+.todo-list li .destroy:hover { >+ color: #af5b5e; >+} >+ >+.todo-list li .destroy:after { >+ content: 'Ã'; >+} >+ >+.todo-list li:hover .destroy { >+ display: block; >+} >+ >+.todo-list li .edit { >+ display: none; >+} >+ >+.todo-list li.editing:last-child { >+ margin-bottom: -1px; >+} >+ >+.footer { >+ color: #777; >+ padding: 10px 15px; >+ height: 20px; >+ text-align: center; >+ border-top: 1px solid #e6e6e6; >+} >+ >+.footer:before { >+ content: ''; >+ position: absolute; >+ right: 0; >+ bottom: 0; >+ left: 0; >+ height: 50px; >+ overflow: hidden; >+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), >+ 0 8px 0 -3px #f6f6f6, >+ 0 9px 1px -3px rgba(0, 0, 0, 0.2), >+ 0 16px 0 -6px #f6f6f6, >+ 0 17px 2px -6px rgba(0, 0, 0, 0.2); >+} >+ >+.todo-count { >+ float: left; >+ text-align: left; >+} >+ >+.todo-count strong { >+ font-weight: 300; >+} >+ >+.filters { >+ margin: 0; >+ padding: 0; >+ list-style: none; >+ position: absolute; >+ right: 0; >+ left: 0; >+} >+ >+.filters li { >+ display: inline; >+} >+ >+.filters li a { >+ color: inherit; >+ margin: 3px; >+ padding: 3px 7px; >+ text-decoration: none; >+ border: 1px solid transparent; >+ border-radius: 3px; >+} >+ >+.filters li a.selected, >+.filters li a:hover { >+ border-color: rgba(175, 47, 47, 0.1); >+} >+ >+.filters li a.selected { >+ border-color: rgba(175, 47, 47, 0.2); >+} >+ >+.clear-completed, >+html .clear-completed:active { >+ float: right; >+ position: relative; >+ line-height: 20px; >+ text-decoration: none; >+ cursor: pointer; >+ position: relative; >+} >+ >+.clear-completed:hover { >+ text-decoration: underline; >+} >+ >+.info { >+ margin: 65px auto 0; >+ color: #bfbfbf; >+ font-size: 10px; >+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); >+ text-align: center; >+} >+ >+.info p { >+ line-height: 1; >+} >+ >+.info a { >+ color: inherit; >+ text-decoration: none; >+ font-weight: 400; >+} >+ >+.info a:hover { >+ text-decoration: underline; >+} >+ >+/* >+ Hack to remove background from Mobile Safari. >+ Can't use it globally since it destroys checkboxes in Firefox >+*/ >+@media screen and (-webkit-min-device-pixel-ratio:0) { >+ .toggle-all, >+ .todo-list li .toggle { >+ background: none; >+ } >+ >+ .todo-list li .toggle { >+ height: 40px; >+ } >+ >+ .toggle-all { >+ -webkit-transform: rotate(90deg); >+ transform: rotate(90deg); >+ -webkit-appearance: none; >+ appearance: none; >+ } >+} >+ >+@media (max-width: 430px) { >+ .footer { >+ height: 50px; >+ } >+ >+ .filters { >+ bottom: 10px; >+ } >+} >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.css b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.css >new file mode 100644 >index 0000000000000000000000000000000000000000..4d25d3c84ac6c6aef26b9683a2bc10c61f960c8f >--- /dev/null >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.css >@@ -0,0 +1,141 @@ >+hr { >+ margin: 20px 0; >+ border: 0; >+ border-top: 1px dashed #c5c5c5; >+ border-bottom: 1px dashed #f7f7f7; >+} >+ >+.learn a { >+ font-weight: normal; >+ text-decoration: none; >+ color: #b83f45; >+} >+ >+.learn a:hover { >+ text-decoration: underline; >+ color: #787e7e; >+} >+ >+.learn h3, >+.learn h4, >+.learn h5 { >+ margin: 10px 0; >+ font-weight: 500; >+ line-height: 1.2; >+ color: #000; >+} >+ >+.learn h3 { >+ font-size: 24px; >+} >+ >+.learn h4 { >+ font-size: 18px; >+} >+ >+.learn h5 { >+ margin-bottom: 0; >+ font-size: 14px; >+} >+ >+.learn ul { >+ padding: 0; >+ margin: 0 0 30px 25px; >+} >+ >+.learn li { >+ line-height: 20px; >+} >+ >+.learn p { >+ font-size: 15px; >+ font-weight: 300; >+ line-height: 1.3; >+ margin-top: 0; >+ margin-bottom: 0; >+} >+ >+#issue-count { >+ display: none; >+} >+ >+.quote { >+ border: none; >+ margin: 20px 0 60px 0; >+} >+ >+.quote p { >+ font-style: italic; >+} >+ >+.quote p:before { >+ content: 'â'; >+ font-size: 50px; >+ opacity: .15; >+ position: absolute; >+ top: -20px; >+ left: 3px; >+} >+ >+.quote p:after { >+ content: 'â'; >+ font-size: 50px; >+ opacity: .15; >+ position: absolute; >+ bottom: -42px; >+ right: 3px; >+} >+ >+.quote footer { >+ position: absolute; >+ bottom: -40px; >+ right: 0; >+} >+ >+.quote footer img { >+ border-radius: 3px; >+} >+ >+.quote footer a { >+ margin-left: 5px; >+ vertical-align: middle; >+} >+ >+.speech-bubble { >+ position: relative; >+ padding: 10px; >+ background: rgba(0, 0, 0, .04); >+ border-radius: 5px; >+} >+ >+.speech-bubble:after { >+ content: ''; >+ position: absolute; >+ top: 100%; >+ right: 30px; >+ border: 13px solid transparent; >+ border-top-color: rgba(0, 0, 0, .04); >+} >+ >+.learn-bar > .learn { >+ position: absolute; >+ width: 272px; >+ top: 8px; >+ left: -300px; >+ padding: 10px; >+ border-radius: 5px; >+ background-color: rgba(255, 255, 255, .6); >+ transition-property: left; >+ transition-duration: 500ms; >+} >+ >+@media (min-width: 899px) { >+ .learn-bar { >+ width: auto; >+ padding-left: 300px; >+ } >+ >+ .learn-bar > .learn { >+ left: 8px; >+ } >+} >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.js b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.js >new file mode 100644 >index 0000000000000000000000000000000000000000..81e006bc502ddc4c74f47bd13a8392277f830db8 >--- /dev/null >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/node_modules/todomvc-common/base.js >@@ -0,0 +1,249 @@ >+/* global _ */ >+(function () { >+ 'use strict'; >+ >+ /* jshint ignore:start */ >+ // Underscore's Template Module >+ // Courtesy of underscorejs.org >+ var _ = (function (_) { >+ _.defaults = function (object) { >+ if (!object) { >+ return object; >+ } >+ for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { >+ var iterable = arguments[argsIndex]; >+ if (iterable) { >+ for (var key in iterable) { >+ if (object[key] == null) { >+ object[key] = iterable[key]; >+ } >+ } >+ } >+ } >+ return object; >+ } >+ >+ // By default, Underscore uses ERB-style template delimiters, change the >+ // following template settings to use alternative delimiters. >+ _.templateSettings = { >+ evaluate : /<%([\s\S]+?)%>/g, >+ interpolate : /<%=([\s\S]+?)%>/g, >+ escape : /<%-([\s\S]+?)%>/g >+ }; >+ >+ // When customizing `templateSettings`, if you don't want to define an >+ // interpolation, evaluation or escaping regex, we need one that is >+ // guaranteed not to match. >+ var noMatch = /(.)^/; >+ >+ // Certain characters need to be escaped so that they can be put into a >+ // string literal. >+ var escapes = { >+ "'": "'", >+ '\\': '\\', >+ '\r': 'r', >+ '\n': 'n', >+ '\t': 't', >+ '\u2028': 'u2028', >+ '\u2029': 'u2029' >+ }; >+ >+ var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; >+ >+ // JavaScript micro-templating, similar to John Resig's implementation. >+ // Underscore templating handles arbitrary delimiters, preserves whitespace, >+ // and correctly escapes quotes within interpolated code. >+ _.template = function(text, data, settings) { >+ var render; >+ settings = _.defaults({}, settings, _.templateSettings); >+ >+ // Combine delimiters into one regular expression via alternation. >+ var matcher = new RegExp([ >+ (settings.escape || noMatch).source, >+ (settings.interpolate || noMatch).source, >+ (settings.evaluate || noMatch).source >+ ].join('|') + '|$', 'g'); >+ >+ // Compile the template source, escaping string literals appropriately. >+ var index = 0; >+ var source = "__p+='"; >+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { >+ source += text.slice(index, offset) >+ .replace(escaper, function(match) { return '\\' + escapes[match]; }); >+ >+ if (escape) { >+ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; >+ } >+ if (interpolate) { >+ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; >+ } >+ if (evaluate) { >+ source += "';\n" + evaluate + "\n__p+='"; >+ } >+ index = offset + match.length; >+ return match; >+ }); >+ source += "';\n"; >+ >+ // If a variable is not specified, place data values in local scope. >+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; >+ >+ source = "var __t,__p='',__j=Array.prototype.join," + >+ "print=function(){__p+=__j.call(arguments,'');};\n" + >+ source + "return __p;\n"; >+ >+ try { >+ render = new Function(settings.variable || 'obj', '_', source); >+ } catch (e) { >+ e.source = source; >+ throw e; >+ } >+ >+ if (data) return render(data, _); >+ var template = function(data) { >+ return render.call(this, data, _); >+ }; >+ >+ // Provide the compiled function source as a convenience for precompilation. >+ template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; >+ >+ return template; >+ }; >+ >+ return _; >+ })({}); >+ >+ if (location.hostname === 'todomvc.com') { >+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ >+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), >+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) >+ })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); >+ ga('create', 'UA-31081062-1', 'auto'); >+ ga('send', 'pageview'); >+ } >+ /* jshint ignore:end */ >+ >+ function redirect() { >+ if (location.hostname === 'tastejs.github.io') { >+ location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); >+ } >+ } >+ >+ function findRoot() { >+ var base = location.href.indexOf('examples/'); >+ return location.href.substr(0, base); >+ } >+ >+ function getFile(file, callback) { >+ if (!location.host) { >+ return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); >+ } >+ >+ var xhr = new XMLHttpRequest(); >+ >+ xhr.open('GET', findRoot() + file, true); >+ xhr.send(); >+ >+ xhr.onload = function () { >+ if (xhr.status === 200 && callback) { >+ callback(xhr.responseText); >+ } >+ }; >+ } >+ >+ function Learn(learnJSON, config) { >+ if (!(this instanceof Learn)) { >+ return new Learn(learnJSON, config); >+ } >+ >+ var template, framework; >+ >+ if (typeof learnJSON !== 'object') { >+ try { >+ learnJSON = JSON.parse(learnJSON); >+ } catch (e) { >+ return; >+ } >+ } >+ >+ if (config) { >+ template = config.template; >+ framework = config.framework; >+ } >+ >+ if (!template && learnJSON.templates) { >+ template = learnJSON.templates.todomvc; >+ } >+ >+ if (!framework && document.querySelector('[data-framework]')) { >+ framework = document.querySelector('[data-framework]').dataset.framework; >+ } >+ >+ this.template = template; >+ >+ if (learnJSON.backend) { >+ this.frameworkJSON = learnJSON.backend; >+ this.frameworkJSON.issueLabel = framework; >+ this.append({ >+ backend: true >+ }); >+ } else if (learnJSON[framework]) { >+ this.frameworkJSON = learnJSON[framework]; >+ this.frameworkJSON.issueLabel = framework; >+ this.append(); >+ } >+ >+ this.fetchIssueCount(); >+ } >+ >+ Learn.prototype.append = function (opts) { >+ var aside = document.createElement('aside'); >+ aside.innerHTML = _.template(this.template, this.frameworkJSON); >+ aside.className = 'learn'; >+ >+ if (opts && opts.backend) { >+ // Remove demo link >+ var sourceLinks = aside.querySelector('.source-links'); >+ var heading = sourceLinks.firstElementChild; >+ var sourceLink = sourceLinks.lastElementChild; >+ // Correct link path >+ var href = sourceLink.getAttribute('href'); >+ sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); >+ sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; >+ } else { >+ // Localize demo links >+ var demoLinks = aside.querySelectorAll('.demo-link'); >+ Array.prototype.forEach.call(demoLinks, function (demoLink) { >+ if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { >+ demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); >+ } >+ }); >+ } >+ >+ document.body.className = (document.body.className + ' learn-bar').trim(); >+ document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); >+ }; >+ >+ Learn.prototype.fetchIssueCount = function () { >+ var issueLink = document.getElementById('issue-count-link'); >+ if (issueLink) { >+ var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); >+ var xhr = new XMLHttpRequest(); >+ xhr.open('GET', url, true); >+ xhr.onload = function (e) { >+ var parsedResponse = JSON.parse(e.target.responseText); >+ if (parsedResponse instanceof Array) { >+ var count = parsedResponse.length; >+ if (count !== 0) { >+ issueLink.innerHTML = 'This app has ' + count + ' open issues'; >+ document.getElementById('issue-count').style.display = 'inline'; >+ } >+ } >+ }; >+ xhr.send(); >+ } >+ }; >+ >+ redirect(); >+ getFile('learn.json', Learn); >+})(); >diff --git a/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/package.json b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/package.json >new file mode 100644 >index 0000000000000000000000000000000000000000..58c5a8063d9da09ee5b5cb2e27331de6b7330a2d >--- /dev/null >+++ b/PerformanceTests/Speedometer/resources/todomvc/vanilla-examples/vanillajs/package.json >@@ -0,0 +1,10 @@ >+{ >+ "private": true, >+ "dependencies": { >+ "todomvc-common": "^1.0.1", >+ "todomvc-app-css": "^2.0.1" >+ }, >+ "devDependencies": { >+ "jasmine-core": "^2.0.0" >+ } >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 171306
:
308247
| 309834