├── .gitignore ├── LICENSE.md ├── README.md ├── bower.json ├── cssom.js ├── demos ├── geo.html ├── geo.jpg ├── keyboard.html ├── keyboard_test.js ├── polyfill.css └── raf.html ├── dom.js ├── es2016.js ├── es2016.md ├── es2017.js ├── es2017.md ├── es5.js ├── es5.md ├── es6.js ├── es6.md ├── experimental ├── README.md ├── es-proposed.js ├── es-proposed.md └── tests │ ├── es-proposed.html │ └── es-proposed.js ├── fetch.js ├── geo.js ├── html.js ├── index.html ├── js.js ├── keyboard.js ├── keyboard.md ├── obsolete ├── README.md ├── bindata.js ├── console.js ├── cookie.js ├── demos │ ├── bindata.html │ └── domexception.html ├── domexception.js ├── sprintf.js ├── storage.js ├── tests │ ├── console.html │ ├── sprintf.html │ └── sprintf_tests.js └── workers.js ├── package.json ├── polyfill.js ├── polyfill.js.md ├── polyfill.min.js ├── tests ├── dom.html ├── dom.js ├── es2016.html ├── es2016.js ├── es2017.html ├── es2017.js ├── es5.html ├── es5.js ├── es6.html ├── es6.js ├── fetch.html ├── fetch.js ├── html.html ├── html.js ├── js.html ├── js.js ├── sample.json ├── sample.txt ├── testhelpers.js ├── typedarray.html ├── typedarray.js ├── url.html ├── url.js ├── xhr.html └── xhr.js ├── timing.js ├── typedarray.js ├── url.js ├── web.js ├── web.js.md ├── web.min.js └── xhr.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ### Polyfills (most of the content of this repository) 2 | 3 | The polyfills code of this repository is released under the Unlicense 4 | license and is also dual-licensed under an MIT license (for the 5 | greatest freedom from restrictions and the widest jurisdictional 6 | compatibility). 7 | 8 | `SPDX-License-Identifier: MIT OR Unlicense` 9 | 10 | #### MIT 11 | 12 | Copyright 2018 Joshua Bell 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining 15 | a copy of this software and associated documentation files (the 16 | "Software"), to deal in the Software without restriction, including 17 | without limitation the rights to use, copy, modify, merge, publish, 18 | distribute, sublicense, and/or sell copies of the Software, and to 19 | permit persons to whom the Software is furnished to do so, subject to 20 | the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be 23 | included in all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 28 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 29 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 30 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 31 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | 33 | #### Unlicense 34 | 35 | This is free and unencumbered software released into the public domain. 36 | 37 | Anyone is free to copy, modify, publish, use, compile, sell, or 38 | distribute this software, either in source code form or as a compiled 39 | binary, for any purpose, commercial or non-commercial, and by any 40 | means. 41 | 42 | In jurisdictions that recognize copyright laws, the author or authors 43 | of this software dedicate any and all copyright interest in the 44 | software to the public domain. We make this dedication for the benefit 45 | of the public at large and to the detriment of our heirs and 46 | successors. We intend this dedication to be an overt act of 47 | relinquishment in perpetuity of all present and future rights to this 48 | software under copyright law. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 51 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 52 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 53 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 54 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 55 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 56 | OTHER DEALINGS IN THE SOFTWARE. 57 | 58 | For more information, please refer to 59 | 60 | ### Typed Arrays - typedarray.js 61 | 62 | Copyright (C) 2010 Linden Research, Inc. 63 | Originally published at: 64 | 65 | Permission is hereby granted, free of charge, to any person obtaining a copy 66 | of this software and associated documentation files (the "Software"), to deal 67 | in the Software without restriction, including without limitation the rights 68 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 69 | copies of the Software, and to permit persons to whom the Software is 70 | furnished to do so, subject to the following conditions: 71 | 72 | The above copyright notice and this permission notice shall be included in 73 | all copies or substantial portions of the Software. 74 | 75 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 76 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 77 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 78 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 79 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 80 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 81 | THE SOFTWARE. 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | polyfill - JavaScript and Web Polyfills 2 | ======================================= 3 | 4 | This is a collection of polyfills covering web platform features, from those defined as part of the ECMAScript standard to new web browser functionality. Most are for features shipping in major browsers. A few are experimental and called out as such, subject to change at any time. 5 | 6 | My philosophy is that it's better to write future-looking code that takes advantage of new Web platform APIs where possible, and fill in the gaps with polyfills. There is no effort to produce 100% compliant behavior, or to completely hide differences in browser behavior. 7 | 8 | I use these in various pages on my sites; most are by me, or I have at least tweaked them. A more comprehensive list of polyfills can be found at [The All-In-One Entirely-Not-Alphabetical No-Bullshit Guide to HTML5 Fallbacks](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills) by Paul Irish. 9 | 10 | ### Getting the Code ### 11 | 12 | You're already here! Great, just download it, or use: 13 | 14 | [git](https://git-scm.com/): `git clone https://github.com/inexorabletash/polyfill.git` 15 | 16 | [bower](http://bower.io/): `bower install js-polyfills` 17 | 18 | [npm](https://www.npmjs.com/): `npm install js-polyfills` 19 | 20 | > It is *not* packaged as Node.js module(s); there's nothing to `require()`, this is just for distribution. 21 | 22 | Or just include scripts directly in your page via CDN (c/o [RawGit](https://rawgit.com/)): 23 | 24 | 25 | 26 | (look at [Releases](https://github.com/inexorabletash/polyfill/releases) for the tag name, e.g. "v1.2.3") 27 | 28 | ### Files ### 29 | 30 | The polyfills are roughly split up into files matching 1:1 with Web standards (specifications, living standards documents, etc). So there is [html.js](html.js) for [HTML](https://html.spec.whatwg.org), [dom.js](dom.js) for [DOM](https://dom.spec.whatwg.org), etc. 31 | 32 | Since I generally use several in my hobby projects, bundled/minified versions are available: 33 | 34 | * [web.js](web.js) (minified: [web.min.js](web.min.js)) includes the most common Web polyfills - it assumes ES2015 support 35 | * Includes: [html.js](html.js) [dom.js](dom.js) [xhr.js](xhr.js) [cssom.js](cssom.js) [url.js](url.js) [fetch.js](fetch.js) 36 | * [polyfill.js](polyfill.js) (minified: [polyfill.min.js](polyfill.min.js)) has everything in [web.js](web.js) plus [es5.js](es5.js) and [es6.js](es6.js) and [es2016.js](es2016.js) and [es2017.js](es2017.js) 37 | 38 | Minification is done via https://github.com/mishoo/UglifyJS2 39 | 40 | > Some of the files use `console.assert()` calls to catch bugs during development. These are 41 | > automatically removed from the included minified versions. If you use your own minifying 42 | > processor it may cause to assertions to appear when unnecessary function names are stripped. 43 | > You can safely remove these lines as part of a build step (e.g. using `grep -V`), or use a 44 | > minifier that does this automatically. For [UglifyJS2](https://github.com/mishoo/UglifyJS2) 45 | > the option is: `drop_console` 46 | 47 | 48 | ECMAScript / JavaScript Polyfills 49 | --------------------------------- 50 | 51 | [ECMAScript 5](es5.md) - Previous standard, supported by browsers circa 2012.. 52 | 53 | [ECMAScript 2015](es6.md) - Previous standard, supported by browsers circa 2016. 54 | 55 | [ECMAScript 2016](es2016.md) - Previous standard, supported by browsers circa 2017. 56 | 57 | [ECMAScript 2017](es2017.md) - Most recent standard. Implementation in progress or complete in latest browsers. 58 | 59 | [ECMAScript proposed](experimental/es-proposed.md) - Proposals for future editions of the standard. Here there be dragons. 60 | 61 | [JavaScript 1.X String Extras](js.js) - [ref](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String) 62 | * String prototype: `trimLeft`, `trimRight`, `quote` 63 | 64 | 65 | HTML 66 | ---- 67 | [script](html.js) - 68 | [tests](https://inexorabletash.github.io/polyfill/tests/html.html) - 69 | [living standard](https://html.spec.whatwg.org) 70 | 71 | * `document.head` (for IE8-) 72 | * 'shiv' of newer HTML elements (`section`, `aside`, etc), to fix parsing (for IE8-) 73 | * `dataset` and `data-*` attributes [spec](https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) (for IE8+, not available in IE7-) 74 | * `str = element.dataset[key]` - yields undefined if data-key attribute not present 75 | * `element.dataset[key] = str` - fails unless data-key attribute already present 76 | * [Base64 utility methods](https://html.spec.whatwg.org/multipage/webappapis.html#atob) (for IE9-) 77 | * `encodedString = window.btoa(binaryString)` - Base64 Encode 78 | * `binaryString = window.atob(encodedString)` - Base64 Decode 79 | * [Animation Frames](https://html.spec.whatwg.org/multipage/webappapis.html#animation-frames) - [demo page](https://inexorabletash.github.io/polyfill/demos/raf.html) 80 | * `id = window.requestAnimationFrame()` 81 | * `window.cancelAnimationFrame(id)` 82 | 83 | 84 | DOM 85 | --- 86 | [script](dom.js) - 87 | [tests](https://inexorabletash.github.io/polyfill/tests/dom.html) - 88 | [living standard](https://dom.spec.whatwg.org) 89 | 90 | * [Selectors](https://dom.spec.whatwg.org/#scope-match-a-selectors-string) (for IE7-) - adapted from [Paul Young](http://ajaxian.com/archives/creating-a-queryselector-for-ie-that-runs-at-native-speed) 91 | * `element = document.querySelector(selector)` 92 | * `elementArray = document.querySelectorAll(selector)` 93 | * `elem.matches(selector)` (for IE, Firefox 3.6, early Webkit and Opera 15.0) 94 | * `elementArray = document.getElementsByClassName(classNames)` (for IE8-) 95 | * `e = element.nextElementSibling`, `e = element.previousElementSibling` (for IE8) 96 | * Node constants: `Node.ELEMENT_NODE`, etc (for IE8-) 97 | * DOMException constants: `DOMException.INDEX_SIZE_ERR` (for IE8-) 98 | * [Events](https://dom.spec.whatwg.org/) (for IE8) 99 | * Where `EventTarget` is `window`, `document`, or any element: 100 | * `EventTarget.addEventListener(event, handler)` - for IE8+ 101 | * `EventTarget.removeEventListener(event, handler)` - for IE8+ 102 | * Event: `target`, `currentTarget`, `eventPhase`, `bubbles`, `cancelable`, `timeStamp`, `defaultPrevented`, `stopPropagation()`, `cancelBubble()` 103 | * Non-standard Event helpers for IE7- - adapted from 104 | * [Custom Events](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent) 105 | * [QuirksMode](http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html) 106 | * `window.addEvent(EventTarget, event, handler)` 107 | * `window.removeEvent(EventTarget, event, handler)` 108 | * [DOMTokenList](https://dom.spec.whatwg.org/#interface-domtokenlist) - `classList`[spec](https://dom.spec.whatwg.org/#dom-element-classlist), `relList`[spec](https://html.spec.whatwg.org/multipage/semantics.html#the-link-element) 109 | * DOMTokenList: `length`, `item(index)`, `contains(token)`, `add(token)`, `remove(token)`, `toggle(token)` 110 | * `tokenList = elem.classList` - for IE8+ 111 | * `tokenList = elem.relList` - for IE8+ 112 | * Non-standard helpers for IE7-: 113 | * `tokenList = window.getClassList(element)` 114 | * `tokenList = window.getRelList(element)` 115 | * ParentNode: `node.prepend(nodes...)`, `node.append(nodes...)` 116 | * ChildNode: `node.before(nodes...)` , `node.after(nodes...)` , `node.replaceWith(nodes...)` , `node.remove()` 117 | 118 | 119 | Fetch 120 | ----- 121 | [script](fetch.js) - 122 | [tests](https://inexorabletash.github.io/polyfill/tests/fetch.html) - 123 | [living standard](https://fetch.spec.whatwg.org) 124 | 125 | Example: 126 | 127 | ```js 128 | fetch('http://example.com/foo.json') 129 | .then(function(response) { return response.json(); }) 130 | .then(function(data) { console.log(data); }); 131 | ``` 132 | 133 | Supported: 134 | * Headers: `new Headers()`, `append(name, value)`, `delete(name)`, `get(name)`, `getAll(name)`, `has(name)`, `set(name, value)`, `[Symbol.iterator]()` 135 | * Body: `arrayBuffer()`, `blob()`, `formData()`, `json()`, `text()` - but conversions are limited 136 | * Request: `new Request(input, init)`, `method`, `headers`, `body`, `url` 137 | * Response: `new Response(body, init)`, `headers`, `url`, `status`, `statusText`, `body` 138 | * `fetch(input, init)` 139 | 140 | 141 | XMLHttpRequest 142 | -------------- 143 | [script](xhr.js) - 144 | [tests](https://inexorabletash.github.io/polyfill/tests/xhr.html) - 145 | [living standard](https://xhr.spec.whatwg.org/) 146 | * [`XMLHttpRequest`](https://xhr.spec.whatwg.org/#interface-xmlhttprequest) (for IE6-) 147 | * [`FormData`](https://xhr.spec.whatwg.org/#interface-formdata) (for IE9-) 148 | 149 | 150 | CSS OM 151 | ------ 152 | [script](cssom.js) - [spec](https://dev.w3.org/csswg/cssom-view/) 153 | 154 | Polyfill for `width` and `height` in `getBoundingClientRect()` in IE8- 155 | 156 | 157 | URL API 158 | ------- 159 | [script](url.js) - 160 | [tests](https://inexorabletash.github.io/polyfill/tests/url.html) - 161 | [living standard](https://url.spec.whatwg.org/) 162 | 163 | ```javascript 164 | var url = new URL(url, base); 165 | var value = url.searchParams.get(name); 166 | var valueArray = url.searchParams.getAll(name); 167 | url.searchParams.append(name, valueOrValues); 168 | url.searchParams.delete(name); 169 | 170 | var p = new URLSearchParams('a=1&b=2'); 171 | ``` 172 | 173 | * URL: `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, `pathname`, `search`, `searchParams`, `hash` 174 | * URLSearchParams: `append(name, value)`, `delete(name)`, `get(name)`, `getAll(name)`, `has(name)`, `set(name, value)`, `entries()`, `keys()`, `values()`, `forEach(callback)` and `[Symbol.iterator]()` (if defined) 175 | 176 | 177 | Uncommon Polyfills 178 | ================== 179 | 180 | The following are of limited use and are *not* included in the `web.js` / `polyfill.js` bundled versions. 181 | 182 | Timing 183 | ------ 184 | [script](timing.js) 185 | 186 | * [Efficient Script Yielding](http://w3c.github.io/setImmediate/) 187 | * `id = setImmediate(callback, args...)` 188 | * `clearImmediate(id)` 189 | 190 | 191 | Keyboard Events 192 | --------------- 193 | [script](keyboard.js) - 194 | [demo page](https://inexorabletash.github.io/polyfill/demos/keyboard.html) - 195 | [draft spec](https://w3c.github.io/uievents/) ([also](https://dvcs.w3.org/hg/d4e/raw-file/tip/source_respec.htm)) 196 | 197 | KeyboardEvent: `code`, `key`, `location`, `KeyboardEvent.queryKeyCap(code)` 198 | 199 | IE7- only: Call `window.identifyKey(keyboardEvent);` in `keydown`/`keyup` handlers before accessing above properties. 200 | 201 | [more details](keyboard.md) 202 | 203 | 204 | Geolocation API 205 | --------------- 206 | [script](geo.js) - 207 | [demo page](https://inexorabletash.github.io/polyfill/demos/geo.html) - 208 | [spec](http://www.w3.org/TR/geolocation-API/) - 209 | uses [freegeoip.net](https://freegeoip.net/) 210 | 211 | ```javascript 212 | navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options); 213 | var watchId = navigator.geolocation.watchPosition(successCallback, errorCallback, options); 214 | navigator.geolocation.clearWatch(watchId); 215 | ``` 216 | 217 | Obsolete 218 | -------- 219 | [Obsolete and Unmaintained Polyfills](obsolete/README.md) 220 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-polyfills", 3 | "version": "0.1.43", 4 | "homepage": "https://github.com/inexorabletash/polyfill", 5 | "authors": [ 6 | "Joshua Bell " 7 | ], 8 | "description": "Various Web API and ECMAScript Polyfills", 9 | "license": "Unlicense", 10 | "main": [ 11 | "polyfill.js" 12 | ], 13 | "keywords": [ 14 | "polyfill", 15 | "standards" 16 | ], 17 | "ignore": [ 18 | "**/.*", 19 | "**/*.html", 20 | "**/*.json", 21 | "**/*.md", 22 | "demos", 23 | "experimental", 24 | "obsolete", 25 | "tests" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /cssom.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | if (!('window' in global && 'document' in global)) 5 | return; 6 | 7 | //---------------------------------------------------------------------- 8 | // 9 | // CSSOM View Module 10 | // https://dev.w3.org/csswg/cssom-view/ 11 | // 12 | //---------------------------------------------------------------------- 13 | 14 | // Fix for IE8-'s Element.getBoundingClientRect() 15 | if ('TextRectangle' in global && !('width' in global.TextRectangle.prototype)) { 16 | Object.defineProperties(global.TextRectangle.prototype, { 17 | width: { get: function() { return this.right - this.left; } }, 18 | height: { get: function() { return this.bottom - this.top; } } 19 | }); 20 | } 21 | }(self)); 22 | -------------------------------------------------------------------------------- /demos/geo.html: -------------------------------------------------------------------------------- 1 | 2 | Geolocation API Polyfill Demo 3 | 4 | 5 | 6 |

Geolocation API Polyfill Demo

7 |

This demo page overrides your browser's built-in geolocation support, if possible.

8 | 9 |
10 | 11 | 12 |
13 |

Equirectangular projection by Strebe. Licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.

14 | 15 |

Checking position...

16 | 17 | 18 | 19 | 20 | 52 | -------------------------------------------------------------------------------- /demos/geo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inexorabletash/polyfill/716a3f36ca10fad032083014faf1a47c638e2502/demos/geo.jpg -------------------------------------------------------------------------------- /demos/keyboard.html: -------------------------------------------------------------------------------- 1 |  2 | Keyboard Event Polyfill Demo 3 | 4 | 5 | 6 | 7 | 8 | 52 | 53 | 54 |
55 | 56 |
57 |
58 | Esc 59 | F1 60 | F2 61 | F3 62 | F4 63 | F5 64 | F6 65 | F7 66 | F8 67 | F9 68 | F10 69 | F11 70 | F12 71 |
72 | 73 |
74 | ` ~ 75 | 1 ! 76 | 2 @ 77 | 3 # 78 | 4 $ 79 | 5 % 80 | 6 ^ 81 | 7 & 82 | 8 * 83 | 9 ( 84 | 0 ) 85 | - _ 86 | = + 87 | Backspace 88 |
89 | 90 |
91 | Tab 92 | Q 93 | W 94 | E 95 | R 96 | T 97 | Y 98 | U 99 | I 100 | O 101 | P 102 | [ { 103 | ] } 104 | \ | 105 |
106 | 107 |
108 | Caps Lock 109 | A 110 | S 111 | D 112 | F 113 | G 114 | H 115 | J 116 | K 117 | L 118 | ; : 119 | ' " 120 | Enter 121 |
122 | 123 |
124 | Shift 125 | Z 126 | X 127 | C 128 | V 129 | B 130 | N 131 | M 132 | , < 133 | . > 134 | / ? 135 | Shift 136 |
137 | 138 |
139 | Ctrl 140 | Win 141 | 142 | Alt 143 | Spacebar 144 | Alt 145 | Win 146 | 147 | Menu 148 | Ctrl 149 |
150 |
151 | 152 |
153 |
154 | PrtScr 155 | Scroll
Lock
156 | Pause
Break
157 |
158 | 159 |
160 | Ins 161 | Home 162 | Page
Up
163 |
164 | 165 |
166 | Del 167 | End 168 | Page
Down
169 |
170 | 171 | 172 |
173 | 174 |
175 | 176 | 177 | 178 |
179 | 180 | 181 | 182 |
183 | 184 |
185 | 186 |
187 | 188 |
189 | 190 | Num
Lock
191 | / 192 | * 193 | - 194 |
195 | + 196 | 7 197 | 8 198 | 9 199 |
200 | 4 201 | 5 202 | 6 203 |
204 | Enter 205 | 1 206 | 2 207 | 3 208 |
209 | 0 210 | . 211 |
212 | 213 |
214 | 215 | 216 | 217 |
218 | 221 |
222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /demos/keyboard_test.js: -------------------------------------------------------------------------------- 1 | 2 | function contains(s, ss) { return s.indexOf(ss) != -1; } 3 | if (contains(navigator.platform, 'Win')) 4 | document.body.classList.add('meta-win'); 5 | if (contains(navigator.platform, 'Mac')) 6 | document.body.classList.add('meta-mac'); 7 | if (contains(navigator.userAgent, 'CrOS')) 8 | document.body.classList.add('meta-cros'); 9 | 10 | function select(event) { 11 | var id = event.code; 12 | return [].map.call(document.querySelectorAll('.' + id), function(x) { return x; }); 13 | 14 | // Can't override |location| on KeyboardEvent in some browsers, so it 15 | // may be wrong, e.g. NumLock in moz-mac 16 | } 17 | 18 | var target = document.getElementById('target'); 19 | 20 | document.documentElement.addEventListener('click', function(e) { 21 | target.focus(); 22 | }); 23 | 24 | // Block Apps (context menu) key in IE 25 | document.documentElement.addEventListener('contextmenu', function (e) { 26 | return false; 27 | }); 28 | 29 | var lastKey = -1; 30 | 31 | // Workaround for Opera which doesn't allow cancelling Tab, IE/Apps 32 | target.addEventListener('blur', function (e) { 33 | if (lastKey === 0x09 || lastKey === 0x5D) 34 | target.focus(); 35 | lastKey = -1; 36 | }); 37 | 38 | function hex(x, w) { 39 | if (x === undefined) return x; 40 | x = Number(x); 41 | w = Number(w) || 2; 42 | return '0x' + ('0'.repeat(w) + x.toString(16)).slice(-w); 43 | } 44 | 45 | function show(selector, e) { 46 | var elem = document.querySelector(selector), 47 | data = [ 48 | { 49 | category: 'UI Events', 50 | link: 'https://w3c.github.io/uievents/', 51 | attributes: { 52 | code: e.code, 53 | key: e.key, 54 | location: e.location 55 | } 56 | }, 57 | 58 | { 59 | category: 'UI Events (extension)', 60 | link: 'https://dvcs.w3.org/hg/d4e/raw-file/tip/source_respec.htm', 61 | attributes: { 62 | 'queryKeyCap()': KeyboardEvent.queryKeyCap(e.code) 63 | } 64 | }, 65 | 66 | { 67 | category: 'Legacy user agents', 68 | attributes: { 69 | charCode: hex(e.charCode), 70 | keyCode: hex(e.keyCode), 71 | which: hex(e.which), 72 | char: e.char // IE only 73 | } 74 | }, 75 | 76 | { 77 | category: 'Abandoned proposals', 78 | attributes: { 79 | keyChar: e.keyChar, 80 | keyIdentifier: e.keyIdentifier, 81 | keyLocation: e.keyLocation, 82 | keyName: e.keyName 83 | } 84 | }, 85 | 86 | { 87 | category: 'Modifiers', 88 | attributes: { 89 | altKey: e.altKey, 90 | ctrlKey: e.ctrlKey, 91 | metaKey: e.metaKey, 92 | shiftKey: e.shiftKey, 93 | repeat: e.repeat 94 | } 95 | } 96 | ]; 97 | 98 | while (elem.hasChildNodes()) 99 | elem.removeChild(elem.firstChild); 100 | 101 | var s = data.map(function(cat) { 102 | return cat.category + ':\n' + 103 | Object.keys(cat.attributes).filter(function(k) { 104 | return typeof cat.attributes[k] !== 'undefined'; 105 | }).map(function(k) { 106 | return ' ' + k + ': ' + cat.attributes[k] + '\n'; 107 | }).join(''); 108 | }).join('\n'); 109 | elem.appendChild(document.createTextNode(s)); 110 | } 111 | 112 | target.addEventListener('keydown', function(e) { 113 | identifyKey(e); // for IE8- 114 | lastKey = e.keyCode; 115 | 116 | show('#eventdata', e); 117 | 118 | select(e).forEach( 119 | function (elem) { 120 | elem.style.backgroundColor = 'red'; 121 | }); 122 | 123 | e.preventDefault(); 124 | e.stopPropagation(); 125 | }); 126 | 127 | target.addEventListener('keyup', function (e) { 128 | identifyKey(e); // for IE8- 129 | if (lastKey == e.keyCode) { lastKey = -1; } 130 | 131 | show('#eventdata', e); 132 | 133 | select(e).forEach( 134 | function (elem) { 135 | elem.style.backgroundColor = '#ffaaaa'; 136 | setTimeout(function() { 137 | elem.style.backgroundColor = '#aaaaaa'; 138 | }, 200); 139 | }); 140 | 141 | e.preventDefault(); 142 | e.stopPropagation(); 143 | }); 144 | 145 | target.addEventListener('keypress', function (e) { 146 | e.preventDefault(); 147 | e.stopPropagation(); 148 | }); 149 | 150 | target.addEventListener('contextmenu', function (e) { 151 | e.preventDefault(); 152 | e.stopPropagation(); 153 | }); 154 | 155 | target.focus(); 156 | -------------------------------------------------------------------------------- /demos/polyfill.css: -------------------------------------------------------------------------------- 1 | body { margin-left: 80px; margin-right: 80px; } 2 | h1 { border-bottom: solid 2px black; margin-bottom: 0; } 3 | h2 { text-decoration: underline; margin-bottom: 0; } 4 | p { margin-top: 0.5em; margin-bottom: 0.5em; } 5 | code { background-color: #eeeeee; } 6 | -------------------------------------------------------------------------------- /demos/raf.html: -------------------------------------------------------------------------------- 1 | 2 | requestAnimationFrame Demo 3 | 4 | 5 | 6 |

requestAnimationFrame Demo

7 |

This demo uses your browser's native requestAnimationFrame support, if present. 8 | 9 |

10 |
11 |
12 |
13 | 14 |
15 | 16 | 17 | 70 | -------------------------------------------------------------------------------- /es2016.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------- 2 | // 3 | // ECMAScript 2016 Polyfills 4 | // 5 | //---------------------------------------------------------------------- 6 | 7 | (function (global) { 8 | "use strict"; 9 | var undefined = (void 0); // Paranoia 10 | 11 | // Helpers 12 | 13 | function isSymbol(s) { 14 | return (typeof s === 'symbol') || ('Symbol' in global && s instanceof global.Symbol); 15 | } 16 | 17 | function define(o, p, v, override) { 18 | if (p in o && !override) 19 | return; 20 | 21 | if (typeof v === 'function') { 22 | // Sanity check that functions are appropriately named (where possible) 23 | console.assert(isSymbol(p) || !('name' in v) || v.name === p || v.name === p + '_', 'Expected function name "' + p.toString() + '", was "' + v.name + '"'); 24 | Object.defineProperty(o, p, { 25 | value: v, 26 | configurable: true, 27 | enumerable: false, 28 | writable: true 29 | }); 30 | } else { 31 | Object.defineProperty(o, p, { 32 | value: v, 33 | configurable: false, 34 | enumerable: false, 35 | writable: false 36 | }); 37 | } 38 | } 39 | 40 | 41 | // Snapshot intrinsic functions 42 | var $isNaN = global.isNaN; 43 | 44 | var abs = Math.abs, 45 | floor = Math.floor, 46 | max = Math.max, 47 | min = Math.min; 48 | 49 | //---------------------------------------- 50 | // 7 Abstract Operations 51 | //---------------------------------------- 52 | 53 | // 7.1.4 54 | function ToInteger(n) { 55 | n = Number(n); 56 | if ($isNaN(n)) return 0; 57 | if (n === 0 || n === Infinity || n === -Infinity) return n; 58 | return ((n < 0) ? -1 : 1) * floor(abs(n)); 59 | } 60 | 61 | // 7.1.13 ToObject 62 | function ToObject(v) { 63 | if (v === null || v === undefined) throw TypeError(); 64 | return Object(v); 65 | } 66 | 67 | // 7.1.15 ToLength ( argument ) 68 | function ToLength(v) { 69 | var len = ToInteger(v); 70 | if (len <= 0) { 71 | return 0; 72 | } 73 | return min(len, 0x20000000000000 - 1); // 2^53-1 74 | } 75 | 76 | //---------------------------------------- 77 | // 7.2 Testing and Comparison Operations 78 | //---------------------------------------- 79 | 80 | // 7.2.10 SameValueZero(x, y) 81 | function SameValueZero(x, y) { 82 | if (typeof x !== typeof y) return false; 83 | switch (typeof x) { 84 | case 'undefined': 85 | return true; 86 | case 'number': 87 | if (x !== x && y !== y) return true; 88 | return x === y; 89 | case 'boolean': 90 | case 'string': 91 | case 'object': 92 | default: 93 | return x === y; 94 | } 95 | } 96 | 97 | //---------------------------------------------------------------------- 98 | // 99 | // ECMAScript 2016 100 | // 101 | //---------------------------------------------------------------------- 102 | 103 | // https://github.com/tc39/Array.prototype.includes/ 104 | define( 105 | Array.prototype, 'includes', 106 | function includes(target) { 107 | var fromIndex = arguments[1]; 108 | 109 | var o = ToObject(this); 110 | var len = ToLength(o["length"]); 111 | if (len === 0) return false; 112 | var n = ToInteger(fromIndex); 113 | if (n >= 0) { 114 | var k = n; 115 | } else { 116 | k = len + n; 117 | if (k < 0) k = 0; 118 | } 119 | while (k < len) { 120 | var elementK = o[k]; 121 | if (SameValueZero(o[k], target)) 122 | return true; 123 | k += 1; 124 | } 125 | return false; 126 | }); 127 | 128 | }(this)); 129 | -------------------------------------------------------------------------------- /es2016.md: -------------------------------------------------------------------------------- 1 | # ECMAScript 2016 Polyfill 2 | 3 | [script](es2016.js) - 4 | [unit tests](https://inexorabletash.github.io/polyfill/tests/es2016.html) 5 | 6 | This assumes ES6, so use [es5.js](es5.js) and [es6.js](es6.js) for older browsers (IE9-). 7 | 8 | [ECMAScript 2016 Standard](http://www.ecma-international.org/ecma-262/7.0/) 9 | 10 | * `Array.prototype.includes()` [ref](http://www.ecma-international.org/ecma-262/7.0/index.html#sec-array.prototype.includes) 11 | -------------------------------------------------------------------------------- /es2017.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------- 2 | // 3 | // ECMAScript 2017 Polyfills 4 | // 5 | //---------------------------------------------------------------------- 6 | 7 | (function (global) { 8 | 'use strict'; 9 | var undefined = (void 0); // Paranoia 10 | 11 | // Helpers 12 | 13 | function isSymbol(s) { 14 | return (typeof s === 'symbol') || ('Symbol' in global && s instanceof global.Symbol); 15 | } 16 | 17 | function define(o, p, v, override) { 18 | if (p in o && !override) 19 | return; 20 | 21 | if (typeof v === 'function') { 22 | // Sanity check that functions are appropriately named (where possible) 23 | console.assert(isSymbol(p) || !('name' in v) || v.name === p || v.name === p + '_', 'Expected function name "' + p.toString() + '", was "' + v.name + '"'); 24 | Object.defineProperty(o, p, { 25 | value: v, 26 | configurable: true, 27 | enumerable: false, 28 | writable: true 29 | }); 30 | } else { 31 | Object.defineProperty(o, p, { 32 | value: v, 33 | configurable: false, 34 | enumerable: false, 35 | writable: false 36 | }); 37 | } 38 | } 39 | 40 | // Snapshot intrinsic functions 41 | var $isNaN = global.isNaN; 42 | 43 | var abs = Math.abs, 44 | floor = Math.floor, 45 | min = Math.min; 46 | 47 | //---------------------------------------- 48 | // 7 Abstract Operations 49 | //---------------------------------------- 50 | 51 | // 7.1.4 52 | function ToInteger(n) { 53 | n = Number(n); 54 | if ($isNaN(n)) return 0; 55 | if (n === 0 || n === Infinity || n === -Infinity) return n; 56 | return ((n < 0) ? -1 : 1) * floor(abs(n)); 57 | } 58 | 59 | // 7.1.13 ToObject 60 | function ToObject(v) { 61 | if (v === null || v === undefined) throw TypeError(); 62 | return Object(v); 63 | } 64 | 65 | // 7.1.15 ToLength ( argument ) 66 | function ToLength(v) { 67 | var len = ToInteger(v); 68 | if (len <= 0) { 69 | return 0; 70 | } 71 | return min(len, 0x20000000000000 - 1); // 2^53-1 72 | } 73 | 74 | //---------------------------------------- 75 | // 7.3 Operations on Objects 76 | //---------------------------------------- 77 | 78 | // 7.3.4 79 | function CreateDataProperty(O, P, V) { 80 | Object.defineProperty(O, P, { 81 | value: V, 82 | writable: true, 83 | enumerable: true, 84 | configurable: true 85 | }); 86 | } 87 | 88 | // 7.3.21 89 | function EnumerableOwnProperties(o, kind) { 90 | var ownKeys = Object.keys(o); 91 | var properties = []; 92 | ownKeys.forEach(function(key) { 93 | var desc = Object.getOwnPropertyDescriptor(o, key); 94 | if (desc && desc.enumerable) { 95 | if (kind === 'key') properties.push(key); 96 | else { 97 | var value = o[key]; 98 | if (kind === 'value') properties.push(value); 99 | else properties.push([key, value]); 100 | } 101 | } 102 | }); 103 | return properties; 104 | } 105 | 106 | 107 | //---------------------------------------------------------------------- 108 | // 19 Fundamental Objects 109 | //---------------------------------------------------------------------- 110 | 111 | // 19.1 Object Objects 112 | // 19.1.2 Properties of the Object Constructor 113 | 114 | // 19.1.2.5 Object.entries 115 | define( 116 | Object, 'entries', 117 | function entries(o) { 118 | var obj = ToObject(o); 119 | return EnumerableOwnProperties(obj, 'key+value'); 120 | }); 121 | 122 | // 19.1.2.8 Object.getOwnPropertyDescriptors ( O ) 123 | define( 124 | Object, 'getOwnPropertyDescriptors', 125 | function getOwnPropertyDescriptors(o) { 126 | var obj = ToObject(o); 127 | // ReturnIfAbrupt(obj) 128 | var keys = Object.getOwnPropertyNames(obj); 129 | // ReturnIfAbrupt(keys) 130 | var descriptors = {}; 131 | for (var i = 0; i < keys.length; ++i) { 132 | var nextKey = keys[i]; 133 | var descriptor = Object.getOwnPropertyDescriptor(obj, nextKey); 134 | // ReturnIfAbrupt(desc) 135 | // ReturnIfAbrupt(descriptor) 136 | CreateDataProperty(descriptors, nextKey, descriptor); 137 | } 138 | return descriptors; 139 | }); 140 | 141 | // 19.1.2.21 Object.values 142 | define( 143 | Object, 'values', 144 | function values(o) { 145 | var obj = ToObject(o); 146 | return EnumerableOwnProperties(obj, 'value'); 147 | }); 148 | 149 | 150 | 151 | //---------------------------------------------------------------------- 152 | // 21 Text Processing 153 | //---------------------------------------------------------------------- 154 | 155 | // 21.1 String Objects 156 | // 21.1.3 Properties of the String Prototype Object 157 | 158 | // 21.1.3.13 String.prototype.padEnd( maxLength [ , fillString ] ) 159 | define( 160 | String.prototype, 'padEnd', 161 | function padEnd(maxLength) { 162 | var fillString = arguments[1]; 163 | 164 | var o = this; 165 | // ReturnIfAbrupt(o) 166 | var s = String(this); 167 | // ReturnIfAbrupt(s) 168 | var stringLength = s.length; 169 | if (fillString === undefined) var fillStr = ''; 170 | else fillStr = String(fillString); 171 | // ReturnIfAbrupt(fillStr) 172 | if (fillStr === '') fillStr = ' '; 173 | var intMaxLength = ToLength(maxLength); 174 | // ReturnIfAbrupt(intMaxLength) 175 | if (intMaxLength <= stringLength) return s; 176 | var fillLen = intMaxLength - stringLength; 177 | var stringFiller = ''; 178 | while (stringFiller.length < fillLen) 179 | stringFiller = stringFiller + fillStr; 180 | return s + stringFiller.substring(0, fillLen); 181 | }); 182 | 183 | // 21.1.3.14 String.prototype.padStart( maxLength [ , fillString ] ) 184 | define( 185 | String.prototype, 'padStart', 186 | function padStart(maxLength) { 187 | var fillString = arguments[1]; 188 | 189 | var o = this; 190 | // ReturnIfAbrupt(o) 191 | var s = String(this); 192 | // ReturnIfAbrupt(s) 193 | var stringLength = s.length; 194 | if (fillString === undefined) var fillStr = ''; 195 | else fillStr = String(fillString); 196 | // ReturnIfAbrupt(fillStr) 197 | if (fillStr === '') fillStr = ' '; 198 | var intMaxLength = ToLength(maxLength); 199 | // ReturnIfAbrupt(intMaxLength) 200 | if (intMaxLength <= stringLength) return s; 201 | var fillLen = intMaxLength - stringLength; 202 | var stringFiller = ''; 203 | while (stringFiller.length < fillLen) 204 | stringFiller = stringFiller + fillStr; 205 | return stringFiller.substring(0, fillLen) + s; 206 | }); 207 | 208 | }(this)); 209 | -------------------------------------------------------------------------------- /es2017.md: -------------------------------------------------------------------------------- 1 | # ECMAScript 2017 Polyfill 2 | 3 | [script](es2017.js) - 4 | [unit tests](https://inexorabletash.github.io/polyfill/tests/es2017.html) 5 | 6 | This assumes ES2016, so use [es5.js](es5.js), [es6.js](es6.js) and [es2016.js](es2016.js) for older browsers (IE9-). 7 | 8 | [ECMAScript Standard](http://www.ecma-international.org/ecma-262/) 9 | 10 | * `Object.entries()` [ref](https://tc39.github.io/ecma262/#sec-object.entries) 11 | * `Object.values()` [ref](https://tc39.github.io/ecma262/#sec-object.values) 12 | * `Object.getOwnPropertyDescriptors` [ref](https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptors) 13 | * `String.prototype.padEnd()` [ref](https://tc39.github.io/ecma262/#sec-string.prototype.padend) 14 | * `String.prototype.padStart()` [ref](https://tc39.github.io/ecma262/#sec-string.prototype.padstart) 15 | -------------------------------------------------------------------------------- /es5.md: -------------------------------------------------------------------------------- 1 | # ECMAScript 5 Polyfill 2 | 3 | [script](es5.js) - 4 | [unit tests](https://inexorabletash.github.io/polyfill/tests/es5.html) 5 | 6 | [ECMAScript 5](http://www.ecma-international.org/publications/standards/Ecma-262.htm) Object, Function, String and Date extras: 7 | * Object: `getPrototypeOf`, `getOwnPropertyNames`, `create`, `defineProperty`, `defineProperties`, `keys` 8 | * Function prototype: `bind` 9 | * Array: `isArray` 10 | * Array prototype: `indexOf`, `lastIndexOf`, `every`, `some`, `forEach`, `map`, `filter`, `reduce`, `reduceRight` 11 | * String prototype: `trim` 12 | * Date: `now` 13 | * Date prototype: `toISOString` 14 | 15 | Does not include JSON - use [json2.js](https://github.com/douglascrockford/JSON-js) 16 | -------------------------------------------------------------------------------- /es6.md: -------------------------------------------------------------------------------- 1 | # ECMAScript 2015 Polyfill 2 | 3 | [script](es6.js) - 4 | [unit tests](https://inexorabletash.github.io/polyfill/tests/es6.html) 5 | 6 | This assumes ES5, so use [es5.js](es5.js) for older browsers (IE9-). 7 | 8 | [ECMAScript 2015 Standard](http://www.ecma-international.org/ecma-262/6.0/) 9 | 10 | #### Fundamental Objects 11 | * Object: `assign()`, `is()`, `setPrototypeOf()` 12 | * Symbol: `Symbol(description)`, `Symbol.for()`, `Symbol.keyFor()`, `Symbol.iterator`, `Symbol.toStringTag` 13 | * No security, just creates an object with a unique string representation. `typeof Symbol()` will incorrectly report `"object"` but `Symbol() instanceof Symbol` will return `true` 14 | * Not supported: `Function.prototype.toMethod()` 15 | 16 | #### Numbers and Dates 17 | * Number: `EPILON`, `isFinite()`, `isInteger()`, `isNaN()`, `isSafeInteger()`, `MAX_SAFE_INTEGER`, `MIN_SAFE_INTEGER`, `parseFloat()`, `parseInt()` 18 | * Math: `acosh()`, `asinh()`, `atanh()`, `cbrt()`, `clz32()`, `cosh()`, `expm1()`, `fround`, `hypot()`, `imul()`, `log1p()`, `log10()`, `log2()`, `sign()`, `sinh()`, `tanh()`, `trunc()` 19 | 20 | #### Text Processing 21 | * See also: [uate - ES5 "Tagged Template Strings"](https://github.com/inexorabletash/uate) 22 | * String: `fromCodePoint()`, `raw` 23 | * String prototype: `codePointAt()`, `endsWith()`, `includes()`, `repeat()`, `startsWith()`, `[@@iterator]()` 24 | * Not supported: `String.prototype.normalize()` - see https://github.com/walling/unorm/ 25 | * RegExp prototype: `@@match()`, `@@replace()`, `@@search()`, `@@split()`, `flags` 26 | * String.prototype `match()`, `replace()`, `search()`, and `split()` dispatch through RegExp symbol methods 27 | 28 | #### Indexed Collections 29 | * Array: `from()`, `of()` 30 | * Array prototype: `copyWithin()`, `entries()`, `fill()`, `find()`, `findIndex()`, `keys()`, `values()`, `[@@iterator]()` 31 | * _TypedArray_ - for browsers without native support (IE9-) include [typedarray.js](#typedarray) 32 | * %TypedArray% prototype: `from()`, `of()` 33 | * %TypedArray% prototype: `copyWithin()`, `entries()`, `every()`, `fill()`, `filter()`, `find()`, `findIndex()`, `forEach()`, `indexOf()`, `join()`, `keys()`, `lastIndexOf()`, `map()`, `reduce()`, `reduceRight()`, `reverse()`, `slice()`, `some()`, `sort()`, `values()`, `[@@iterator]()` 34 | 35 | #### Keyed Collections 36 | * Map: `clear()`, `delete()`, `entries()`, `forEach()`, `get()`, `keys()`, `has()`, `set()`, `size`, `values()`, `[@@iterator]()` 37 | * Set: `add()`, `clear()`, `delete()`, `entries()`, `forEach()`, `has()`, `size`, `values()`, `[@@iterator]()` 38 | * WeakMap: `clear()`, `delete()`, `get()`, `has()`, `set()` 39 | * WeakSet: `add()`, `clear()`, `delete()`, `has()` 40 | * WeakMap and WeakSet are intrusive and modify the `valueOf` property of keys 41 | 42 | #### Asynchronous Programming 43 | * Promise: `p = new Promise()`, `Promise.resolve()`, `Promise.reject()`, `Promise.cast()`, `Promise.race()`, `Promise.all()`, `p.then()`, `p.catch()` 44 | 45 | See also: [uate - ES5 "tagged template strings"](https://github.com/inexorabletash/uate) 46 | 47 | 48 | 49 | # Typed Arrays 50 | [script](typedarray.js) - 51 | [unit tests](https://inexorabletash.github.io/polyfill/tests/typedarray.html) - 52 | [spec](https://www.khronos.org/registry/typedarray/specs/latest/) 53 | 54 | Originally specified separately, Typed Arrays are now included in ES2015. 55 | 56 | * `ArrayBuffer` 57 | * `Uint8Array`, `Int8Array`, `Uint16Array`, `Int16Array`, `Uint32Array`, `Int32Array`, `Float32Array`, `Float64Array` 58 | * `DataView` 59 | 60 | Creating index getter/setters (i.e. `array[0]`, `array[1]`, etc) is slow and consumes significant memory. Arrays larger than 100k entries will be too slow to initialize in most cases so an upper limit is placed on the size of arrays and exception is thrown on construction. 61 | -------------------------------------------------------------------------------- /experimental/README.md: -------------------------------------------------------------------------------- 1 | Experimental 2 | ============ 3 | 4 | ### ECMAScript - Proposals for ECMAScript 2017 and beyond 5 | 6 | [Details](es-proposed.md) 7 | -------------------------------------------------------------------------------- /experimental/es-proposed.md: -------------------------------------------------------------------------------- 1 | # ECMAScript - Proposals for Future Editions 2 | 3 | Per https://github.com/tc39/ecma262 4 | 5 | [script](es-proposed.js) - 6 | [unit tests](https://inexorabletash.github.io/polyfill/experimental/tests/es-proposed.html) 7 | 8 | #### Stage 3 9 | 10 | See also: 11 | 12 | * Promise prototype: `finally()` [ref](https://github.com/tc39/proposal-promise-finally) 13 | * Array prototype: `flatten()` and `flatMap()` [ref](https://tc39.github.io/proposal-flatMap/) 14 | 15 | #### Stage 2 16 | 17 | * String prototype: `trimStart()`, `trimEnd()` (and `trimLeft()`, `trimRight()` aliases) [ref](https://github.com/sebmarkbage/ecmascript-string-left-right-trim) 18 | * String.prototype: `matchAll()` [ref](https://github.com/ljharb/String.prototype.matchAll) 19 | 20 | #### Stage 1 21 | 22 | * Math extensions: `clamp()`, `scale()`, `radians()`, `degrees()`, `RAD_PER_DEG`, `DEG_PER_RAD` [ref](https://github.com/rwaldron/proposal-math-extensions/blob/master/README.md) 23 | * Set/Map/WeakSet/WeakMap `.of()` and `.from()` [ref](https://github.com/leobalter/proposal-setmap-offrom) 24 | * Promise: `try()` [ref](https://github.com/ljharb/proposal-promise-try) 25 | * String prototype: `replaceAll()` [ref](https://github.com/tc39/proposal-string-replace-all) 26 | * String prototype: `codePoints()` [ref](https://github.com/RReverser/string-prototype-codepoints) 27 | * Math: `signbit()` [ref](http://jfbastien.github.io/papers/Math.signbit.html) 28 | 29 | See also: 30 | 31 | * [Intl.Segmenter](https://gist.github.com/inexorabletash/8c4d869a584bcaa18514729332300356) 32 | 33 | #### Stage 0 34 | 35 | * `String.prototype.at()` [ref](https://github.com/mathiasbynens/String.prototype.at) 36 | 37 | See also: 38 | 39 | * [Int64](https://github.com/inexorabletash/int64) 40 | 41 | #### Obsolete/Abandoned Proposals 42 | 43 | * Number: `compare()` 44 | * Array prototype: `pushAll()` 45 | * Reflection: `Object.getPropertyDescriptor(o)`, `Object.getPropertyNames(o)` 46 | * Math: `denormz()`, `fdenormz()` 47 | * 64-bit Math: `imulh()`, `umulh()`, `iaddh()`, `isubh()` [ref](https://gist.github.com/BrendanEich/4294d5c212a6d2254703) 48 | -------------------------------------------------------------------------------- /experimental/tests/es-proposed.html: -------------------------------------------------------------------------------- 1 | 2 | ECMAScript Proposals Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /experimental/tests/es-proposed.js: -------------------------------------------------------------------------------- 1 | /*global QUnit,global*/ 2 | 3 | // ------------------------------------------------------------ 4 | 5 | QUnit.module("Stage 3"); 6 | 7 | QUnit.test("global", function(assert) { 8 | assert.equal(global, Function('return this')()); 9 | }); 10 | 11 | QUnit.test('Promise.prototype.finally', function(assert) { 12 | assert.ok('finally' in Promise.prototype); 13 | assert.equal(typeof Promise.prototype.finally, 'function'); 14 | assert.equal(Promise.prototype.finally.length, 1); 15 | 16 | function async(f) { 17 | var done = assert.async(); 18 | f(done); 19 | } 20 | 21 | async(function(done) { 22 | Promise.resolve().finally(function() { 23 | assert.equal(arguments.length, 0, 'Finally callback gets no args - resolved promise'); 24 | done(); 25 | }); 26 | }); 27 | 28 | async(function(done) { 29 | Promise.reject().finally(function() { 30 | assert.equal(arguments.length, 0, 'Finally callback gets no args - rejected promise'); 31 | done(); 32 | }).catch(function() {}); 33 | }); 34 | 35 | async(function(done) { 36 | Promise.resolve(2).finally(function() {}).then(function(r) { 37 | assert.equal(r, 2, 'Finally passes resolution through promise chain'); 38 | done(); 39 | }); 40 | }); 41 | 42 | async(function(done) { 43 | Promise.reject(3).finally(function() {}).catch(function(r) { 44 | assert.equal(r, 3, 'Finally passes rejection through promise chain'); 45 | done(); 46 | }); 47 | }); 48 | 49 | }); 50 | 51 | QUnit.test('Array.prototype.flatMap', function(assert) { 52 | assert.deepEqual(['abc', 'def'].flatMap(function(s) { return s.split('');}), 53 | ['a', 'b', 'c', 'd', 'e', 'f']); 54 | }); 55 | 56 | QUnit.test('Array.prototype.flatten', function(assert) { 57 | assert.deepEqual([[1,2,3], [4,5,6]].flatten(), [1,2,3,4,5,6]); 58 | assert.deepEqual([[1,[2,[3]]]].flatten(), [1,[2,[3]]]); 59 | assert.deepEqual([[1,[2,[3]]]].flatten(0), [[1,[2,[3]]]]); 60 | assert.deepEqual([[1,[2,[3]]]].flatten(1), [1,[2,[3]]]); 61 | assert.deepEqual([[1,[2,[3]]]].flatten(2), [1,2,[3]]); 62 | assert.deepEqual([[1,[2,[3]]]].flatten(3), [1,2,3]); 63 | }); 64 | 65 | // ------------------------------------------------------------ 66 | 67 | QUnit.module("Stage 2"); 68 | 69 | QUnit.test("String trimStart/trimEnd and aliases", function(assert) { 70 | 71 | assert.equal(''.trimStart(), ''); 72 | assert.equal('a'.trimStart(), 'a'); 73 | assert.equal(' \t\r\n\u00A0a \t\r\n\u00A0'.trimStart(), 'a \t\r\n\u00A0'); 74 | assert.equal(' \t\r\n\u00A0 \t\r\n\u00A0'.trimStart(), ''); 75 | assert.equal(''.trimEnd(), ''); 76 | assert.equal('a'.trimEnd(), 'a'); 77 | assert.equal(' \t\r\n\u00A0a \t\r\n\u00A0'.trimEnd(), ' \t\r\n\u00A0a'); 78 | assert.equal(' \t\r\n\u00A0 \t\r\n\u00A0'.trimEnd(), ''); 79 | 80 | // Annex B versions 81 | assert.equal(''.trimLeft(), ''); 82 | assert.equal('a'.trimLeft(), 'a'); 83 | assert.equal(' \t\r\n\u00A0a \t\r\n\u00A0'.trimLeft(), 'a \t\r\n\u00A0'); 84 | assert.equal(' \t\r\n\u00A0 \t\r\n\u00A0'.trimLeft(), ''); 85 | assert.equal(''.trimRight(), ''); 86 | assert.equal('a'.trimRight(), 'a'); 87 | assert.equal(' \t\r\n\u00A0a \t\r\n\u00A0'.trimRight(), ' \t\r\n\u00A0a'); 88 | assert.equal(' \t\r\n\u00A0 \t\r\n\u00A0'.trimRight(), ''); 89 | }); 90 | 91 | QUnit.test("String matchAll", function(assert) { 92 | 93 | assert.equal(typeof "".matchAll(/./)[Symbol.iterator], 'function'); 94 | 95 | var it = "".matchAll(/./); 96 | assert.equal(it, it[Symbol.iterator]()); 97 | 98 | function matchAllTest(string, regex, expected) { 99 | var results = []; 100 | for (var iter = string.matchAll(regex), step = iter.next(); !step.done; step = iter.next()) { 101 | results.push(step.value[0]); 102 | } 103 | assert.deepEqual(results, expected); 104 | } 105 | 106 | matchAllTest('banana CAKE', /a./, ['an', 'an', 'a ']); 107 | matchAllTest('banana CAKE', /a./g, ['an', 'an', 'a ']); 108 | matchAllTest('banana CAKE', /A./, ['AK']); 109 | matchAllTest('banana CAKE', /A./g, ['AK']); 110 | matchAllTest('banana CAKE', /a./i, ['an', 'an', 'a ', 'AK']); 111 | matchAllTest('banana CAKE', /a./ig, ['an', 'an', 'a ', 'AK']); 112 | 113 | assert.throws(function() { 'abc'.matchAll({}); }, TypeError); 114 | }); 115 | 116 | 117 | // ------------------------------------------------------------ 118 | 119 | QUnit.module("Stage 1"); 120 | 121 | QUnit.test('Math extensions', function(assert) { 122 | assert.deepEqual(Math.clamp(0, 0, NaN), NaN); 123 | assert.deepEqual(Math.clamp(0, NaN, 0), NaN); 124 | assert.deepEqual(Math.clamp(NaN, 0, 0), NaN); 125 | assert.equal(Math.clamp(0, 1, 3), 1); 126 | assert.equal(Math.clamp(4, 1, 3), 3); 127 | assert.equal(Math.clamp(2, 1, 3), 2); 128 | 129 | assert.deepEqual(Math.scale(NaN, 0, 0, 0, 0), NaN); 130 | assert.deepEqual(Math.scale(0, NaN, 0, 0, 0), NaN); 131 | assert.deepEqual(Math.scale(0, 0, NaN, 0, 0), NaN); 132 | assert.deepEqual(Math.scale(0, 0, 0, NaN, 0), NaN); 133 | assert.deepEqual(Math.scale(0, 0, 0, 0, NaN), NaN); 134 | assert.equal(Math.scale(Infinity, 1, 2, 3, 4), Infinity); 135 | assert.equal(Math.scale(-Infinity, 1, 2, 3, 4), -Infinity); 136 | assert.equal(Math.scale(0, 1, 2, 3, 4), 2); 137 | assert.equal(Math.scale(3, 1, 2, 3, 4), 5); 138 | assert.equal(Math.scale(0, 1, 1, 3, 4), -Infinity); 139 | assert.equal(Math.scale(1, 0, 10, 0, 100), 10); 140 | assert.equal(Math.scale(10, 0, 100, 0, 10), 1); 141 | 142 | 143 | assert.deepEqual(Math.radians(NaN), NaN); 144 | assert.equal(Math.radians(Infinity), Infinity); 145 | assert.equal(Math.radians(-Infinity), -Infinity); 146 | assert.equal(Math.radians(180), Math.PI); 147 | 148 | assert.deepEqual(Math.degrees(NaN), NaN); 149 | assert.equal(Math.degrees(Infinity), Infinity); 150 | assert.equal(Math.degrees(-Infinity), -Infinity); 151 | assert.equal(Math.degrees(Math.PI/2), 90); 152 | }); 153 | 154 | 155 | QUnit.test('Set and Map .of and .from', function(assert) { 156 | var k1 = {}, k2 = {}; 157 | 158 | [Set, Map, WeakSet, WeakMap].forEach(function(t) { 159 | assert.ok('of' in t); 160 | assert.equal(typeof t.of, 'function'); 161 | assert.equal(t.of.length, 0); 162 | }); 163 | 164 | assert.deepEqual([1, 2, 3], Array.from(Set.of(1, 2, 3))); 165 | assert.deepEqual([[1, 2], [3, 4], [5, 6]], Array.from(Map.of([1, 2], [3, 4], [5, 6]))); 166 | assert.ok(WeakSet.of(k1, k2).has(k2)); 167 | assert.equal(WeakMap.of([k1, 1], [k2, 2]).get(k2), 2); 168 | 169 | [Set, Map, WeakSet, WeakMap].forEach(function(t) { 170 | assert.ok('from' in t); 171 | assert.equal(typeof t.from, 'function'); 172 | assert.equal(t.from.length, 1); 173 | }); 174 | 175 | assert.deepEqual([1, 2, 3], Array.from(Set.from([1, 2, 3]))); 176 | assert.deepEqual([[1, 2], [3, 4], [5, 6]], Array.from(Map.from([[1, 2], [3, 4], [5, 6]]))); 177 | assert.ok(WeakSet.from([k1, k2]).has(k2)); 178 | assert.equal(WeakMap.from([[k1, 1], [k2, 2]]).get(k2), 2); 179 | }); 180 | 181 | QUnit.test('Promise.try', function(assert) { 182 | function async(f) { 183 | var done = assert.async(); 184 | f(done); 185 | } 186 | 187 | async(function(done) { 188 | Promise.try().catch(function(x) { 189 | assert.ok(x instanceof TypeError, 'Throws TypeError when non-function passed'); 190 | done(); 191 | }); 192 | }); 193 | 194 | async(function(done) { 195 | Promise.try(function(){ return 123; }).then(function(x) { 196 | assert.equal(x, 123, 'Calls function, resolves with synchronous return value'); 197 | done(); 198 | }); 199 | }); 200 | 201 | async(function(done) { 202 | Promise.try(function(){ return Promise.resolve(123); }).then(function(x) { 203 | assert.equal(x, 123, 'Calls function, resolves with asynchronous return value'); 204 | done(); 205 | }); 206 | }); 207 | async(function(done) { 208 | Promise.try(function(){ throw 123; }).catch(function(x) { 209 | assert.equal(x, 123, 'Calls function, rejects with synchronous failure value'); 210 | done(); 211 | }); 212 | }); 213 | 214 | async(function(done) { 215 | Promise.try(function(){ return Promise.reject(123); }).catch(function(x) { 216 | assert.equal(x, 123, 'Calls function, rejects with asynchronous failure value'); 217 | done(); 218 | }); 219 | }); 220 | 221 | }); 222 | 223 | QUnit.test("String replaceAll", function(assert) { 224 | assert.equal(''.replaceAll('a', 'b'), ''); 225 | assert.equal('abcabc'.replaceAll('a', 'x'), 'xbcxbc'); 226 | assert.equal('abcabc'.replaceAll('bc', 'x'), 'axax'); 227 | assert.equal('abcabc'.replaceAll('ca', 'x'), 'abxbc'); 228 | assert.equal('abcabc'.replaceAll('a', 'xxx'), 'xxxbcxxxbc'); 229 | assert.equal('abcabc'.replaceAll('bc', 'xxx'), 'axxxaxxx'); 230 | assert.equal('abcabc'.replaceAll('ca', 'xxx'), 'abxxxbc'); 231 | }); 232 | 233 | QUnit.test("String codePoints", function(assert) { 234 | 235 | assert.equal(typeof "".codePoints()[Symbol.iterator], 'function'); 236 | 237 | var it = "".codePoints(/./); 238 | assert.equal(it, it[Symbol.iterator]()); 239 | 240 | function codePointsTest(string, expected) { 241 | var results = []; 242 | for (var iter = string.codePoints(), step = iter.next(); !step.done; step = iter.next()) { 243 | results.push(step.value); 244 | } 245 | assert.deepEqual(results, expected); 246 | } 247 | 248 | codePointsTest('', []); 249 | codePointsTest('abc', [97, 98, 99]); 250 | codePointsTest('abc\uD800\uDC00def', [97, 98, 99, 0x10000, 100, 101, 102]); 251 | codePointsTest('abc\uDC00\uD800def', [97, 98, 99, 0xDC00, 0xD800, 100, 101, 102]); 252 | }); 253 | 254 | QUnit.test('Math signbit()', function(assert) { 255 | assert.equal(Math.signbit('x'), false); 256 | assert.equal(Math.signbit(NaN), false); 257 | assert.equal(Math.signbit(undefined), false); 258 | 259 | assert.equal(Math.signbit(0), false); 260 | assert.equal(Math.signbit(123), false); 261 | assert.equal(Math.signbit(Infinity), false); 262 | 263 | assert.equal(Math.signbit(-0), true); 264 | assert.equal(Math.signbit(-123), true); 265 | assert.equal(Math.signbit(-Infinity), true); 266 | }); 267 | 268 | // ------------------------------------------------------------ 269 | 270 | QUnit.module("Stage 0"); 271 | 272 | QUnit.test('String.prototype.at()', function(assert) { 273 | assert.equal('a'.at(0), 'a'); 274 | assert.equal('a'.at(-1), ''); 275 | assert.equal('a'.at(2), ''); 276 | assert.equal('\uD800\uDC00'.at(0), '\uD800\uDC00'); 277 | assert.equal('\uD800'.at(0), '\uD800'); 278 | assert.equal('\uDC00'.at(0), '\uDC00'); 279 | assert.equal('\uD800\uDC00'.at(1), '\uDC00'); 280 | }); 281 | 282 | // ------------------------------------------------------------ 283 | 284 | QUnit.module("Obsolete/Abandoned"); 285 | 286 | QUnit.test("Number.compare", function(assert) { 287 | // Number.compare 288 | assert.ok('compare' in Number); 289 | assert.equal(typeof Number.compare, 'function'); 290 | assert.equal(Number.compare(0, 0), 0); 291 | assert.equal(Number.compare(1, 0), 1); 292 | assert.equal(Number.compare(0, 1), -1); 293 | assert.equal(Number.compare(0, 0, 1), 0); 294 | assert.equal(Number.compare(1, 0, 1), 0); 295 | assert.equal(Number.compare(0, 1, 1), 0); 296 | }); 297 | 298 | QUnit.test("Array.prototype.pushAll", function(assert) { 299 | assert.ok('pushAll' in Array.prototype); 300 | assert.equal(typeof Array.prototype.pushAll, 'function'); 301 | var a; 302 | a = []; a.pushAll([]); assert.deepEqual(a, []); 303 | a = [1,2]; a.pushAll([]); assert.deepEqual(a, [1,2]); 304 | a = []; a.pushAll([1,2]); assert.deepEqual(a, [1,2]); 305 | a = [1,2]; a.pushAll([1,2]); assert.deepEqual(a, [1,2,1,2]); 306 | }); 307 | 308 | QUnit.test("Math: denormalized-to-zero", function(assert) { 309 | assert.equal(Math.denormz(0), 0); 310 | assert.equal(Math.denormz(-0), -0); 311 | assert.equal(Math.denormz(1), 1); 312 | assert.equal(Math.denormz(-1), -1); 313 | assert.equal(Math.denormz(Math.pow(2,-126)), Math.pow(2,-126)); 314 | assert.equal(Math.denormz(Math.pow(2,-127)), Math.pow(2,-127)); 315 | assert.equal(Math.denormz(Math.pow(2,-1022)), Math.pow(2,-1022)); 316 | assert.equal(Math.denormz(Math.pow(2,-1023)), 0); 317 | assert.equal(Math.denormz(Number.MIN_VALUE), 0); 318 | assert.equal(Math.denormz(-Math.pow(2,-126)), -Math.pow(2,-126)); 319 | assert.equal(Math.denormz(-Math.pow(2,-127)), -Math.pow(2,-127)); 320 | assert.equal(Math.denormz(-Math.pow(2,-1022)), -Math.pow(2,-1022)); 321 | assert.equal(Math.denormz(-Math.pow(2,-1023)), -0); 322 | assert.equal(Math.denormz(-Number.MIN_VALUE), -0); 323 | 324 | assert.equal(Math.fdenormz(0), 0); 325 | assert.equal(Math.fdenormz(-0), -0); 326 | assert.equal(Math.fdenormz(1), 1); 327 | assert.equal(Math.fdenormz(-1), -1); 328 | assert.equal(Math.fdenormz(Math.pow(2,-126)), Math.pow(2,-126)); 329 | assert.equal(Math.fdenormz(Math.pow(2,-127)), 0); 330 | assert.equal(Math.fdenormz(Math.pow(2,-1022)), 0); 331 | assert.equal(Math.fdenormz(Math.pow(2,-1023)), 0); 332 | assert.equal(Math.fdenormz(Number.MIN_VALUE), 0); 333 | assert.equal(Math.fdenormz(-Math.pow(2,-126)), -Math.pow(2,-126)); 334 | assert.equal(Math.fdenormz(-Math.pow(2,-127)), -0); 335 | assert.equal(Math.fdenormz(-Math.pow(2,-1022)), -0); 336 | assert.equal(Math.fdenormz(-Math.pow(2,-1023)), -0); 337 | assert.equal(Math.fdenormz(-Number.MIN_VALUE), -0); 338 | }); 339 | -------------------------------------------------------------------------------- /geo.js: -------------------------------------------------------------------------------- 1 | 2 | // W3C Geolocation API (Level 1) "Polyfill" Implementation 3 | // This uses freegeoip.org to estimate location from IP address 4 | // by Joshua Bell - https://github.com/inexorabletash.polyfill 5 | 6 | // PUBLIC DOMAIN 7 | 8 | (function() { 9 | 'use strict'; 10 | if (!navigator || !window || !document) { return; } 11 | 12 | if (!('now' in Date)) Date.now = function() { return Number(new Date); }; 13 | 14 | var GEOIP_SERVICE_JSONP = 'https://freegeoip.net/json/google.com?callback='; 15 | var SERVICE_THROTTLE_QPS = 1000 / (60 * 60); // 1000/hour 16 | var POLITENESS_FACTOR = 2; 17 | var POLL_TIMER_MS = (1000 / SERVICE_THROTTLE_QPS) * POLITENESS_FACTOR; 18 | 19 | var DISTANCE_THRESHOLD_M = 20; 20 | var EARTH_RADIUS_M = 6.384e6; 21 | 22 | // TODO: Implement user prompt and store preference w/ cookies 23 | 24 | function PositionError(code, message) { 25 | this.code = code; 26 | this.message = message; 27 | } 28 | PositionError.PERMISSION_DENIED = 1; 29 | PositionError.POSITION_UNAVAILABLE = 2; 30 | PositionError.TIMEOUT = 3; 31 | PositionError.prototype = new Error(); 32 | 33 | function Coordinates(data) { 34 | this.accuracy = EARTH_RADIUS_M * Math.PI; 35 | this.altitude = null; 36 | this.altitudeAccuracy = null; 37 | this.heading = null; 38 | this.latitude = Number(data.latitude); 39 | this.longitude = Number(data.longitude); 40 | this.speed = null; 41 | } 42 | 43 | function Geoposition(data) { 44 | this.timestamp = Date.now(); 45 | this.coords = new Coordinates(data); 46 | } 47 | 48 | Geoposition.distance = function(p1, p2) { 49 | if (p1 === p2) { 50 | return 0; 51 | } 52 | if (!p1 || !p2) { 53 | return Infinity; 54 | } 55 | // c/o http://jsp.vs19.net/lr/sphere-distance.php 56 | function angle(b1, l1, b2, l2) { 57 | function d2r(d) { return d * Math.PI / 180; } 58 | var p1 = Math.cos(d2r(l1 - l2)), 59 | p2 = Math.cos(d2r(b1 - b2)), 60 | p3 = Math.cos(d2r(b1 + b2)); 61 | return Math.acos(((p1 * (p2 + p3)) + (p2 - p3)) / 2); 62 | } 63 | return EARTH_RADIUS_M * angle(p1.coords.latitude, p1.coords.longitude, 64 | p2.coords.latitude, p2.coords.longitude); 65 | }; 66 | 67 | function GeolocationPolyfill() { 68 | var cached = null; 69 | 70 | function dispatch(handler, data) { 71 | if (typeof handler === 'function') { 72 | setTimeout(function() { handler(data); }, 0); 73 | } else if (typeof handler === 'object' && handler && 'handleEvent' in handler) { 74 | handler = handler.handleEvent; 75 | setTimeout(function() { handler(data); }, 0); 76 | } 77 | } 78 | 79 | function acquireLocation(onSuccess, onFailure, enableHighAccuracy) { 80 | var script = document.createElement('SCRIPT'), 81 | cbname = '_geoip_callback_' + Math.floor(Math.random() * (1<<30)); 82 | function cleanup() { 83 | if (script.parentNode) { script.parentNode.removeChild(script); } 84 | try { delete window[cbname]; } catch (ex) { window[cbname] = (void 0); /*IE8-*/ } 85 | } 86 | window[cbname] = function(data) { 87 | cleanup(); 88 | onSuccess(new Geoposition(data)); 89 | }; 90 | script.onerror = function(e) { 91 | cleanup(); 92 | onFailure(e); 93 | }; 94 | script.src = GEOIP_SERVICE_JSONP + encodeURIComponent(cbname); 95 | (document.head || document.body || document.documentElement).appendChild(script); 96 | return cleanup; 97 | } 98 | 99 | 100 | this.getCurrentPosition = function(successCallback, errorCallback, options) { 101 | if (!successCallback) { throw TypeError('The successCallback parameter is null.'); } 102 | 103 | if (options && 'maximumAge' in options) { 104 | var maximumAge = options.maximumAge|0; 105 | if (maximumAge < 0) maximumAge = 0; 106 | } else { 107 | maximumAge = 0; 108 | } 109 | 110 | if (options && 'timeout' in options) { 111 | var timeout = options.timeout|0; 112 | if (timeout < 0) timeout = 0; 113 | } else { 114 | timeout = Infinity; 115 | } 116 | 117 | if (options && 'enableHighAccuracy' in options) { 118 | var enableHighAccuracy = Boolean(enableHighAccuracy); 119 | } else { 120 | enableHighAccuracy = false; 121 | } 122 | 123 | if (cached && ((Date.now() - cached.timestamp) <= maximumAge)) { 124 | dispatch(successCallback, cached); 125 | return; 126 | } 127 | 128 | if (timeout === 0) { 129 | dispatch(errorCallback, 130 | new PositionError(PositionError.TIMEOUT, 'Timed out')); 131 | return; 132 | } 133 | 134 | var cancelOperation = acquireLocation(onSuccess, onFailure, enableHighAccuracy); 135 | 136 | var timedOut = false, timerId = 0; 137 | if (isFinite(timeout)) { 138 | timerId = setTimeout(function() { 139 | timedOut = true; 140 | cancelOperation(); 141 | dispatch(errorCallback, 142 | new PositionError(PositionError.TIMEOUT, 'Timed out')); 143 | }, timeout); 144 | } 145 | 146 | function onSuccess(position) { 147 | cached = position; 148 | if (timedOut) return; 149 | if (timerId) clearTimeout(timerId); 150 | dispatch(successCallback, position); 151 | } 152 | 153 | function onFailure() { 154 | if (timedOut) return; 155 | if (timerId) clearTimeout(timerId); 156 | dispatch(errorCallback, 157 | new PositionError(PositionError.POSITION_UNAVAILABLE, 'Position unavailable')); 158 | } 159 | }; 160 | 161 | var timers = [], counter = 0; 162 | 163 | this.watchPosition = function(successCallback, errorCallback, options) { 164 | if (!successCallback) { throw TypeError('The successCallback parameter is null.'); } 165 | 166 | if (options && 'maximumAge' in options) { 167 | var maximumAge = options.maximumAge|0; 168 | if (maximumAge < 0) maximumAge = 0; 169 | } else { 170 | maximumAge = 0; 171 | } 172 | 173 | if (options && 'timeout' in options) { 174 | var timeout = options.timeout|0; 175 | if (timeout < 0) timeout = 0; 176 | } else { 177 | timeout = Infinity; 178 | } 179 | 180 | if (options && 'enableHighAccuracy' in options) { 181 | var enableHighAccuracy = Boolean(enableHighAccuracy); 182 | } else { 183 | enableHighAccuracy = false; 184 | } 185 | 186 | if (cached && ((Date.now() - cached.timestamp) < maximumAge)) { 187 | dispatch(successCallback, cached); 188 | } 189 | 190 | var intervalId = setInterval(systemEvent, POLL_TIMER_MS); 191 | var timerDetails = { 192 | intervalId: intervalId, 193 | cleared: false 194 | }; 195 | 196 | var lastPosition = null, timerId = 0; 197 | acquisitionSteps(); 198 | function acquisitionSteps() { 199 | var cancelOperation = acquireLocation(onSuccess, onFailure, enableHighAccuracy); 200 | 201 | var timedOut = false; 202 | if (isFinite(timeout) && !timerId) { 203 | timerId = setTimeout(function() { 204 | timedOut = true; 205 | timerId = 0; 206 | cancelOperation(); 207 | if (!timerDetails.cleared) 208 | dispatch(errorCallback, 209 | new PositionError(PositionError.TIMEOUT, 'Timed out')); 210 | }, timeout); 211 | } 212 | 213 | function onSuccess(position) { 214 | cached = position; 215 | if (timedOut || timerDetails.cleared) return; 216 | if (timerId) timerId = clearTimeout(timerId); 217 | 218 | if (Geoposition.distance(lastPosition, position) >= DISTANCE_THRESHOLD_M) { 219 | lastPosition = position; 220 | dispatch(successCallback, position); 221 | } 222 | } 223 | 224 | function onFailure() { 225 | if (timedOut || timerDetails.cleared) return; 226 | if (timerId) timerId = clearTimeout(timerId); 227 | dispatch(errorCallback, 228 | new PositionError(PositionError.POSITION_UNAVAILABLE, 'Position unavailable')); 229 | } 230 | } 231 | 232 | function systemEvent() { 233 | acquisitionSteps(); 234 | } 235 | 236 | var watchId = ++counter; 237 | timers[watchId] = timerDetails; 238 | return watchId; 239 | }; 240 | 241 | this.clearWatch = function(watchId) { 242 | watchId = watchId|0; 243 | if (!(watchId in timers)) { 244 | return; 245 | } 246 | 247 | var timerDetails = timers[watchId]; 248 | delete timers[watchId]; 249 | clearInterval(timerDetails.intervalId); 250 | timerDetails.cleared = true; 251 | }; 252 | } 253 | 254 | // Exports 255 | if (!navigator.geolocation) { 256 | navigator.geolocation = new GeolocationPolyfill(); 257 | } 258 | }()); 259 | -------------------------------------------------------------------------------- /html.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | if (!('window' in global && 'document' in global)) 5 | return; 6 | 7 | //---------------------------------------------------------------------- 8 | // 9 | // HTML 10 | // https://html.spec.whatwg.org 11 | // 12 | //---------------------------------------------------------------------- 13 | 14 | // document.head attribute 15 | // Needed for: IE8- 16 | if (!('head' in document)) 17 | document.head = document.getElementsByTagName('head')[0]; 18 | 19 | // Ensure correct parsing of newish elements ("shiv") 20 | // Needed for: IE8- 21 | [ 22 | 'abbr', 'article', 'aside', 'audio', 'bdi', 'canvas', 'data', 'datalist', 23 | 'details', 'dialog', 'figcaption', 'figure', 'footer', 'header', 'hgroup', 24 | 'main', 'mark', 'meter', 'nav', 'output', 'picture', 'progress', 'section', 25 | 'summary', 'template', 'time', 'video'].forEach(function(tag) { 26 | document.createElement(tag); 27 | }); 28 | 29 | // HTMLElement.dataset 30 | // Needed for: IE10- 31 | if (!('dataset' in document.createElement('span')) && 32 | 'Element' in global && Element.prototype && Object.defineProperty) { 33 | Object.defineProperty(Element.prototype, 'dataset', { get: function() { 34 | var result = Object.create(null); 35 | for (var i = 0; i < this.attributes.length; ++i) { 36 | var attr = this.attributes[i]; 37 | if (attr.specified && attr.name.substring(0, 5) === 'data-') { 38 | (function(element, name) { 39 | var prop = name.replace(/-([a-z])/g, function(m, p) { 40 | return p.toUpperCase(); 41 | }); 42 | result[prop] = element.getAttribute('data-' + name); // Read-only, for IE8- 43 | Object.defineProperty(result, prop, { 44 | get: function() { 45 | return element.getAttribute('data-' + name); 46 | }, 47 | set: function(value) { 48 | element.setAttribute('data-' + name, value); 49 | }}); 50 | }(this, attr.name.substring(5))); 51 | } 52 | } 53 | return result; 54 | }}); 55 | } 56 | 57 | // Base64 utility methods 58 | // Needed for: IE9- 59 | (function() { 60 | if ('atob' in global && 'btoa' in global) 61 | return; 62 | 63 | var B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 64 | function atob(input) { 65 | input = String(input); 66 | var position = 0, 67 | output = [], 68 | buffer = 0, bits = 0, n; 69 | 70 | input = input.replace(/\s/g, ''); 71 | if ((input.length % 4) === 0) { input = input.replace(/=+$/, ''); } 72 | if ((input.length % 4) === 1) { throw Error("InvalidCharacterError"); } 73 | if (/[^+/0-9A-Za-z]/.test(input)) { throw Error("InvalidCharacterError"); } 74 | 75 | while (position < input.length) { 76 | n = B64_ALPHABET.indexOf(input.charAt(position)); 77 | buffer = (buffer << 6) | n; 78 | bits += 6; 79 | 80 | if (bits === 24) { 81 | output.push(String.fromCharCode((buffer >> 16) & 0xFF)); 82 | output.push(String.fromCharCode((buffer >> 8) & 0xFF)); 83 | output.push(String.fromCharCode(buffer & 0xFF)); 84 | bits = 0; 85 | buffer = 0; 86 | } 87 | position += 1; 88 | } 89 | 90 | if (bits === 12) { 91 | buffer = buffer >> 4; 92 | output.push(String.fromCharCode(buffer & 0xFF)); 93 | } else if (bits === 18) { 94 | buffer = buffer >> 2; 95 | output.push(String.fromCharCode((buffer >> 8) & 0xFF)); 96 | output.push(String.fromCharCode(buffer & 0xFF)); 97 | } 98 | 99 | return output.join(''); 100 | }; 101 | 102 | function btoa(input) { 103 | input = String(input); 104 | var position = 0, 105 | out = [], 106 | o1, o2, o3, 107 | e1, e2, e3, e4; 108 | 109 | if (/[^\x00-\xFF]/.test(input)) { throw Error("InvalidCharacterError"); } 110 | 111 | while (position < input.length) { 112 | o1 = input.charCodeAt(position++); 113 | o2 = input.charCodeAt(position++); 114 | o3 = input.charCodeAt(position++); 115 | 116 | // 111111 112222 222233 333333 117 | e1 = o1 >> 2; 118 | e2 = ((o1 & 0x3) << 4) | (o2 >> 4); 119 | e3 = ((o2 & 0xf) << 2) | (o3 >> 6); 120 | e4 = o3 & 0x3f; 121 | 122 | if (position === input.length + 2) { 123 | e3 = 64; 124 | e4 = 64; 125 | } 126 | else if (position === input.length + 1) { 127 | e4 = 64; 128 | } 129 | 130 | out.push(B64_ALPHABET.charAt(e1), 131 | B64_ALPHABET.charAt(e2), 132 | B64_ALPHABET.charAt(e3), 133 | B64_ALPHABET.charAt(e4)); 134 | } 135 | 136 | return out.join(''); 137 | }; 138 | 139 | global.atob = atob; 140 | global.btoa = btoa; 141 | }()); 142 | 143 | // requestAnimationFrame - needed for IE9- 144 | (function() { 145 | if ('requestAnimationFrame' in global) 146 | return; 147 | 148 | var TARGET_FPS = 60, 149 | requests = Object.create(null), 150 | raf_handle = 0, 151 | timeout_handle = -1; 152 | 153 | function isVisible(element) { 154 | return element.offsetWidth > 0 && element.offsetHeight > 0; 155 | } 156 | 157 | function onFrameTimer() { 158 | var cur_requests = requests; 159 | requests = Object.create(null); 160 | timeout_handle = -1; 161 | Object.keys(cur_requests).forEach(function(id) { 162 | var request = cur_requests[id]; 163 | if (!request.element || isVisible(request.element)) 164 | request.callback(Date.now()); 165 | }); 166 | } 167 | 168 | function requestAnimationFrame(callback, element) { 169 | var cb_handle = ++raf_handle; 170 | requests[cb_handle] = {callback: callback, element: element}; 171 | if (timeout_handle === -1) 172 | timeout_handle = global.setTimeout(onFrameTimer, 1000 / TARGET_FPS); 173 | return cb_handle; 174 | } 175 | 176 | function cancelAnimationFrame(handle) { 177 | delete requests[handle]; 178 | if (Object.keys(requests).length === 0) { 179 | global.clearTimeout(timeout_handle); 180 | timeout_handle = -1; 181 | } 182 | } 183 | 184 | global.requestAnimationFrame = requestAnimationFrame; 185 | global.cancelAnimationFrame = cancelAnimationFrame; 186 | }()); 187 | 188 | }(self)); 189 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /js.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------- 2 | // 3 | // Non-standard JavaScript (Mozilla) functions 4 | // 5 | //---------------------------------------------------------------------- 6 | 7 | (function () { 8 | 'use strict'; 9 | 10 | // JavaScript 1.8.1 11 | String.prototype.trimLeft = String.prototype.trimLeft || function () { 12 | return String(this).replace(/^\s+/, ''); 13 | }; 14 | 15 | // JavaScript 1.8.1 16 | String.prototype.trimRight = String.prototype.trimRight || function () { 17 | return String(this).replace(/\s+$/, ''); 18 | }; 19 | 20 | // JavaScript 1.? 21 | var ESCAPES = { 22 | //'\x00': '\\0', Special case in FF3.6, removed by FF10 23 | '\b': '\\b', 24 | '\t': '\\t', 25 | '\n': '\\n', 26 | '\f': '\\f', 27 | '\r': '\\r', 28 | '"' : '\\"', 29 | '\\': '\\\\' 30 | }; 31 | String.prototype.quote = String.prototype.quote || function() { 32 | return '"' + String(this).replace(/[\x00-\x1F"\\\x7F-\uFFFF]/g, function(c) { 33 | if (Object.prototype.hasOwnProperty.call(ESCAPES, c)) 34 | return ESCAPES[c]; 35 | var cc = c.charCodeAt(0); 36 | if (cc <= 0xFF) 37 | return '\\x' + ('00' + cc.toString(16).toUpperCase()).slice(-2); 38 | return '\\u' + ('0000' + cc.toString(16).toUpperCase()).slice(-4); 39 | }) + '"'; 40 | }; 41 | }()); 42 | -------------------------------------------------------------------------------- /keyboard.md: -------------------------------------------------------------------------------- 1 | # KeyboardEvent Polyfill 2 | 3 | Polyfill for the additional `KeyboardEvent` properties defined in the UI Events draft specifications: 4 | 5 | * [UI Events](https://w3c.github.io/uievents/) 6 | * [UI Events](https://dvcs.w3.org/hg/d4e/raw-file/tip/source_respec.htm) (queryKeyCaps addition) 7 | 8 | Demo: https://inexorabletash.github.io/polyfill/demos/keyboard.html 9 | 10 | ## Details 11 | 12 | For all browsers (except IE8-) this adds the following properties to 13 | KeyboardEvent instances: 14 | 15 | * `event.code` - (string) identifies the physical key - [code values](https://w3c.github.io/uievents-code/) 16 | * `event.key` - (string) printed representation of the key, or control key identifier - [key values](https://w3c.github.io/uievents-key/) 17 | * `event.location` - (number) location of key (0 = standard, 1 = left, 2 = right, 3 = numpad) 18 | 19 | It also adds a static method: 20 | 21 | * `KeyboardEvent.queryKeyCap(code)` - yields a human readable label for the key 22 | 23 | As a helper for IE8-, it also a non-standard function to the global namespace: 24 | 25 | * `identifyKey(keyboardEvent)` 26 | 27 | The keyboardEvent argument should be a keyup/keydown DOM event. After 28 | calling this, the event will be populated with `code`, `key` and `location` 29 | properties. 30 | 31 | ## Example 32 | 33 | ```js 34 | // Applications that need logical key should use `key`: 35 | div.onkeydown = function(e) { 36 | identifyKey(e); // for IE8- 37 | switch (e.key) { 38 | case 'ArrowLeft': map.scroll(-10, 0); break; 39 | case 'ArrowRight': map.scroll(10, 0); break; 40 | case 'ArrowUp': map.scroll(0, -10); break; 41 | case 'ArrowDown': map.scroll(0, 10); break; 42 | } 43 | }; 44 | 45 | // Applications that need physical keys should use `code`: 46 | div.onkeydown = function(e) { 47 | identifyKey(e); // for IE8- 48 | switch (e.code) { 49 | case 'KeyW': character.moveForward(); break; 50 | case 'KeyA': character.moveLeft(); break; 51 | case 'KeyS': character.moveBackward(); break; 52 | case 'KeyD': character.moveRight(); break; 53 | } 54 | }; 55 | ``` 56 | 57 | ## Background: Keys vs. Characters 58 | 59 | In most operating systems, physical key presses generate events 60 | (keydown, keyup) which are delivered to applications but 61 | simultaneously processed by the operating system to perform actions 62 | or generate characters. For example: 63 | 64 | * Keys: [A] - generate character 'a' 65 | * Keys: [Shift] - no character generated 66 | * Keys: [Shift]+[A] - generate character 'A' 67 | * Keys: [9] - generate character '9' 68 | * Keys: [Shift]+[9] - generate character '(' 69 | * Keys: [;:] - generate character ';' 70 | * Keys: [Shift]+[;:] - generate character ':' 71 | * Keys: [Alt]+[`~], [E] - generate e with grave accent 72 | * Keys: [Alt]+[0],[1],[2],[8] - generate Euro symbol 73 | * Keys: [Enter] - generate 0x0D character (maybe) 74 | * Keys: [Esc] - generate 0x1B character (maybe) 75 | 76 | And of course, for non-Latin languages things get even more 77 | complicated including IMEs where multiple keystrokes may generate a 78 | list of candidate characters in an OS- or application-provided 79 | display, from which the user selects before the character is 80 | presented to the application. 81 | 82 | ## Legacy Key Events 83 | 84 | Keyboard events were implemented before a specification was 85 | written; as such, the behavior across browsers is very different. 86 | The HTML4 spec defines the model as sending 'keydown' and 'keyup' 87 | events corresponding to physical actions with the keys (with 88 | possibly repeated 'keydown' events due to auto-repeat), and 89 | 'keypress' events corresponding to the generation of a character. 90 | The legacy properties identifying the key are: 91 | 92 | ```webidl 93 | readonly attribute unsigned long keyCode; 94 | readonly attribute unsigned long charCode; 95 | readonly attribute unsigned long which; 96 | ``` 97 | 98 | * `keyCode` is a OS/browser dependent code identifying the 99 | specific key; sent on keydown and keyup events 100 | 101 | * `charCode` is the Unicode code point of character generated by 102 | key press sequence, sent on keypress events which generate 103 | character input 104 | 105 | * `which` is only used in some browsers - it's basically like 106 | `charCode` combined with `keyCode` 107 | 108 | For compatibility most browsers conform on the `keyCode` values 109 | produced by Microsoft Internet Explorer on Windows. But some browsers deviate - 110 | notably Firefox - and there several are OS- and browser-specific quirks. 111 | 112 | IE's keyCode values derive from Windows Virtual Key Codes. 113 | [MSDN](http://msdn.microsoft.com/en-us/library/dd375731(VS.85).aspx) 114 | 115 | Safari and Chrome adopted the IE model and codes for compatibility 116 | [webkit-dev](https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html) 117 | 118 | Firefox (Mozilla/Gecko) uses a very similar set of codes, which differ 119 | for a handful of keys for punctuation symbols. 120 | [MDN](https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent) 121 | 122 | Older versions of Opera also uses different codes for some non-alphabetic keys. 123 | [dev.opera.com](http://dev.opera.com/articles/view/keyboard-accessible-web-applications-3/) 124 | 125 | Other references: 126 | 127 | * http://unixpapa.com/js/key.html 128 | * http://turboajax.com/dojo/key_compat.html 129 | * http://msdn.microsoft.com/en-us/scriptjunkie/ff928319 130 | * http://www.usb.org/developers/devclass_docs/Hut1_11.pdf 131 | 132 | ## Emerging Standards and Directions 133 | 134 | The [UI Events](https://w3c.github.io/uievents/) draft specification defines new 135 | properties for `KeyboardEvent`: 136 | 137 | ```idl 138 | readonly attribute DOMString code; 139 | readonly attribute DOMString key; 140 | readonly attribute unsigned long location; 141 | ``` 142 | 143 | `code` is a standardized key identifier mapping to a physical key 144 | on the device, rather like a USB code. 145 | 146 | `key` is a string giving the printed representation of the key, 147 | or other identifier. 148 | 149 | `location` is a number identifying the physical location of the 150 | key - standard, left vs. right, numpad, etc. 151 | 152 | Earlier drafts of the specs use `keyLocation` instead of 153 | `location`, `keyIdentifier` instead of `code`, and `keyChar` instead 154 | of `key`. Some browsers (Chrome, Safari) have partial 155 | implementation of these earlier properties. 156 | 157 | Another [UI Events](https://dvcs.w3.org/hg/d4e/raw-file/tip/source_respec.htm) 158 | draft specification proposes a new method on `KeyboardEvent`: 159 | 160 | ```idl 161 | static DOMString queryKeyCap (DOMString code, optional DOMString locale); 162 | ``` 163 | 164 | For cross-browser legacy mappings, see: 165 | 166 | http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/Note-KeyProps.html 167 | 168 | ## Known Issues 169 | 170 | * Win/Chrome, Win/Safari, Win/IE, Win/Firefox - PrintScreen and Scroll only generate keyup events 171 | * Win/Chrome - Apps doesn't send keyup 172 | * Win/Opera - Tab moves focus even if cancelled; need explicit workaround to return focus 173 | * Windows w/ Apple's Bootcamp: Backquote, BackSlash and Quote are mixed up in Chrome and IE 174 | -------------------------------------------------------------------------------- /obsolete/README.md: -------------------------------------------------------------------------------- 1 | Obsolete 2 | ======== 3 | 4 | These are basically unmaintained dead ends. Use at your own risk. 5 | 6 | W3C Web Storage 7 | --------------- 8 | [script](storage.js) - 9 | [spec](http://dev.w3.org/html5/webstorage/) - 10 | adapted from [Remy Sharp](https://gist.github.com/350433) 11 | 12 | ```javascript 13 | storage = window.localStorage 14 | storage = window.sessionStorage 15 | storage.clear() 16 | valueString = storage.getItem(keyString) 17 | valueString = storage.key(index) 18 | storage.removeItem(keyString) 19 | storage.setItem(keyString, valueString) 20 | storage.length 21 | ```` 22 | 23 | W3C Workers 24 | ----------- 25 | [script](workers.js) - 26 | [spec](http://dev.w3.org/html5/workers/) - 27 | just for kicks; you probably don't want to use this 28 | 29 | 30 | Console 31 | ------- 32 | [script](console.js) - 33 | [unit tests](https://inexorabletash.github.io/polyfill/obsolete/tests/console.html) - 34 | *de facto* standard in modern browsers based on [FireBug Console API](http://getfirebug.com/wiki/index.php/Console_API) 35 | 36 | ```javascript 37 | console.log(messageObject, arguments...); // and variations: debug, info, warn, error 38 | console.assert(assertion, messageObject, arguments...); 39 | console.count(name); 40 | console.time(name); console.timeEnd(name); 41 | console.group(name); console.groupEnd(); 42 | console.trace(); 43 | console.clear(); 44 | ``` 45 | 46 | Cookie API 47 | ---------- 48 | [script](cookie.js) - 49 | Adam Barth's [Cookie API proposal](https://docs.google.com/Doc?docid=0AZpchfQ5mBrEZGQ0cDh3YzRfMTRmdHFma21kMg&hl=en&pli=1) - 50 | abandoned 51 | 52 | ```javascript 53 | var cookie = document.getCookie(name, callback); 54 | alert(cookie.name); 55 | alert(cookie.value); 56 | 57 | var cookieArray = document.getAllCookies(callback); 58 | document.setCookie(cookie, errorCallback); 59 | document.deleteCookie(name, errorCallback); 60 | ``` 61 | 62 | DOMException (helper) 63 | --------------------- 64 | [script](domexception.js) - 65 | [demo page](https://inexorabletash.github.io/polyfill/obsolete/demos/domexception.html) - 66 | 67 | Creates a native DOMException of the specified type if possible, 68 | otherwise a similar looking object. Useful when implementing other polyfills. 69 | 70 | ```javascript 71 | exception = DOMException.create(code) 72 | ``` 73 | 74 | sprintf (other) 75 | --------------- 76 | [script](sprintf.js) - 77 | [unit tests](https://inexorabletash.github.io/polyfill/obsolete/tests/sprintf.html) - 78 | used for a few C-to-JavaScript porting projects 79 | 80 | ```javascript 81 | var str = sprintf("Foo %s bar %d", "hello", 123); 82 | ``` 83 | 84 | 85 | Binary Data 86 | ----------- 87 | [script](bindata.js) - 88 | [demo page](https://inexorabletash.github.io/polyfill/obsolete/demos/bindata.html) 89 | 90 | Abandoned proposal for ECMAScript http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects 91 | -------------------------------------------------------------------------------- /obsolete/bindata.js: -------------------------------------------------------------------------------- 1 | // http://wiki.ecmascript.org/doku.php?id=harmony:binary_data 2 | (function(global){ 3 | 4 | function ASSERT(c, m) { if (!c) { throw Error(m); } } 5 | 6 | (function() { 7 | var orig = Object.prototype.toString; 8 | Object.prototype.toString = function toString() { 9 | if (Object(this) === this && '__Class__' in this) { 10 | return '[object ' + this.__Class__ + ']'; 11 | } 12 | return orig.call(this, arguments); 13 | }; 14 | }()); 15 | 16 | function Data() { throw TypeError(); } 17 | Data.prototype = {}; 18 | Object.defineProperties(Data.prototype, { 19 | buffer: { get: function() { return this.__Value__.buffer; }}, 20 | byteOffset: { get: function() { return this.__Value__.byteOffset; }}, 21 | byteLength: { get: function() { return this.__Value__.byteLength; }}, 22 | update: { value: function(val) { 23 | if (this !== Object(this) || !(this instanceof Data)) { throw TypeError(); } 24 | var r = this.__DataType__.__Convert__(val); 25 | viewCopy(r, this.__Value__, 0, r.byteLength); 26 | }} 27 | }); 28 | global.Data = Data; 29 | 30 | function Type() { throw TypeError(); } 31 | Type.prototype = Data; 32 | global.Type = Type; 33 | 34 | // "block" is defined as an object with a __Value__ property (which is an ArrayBufferView) 35 | function isBlockObject(v) { return '__Value__' in Object(v); } 36 | function isBlockType(t) { return 'bytes' in t && '__Convert__' in t && '__Reify__' in t; } 37 | 38 | var littleEndian = true; 39 | 40 | // Intrinsic types 41 | [ 42 | {name: 'int8', type: 'Int8', domain: function(x) { return -0x80 <= x && x < 0x80; } }, 43 | {name: 'int16', type: 'Int16', domain: function(x) { return -0x8000 <= x && x < 0x8000; } }, 44 | {name: 'int32', type: 'Int32', domain: function(x) { return -0x80000000 <= x && x < 0x80000000; } }, 45 | {name: 'uint8', type: 'Uint8', domain: function(x) { return 0 <= x && x <= 0xff; } }, 46 | {name: 'uint16', type: 'Uint16', domain: function(x) { return 0 <= x && x <= 0xffff; } }, 47 | {name: 'uint32', type: 'Uint32', domain: function(x) { return 0 <= x && x <= 0xffffffff; } }, 48 | {name: 'float32', type: 'Float32', domain: function(x) { return true; } }, 49 | {name: 'float64', type: 'Float64', domain: function(x) { return true; } } 50 | ].forEach(function(desc) { 51 | var proto = Object.create(Data.prototype); 52 | var arrayType = global[desc.type + 'Array'], 53 | getter = 'get' + desc.type, 54 | setter = 'set' + desc.type, 55 | bytes = arrayType.BYTES_PER_ELEMENT; 56 | 57 | var t = function SomeNumericType(val) { 58 | if (this instanceof SomeNumericType) { throw TypeError("Numeric types should have value semantics"); } 59 | var x = t.__Cast__(val); 60 | return t.__Reify__(x); 61 | }; 62 | //t.__proto__ = Type.prototype; // Needed for uint8 instanceof Type, but contradicts spec? 63 | t.__DataType__ = desc.name; 64 | t.__Convert__ = function Convert(value) { 65 | var block = Object.create(proto); 66 | block.__Value__ = new Uint8Array(bytes); 67 | if (value === true) { 68 | value = 1; 69 | } else if (value === false) { 70 | value = 0; 71 | } else if (typeof value === 'number' && desc.domain(value)) { 72 | // ok 73 | } else { 74 | throw TypeError("Value " + value + " is not a " + desc.name); 75 | } 76 | 77 | (new DataView(block.__Value__.buffer, 0, bytes))[setter](0, value, littleEndian); 78 | return block; 79 | }; 80 | t.__IsSame__ = function IsSame(u) { 81 | return t.__DataType__ === Object(u).__DataType__; 82 | }; 83 | t.__Cast__ = function Cast(val) { 84 | var v; 85 | try { 86 | v = t.__Convert__(val); 87 | return t.__Reify(v); 88 | } catch (e) {} 89 | if (val === Infinity || val === -Infinity || val !== val) { 90 | return 0; // TODO: Per spec, but bogus for float types? 91 | } 92 | if (typeof val === 'number') { 93 | return t.__CCast__(val); 94 | } 95 | if (typeof val === 'string') { 96 | v = Number(val); 97 | return t.__CCast__(v); 98 | } 99 | throw TypeError("Cannot cast " + val + " to " + desc.name); 100 | }; 101 | t.__CCast__ = function CCast(n) { 102 | var a = new arrayType(1); 103 | a[0] = n; 104 | n = a[0]; 105 | return t.__Convert__(n); 106 | }; 107 | t.__Reify__ = function Reify(block) { 108 | var view = block.__Value__; 109 | return (new DataView(view.buffer, view.byteOffset, bytes))[getter](0, littleEndian); 110 | }; 111 | 112 | t.__Class__ = 'DataType'; // TODO: Not in spec? 113 | t.prototype = proto; // TODO: Not in spec? 114 | t.prototype.constructor = t; // TODO: Not in spec? 115 | 116 | Object.defineProperty(t, 'bytes', { get: function() { return bytes; }}); 117 | 118 | global[desc.name] = t; 119 | }); 120 | 121 | function viewCopy(src, dst, offset, length) { 122 | var srcv = new Uint8Array(src.buffer, src.byteOffset, length); 123 | var dstv = new Uint8Array(dst.buffer, dst.byteOffset + offset, length); 124 | for (var i = 0; i < length; ++i) { 125 | dstv[i] = srcv[i]; 126 | } 127 | } 128 | 129 | function ArrayType(elementType, length) { 130 | if (!(this instanceof ArrayType)) { throw TypeError("ArrayType cannot be called as a function."); } 131 | 132 | var proto = Object.create(ArrayType.prototype.prototype); 133 | length = length | 0; 134 | 135 | if (!isBlockType(elementType)) { throw TypeError("Type is not a block type"); } 136 | 137 | var bytes = elementType.bytes * length; 138 | 139 | function getter(thisobj, index) { 140 | var view = thisobj.__Value__; 141 | var offset = elementType.bytes * index; 142 | return elementType.__Reify__({__Value__: new Uint8Array(view.buffer, view.byteOffset + offset, elementType.bytes)}); 143 | } 144 | function setter(thisobj, index, value) { 145 | var src = elementType.__Convert__(value).__Value__; 146 | var dst = thisobj.__Value__; 147 | viewCopy(src, dst, elementType.bytes * index, elementType.bytes); 148 | } 149 | 150 | for (var i = 0; i < length; ++i) { 151 | (function(index) { 152 | Object.defineProperty(proto, index, { 153 | get: function() { 154 | return getter(this, index); 155 | }, 156 | set: function(value) { 157 | setter(this, index, value); 158 | } 159 | }); 160 | }(i)); 161 | } 162 | 163 | var t = function SomeArrayType(value) { 164 | if (!(this instanceof SomeArrayType)) { throw TypeError("Cannot call as a function"); } 165 | if (value === void 0 || Array.isArray(value)) { 166 | var a = t.__Construct__(); 167 | if (value !== void 0) { 168 | var r = t.__Convert__(value); 169 | viewCopy(r.__Value__, a.__Value__, 0, bytes); 170 | } 171 | return a; 172 | } else { 173 | var length = Number(value); 174 | return new (new ArrayType(elementType, length)); // TODO: Return instance or type? 175 | } 176 | }; 177 | t.__Class__ = 'DataType'; 178 | t.__proto__ = ArrayType.prototype; 179 | t.__DataType__ = 'ArrayType'; 180 | t.__ElementType__ = elementType; 181 | t.__Length__ = length; 182 | t.__Convert__ = function Convert(value) { 183 | // TODO: Precondition checks from spec. 184 | var block = t.__Construct__(); 185 | for (var i = 0; i < length; ++i) { 186 | setter(block, i, value ? value[i] : void 0); 187 | }; 188 | return block; 189 | }; 190 | t.__IsSame__ = function IsSame(u) { 191 | u = Object(u); 192 | return u.__DataType__ === 'ArrayType' && 193 | t.__ElementType__ === u.__ElementType__ && 194 | t.__Length__ === u.__Length___; 195 | }; 196 | t.__Construct__ = function Construct() { 197 | var block = Object.create(proto); 198 | block.__Class__ = 'Data'; 199 | block.__Value__ = new Uint8Array(bytes); 200 | block.__DataType__ = t; 201 | block.length = length; 202 | return block; 203 | }; 204 | t.__Reify__ = function Reify(block) { 205 | var result = []; 206 | for (var i = 0; i < length; ++i) { 207 | result[i] = getter(block, i); 208 | } 209 | return result; 210 | }; 211 | 212 | t.prototype = proto; 213 | t.prototype.constructor = t; 214 | t.prototype.forEach = Array.prototype.forEach; 215 | t.prototype.fill = function fill(value) { 216 | if (this !== Object(this) || this.__Class__ !== 'Data' || !(this.__DataType__.__IsSame__(t))) { 217 | throw TypeError(); 218 | } 219 | for (var i = 0; i < t.__Length__; ++i) { 220 | setter(this, i, value); 221 | } 222 | }; 223 | t.prototype.cursor = function cursor(fields) { 224 | var view = new StructView(elementType); 225 | var index = 0; 226 | var array = this; 227 | return { 228 | 'next': function() { 229 | if (index >= array.length) { throw global.StopIteration; } // TODO: Real iterator 230 | view.setView.apply(view, [array, index].concat(fields)); 231 | ++index; 232 | return view; 233 | } 234 | }; 235 | }; 236 | Object.defineProperty(t, 'elementType', { get: function() { return elementType; }}); 237 | t.length = length; // TODO: Fails because t is a Function 238 | Object.defineProperty(t, 'bytes', { get: function() { return bytes; }}); 239 | 240 | return t; 241 | } 242 | ArrayType.prototype = Object.create(Type.prototype); 243 | ArrayType.prototype.constructor = ArrayType; 244 | // TODO: ArrayType.prototype.repeat 245 | // TODO: ArrayType.prototype.prototype.forEach (indirection?) 246 | // TODO: ArrayType.prototype.prototype.subarray (indirection?) 247 | 248 | 249 | function StructType(fields) { 250 | if (!(this instanceof StructType)) { throw TypeError("StructType cannot be called as a function."); } 251 | 252 | var proto = Object.create(Data.prototype); 253 | 254 | var desc = {}; 255 | var bytes = 0; 256 | Object.keys(fields).forEach(function(name) { 257 | var type = fields[name]; 258 | if (!isBlockType(type)) { throw TypeError("Type for '" + name + "' is not a block type"); } 259 | desc[name] = { 260 | type: type, 261 | offset: bytes 262 | }; 263 | bytes += type.bytes; 264 | }); 265 | 266 | function getter(thisobj, key) { 267 | var field = desc[key]; 268 | var view = thisobj.__Value__; 269 | return field.type.__Reify__({__Value__: new Uint8Array(view.buffer, view.byteOffset + field.offset, field.type.bytes)}); 270 | } 271 | function setter(thisobj, key, value) { 272 | var field = desc[key]; 273 | var src = field.type.__Convert__(value).__Value__; 274 | var dst = thisobj.__Value__; 275 | viewCopy(src, dst, field.offset, field.type.bytes); 276 | } 277 | 278 | Object.keys(desc).forEach(function(name) { 279 | Object.defineProperty(proto, name, { 280 | get: function() { 281 | return getter(this, name); 282 | }, 283 | set: function(value) { 284 | setter(this, name, value); 285 | } 286 | }); 287 | }); 288 | 289 | var t = function SomeStructType(value) { 290 | if (!(this instanceof SomeStructType)) { throw TypeError("objects have reference semantics"); } 291 | return t.__Convert__(value); 292 | }; 293 | t.__Class__ = 'DataType'; 294 | t.__proto__ = StructType.prototype; 295 | t.__DataType__ = 'StructType'; 296 | t.__Convert__ = function Convert(value) { 297 | var block = t.__Construct__(); 298 | Object.keys(desc).forEach(function(name) { 299 | setter(block, name, value ? value[name] : void 0); 300 | }); 301 | return block; 302 | }; 303 | t.__IsSame__ = function IsSame(u) { 304 | return t === u; 305 | }; 306 | t.__Construct__ = function Construct() { 307 | var block = Object.create(proto); 308 | block.__Value__ = new Uint8Array(bytes); 309 | block.__Class__ = 'Block'; 310 | block.__DataType__ = t; 311 | return block; 312 | }; 313 | t.__Reify__ = function Reify(block) { 314 | var result = {}; 315 | Object.keys(desc).forEach(function(name) { 316 | result[name] = getter(block, name); 317 | }); 318 | return result; 319 | }; 320 | 321 | t.prototype = proto; 322 | t.prototype.constructor = t; 323 | Object.defineProperty(t, 'fields', { get: function() { 324 | var result = {}; 325 | Object.keys(desc).forEach(function(name) { 326 | result[name] = desc[name].type; 327 | }); 328 | return result; 329 | }}); 330 | Object.defineProperty(t, 'bytes', { get: function() { return bytes; }}); 331 | 332 | // For StructView 333 | t.__Fields__ = desc; 334 | 335 | return t; 336 | } 337 | StructType.prototype = Object.create(Type.prototype); 338 | StructType.prototype.constructor = StructType; 339 | 340 | global.ArrayType = ArrayType; 341 | global.StructType = StructType; 342 | 343 | function StructView(type) { 344 | if (!(this instanceof StructView)) { throw TypeError("StructView cannot be called as a function."); } 345 | if (Object(type).__DataType__ !== 'StructType') { throw TypeError("Type is not a StructType"); } 346 | 347 | // TODO: fields in StructView.prototype.setView(array, index, [...fields]) 348 | // TODO: (Lazily) define as StructType.ViewType? 349 | 350 | function getter(array, index, key) { 351 | var field = type.__Fields__[key]; 352 | var view = array.__Value__; 353 | return field.type.__Reify__({__Value__: new Uint8Array(view.buffer, view.byteOffset + type.bytes * index + field.offset, field.type.bytes)}); 354 | } 355 | function setter(array, index, key, value) { 356 | var field = type.__Fields__[key]; 357 | var src = field.type.__Convert__(value).__Value__; 358 | var dst = array.__Value__; 359 | viewCopy(src, dst, field.offset + type.bytes * index, field.type.bytes); 360 | } 361 | 362 | var that = this; 363 | Object.keys(type.__Fields__).forEach(function(name) { 364 | Object.defineProperty(that, name, { 365 | get: function() { 366 | if (!this.__Array__) throw Error(); 367 | return getter(this.__Array__, this.__Index__, name); 368 | }, 369 | set: function(value) { 370 | if (!this.__Array__) throw Error(); 371 | setter(this.__Array__, this.__Index__, name, value); 372 | } 373 | }); 374 | }); 375 | 376 | this.__DataType__ = type; // TODO: Not in spec. 377 | Object.defineProperty(this, 'fields', { get: function() { return type.fields; }}); 378 | Object.defineProperty(this, 'bytes', { get: function() { return type.bytes; }}); 379 | } 380 | StructView.prototype = {}; 381 | Object.defineProperty(StructView.prototype, 'setView', { value: function(array, index) { 382 | if (!Object(array).__DataType__) { throw TypeError(); } 383 | if (Object(Object(array).__DataType__).__DataType__ !== 'ArrayType') { throw TypeError(); } 384 | if (Object(Object(array).__DataType__).__ElementType__ !== this.__DataType__) { throw TypeError(); } 385 | 386 | index = index >> 0; 387 | if (index < 0 || index >= array.__Length__) { throw RangeError(); } 388 | this.__Array__ = array; 389 | this.__Index__ = index; 390 | }}); 391 | 392 | global.StructView = StructView; 393 | 394 | }(self)); 395 | -------------------------------------------------------------------------------- /obsolete/console.js: -------------------------------------------------------------------------------- 1 | // Depends on ECMAScript 5 or appropriate polyfill for: 2 | // Array.prototype.map, JSON.stringify 3 | 4 | (function () { 5 | var MAX_LINES = 1000, 6 | CONSOLE_CSS = '#SHIMCONSOLE { visibility: hidden; position: fixed; z-index: 9999; left: 0; right: 0; bottom: 0; height: 200px; border-top: solid 1px #808080; overflow: auto; word-wrap: break-word; padding: 5px; background-color: #eeeeee; color: #000000; font-family: monospace; font-size: 10pt; font-weight: normal; font-style: normal; }' 7 | + '#SHIMCONSOLE .SHIMCONSOLE_GROUP { margin-left: 20px; }' 8 | + '#SHIMCONSOLE .SHIMCONSOLE_ERROR { color: #ff0000; }' 9 | + '#SHIMCONSOLE .SHIMCONSOLE_WARN { color: #ff8000; }' 10 | + '#SHIMCONSOLE table { width: 100%; table-layout: fixed; border-collapse: collapse; border: 1px solid gray; }' 11 | + '#SHIMCONSOLE th { font-weight: normal; text-align: left; border: 1px solid gray; }' 12 | + '#SHIMCONSOLE td { font-weight: normal; text-align: left; border: 1px gray; border-left: 1px solid gray;}' 13 | + '#SHIMCONSOLE tr:nth-child(even) { background-color: white; }' 14 | + '#SHIMCONSOLE tr:nth-child(odd) { background-color: #f0f0ff; }' 15 | + '#SHIMCONSOLE tr:nth-child(1) { background-color: lightgray; }', 16 | FORMAT_REGEXP = /([^%]|%([\-+0]*)(\d+)?(\.\d+)?([%sdilfox]))/g; 17 | 18 | function Console() { 19 | // Add stylesheet 20 | (function () { 21 | var style = document.createElement('style'); 22 | (document.getElementsByTagName('HEAD')[0] || document.documentElement).appendChild(style); 23 | if ('styleSheet' in style) 24 | style.styleSheet.cssText = CONSOLE_CSS; 25 | else 26 | style.appendChild(document.createTextNode(CONSOLE_CSS)); 27 | }()); 28 | 29 | var display; 30 | var counts = {}; 31 | var times = {}; 32 | var groups = []; 33 | 34 | display = document.createElement('div'); 35 | display.id = 'SHIMCONSOLE'; 36 | (document.body || document.documentElement).appendChild(display); 37 | 38 | function format(o) { 39 | var span = document.createElement('span'), text, color, classOf; 40 | switch (typeof o) { 41 | case 'undefined': 42 | text = String(o); 43 | color = 'gray'; 44 | break; 45 | case 'boolean': 46 | text = String(o); 47 | color = 'green'; 48 | break; 49 | case 'number': 50 | text = String(o); 51 | color = 'blue'; 52 | break; 53 | case 'string': 54 | color = 'darkred'; 55 | text = JSON.stringify(o); 56 | break; 57 | default: // object and null 58 | if (!o) { 59 | text = String(o); 60 | color = 'gray'; 61 | break; 62 | } 63 | 64 | classOf = Object.prototype.toString.call(o); 65 | if (classOf === '[object Array]' || classOf === '[object Object]') { 66 | try { 67 | text = JSON.stringify(o); 68 | } catch (e) { 69 | // Cyclic, use fallback 70 | text = classOf; 71 | } 72 | } else { 73 | text = String(o); 74 | } 75 | } 76 | 77 | span.appendChild(document.createTextNode(text)); 78 | if (color) 79 | span.style.color = color; 80 | return span; 81 | } 82 | 83 | function show(args, type, prefix) { 84 | if (!args.length) 85 | return; 86 | var line = document.createElement('div'); 87 | line.className = 'SHIMCONSOLE_' + type; 88 | 89 | line.style.whiteSpace = 'pre'; 90 | 91 | if (prefix) 92 | line.appendChild(document.createTextNode(prefix)); 93 | 94 | function trunc(n) { 95 | return n < 0 ? Math.ceil(n) : Math.floor(n); 96 | } 97 | function repl(str, unit, flags, width, precision, specifier) { 98 | if (unit.charAt(0) != '%' || !args.length) 99 | return unit; 100 | if (specifier === '%') 101 | return '%'; 102 | var arg = args.shift(); 103 | switch (specifier) { 104 | case 's': return String(arg); 105 | case 'd': 106 | case 'i': 107 | case 'l': return String(trunc(Number(arg))); 108 | case 'f': return String(Number(arg)); 109 | default: 110 | case 'o': 111 | try { 112 | return JSON.stringify(arg); 113 | } catch (e) { 114 | return String(arg); 115 | } 116 | } 117 | return void 0; 118 | } 119 | 120 | if (typeof args[0] === 'string') { 121 | line.appendChild(document.createTextNode(args.shift().replace(FORMAT_REGEXP, repl))); 122 | line.appendChild(document.createTextNode(' ')); 123 | } 124 | 125 | // pretty-print remaining arguments 126 | while (args.length) { 127 | line.appendChild(format(args.shift())); 128 | line.appendChild(document.createTextNode(' ')); 129 | } 130 | 131 | append(line); 132 | } 133 | 134 | function append(e) { 135 | var parent = groups.length ? groups[groups.length - 1] : display; 136 | parent.appendChild(e); 137 | 138 | while (display.children.length > MAX_LINES) 139 | display.removeChild(display.firstChild); 140 | 141 | display.scrollTop = display.scrollHeight; 142 | display.style.visibility = 'visible'; 143 | } 144 | 145 | function toArray(a) { 146 | var result = [], i; 147 | for (i = 0; i < a.length; i += 1) 148 | result[i] = a[i]; 149 | return result; 150 | } 151 | 152 | this.log = function log(messageObject) { show(toArray(arguments), 'LOG'); }; 153 | this.debug = function debug(messageObject) { show(toArray(arguments), 'DEBUG'); }; 154 | this.info = function info(messageObject) { show(toArray(arguments), 'INFO'); }; 155 | this.warn = function warn(messageObject) { show(toArray(arguments), 'WARN', 'Warning: '); }; 156 | this.error = function error(messageObject) { show(toArray(arguments), 'ERROR', 'Error: '); }; 157 | 158 | this.assert = function assert(a, messageObject) { 159 | if (!a) { 160 | var args = toArray(arguments); 161 | args.shift(); 162 | show(args, 'ERROR', 'Assertion failed: '); 163 | } 164 | }; 165 | 166 | this.count = function count(name) { 167 | name = (name || ''); 168 | if (!(('$' + name) in counts)) 169 | counts['$' + name] = 0; 170 | counts['$' + name] += 1; 171 | show([name + ': ' + counts['$' + name]], 'INFO'); 172 | }; 173 | 174 | this.time = function time(name) { 175 | name = (name || ''); 176 | times['$' + name] = Number(new Date); 177 | }; 178 | this.timeEnd = function timeEnd(name) { 179 | name = (name || ''); 180 | if (('$' + name) in times) { 181 | show([name + ': ' + (Number(new Date) - times['$' + name]) + 'ms'], 'INFO'); 182 | delete times['$' + name]; 183 | } 184 | }; 185 | 186 | this.group = function group(name) { 187 | name = (name || ''); 188 | show(['>' + name], 'INFO'); 189 | var div = document.createElement('div'); 190 | div.className = 'SHIMCONSOLE_GROUP'; 191 | var parent = groups.length ? groups[groups.length - 1] : display; 192 | parent.appendChild(div); 193 | groups.push(div); 194 | }; 195 | this.groupEnd = function groupEnd() { 196 | groups.pop(); 197 | }; 198 | 199 | this.dir = function dir(name) { 200 | show([name], 'INFO'); 201 | }; 202 | this.dirxml = this.dir; 203 | 204 | this.table = function table(data) { 205 | function add(parent, type, text) { 206 | var e = parent.appendChild(document.createElement(type)); 207 | if (text !== undefined) e.appendChild(document.createTextNode(text)); 208 | return e; 209 | } 210 | 211 | if (!data) return; 212 | var i, len = Math.max.apply(null, data.map(function(row) { return row.length; })); 213 | 214 | var table = document.createElement('table'); 215 | var tbody = add(table, 'tbody'); 216 | var tr = add(tbody, 'tr'); 217 | add(tr, 'th', '(index)'); 218 | for (i = 0; i < len; ++i) 219 | add(tr, 'th', String(i)); 220 | for (i = 0; i < data.length; ++i) { 221 | tr = add(tbody, 'tr'); 222 | add(tr, 'td', String(i)); 223 | var row = data[i]; 224 | for (var j = 0; j < len; ++j) 225 | add(tr, 'td').appendChild(format(row[j])); 226 | } 227 | append(table); 228 | }; 229 | 230 | this.trace = function trace() { 231 | try { 232 | this.wont.work = 0; 233 | } catch (e) { 234 | if (e.stack) 235 | show([e.stack.replace(/^[^\n]*\n/, 'Stack trace:\n')], 'INFO'); 236 | } 237 | }; 238 | 239 | this.clear = function clear() { 240 | while (display.children.length) 241 | display.removeChild(display.firstChild); 242 | }; 243 | } 244 | 245 | window.console = window.console || new Console(); 246 | }()); 247 | -------------------------------------------------------------------------------- /obsolete/cookie.js: -------------------------------------------------------------------------------- 1 | 2 | // Adam Barth's abandoned(?) HTML Cookie API proposal 3 | // https://docs.google.com/Doc?docid=0AZpchfQ5mBrEZGQ0cDh3YzRfMTRmdHFma21kMg&hl=en&pli=1 4 | 5 | (function () { 6 | "use strict"; 7 | 8 | /** @constructor */ 9 | function Cookie(name, value) { 10 | this.name = name; 11 | this.value = value; 12 | this.httpOnly = false; 13 | } 14 | 15 | function parseCookies(findName) { 16 | var array = document.cookie.split(/;\s+/), 17 | i, str, idx, cookie; 18 | for (i = 0; i < array.length; i += 1) { 19 | str = array[i]; 20 | idx = str.indexOf('='); 21 | cookie = new Cookie(str.substring(0, idx), str.substring(idx + 1)); 22 | if (cookie.name === findName) { 23 | return cookie; 24 | } else { 25 | array[i] = cookie; 26 | } 27 | } 28 | return arguments.length > 0 ? null : array; 29 | } 30 | 31 | function getCookie(name, callback) { 32 | setTimeout(function () { 33 | callback(parseCookies(name)); 34 | }, 0); 35 | } 36 | 37 | function getAllCookies(callback) { 38 | setTimeout(function () { 39 | callback(parseCookies()); 40 | }, 0); 41 | } 42 | 43 | function setCookie(cookie, errback) { 44 | setTimeout(function () { 45 | var str = cookie.name + '=' + (cookie.value || ""), 46 | foundCookie, expectDeleted; 47 | 48 | if (cookie.expires) { 49 | str += "; expires=" + cookie.expires; 50 | } 51 | if (cookie.domain) { 52 | str += "; domain=" + cookie.domain; 53 | } 54 | if (cookie.secure) { 55 | str += "; secure"; 56 | } 57 | str += "; path=" + (cookie.path || "/"); 58 | document.cookie = str; 59 | 60 | foundCookie = parseCookies(cookie.name); 61 | expectDeleted = cookie.expires && (new Date(cookie.expires) < new Date()); 62 | if (!foundCookie && !expectDeleted) { 63 | errback("Cookie not set"); 64 | } else if (foundCookie.value !== cookie.value) { 65 | errback("Cookie not set"); 66 | } 67 | }, 0); 68 | } 69 | 70 | function deleteCookie(name, errback) { 71 | setTimeout(function () { 72 | document.cookie = name + "=; expires=" + (new Date(0).toGMTString()) + "; path=/"; 73 | var foundCookie = parseCookies(name); 74 | if (foundCookie) { 75 | errback("Cookie not deleted"); 76 | } 77 | }, 0); 78 | } 79 | 80 | document.getCookie = document.getCookie || getCookie; 81 | document.getAllCookies = document.getAllCookies || getAllCookies; 82 | document.setCookie = document.setCookie || setCookie; 83 | document.deleteCookie = document.deleteCookie || deleteCookie; 84 | 85 | } ()); 86 | -------------------------------------------------------------------------------- /obsolete/demos/bindata.html: -------------------------------------------------------------------------------- 1 | 2 | Binary Data Polyfill Demo 3 | 4 | 5 | 6 | 7 | 13 | 200 | 201 | -------------------------------------------------------------------------------- /obsolete/demos/domexception.html: -------------------------------------------------------------------------------- 1 | 2 | DOMException Polyfill Demo 3 | 4 | 5 | 6 | 7 | 32 | 33 | 42 | 43 | 44 | 45 | 47 |
keycodenamemessagenative? 46 |
48 | -------------------------------------------------------------------------------- /obsolete/domexception.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | 3 | window.DOMException = window.DOMException || function DOMException() { throw TypeError("Illegal constructor"); }; 4 | var code_to_name = {}, 5 | name_to_code = { 6 | // Introduced in DOM Level 1: 7 | INDEX_SIZE_ERR: 1, 8 | DOMSTRING_SIZE_ERR: 2, 9 | HIERARCHY_REQUEST_ERR: 3, 10 | WRONG_DOCUMENT_ERR: 4, 11 | INVALID_CHARACTER_ERR: 5, 12 | NO_DATA_ALLOWED_ERR: 6, 13 | NO_MODIFICATION_ALLOWED_ERR: 7, 14 | NOT_FOUND_ERR: 8, 15 | NOT_SUPPORTED_ERR: 9, 16 | INUSE_ATTRIBUTE_ERR: 10, 17 | // Introduced in DOM Level 2: 18 | INVALID_STATE_ERR: 11, 19 | SYNTAX_ERR: 12, 20 | INVALID_MODIFICATION_ERR: 13, 21 | NAMESPACE_ERR: 14, 22 | INVALID_ACCESS_ERR: 15, 23 | // Introduced in DOM Level 3: 24 | VALIDATION_ERR: 16, 25 | TYPE_MISMATCH_ERR: 17, 26 | // From other specifications: 27 | SECURITY_ERR: 18, 28 | NETWORK_ERR: 19, 29 | ABORT_ERR: 20, 30 | URL_MISMATCH_ERR: 21, 31 | QUOTA_EXCEEDED_ERR: 22, 32 | TIMEOUT_ERR: 23, 33 | INVALID_NODE_TYPE_ERR: 24, 34 | DATA_CLONE_ERR: 25 35 | }; 36 | 37 | Object.keys(name_to_code).forEach(function (n) { 38 | code_to_name[name_to_code[n]] = n; 39 | window.DOMException[n] = name_to_code[n]; 40 | }); 41 | 42 | var funcs = (function () { 43 | var funcs = [], w = window, d = document, im = d.implementation, de = DOMException; 44 | funcs[de.INDEX_SIZE_ERR] = function () { d.createTextNode("").splitText(1); }; 45 | //funcs[de.DOMSTRING_SIZE_ERR] = function () {}; 46 | funcs[de.HIERARCHY_REQUEST_ERR] = function () { d.appendChild(d); }; 47 | funcs[de.WRONG_DOCUMENT_ERR] = function () { var dt = im.createDocumentType('x'); im.createDocument('', '', dt); im.createDocument('', '', dt); }; 48 | funcs[de.INVALID_CHARACTER_ERR] = function () { document.createElement("!"); }; 49 | //funcs[de.NO_DATA_ALLOWED_ERR] = function () {}; 50 | //funcs[de.NO_MODIFICATION_ALLOWED_ERR] = function () {}; 51 | funcs[de.NOT_FOUND_ERR] = function () { d.removeChild(null); }; 52 | funcs[de.NOT_SUPPORTED_ERR] = function () { d.createCDATASection(); }; 53 | funcs[de.INUSE_ATTRIBUTE_ERR] = function () { var a = d.createElement('a'), n = d.createAttribute('n'); a.setAttributeNode(n); d.createElement('b').setAttributeNode(n); }; 54 | funcs[de.INVALID_STATE_ERR] = function () { var r = d.createRange(); r.detach(); r.detach(); }; 55 | funcs[de.SYNTAX_ERR] = function () { d.createElement('a').contentEditable = 'bogus'; }; 56 | //funcs[de.INVALID_MODIFICATION_ERR] = function () {}; 57 | funcs[de.NAMESPACE_ERR] = function () { d.createAttributeNS('', 'a:b'); }; 58 | //funcs[de.INVALID_ACCESS_ERR] = function () {}; 59 | //funcs[de.VALIDATION_ERR] = function () {}; 60 | //funcs[de.TYPE_MISMATCH_ERR] = function () {}; 61 | // ... 62 | funcs[de.INVALID_NODE_TYPE_ERR] = function () { d.createRange().selectNode(d.createElement('a')); }; // "DOM Range Exception 2" 63 | funcs[de.DATA_CLONE_ERR] = function () { w.postMessage(w); }; 64 | return funcs; 65 | }()); 66 | 67 | var DOMExceptionShim = (function () { 68 | function DOMException(name) { 69 | this.name = name || 'UNKNOWN_ERR'; 70 | } 71 | DOMException.prototype = new DOMException(); 72 | return DOMException; 73 | }()); 74 | 75 | window.DOMException.create = function (code) { 76 | code = Number(code); 77 | 78 | if (Object.prototype.hasOwnProperty.call(funcs, code)) { 79 | try { 80 | funcs[code](); 81 | } catch (e) { 82 | if (e.code === code) { 83 | e.name = e.name || code_to_name[code]; 84 | return e; 85 | } 86 | } 87 | } 88 | 89 | var name = Object.prototype.hasOwnProperty.call(code_to_name, code) ? code_to_name[code] : null, 90 | ex = new DOMExceptionShim(name); 91 | ex.code = code; 92 | ex.message = ex.name + ': DOM Exception ' + String(code); 93 | return ex; 94 | }; 95 | 96 | }(self)); 97 | -------------------------------------------------------------------------------- /obsolete/sprintf.js: -------------------------------------------------------------------------------- 1 | // 2 | // Format arguments into a string 3 | // var s = sprintf( pattern[, arg1[, arg2[, ... ] ] ] ) 4 | // Arguments: 5 | // pattern: format string 6 | // 7 | // Format sequences: 8 | // %[flags][width][.precision]specifier 9 | // flags: 10 | // - left-justify (default is right-justify) 11 | // + show '+' sign if positive 12 | // 0 pad with zeros, if numeric and right-justified (default is space) 13 | // width: 14 | // minimum width; if output would be narrower, padded with padding character 15 | // precision: 16 | // for strings, maximum width (truncated to fit) 17 | // for floating point values, number of decimal places (rounded or padded to fit) 18 | // otherwise, ignored 19 | // specifier: 20 | // % - literal '%' character, e.g. sprintf("we're %d%% complete", percent) 21 | // c - character; numeric arg treated as char code; otherwise first char of arg as string 22 | // s - string 23 | // d - decimal integer, via Math.floor() 24 | // i - decimal integer, via |0 25 | // u - decimal integer, via >>>0 26 | // o - octal integer, via Math.floor() 27 | // b - binary integer, via Math.floor() 28 | // x - hexadecimal integer (lowercase, e.g. d00d), via Math.floor() 29 | // X - hexadecimal integer (uppercase, e.g. D00D), via Math.floor() 30 | // f - decimal floating point 31 | // 32 | // Exceptions: 33 | // If insufficient arguments specified, e.g. sprintf("%s %s", "abc") 34 | // Unsupported specifier, e.g. sprintf("%r", obj) 35 | 36 | (function (global) { 37 | function sprintf(pattern) { 38 | var arg = 1; 39 | var args = arguments; 40 | function nextarg() { 41 | if (arg >= args.length) { 42 | throw Error("Ran out of arguments: " + arg); 43 | } 44 | return args[arg++]; 45 | } 46 | 47 | var regex = /([^%]|%([\-+0]*)(\d+)?(\.\d+)?([%csiudobxXf]))/g; 48 | 49 | function repl(str, unit, flags, width, precision, specifier) { 50 | 51 | if (unit.charAt(0) != '%') { 52 | return unit; 53 | } 54 | 55 | function toNumber(x) { 56 | if (typeof x === 'number') { return x; } 57 | if (typeof x === 'boolean') { return x ? 1 : 0; } 58 | return parseFloat(String(x)); 59 | } 60 | 61 | // flags 62 | var left = !!(flags.match(/-/)); 63 | var zero = !!(flags.match(/0/)); 64 | var sign = !!(flags.match(/\+/)); 65 | 66 | width = (typeof width === 'string' && width.length > 0) ? parseInt(width, 10) : (void 0); 67 | precision = (typeof precision === 'string' && precision.length > 1) ? parseInt(precision.substring(1), 10) : (void 0); 68 | 69 | //console.log(unit, "left=", left, "zero=", zero, "width=", width, "precision=", precision, "specifier=", specifier); 70 | 71 | // Stringify 72 | 73 | var r, neg; 74 | 75 | switch (specifier) { 76 | case '%': // escaped % 77 | return '%'; 78 | 79 | case 'c': // character - handle both printf('%c',65) and printf('%c','A') 80 | r = nextarg(); 81 | if (typeof r === 'number') { r = String.fromCharCode(r); } 82 | r = String(r).charAt(0); 83 | break; 84 | 85 | case 's': // string 86 | r = String(nextarg()); 87 | if (precision !== (void 0)) { r = r.substring(0, precision); } 88 | break; 89 | 90 | case 'i': // int32 91 | case 'u': // uint32 92 | case 'd': // decimal 93 | case 'o': // octal 94 | case 'b': // binary 95 | case 'x': // hexadecimal 96 | case 'X': // hexadecimal (uppercase) 97 | case 'f': // floating point 98 | r = toNumber(nextarg()); 99 | neg = (r < 0); 100 | r = Math.abs(r); 101 | switch (specifier) { 102 | case 'i': r = (r|0).toString(10); break; 103 | case 'u': r = (r>>>0).toString(10); break; 104 | case 'd': r = Math.floor(r).toString(10); break; 105 | case 'o': r = Math.floor(r).toString(8); break; 106 | case 'b': r = Math.floor(r).toString(2); break; 107 | case 'x': r = Math.floor(r).toString(16).toLowerCase(); break; 108 | case 'X': r = Math.floor(r).toString(16).toUpperCase(); break; 109 | case 'f': r = (precision !== (void 0)) ? r.toFixed(precision) : r.toString(10); break; 110 | } 111 | break; 112 | 113 | default: 114 | throw Error("Unsupported formatting specifier: " + specifier); 115 | } 116 | 117 | // Format 118 | 119 | var pad_len = width ? width - r.length : 0; 120 | var sign_char = ''; 121 | if (neg || sign) { 122 | --pad_len; 123 | sign_char = neg ? '-' : '+'; 124 | } 125 | 126 | var pad_char = (zero && !left) ? '0' : ' '; 127 | var pad = (pad_len > 0) ? (new Array(pad_len + 1)).join(pad_char) : ""; 128 | 129 | if (left) { 130 | return sign_char + r + pad; 131 | } 132 | else if (zero) { 133 | return sign_char + pad + r; 134 | } 135 | else { 136 | return pad + sign_char + r; 137 | } 138 | } 139 | 140 | return pattern.replace(regex, repl); 141 | } 142 | 143 | global.sprintf = sprintf; 144 | }(self)); 145 | -------------------------------------------------------------------------------- /obsolete/storage.js: -------------------------------------------------------------------------------- 1 | // Storage polyfill by Remy Sharp 2 | // https://gist.github.com/350433 3 | // Needed for IE7- 4 | 5 | // Dependencies: 6 | // JSON (use json2.js if necessary) 7 | 8 | // Tweaks by Joshua Bell (inexorabletash@gmail.com) 9 | // * URI-encode item keys 10 | // * Use String() for stringifying 11 | // * added length 12 | 13 | if (!window.localStorage || !window.sessionStorage) (function() { 14 | 15 | var Storage = function(type) { 16 | function createCookie(name, value, days) { 17 | var date, expires; 18 | 19 | if (days) { 20 | date = new Date(); 21 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 22 | expires = "; expires=" + date.toGMTString(); 23 | } else { 24 | expires = ""; 25 | } 26 | document.cookie = name + "=" + value + expires + "; path=/"; 27 | } 28 | 29 | function readCookie(name) { 30 | var nameEQ = name + "=", 31 | ca = document.cookie.split(';'), 32 | i, c; 33 | 34 | for (i = 0; i < ca.length; i++) { 35 | c = ca[i]; 36 | while (c.charAt(0) == ' ') { 37 | c = c.substring(1, c.length); 38 | } 39 | 40 | if (c.indexOf(nameEQ) == 0) { 41 | return c.substring(nameEQ.length, c.length); 42 | } 43 | } 44 | return null; 45 | } 46 | 47 | function setData(data) { 48 | data = JSON.stringify(data); 49 | if (type == 'session') { 50 | window.name = data; 51 | } else { 52 | createCookie('localStorage', data, 365); 53 | } 54 | } 55 | 56 | function clearData() { 57 | if (type == 'session') { 58 | window.name = ''; 59 | } else { 60 | createCookie('localStorage', '', 365); 61 | } 62 | } 63 | 64 | function getData() { 65 | var data = type == 'session' ? window.name : readCookie('localStorage'); 66 | return data ? JSON.parse(data) : {}; 67 | } 68 | 69 | 70 | // initialise if there's already data 71 | var data = getData(); 72 | 73 | function numKeys() { 74 | var n = 0; 75 | for (var k in data) { 76 | if (data.hasOwnProperty(k)) { 77 | n += 1; 78 | } 79 | } 80 | return n; 81 | } 82 | 83 | return { 84 | clear: function() { 85 | data = {}; 86 | clearData(); 87 | this.length = numKeys(); 88 | }, 89 | getItem: function(key) { 90 | key = encodeURIComponent(key); 91 | return data[key] === undefined ? null : data[key]; 92 | }, 93 | key: function(i) { 94 | // not perfect, but works 95 | var ctr = 0; 96 | for (var k in data) { 97 | if (ctr == i) return decodeURIComponent(k); 98 | else ctr++; 99 | } 100 | return null; 101 | }, 102 | removeItem: function(key) { 103 | key = encodeURIComponent(key); 104 | delete data[key]; 105 | setData(data); 106 | this.length = numKeys(); 107 | }, 108 | setItem: function(key, value) { 109 | key = encodeURIComponent(key); 110 | data[key] = String(value); 111 | setData(data); 112 | this.length = numKeys(); 113 | }, 114 | length: 0 115 | }; 116 | }; 117 | 118 | if (!window.localStorage) window.localStorage = new Storage('local'); 119 | if (!window.sessionStorage) window.sessionStorage = new Storage('session'); 120 | 121 | })(); 122 | -------------------------------------------------------------------------------- /obsolete/tests/console.html: -------------------------------------------------------------------------------- 1 | 2 | Console Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 144 | -------------------------------------------------------------------------------- /obsolete/tests/sprintf.html: -------------------------------------------------------------------------------- 1 | 2 | sprintf Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /obsolete/tests/sprintf_tests.js: -------------------------------------------------------------------------------- 1 | /*global QUnit, sprintf*/ 2 | 3 | QUnit.test("Basic substitutions", function(assert) { 4 | assert.expect(52); 5 | 6 | // Basic substitution: %s (string) 7 | assert.deepEqual(sprintf("%s", "Hello world"), "Hello world"); 8 | assert.deepEqual(sprintf("%s", 65.432), "65.432"); 9 | assert.deepEqual(sprintf("%s", 69), "69"); 10 | assert.deepEqual(sprintf("%s", true), "true"); 11 | 12 | // Basic substitution: %f (float) 13 | assert.deepEqual(sprintf("%f", "Hello world"), "NaN"); 14 | assert.deepEqual(sprintf("%f", 65.432), "65.432"); 15 | assert.deepEqual(sprintf("%f", 69), "69"); 16 | assert.deepEqual(sprintf("%f", true), "1"); 17 | 18 | // Basic substitution: %d (decimal integer) 19 | assert.deepEqual(sprintf("%d", "Hello world"), "NaN"); 20 | assert.deepEqual(sprintf("%d", 65.432), "65"); 21 | assert.deepEqual(sprintf("%d", 69), "69"); 22 | assert.deepEqual(sprintf("%d", true), "1"); 23 | 24 | // Basic substitution: %c (character) 25 | assert.deepEqual(sprintf("%c", "Hello world"), "H"); 26 | assert.deepEqual(sprintf("%c", 65.432), "A"); 27 | assert.deepEqual(sprintf("%c", 69), "E"); 28 | assert.deepEqual(sprintf("%c", true), "t"); 29 | 30 | // Floating point 31 | assert.deepEqual(sprintf("%f", 0), "0"); 32 | assert.deepEqual(sprintf("%f", 1), "1"); 33 | assert.deepEqual(sprintf("%f", 10), "10"); 34 | assert.deepEqual(sprintf("%f", 123.456), "123.456"); 35 | assert.deepEqual(sprintf("%f", -1), "-1"); 36 | assert.deepEqual(sprintf("%f", -123.456), "-123.456"); 37 | 38 | // Decimal integer 39 | assert.deepEqual(sprintf("%d", 0), "0"); 40 | assert.deepEqual(sprintf("%d", 1), "1"); 41 | assert.deepEqual(sprintf("%d", 10), "10"); 42 | assert.deepEqual(sprintf("%d", 123.456), "123"); 43 | assert.deepEqual(sprintf("%d", -1), "-1"); 44 | assert.deepEqual(sprintf("%d", -123.456), "-123"); 45 | 46 | // Octal 47 | assert.deepEqual(sprintf("%o", 0), "0"); 48 | assert.deepEqual(sprintf("%o", 1), "1"); 49 | assert.deepEqual(sprintf("%o", 10), "12"); 50 | assert.deepEqual(sprintf("%o", 123.456), "173"); 51 | assert.deepEqual(sprintf("%o", -1), "-1"); 52 | assert.deepEqual(sprintf("%o", -123.456), "-173"); 53 | 54 | // Binary 55 | assert.deepEqual(sprintf("%b", 0), "0"); 56 | assert.deepEqual(sprintf("%b", 1), "1"); 57 | assert.deepEqual(sprintf("%b", 10), "1010"); 58 | assert.deepEqual(sprintf("%b", 123.456), "1111011"); 59 | assert.deepEqual(sprintf("%b", -1), "-1"); 60 | assert.deepEqual(sprintf("%b", -123.456), "-1111011"); 61 | 62 | // Lower Hex 63 | assert.deepEqual(sprintf("%x", 0), "0"); 64 | assert.deepEqual(sprintf("%x", 1), "1"); 65 | assert.deepEqual(sprintf("%x", 10), "a"); 66 | assert.deepEqual(sprintf("%x", 123.456), "7b"); 67 | assert.deepEqual(sprintf("%x", -1), "-1"); 68 | assert.deepEqual(sprintf("%x", -123.456), "-7b"); 69 | 70 | // Upper Hex 71 | assert.deepEqual(sprintf("%X", 0), "0"); 72 | assert.deepEqual(sprintf("%X", 1), "1"); 73 | assert.deepEqual(sprintf("%X", 10), "A"); 74 | assert.deepEqual(sprintf("%X", 123.456), "7B"); 75 | assert.deepEqual(sprintf("%X", -1), "-1"); 76 | assert.deepEqual(sprintf("%X", -123.456), "-7B"); 77 | }); 78 | 79 | QUnit.test("Floating point precision", function(assert) { 80 | assert.expect(72); 81 | 82 | // Floating point precision (0) 83 | assert.deepEqual(sprintf("%.0f", 0), "0"); 84 | assert.deepEqual(sprintf("%.0f", 1), "1"); 85 | assert.deepEqual(sprintf("%.0f", 10), "10"); 86 | assert.deepEqual(sprintf("%.0f", 123.456), "123"); 87 | assert.deepEqual(sprintf("%.0f", -1), "-1"); 88 | assert.deepEqual(sprintf("%.0f", -123.456), "-123"); 89 | 90 | // Floating point precision (1) 91 | assert.deepEqual(sprintf("%.1f", 0), "0.0"); 92 | assert.deepEqual(sprintf("%.1f", 1), "1.0"); 93 | assert.deepEqual(sprintf("%.1f", 10), "10.0"); 94 | assert.deepEqual(sprintf("%.1f", 123.456), "123.5"); 95 | assert.deepEqual(sprintf("%.1f", -1), "-1.0"); 96 | assert.deepEqual(sprintf("%.1f", -123.456), "-123.5"); 97 | 98 | // Floating point precision (2) 99 | assert.deepEqual(sprintf("%.2f", 0), "0.00"); 100 | assert.deepEqual(sprintf("%.2f", 1), "1.00"); 101 | assert.deepEqual(sprintf("%.2f", 10), "10.00"); 102 | assert.deepEqual(sprintf("%.2f", 123.456), "123.46"); 103 | assert.deepEqual(sprintf("%.2f", -1), "-1.00"); 104 | assert.deepEqual(sprintf("%.2f", -123.456), "-123.46"); 105 | 106 | // Floating point precision (6) 107 | assert.deepEqual(sprintf("%.6f", 0), "0.000000"); 108 | assert.deepEqual(sprintf("%.6f", 1), "1.000000"); 109 | assert.deepEqual(sprintf("%.6f", 10), "10.000000"); 110 | assert.deepEqual(sprintf("%.6f", 123.456), "123.456000"); 111 | assert.deepEqual(sprintf("%.6f", -1), "-1.000000"); 112 | assert.deepEqual(sprintf("%.6f", -123.456), "-123.456000"); 113 | 114 | // Floating point width (2) 115 | assert.deepEqual(sprintf("%2f", 0), " 0"); 116 | assert.deepEqual(sprintf("%2f", 1), " 1"); 117 | assert.deepEqual(sprintf("%2f", 10), "10"); 118 | assert.deepEqual(sprintf("%2f", 123.456), "123.456"); 119 | assert.deepEqual(sprintf("%2f", -1), "-1"); 120 | assert.deepEqual(sprintf("%2f", -123.456), "-123.456"); 121 | 122 | // Floating point width (5) 123 | assert.deepEqual(sprintf("%5f", 0), " 0"); 124 | assert.deepEqual(sprintf("%5f", 1), " 1"); 125 | assert.deepEqual(sprintf("%5f", 10), " 10"); 126 | assert.deepEqual(sprintf("%5f", 123.456), "123.456"); 127 | assert.deepEqual(sprintf("%5f", -1), " -1"); 128 | assert.deepEqual(sprintf("%5f", -123.456), "-123.456"); 129 | 130 | // Floating point width (2), left-align 131 | assert.deepEqual(sprintf("%-2f", 0), "0 "); 132 | assert.deepEqual(sprintf("%-2f", 1), "1 "); 133 | assert.deepEqual(sprintf("%-2f", 10), "10"); 134 | assert.deepEqual(sprintf("%-2f", 123.456), "123.456"); 135 | assert.deepEqual(sprintf("%-2f", -1), "-1"); 136 | assert.deepEqual(sprintf("%-2f", -123.456), "-123.456"); 137 | 138 | // Floating point width (5), left-align 139 | assert.deepEqual(sprintf("%-5f", 0), "0 "); 140 | assert.deepEqual(sprintf("%-5f", 1), "1 "); 141 | assert.deepEqual(sprintf("%-5f", 10), "10 "); 142 | assert.deepEqual(sprintf("%-5f", 123.456), "123.456"); 143 | assert.deepEqual(sprintf("%-5f", -1), "-1 "); 144 | assert.deepEqual(sprintf("%-5f", -123.456), "-123.456"); 145 | 146 | // Floating point width (2), zero-pad 147 | assert.deepEqual(sprintf("%02f", 0), "00"); 148 | assert.deepEqual(sprintf("%02f", 1), "01"); 149 | assert.deepEqual(sprintf("%02f", 10), "10"); 150 | assert.deepEqual(sprintf("%02f", 123.456), "123.456"); 151 | assert.deepEqual(sprintf("%02f", -1), "-1"); 152 | assert.deepEqual(sprintf("%02f", -123.456), "-123.456"); 153 | 154 | // Floating point width (5), zero-pad 155 | assert.deepEqual(sprintf("%05f", 0), "00000"); 156 | assert.deepEqual(sprintf("%05f", 1), "00001"); 157 | assert.deepEqual(sprintf("%05f", 10), "00010"); 158 | assert.deepEqual(sprintf("%05f", 123.456), "123.456"); 159 | assert.deepEqual(sprintf("%05f", -1), "-0001"); 160 | assert.deepEqual(sprintf("%05f", -123.456), "-123.456"); 161 | 162 | // Floating point width (2), zero-pad, left-align 163 | assert.deepEqual(sprintf("%-02f", 0), "0 "); 164 | assert.deepEqual(sprintf("%-02f", 1), "1 "); 165 | assert.deepEqual(sprintf("%-02f", 10), "10"); 166 | assert.deepEqual(sprintf("%-02f", 123.456), "123.456"); 167 | assert.deepEqual(sprintf("%-02f", -1), "-1"); 168 | assert.deepEqual(sprintf("%-02f", -123.456), "-123.456"); 169 | 170 | // Floating point width (5), zero-pad, left-align 171 | assert.deepEqual(sprintf("%-05f", 0), "0 "); 172 | assert.deepEqual(sprintf("%-05f", 1), "1 "); 173 | assert.deepEqual(sprintf("%-05f", 10), "10 "); 174 | assert.deepEqual(sprintf("%-05f", 123.456), "123.456"); 175 | assert.deepEqual(sprintf("%-05f", -1), "-1 "); 176 | assert.deepEqual(sprintf("%-05f", -123.456), "-123.456"); 177 | }); 178 | 179 | QUnit.test("Integer flags (align, padding, sign)", function(assert) { 180 | assert.expect(24); 181 | 182 | // Flags (align, padding, sign) 183 | assert.deepEqual(sprintf("%3d", 0), " 0"); 184 | assert.deepEqual(sprintf("%3d", 1), " 1"); 185 | assert.deepEqual(sprintf("%3d", -1), " -1"); 186 | assert.deepEqual(sprintf("%03d", 0), "000"); 187 | assert.deepEqual(sprintf("%03d", 1), "001"); 188 | assert.deepEqual(sprintf("%03d", -1), "-01"); 189 | assert.deepEqual(sprintf("%-3d", 0), "0 "); 190 | assert.deepEqual(sprintf("%-3d", 1), "1 "); 191 | assert.deepEqual(sprintf("%-3d", -1), "-1 "); 192 | assert.deepEqual(sprintf("%-03d", 0), "0 "); 193 | assert.deepEqual(sprintf("%-03d", 1), "1 "); 194 | assert.deepEqual(sprintf("%-03d", -1), "-1 "); 195 | assert.deepEqual(sprintf("%+3d", 0), " +0"); 196 | assert.deepEqual(sprintf("%+3d", 1), " +1"); 197 | assert.deepEqual(sprintf("%+3d", -1), " -1"); 198 | assert.deepEqual(sprintf("%+03d", 0), "+00"); 199 | assert.deepEqual(sprintf("%+03d", 1), "+01"); 200 | assert.deepEqual(sprintf("%+03d", -1), "-01"); 201 | assert.deepEqual(sprintf("%-+3d", 0), "+0 "); 202 | assert.deepEqual(sprintf("%-+3d", 1), "+1 "); 203 | assert.deepEqual(sprintf("%-+3d", -1), "-1 "); 204 | assert.deepEqual(sprintf("%-+03d", 0), "+0 "); 205 | assert.deepEqual(sprintf("%-+03d", 1), "+1 "); 206 | assert.deepEqual(sprintf("%-+03d", -1), "-1 "); 207 | }); 208 | 209 | QUnit.test("Floating point flags (align, padding, sign)", function(assert) { 210 | assert.expect(12); 211 | 212 | // Floating point, width, padding, precision 213 | assert.deepEqual(sprintf("%7.2f", 12.34), " 12.34"); 214 | assert.deepEqual(sprintf("%7.2f", -12.34), " -12.34"); 215 | assert.deepEqual(sprintf("%7.2f", 12), " 12.00"); 216 | assert.deepEqual(sprintf("%7.2f", 0.34), " 0.34"); 217 | assert.deepEqual(sprintf("%7.0f", 12.34), " 12"); 218 | assert.deepEqual(sprintf("%7.0f", -12.34), " -12"); 219 | assert.deepEqual(sprintf("%7.0f", 12), " 12"); 220 | assert.deepEqual(sprintf("%7.0f", 0.34), " 0"); 221 | assert.deepEqual(sprintf("%07.2f", 12.34), "0012.34"); 222 | assert.deepEqual(sprintf("%07.2f", -12.34), "-012.34"); 223 | assert.deepEqual(sprintf("%07.2f", 12), "0012.00"); 224 | assert.deepEqual(sprintf("%07.2f", 0.34), "0000.34"); 225 | // TODO: need more here 226 | }); 227 | 228 | QUnit.test("String flags (align, padding)", function(assert) { 229 | assert.expect(18); 230 | 231 | // String width 232 | assert.deepEqual(sprintf("|%s|\n", "test"), "|test|\n"); 233 | assert.deepEqual(sprintf("|%s|\n", "test but longer"), "|test but longer|\n"); 234 | assert.deepEqual(sprintf("|%2s|\n", "test"), "|test|\n"); 235 | assert.deepEqual(sprintf("|%2s|\n", "test but longer"), "|test but longer|\n"); 236 | assert.deepEqual(sprintf("|%-2s|\n", "test"), "|test|\n"); 237 | assert.deepEqual(sprintf("|%-2s|\n", "test but longer"), "|test but longer|\n"); 238 | assert.deepEqual(sprintf("|%20s|\n", "test"), "| test|\n"); 239 | assert.deepEqual(sprintf("|%20s|\n", "test but longer"), "| test but longer|\n"); 240 | assert.deepEqual(sprintf("|%-20s|\n", "test"), "|test |\n"); 241 | assert.deepEqual(sprintf("|%-20s|\n", "test but longer"), "|test but longer |\n"); 242 | 243 | // String width and precision 244 | assert.deepEqual(sprintf("|%2.2s|\n", "test"), "|te|\n"); 245 | assert.deepEqual(sprintf("|%2.2s|\n", "test but longer"), "|te|\n"); 246 | assert.deepEqual(sprintf("|%-2.2s|\n", "test"), "|te|\n"); 247 | assert.deepEqual(sprintf("|%-2.2s|\n", "test but longer"), "|te|\n"); 248 | assert.deepEqual(sprintf("|%20.2s|\n", "test"), "| te|\n"); 249 | assert.deepEqual(sprintf("|%20.2s|\n", "test but longer"), "| te|\n"); 250 | assert.deepEqual(sprintf("|%-20.2s|\n", "test"), "|te |\n"); 251 | assert.deepEqual(sprintf("|%-20.2s|\n", "test but longer"), "|te |\n"); 252 | }); 253 | 254 | QUnit.test("Regression tests", function(assert) { 255 | assert.deepEqual(sprintf("%.0f", 123.456), "123"); 256 | }); 257 | -------------------------------------------------------------------------------- /obsolete/workers.js: -------------------------------------------------------------------------------- 1 | // 2 | // Web Workers (http://www.whatwg.org/specs/web-workers/current-work/) 3 | // 4 | window.Worker = window.Worker || (function () { 5 | 6 | function __loadScript(src, callback, errback) { 7 | var HTTP_OK = 200, 8 | FILE_OK = 0, 9 | xhr = new XMLHttpRequest(), 10 | async = true; 11 | xhr.open("GET", src, async); 12 | xhr.onreadystatechange = function() { 13 | if (xhr.readyState === XMLHttpRequest.DONE) { 14 | if (status === FILE_OK || status === HTTP_OK) { 15 | callback(xhr.responseText); 16 | } 17 | else if (errback) { 18 | errback(xhr.responseText); 19 | } 20 | } 21 | }; 22 | xhr.send(null); 23 | } 24 | 25 | function Worker(src) { 26 | //-------------------------------------------------- 27 | // Environment exposed to the worker script 28 | //-------------------------------------------------- 29 | 30 | var worker = this, 31 | onmessage = null, // set by worker, called on post by parent 32 | __tasks = [], // queue of messages from parent 33 | __closing = false; // flag, set when closing 34 | 35 | //-------------------------------------------------- 36 | // API exposed from the Worker object 37 | //-------------------------------------------------- 38 | 39 | // set by parent, called on post by worker 40 | worker.onmessage = null; 41 | 42 | // set by parent, called on post by worker 43 | worker.onerror = null; 44 | 45 | // post message to the worker 46 | worker.postMessage = function (message) { 47 | if (__closing) { return; } 48 | __tasks.push(setTimeout(function () { 49 | try { 50 | if (typeof onmessage === 'function') { 51 | onmessage({ data: message }); 52 | } 53 | } 54 | catch (e) { 55 | if (typeof worker.onerror === 'function') { 56 | worker.onerror(e); 57 | } 58 | } 59 | }, 0)); 60 | }; 61 | 62 | // terminate the worker 63 | worker.terminate = function () { 64 | __closing = true; 65 | while (__tasks.length) { 66 | clearTimeout(__tasks.shift()); 67 | } 68 | }; 69 | 70 | //-------------------------------------------------- 71 | // API exposed to the worker script 72 | //-------------------------------------------------- 73 | 74 | var workerContext = { 75 | // post message from the worker to the parent 76 | postMessage: function(message) { 77 | setTimeout(function () { 78 | if (typeof worker.onmessage === 'function') { 79 | worker.onmessage({ data: message }); 80 | } 81 | }, 0); 82 | }, 83 | 84 | // discard tasks and prevent further task queueing 85 | close: function() { 86 | worker.terminate(); 87 | }, 88 | 89 | importScripts: function(urls) { 90 | var i; 91 | for (i = 0; i < arguments.length; i += 1) { 92 | __loadScript(src, function (script) { 93 | eval(script); 94 | }, function (error) { 95 | throw Error(error); 96 | }); 97 | } 98 | } 99 | }; 100 | 101 | workerContext.self = workerContext; 102 | 103 | __loadScript(src, function (script) { 104 | with (workerContext) { 105 | eval("(function(){" + script + "}).call(workerContext)"); 106 | } 107 | }); 108 | }; 109 | 110 | return Worker; 111 | 112 | } ()); 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-polyfills", 3 | "author": "Joshua Bell ", 4 | "version": "0.1.43", 5 | "description": "Collection of Web polyfills.", 6 | "license": "(Unlicense OR MIT)", 7 | "main": "polyfill.js", 8 | "files": [ 9 | "README.md", 10 | "LICENSE.md", 11 | "polyfill.js", 12 | "polyfill.min.js", 13 | "web.js", 14 | "web.min.js", 15 | "es5.md", 16 | "es5.js", 17 | "es6.md", 18 | "es6.js", 19 | "es2016.md", 20 | "es2016.js", 21 | "es2017.md", 22 | "es2017.js", 23 | "html.js", 24 | "dom.js", 25 | "xhr.js", 26 | "url.js", 27 | "fetch.js", 28 | "cssom.js", 29 | "timing.js", 30 | "keyboard.md", 31 | "keyboard.js", 32 | "typedarray.js" 33 | ], 34 | "repository": { 35 | "type": "git", 36 | "url": "https://github.com/inexorabletash/polyfill.git" 37 | }, 38 | "keywords": [ 39 | "polyfill", 40 | "standards" 41 | ], 42 | "bugs": { 43 | "url": "https://github.com/inexorabletash/polyfill/issues" 44 | }, 45 | "homepage": "https://github.com/inexorabletash/polyfill" 46 | } 47 | -------------------------------------------------------------------------------- /polyfill.js.md: -------------------------------------------------------------------------------- 1 | Polyfill Bundle 2 | --------------- 3 | [script](polyfill.js) 4 | 5 | Since many of the Web standards polyfills require ES5 or ES2015 support, 6 | a bundle is provided that includes nearly everything. 7 | 8 | polyfill.js is a concatenation of the following files: 9 | 10 | es5.js - ECMAScript 5 polyfills 11 | es6.js - ECMAScript 2015 polyfills 12 | es2016.js - ECMAScript 2016 polyfills 13 | es2017.js - ECMAScript 2017 polyfills 14 | web.js (which is itself a concatenation of files) 15 | 16 | polyfill.min.js is a minimized version, c/o http://javascript-minifier.com 17 | -------------------------------------------------------------------------------- /tests/dom.html: -------------------------------------------------------------------------------- 1 | 2 | DOM Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | test markup, will be hidden 12 | 13 |
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 |
34 | 1 35 | a b 36 | 2 37 | c d 38 | 3 39 |
40 | 41 |
42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /tests/es2016.html: -------------------------------------------------------------------------------- 1 | 2 | ECMAScript 2016 Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/es2016.js: -------------------------------------------------------------------------------- 1 | /*global QUnit*/ 2 | QUnit.test('Array.prototype.includes', function(assert) { 3 | assert.ok('includes' in Array.prototype); 4 | assert.equal(typeof Array.prototype.includes, 'function'); 5 | assert.ok([1,2,3].includes(1)); 6 | assert.notOk([1,2,3].includes(0)); 7 | assert.ok([1,NaN,3].includes(NaN)); 8 | assert.notOk([1,2,3].includes(NaN)); 9 | assert.ok([1,-0,3].includes(-0)); 10 | assert.ok([1,-0,3].includes(0)); 11 | assert.notOk([1,[],3].includes([])); 12 | assert.notOk([1,{},3].includes({})); 13 | assert.notOk([1,2,3].includes(Math)); 14 | assert.ok([1,Math,3].includes(Math)); 15 | assert.notOk([1,2,3].includes(undefined)); 16 | assert.ok([1,undefined,3].includes(undefined)); 17 | assert.notOk([1,2,3].includes(null)); 18 | assert.ok([1,null,3].includes(null)); 19 | 20 | assert.ok([1,2,3].includes(3, 2)); 21 | assert.notOk([1,2,3].includes(2, 2)); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/es2017.html: -------------------------------------------------------------------------------- 1 | 2 | ECMAScript 2017 Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/es2017.js: -------------------------------------------------------------------------------- 1 | /*global QUnit,global*/ 2 | 3 | QUnit.module("Object Objects"); 4 | 5 | QUnit.test("Object.values/entries", function(assert) { 6 | var d = Object.create(null); 7 | d['a'] = 1; 8 | d['b'] = 2; 9 | 10 | assert.ok(Array.isArray(Object.keys(d))); 11 | assert.ok(Array.isArray(Object.values(d))); 12 | assert.ok(Array.isArray(Object.entries(d))); 13 | 14 | assert.deepEqual(Object.keys(d), ['a', 'b']); 15 | assert.deepEqual(Object.values(d), [1, 2]); 16 | assert.deepEqual(Object.entries(d), [['a', 1], ['b', 2]]); 17 | 18 | assert.deepEqual(Object.values({a: 1, get b(){delete this.c; return 2;}, c: 3}), [1,2]); 19 | assert.deepEqual(Object.entries({a: 1, get b(){delete this.c; return 2;}, c: 3}), [['a',1],['b',2]]); 20 | }); 21 | 22 | QUnit.test("Object.getOwnPropertyDescriptors", function(assert) { 23 | var d = Object.create(null); 24 | d['a'] = 1; 25 | d['b'] = 2; 26 | 27 | assert.deepEqual(Object.keys(d), ['a', 'b']); 28 | assert.deepEqual(Object.values(d), [1, 2]); 29 | assert.deepEqual(Object.entries(d), [['a', 1], ['b', 2]]); 30 | 31 | assert.deepEqual(Object.getOwnPropertyDescriptors(d), 32 | {a: {configurable: true, 33 | enumerable: true, 34 | writable: true, 35 | value: 1 36 | }, 37 | b: {configurable: true, 38 | enumerable: true, 39 | writable: true, 40 | value: 2 41 | } 42 | }); 43 | }); 44 | 45 | QUnit.module("String Objects"); 46 | 47 | QUnit.test("String padEnd", function(assert) { 48 | assert.equal('a'.padEnd(), 'a'); 49 | assert.equal('a'.padEnd(1), 'a'); 50 | assert.equal('a'.padEnd(2), 'a '); 51 | assert.equal('a'.padEnd(2, '_'), 'a_'); 52 | assert.equal('a'.padEnd(3, '__'), 'a__'); 53 | assert.equal('a'.padEnd(2, '[]'), 'a['); 54 | }); 55 | 56 | QUnit.test("String padStart", function(assert) { 57 | assert.equal('a'.padEnd(), 'a'); 58 | assert.equal('a'.padEnd(1), 'a'); 59 | assert.equal('a'.padEnd(2), 'a '); 60 | assert.equal('a'.padEnd(2, '_'), 'a_'); 61 | assert.equal('a'.padEnd(3, '__'), 'a__'); 62 | assert.equal('a'.padEnd(2, '[]'), 'a['); 63 | }); 64 | -------------------------------------------------------------------------------- /tests/es5.html: -------------------------------------------------------------------------------- 1 | 2 | ECMAScript 5 Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/es5.js: -------------------------------------------------------------------------------- 1 | /*global QUnit*/ 2 | 3 | QUnit.test("Object constructor properties", function(assert) { 4 | assert.equal(Object.getPrototypeOf([]), Array.prototype); 5 | assert.equal(Object.getPrototypeOf({}), Object.prototype); 6 | 7 | var p = { a: 1, b: 2 }; 8 | var C = function() { this.c = 3; }; 9 | C.prototype = p; 10 | 11 | // Not supported by shim, since constructor is mutated 12 | // by prototype assignment. 13 | //assert.equal(Object.getPrototypeOf(new C), p); 14 | assert.equal(Object.getPrototypeOf(Object.create(p)), p); 15 | 16 | assert.equal(String(Object.getOwnPropertyNames(new C)), 'c'); 17 | 18 | var o = Object.create(p, {d: {value: 4}, e: {value: 5}}); 19 | assert.equal(o.a, 1); 20 | assert.equal(o.b, 2); 21 | assert.equal(o.d, 4); 22 | assert.equal(o.e, 5); 23 | 24 | Object.defineProperty(o, "c", {value: 3}); 25 | assert.equal(o.c, 3); 26 | 27 | Object.defineProperties(o, {f: {value: 6}, g: {value: 7}}); 28 | assert.equal(o.f, 6); 29 | assert.equal(o.g, 7); 30 | 31 | assert.equal(String(Object.keys({a: 1, b: 2, c: 3})), 'a,b,c'); 32 | }); 33 | 34 | QUnit.test("Function prototype properties", function(assert) { 35 | assert.equal(((function(){ return this.foo; }).bind({foo:123}))(), 123); 36 | 37 | (function() { 38 | // non-strict 39 | function that(o) { return this; } 40 | assert.equal(that.bind(123)(), 123); 41 | assert.equal(that.bind(0)(), 0); 42 | assert.equal(that.bind(false)(), false); 43 | assert.equal(that.bind(null)(), self); 44 | assert.equal(that.bind(undefined)(), self); 45 | }); 46 | (function() { 47 | 'use strict'; 48 | function that(o) { return this; } 49 | assert.equal(that.bind(123)(), 123); 50 | assert.equal(that.bind(0)(), 0); 51 | assert.equal(that.bind(false)(), false); 52 | assert.equal(that.bind(null)(), null); 53 | assert.equal(that.bind(undefined)(), undefined); 54 | }()); 55 | 56 | assert.equal(Function.prototype.bind()(), undefined); 57 | }); 58 | 59 | QUnit.test("Array constructor properties", function(assert) { 60 | assert.ok(Array.isArray([])); 61 | assert.ok(Array.isArray(new Array)); 62 | assert.notOk(Array.isArray({length:0})); 63 | }); 64 | 65 | QUnit.test("Array prototype properties", function(assert) { 66 | assert.equal([1,2,3,2,3].indexOf(3), 2); 67 | assert.equal([1,2,3,2,3].lastIndexOf(3), 4); 68 | assert.ok([0,2,4,6,8].every(function(x){return !(x % 2);})); 69 | assert.notOk([0,2,4,5,6].every(function(x){return !(x % 2);})); 70 | assert.ok([1,2,3,4,5].some(function(x){return x > 3;})); 71 | assert.notOk([1,2,3,4,5].some(function(x){return x > 8;})); 72 | assert.equal((function(){var t = 0; [1,2,3,4,5,6,7,8,9,10].forEach(function(x){t += x;}); return t;}()), 55); 73 | assert.equal(String([1,2,3,4,5].map(function(x){return x * x;})), '1,4,9,16,25'); 74 | assert.equal(String([1,2,3,4,5].filter(function(x){return x % 2;})), '1,3,5'); 75 | assert.equal([1,2,3,4,5,6,7,8,9,10].reduce(function(a,b){return a-b;}), -53); 76 | assert.equal([1,2,3,4,5,6,7,8,9,10].reduceRight(function(a,b){return a-b;}), -35); 77 | }); 78 | 79 | QUnit.test("String prototype properties", function(assert) { 80 | assert.equal(''.trim(), ''); 81 | assert.equal(' '.trim(), ''); 82 | assert.equal('abc'.trim(), 'abc'); 83 | assert.equal(' abc '.trim(), 'abc'); 84 | assert.equal(' \t\n\rabc\t\n\r'.trim(), 'abc'); 85 | assert.equal(' a b c '.trim(), 'a b c'); 86 | }); 87 | 88 | QUnit.test("Date constructor properties", function(assert) { 89 | assert.ok(Math.abs(Date.now() - Number(new Date())) < 100); 90 | }); 91 | 92 | QUnit.test("Date prototype properties", function(assert) { 93 | // milliseconds are optional, so verify with a regexp 94 | assert.ok(/1970-01-01T00:00:00(\.000)?Z/.test(new Date(0).toISOString())); 95 | assert.ok(/2001-09-09T01:46:40(\.000)?Z/.test(new Date(1e12).toISOString())); 96 | }); 97 | -------------------------------------------------------------------------------- /tests/es6.html: -------------------------------------------------------------------------------- 1 | 2 | ECMAScript 2015 Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |

10 | Native implementations of the following functions exist, so the 11 | test results do not reflect the polyfill: 12 |
13 | 14 |

15 | 16 |

17 | The following lack native implementations, so the polyfills are tested: 18 |
19 | 20 |

21 |
22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/fetch.html: -------------------------------------------------------------------------------- 1 |  2 | Fetch Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/fetch.js: -------------------------------------------------------------------------------- 1 | /*global QUnit, Request, Response, Headers, fetch*/ 2 | 3 | function promiseTest(name, func) { 4 | QUnit.test(name, function(assert) { 5 | var done = assert.async(); 6 | new Promise(function(resolve, reject) { resolve(func(assert)); }) 7 | .catch(function(error) { assert.ok(false, 'Unexpected rejection: ' + error); }) 8 | .then(function() { done(); }); 9 | }); 10 | } 11 | 12 | function rejectingPromiseTest(name, func, assert_func) { 13 | QUnit.test(name, function(assert) { 14 | var done = assert.async(); 15 | new Promise(function(resolve, reject) { resolve(func(assert)); }) 16 | .then(function(v) { assert.ok(false, 'Unexpected fulfill: ' + v); }, 17 | function(r) { assert_func(assert, r); }) 18 | .then(function() { done(); }); 19 | }); 20 | } 21 | 22 | function blobAsText(blob, encoding) { 23 | encoding = encoding || 'utf-8'; 24 | return new Promise(function(resolve, reject) { 25 | var reader = new FileReader(); 26 | reader.readAsText(blob, encoding); 27 | reader.onload = function(e) { resolve(reader.result); }; 28 | reader.onerror = function(e) { reject(reader.error); }; 29 | }); 30 | } 31 | 32 | // Tests 33 | 34 | promiseTest('basic fetch', function(assert) { 35 | return fetch('sample.txt') 36 | .then(function(response) { 37 | assert.equal(response.status, 200, 'Response status should be 200'); 38 | assert.equal(response.ok, true, 'Response should be ok'); 39 | return response.text(); 40 | }) 41 | .then(function(text) { 42 | assert.equal(text, 'Hello, world!\n', 'Fetch should retrieve sample text'); 43 | }); 44 | }); 45 | 46 | promiseTest('basic failed fetch', function(assert) { 47 | return fetch('no-such-resource') 48 | .then(function(response) { 49 | assert.equal(response.status, 404, 'Response status should be 404'); 50 | assert.equal(response.ok, false, 'Response should be not ok'); 51 | assert.equal(response.statusText, 'Not Found', 'Response status should be "Not Found"'); 52 | }); 53 | }); 54 | 55 | promiseTest('CORS-denied fetch', function(assert) { 56 | return fetch('http://example.com') 57 | .then(function(response) { 58 | assert.ok(false, 'Cross-origin fetch should have failed'); 59 | }, function(error) { 60 | assert.equal(error.name, 'TypeError'); 61 | }); 62 | }); 63 | 64 | promiseTest('CORS-accepted fetch (via httpbin.org)', function(assert) { 65 | return fetch('//httpbin.org/get?key=value') 66 | .then(function(response) { 67 | return response.json(); 68 | }) 69 | .then(function(json) { 70 | assert.deepEqual(json.args, {'key': 'value'}); 71 | }); 72 | }); 73 | 74 | promiseTest('Response.text()', function(assert) { 75 | return fetch('sample.json') 76 | .then(function(response) { 77 | return response.text(); 78 | }) 79 | .then(function(text) { 80 | assert.equal(text, '{"key": "value"}\n', 'text() should produce string'); 81 | }); 82 | }); 83 | 84 | promiseTest('Response.json()', function(assert) { 85 | return fetch('sample.json') 86 | .then(function(response) { 87 | return response.json(); 88 | }) 89 | .then(function(json) { 90 | assert.deepEqual(json, {key: 'value'}, 'json() should parse JSON data file'); 91 | }); 92 | }); 93 | 94 | promiseTest('Response.arrayBuffer()', function(assert) { 95 | return fetch('sample.json') 96 | .then(function(response) { 97 | return response.arrayBuffer(); 98 | }) 99 | .then(function(buffer) { 100 | assert.deepEqual([].slice.call(new Uint8Array(buffer)), 101 | [123, 34, 107, 101, 121, 34, 58, 32, 34, 118, 97, 108, 117, 101, 34, 125, 10], 102 | 'arrayBuffer() should return buffer with expected octets'); 103 | }); 104 | }); 105 | 106 | promiseTest('Response.blob()', function(assert) { 107 | return fetch('sample.json') 108 | .then(function(response) { 109 | return response.blob(); 110 | }) 111 | .then(function(blob) { 112 | assert.equal(blob.size, 17, 'blob() should yield appropriately sized Blob'); 113 | return blobAsText(blob); 114 | }) 115 | .then(function(text) { 116 | assert.equal(text, '{"key": "value"}\n', 'blob() should decode to expected text'); 117 | }); 118 | }); 119 | 120 | rejectingPromiseTest('Response.bodyUsed flag', function(assert) { 121 | return fetch('sample.json') 122 | .then(function(response) { 123 | assert.equal(response.bodyUsed, false, 'bodyUsed flag is not set'); 124 | response.body.text(); 125 | assert.equal(response.bodyUsed, true, 'bodyUsed flag is not set'); 126 | return response.body.text(); 127 | }); 128 | }, function(assert, error) { 129 | assert.equal(error.name, 'TypeError', 130 | 'FetchBodyStream.asXXX throws once read flag is set'); 131 | }); 132 | 133 | QUnit.test('Request constructor - ScalarValueString', function(assert) { 134 | var r = new Request('http://example.com'); 135 | assert.equal(r.method, 'GET', 'Default method is GET'); 136 | assert.equal(r.url, 'http://example.com/', 'url property is normalized'); 137 | assert.ok(r.headers instanceof Headers, 'headers property exists'); 138 | }); 139 | 140 | QUnit.test('Request constructor - Request', function(assert) { 141 | var o = new Request('http://example.com', { 142 | method: 'POST', 143 | headers: new Headers({A: 1}) 144 | }); 145 | var r = new Request(o); 146 | assert.equal(r.method, 'POST', 'Method copied'); 147 | assert.equal(r.url, 'http://example.com/', 'URL copied'); 148 | assert.equal(r.headers.get('A'), '1'); 149 | 150 | o = new Request('http://example.com', { 151 | method: 'POST', 152 | headers: new Headers({A: 1}) 153 | }); 154 | r = new Request(o, {headers: new Headers({B: 2})}); 155 | assert.equal(r.method, 'POST', 'Method copied'); 156 | assert.equal(r.url, 'http://example.com/', 'URL copied'); 157 | assert.equal(r.headers.get('A'), null); 158 | assert.equal(r.headers.get('B'), '2'); 159 | }); 160 | 161 | QUnit.test('Response constructor', function(assert) { 162 | assert.equal(new Response().status, 200); 163 | assert.equal(new Response().statusText, 'OK'); 164 | assert.equal(new Response().ok, true); 165 | assert.equal(new Response('', {status: 234}).status, 234); 166 | assert.equal(new Response('', {status: 234}).ok, true); 167 | assert.equal(new Response('', {status: 345}).ok, false); 168 | assert.equal(new Response('', {statusText: 'nope'}).statusText, 'nope'); 169 | assert.throws(function() { new Response('', {status: 0}); }); 170 | assert.throws(function() { new Response('', {status: 600}); }); 171 | assert.throws(function() { new Response('', {statusText: 'bogus \u0100'}); }); 172 | }); 173 | 174 | promiseTest('Synthetic Response.text()', function(assert) { 175 | return new Response('sample body').text() 176 | .then(function(text) { 177 | assert.equal(text, 'sample body'); 178 | }); 179 | }); 180 | 181 | 182 | promiseTest('FormData POST (via httpbin.org)', function(assert) { 183 | var fd = new FormData(); 184 | fd.append('a', '1'); 185 | fd.append('b', '2'); 186 | return fetch('//httpbin.org/post', { 187 | method: 'POST', 188 | body: fd 189 | }) 190 | .then(function(response) { 191 | window.r = response; 192 | return response.json(); 193 | }) 194 | .then(function(json) { 195 | assert.deepEqual(json.form, {a: '1', b: '2'}, 'FormData key/value pairs should be sent'); 196 | }); 197 | }); 198 | 199 | QUnit.test('Invalid request header', function(assert) { 200 | var request = new Request('http://example.com'); 201 | var headers = request.headers; 202 | headers.append('Cookie', 'abc'); 203 | assert.equal(headers.get('Cookie'), null, 'Forbidden header should not be set, yielding null'); 204 | }); 205 | 206 | QUnit.test('Method normalization', function(assert) { 207 | assert.equal(new Request('http://example.com', {method: 'get'}).method, 'GET', 208 | 'Standard method should be normalized to upper case'); 209 | assert.equal(new Request('http://example.com', {method: 'nonstandard'}).method, 'nonstandard', 210 | 'Nonstandard method should be normalized to upper case'); 211 | }); 212 | 213 | rejectingPromiseTest('Bad protocol', function(assert) { 214 | return fetch('no-such-protocol://invalid'); 215 | }, function(assert, error) { 216 | assert.equal(error.name, 'TypeError', 'Network error appears as TypeError'); 217 | }); 218 | 219 | QUnit.test('Regression test: Header name validity', function(assert) { 220 | var headers = new Headers({P3P: 1}); 221 | assert.equal(headers.get('P3P'), '1', '0-9 are valid header characters'); 222 | }); 223 | -------------------------------------------------------------------------------- /tests/html.html: -------------------------------------------------------------------------------- 1 | 2 | HTML Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | test markup, will be hidden 12 | 13 |
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/html.js: -------------------------------------------------------------------------------- 1 | /*global QUnit*/ 2 | 3 | QUnit.test("Parser Shiv", function(assert) { 4 | var div = document.getElementById('sectiondiv'); 5 | var section = document.getElementById('section'); 6 | var span = document.getElementById('sectionspan'); 7 | assert.equal(div, section.parentNode, 'div should be parent of section'); 8 | assert.equal(section, span.parentNode, 'section should be parent of span'); 9 | }); 10 | 11 | QUnit.test("document.head", function(assert) { 12 | assert.expect(2); 13 | 14 | // document.head 15 | assert.notEqual(document.head, null); 16 | assert.equal(document.head.tagName, "HEAD"); 17 | }); 18 | 19 | QUnit.test("base64 encoding", function(assert) { 20 | assert.expect(4); 21 | 22 | // window.atob() / window.btoa() 23 | assert.equal(window.btoa(''), ''); 24 | assert.equal(window.atob(''), ''); 25 | assert.equal(window.btoa('\x00\x01\x02\xfd\xfe\xff'), 'AAEC/f7/'); 26 | assert.equal(window.atob('AAEC/f7/'), '\x00\x01\x02\xfd\xfe\xff'); 27 | }); 28 | 29 | QUnit.test("Element.dataset and data-* attributes", function(assert) { 30 | 31 | // dataset 32 | if ('dataset' in document.getElementById('datadiv')) { 33 | assert.expect(6); 34 | assert.equal(document.getElementById('datadiv').dataset.foo, "bar"); 35 | assert.equal(document.getElementById('datadiv').dataset.bar, "123"); 36 | assert.equal(document.getElementById('datadiv').dataset.aaBbCc, "abc"); 37 | assert.equal(document.getElementById('dataspan').dataset['123'], "blah"); // Broken in Chrome 23! 38 | assert.notOk(document.getElementById('dataspan').dataset['abc']); 39 | 40 | document.getElementById('datadiv').dataset.foo = "new"; 41 | assert.equal(document.getElementById('datadiv').dataset.foo, "new"); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /tests/js.html: -------------------------------------------------------------------------------- 1 | 2 | JavaScript 1.x Extras Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/js.js: -------------------------------------------------------------------------------- 1 | /*global QUnit*/ 2 | QUnit.test("Non-Standard JavaScript 1.x Extras", function(assert) { 3 | 4 | // String.prototype.trimLeft() 5 | assert.equal(''.trimLeft(), ''); 6 | assert.equal(' '.trimLeft(), ''); 7 | assert.equal('abc'.trimLeft(), 'abc'); 8 | assert.equal(' abc'.trimLeft(), 'abc'); 9 | assert.equal(' \t\n\rabc'.trimLeft(), 'abc'); 10 | assert.equal(' a b c '.trimLeft(), 'a b c '); 11 | 12 | // String.prototype.trimRight() 13 | assert.equal(''.trimRight(), ''); 14 | assert.equal(' '.trimRight(), ''); 15 | assert.equal('abc'.trimRight(), 'abc'); 16 | assert.equal('abc '.trimRight(), 'abc'); 17 | assert.equal('abc \t\n\r'.trimRight(), 'abc'); 18 | assert.equal(' a b c '.trimRight(), ' a b c'); 19 | 20 | // String.prototype.quote() 21 | assert.equal(''.quote(), "\"\""); 22 | assert.equal('abc'.quote(), "\"abc\""); 23 | assert.equal('"'.quote(), '"\\""'); 24 | assert.equal('\b\r\n\t\f'.quote(), "\"\\b\\r\\n\\t\\f\""); 25 | //assert.equal('\u0000'.quote(), "\"\\x00\""); // Older FF produces \0 26 | assert.equal('\u001f'.quote(), "\"\\x1F\""); 27 | assert.equal('\u0020'.quote(), "\" \""); 28 | assert.equal('\u007f'.quote(), "\"\\x7F\""); 29 | assert.equal('\u0080'.quote(), "\"\\x80\""); 30 | assert.equal('\u00ff'.quote(), "\"\\xFF\""); 31 | assert.equal('\u0100'.quote(), "\"\\u0100\""); 32 | assert.equal('\u0fff'.quote(), "\"\\u0FFF\""); 33 | assert.equal('\u1000'.quote(), "\"\\u1000\""); 34 | assert.equal('\u1234'.quote(), "\"\\u1234\""); 35 | assert.equal('\ucdef'.quote(), "\"\\uCDEF\""); 36 | assert.equal('\uffff'.quote(), "\"\\uFFFF\""); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/sample.json: -------------------------------------------------------------------------------- 1 | {"key": "value"} 2 | -------------------------------------------------------------------------------- /tests/sample.txt: -------------------------------------------------------------------------------- 1 | Hello, world! 2 | -------------------------------------------------------------------------------- /tests/testhelpers.js: -------------------------------------------------------------------------------- 1 | 2 | function assertTrue(_expr) { 3 | var _x_; 4 | try { eval("_x_ = (" + _expr + ")"); } catch(e) { ok(false, _expr + " threw exception: " + e); return; } 5 | ok(_x_, String(_expr) + " was: " + String(_x_) + ", expected true"); 6 | } 7 | 8 | function assertFalse(_expr) { 9 | var _x_; 10 | try { eval("_x_ = (" + _expr + ")"); } catch(e) { ok(false, _expr + " threw exception: " + e); return; } 11 | ok(!_x_, String(_expr) + " was: " + String(_x_) + ", expected false"); 12 | } 13 | 14 | function assertEqual(_expr, _value) { 15 | function _s(x) { 16 | return (1/Number(x) === -Infinity) ? "-0" : (1/Number(x) === Infinity) ? "+0" : String(x); 17 | } 18 | 19 | var _x; 20 | try { eval("_x = (" + _expr + ")"); } catch(e) { ok(false, _expr + " threw exception: " + e); return; } 21 | if (_value instanceof RegExp) { 22 | ok(_value.test(_x), _s(_expr) + " was: " + _s(_x) + ", expected to match: " + _s(_value)); 23 | } else if (_value !== _value) { 24 | ok(_x !== _x, _s(_expr) + " was: " + _s(_x) + ", expected NaN"); 25 | } else if (_value === 0) { 26 | ok(1/_x === 1/_value, _s(_expr) + " was: " + _s(_x) + ", expected " + _s(_value)); 27 | } else { 28 | strictEqual(_x, _value, _s(_expr) + " was: " + _s(_x) + ", expected " + _s(_value)); 29 | } 30 | } 31 | 32 | function assertThrows(_expr, expected) { 33 | raises(function() { eval("(" + _expr + ")"); }, expected, String(_expr) + " expected to throw"); 34 | } 35 | 36 | function assertEpsilon(_expr, _value, _epsilon) { 37 | var _x; 38 | try { eval("_x = (" + _expr + ")"); } catch(e) { ok(false, _expr + " threw exception: " + e); return; } 39 | var p; 40 | if (_x === _value) { 41 | p = true; 42 | } else { 43 | p = (Math.abs((_x - _value)/Math.max(Math.abs(_x), Math.abs(_value))) < _epsilon); 44 | } 45 | 46 | ok(p, _expr + " was " + _x + ", expected approximately " + _value); 47 | } 48 | 49 | // Like QUnit's strictEqual, but NaN and -0 savvy 50 | function stricterEqual(actual, expected, message) { 51 | 52 | function isPositiveZero(x) { return x === 0 && 1 / x === +Infinity; } 53 | function isNegativeZero(x) { return x === 0 && 1 / x === -Infinity; } 54 | function isReallyNaN(x) { return typeof x === 'number' && x !== x; } 55 | function str(x) { return isNegativeZero(x) ? "-0" : String(x); } 56 | 57 | if (isReallyNaN(expected)) { 58 | message = arguments.length > 2 ? message : "Expected " + str(expected) + " , saw: " + str(actual); 59 | ok(isReallyNaN(actual), message); 60 | } else if (expected === 0) { 61 | message = arguments.length > 2 ? message : "Expected " + str(expected) + " , saw: " + str(actual); 62 | ok(isPositiveZero(actual) === isPositiveZero(expected), message); 63 | } else { 64 | strictEqual(actual, expected, message); 65 | } 66 | } 67 | 68 | // Compare Typed Array with JavaScript array 69 | function arrayEqual(typed_array, test) { 70 | var array = [], i, length = typed_array.length; 71 | for (i = 0; i < length; i += 1) { 72 | array[i] = typed_array.get(i); // See shim below 73 | } 74 | deepEqual(array, test, JSON.stringify(array) + " == " + JSON.stringify(test) + " ?"); 75 | } 76 | 77 | var array_types = ['Int8Array', 'Uint8Array', 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array']; 78 | array_types.forEach(function(typeName) { 79 | if (typeName in self) { 80 | var type = self[typeName]; 81 | // Add a TypedArray.get(index) accessor if not present, for 82 | // testing native implementations. 83 | if (typeof type.prototype.get !== 'function') { 84 | type.prototype.get = function(idx) { 85 | return this[idx]; 86 | }; 87 | } 88 | // Shim to work with older impls that use "slice" instead of "subarray" 89 | if (typeof type.prototype.subarray !== 'function') { 90 | type.prototype.subarray = type.prototype.slice; 91 | } 92 | } 93 | }); 94 | -------------------------------------------------------------------------------- /tests/typedarray.html: -------------------------------------------------------------------------------- 1 | 2 | Typed Array Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/url.html: -------------------------------------------------------------------------------- 1 | 2 | URL Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/url.js: -------------------------------------------------------------------------------- 1 | /*global QUnit, URLSearchParams*/ 2 | function toArray(it) { 3 | var a = []; 4 | for (var cur = it.next(); !cur.done; cur = it.next()) { 5 | a.push(cur.value); 6 | } 7 | return a; 8 | } 9 | 10 | 11 | QUnit.test('URL IDL', function(assert) { 12 | var url = new URL('http://example.com:8080/foo/bar?a=1&b=2#p1'); 13 | assert.equal(typeof url.protocol, 'string', 'protocol'); 14 | assert.equal(typeof url.host, 'string', 'host'); 15 | assert.equal(typeof url.hostname, 'string', 'hostname'); 16 | assert.equal(typeof url.port, 'string', 'port'); 17 | assert.equal(typeof url.pathname, 'string', 'pathname'); 18 | assert.equal(typeof url.search, 'string', 'search'); 19 | assert.equal(typeof url.hash, 'string', 'hash'); 20 | assert.equal(typeof url.origin, 'string', 'origin'); 21 | assert.equal(typeof url.href, 'string', 'href'); 22 | }); 23 | 24 | QUnit.test('URL Stringifying', function(assert) { 25 | assert.equal(String(new URL('http://example.com')), 'http://example.com/'); 26 | assert.equal(String(new URL('http://example.com:8080')), 'http://example.com:8080/'); 27 | }); 28 | 29 | QUnit.test('URL Parsing', function(assert) { 30 | var url = new URL('http://example.com:8080/foo/bar?a=1&b=2#p1'); 31 | assert.equal(url.protocol, 'http:'); 32 | assert.equal(url.hostname, 'example.com'); 33 | assert.equal(url.port, '8080'); 34 | assert.equal(url.host, 'example.com:8080'); 35 | assert.equal(url.pathname, '/foo/bar'); 36 | assert.equal(url.search, '?a=1&b=2'); 37 | assert.equal(url.hash, '#p1'); 38 | assert.equal(url.origin, 'http://example.com:8080'); 39 | assert.equal(url.href, 'http://example.com:8080/foo/bar?a=1&b=2#p1'); 40 | }); 41 | 42 | QUnit.test('URL Mutation', function(assert) { 43 | var url = new URL('http://example.com'); 44 | assert.equal(url.href, 'http://example.com/'); 45 | assert.equal(url.origin, 'http://example.com'); 46 | assert.equal(url.host, 'example.com'); 47 | 48 | url.protocol = 'ftp'; 49 | assert.equal(url.protocol, 'ftp:'); 50 | assert.equal(url.href, 'ftp://example.com/'); 51 | assert.equal(url.origin, 'ftp://example.com'); 52 | assert.equal(url.host, 'example.com'); 53 | url.protocol = 'http'; 54 | assert.equal(url.protocol, 'http:'); 55 | assert.equal(url.href, 'http://example.com/'); 56 | assert.equal(url.origin, 'http://example.com'); 57 | assert.equal(url.host, 'example.com'); 58 | 59 | url = new URL('http://example.com'); 60 | url.hostname = 'example.org'; 61 | assert.equal(url.href, 'http://example.org/'); 62 | assert.equal(url.origin, 'http://example.org'); 63 | assert.equal(url.host, 'example.org'); 64 | url.hostname = 'example.com'; 65 | assert.equal(url.href, 'http://example.com/'); 66 | assert.equal(url.origin, 'http://example.com'); 67 | assert.equal(url.host, 'example.com'); 68 | 69 | url = new URL('http://example.com'); 70 | url.port = 8080; 71 | assert.equal(url.href, 'http://example.com:8080/'); 72 | assert.equal(url.origin, 'http://example.com:8080'); 73 | assert.equal(url.host, 'example.com:8080'); 74 | url.port = 80; 75 | assert.equal(url.href, 'http://example.com/'); 76 | assert.equal(url.origin, 'http://example.com'); 77 | assert.equal(url.host, 'example.com'); 78 | 79 | url = new URL('http://example.com'); 80 | url.pathname = 'foo'; 81 | assert.equal(url.href, 'http://example.com/foo'); 82 | assert.equal(url.origin, 'http://example.com'); 83 | url.pathname = 'foo/bar'; 84 | assert.equal(url.href, 'http://example.com/foo/bar'); 85 | assert.equal(url.origin, 'http://example.com'); 86 | url.pathname = ''; 87 | assert.equal(url.href, 'http://example.com/'); 88 | assert.equal(url.origin, 'http://example.com'); 89 | 90 | url = new URL('http://example.com'); 91 | url.search = 'a=1&b=2'; 92 | assert.equal(url.href, 'http://example.com/?a=1&b=2'); 93 | assert.equal(url.origin, 'http://example.com'); 94 | url.search = ''; 95 | assert.equal(url.href, 'http://example.com/'); 96 | assert.equal(url.origin, 'http://example.com'); 97 | 98 | url = new URL('http://example.com'); 99 | url.hash = 'p1'; 100 | assert.equal(url.href, 'http://example.com/#p1'); 101 | assert.equal(url.origin, 'http://example.com'); 102 | url.hash = ''; 103 | assert.equal(url.href, 'http://example.com/'); 104 | assert.equal(url.origin, 'http://example.com'); 105 | }); 106 | 107 | QUnit.test('Parameter Mutation', function(assert) { 108 | var url = new URL('http://example.com'); 109 | assert.equal(url.href, 'http://example.com/'); 110 | assert.equal(url.search, ''); 111 | assert.equal(url.searchParams.get('a'), null); 112 | assert.equal(url.searchParams.get('b'), null); 113 | 114 | url.searchParams.append('a', '1'); 115 | assert.equal(url.searchParams.get('a'), '1'); 116 | assert.deepEqual(url.searchParams.getAll('a'), ['1']); 117 | assert.equal(url.search, '?a=1'); 118 | assert.equal(url.href, 'http://example.com/?a=1'); 119 | 120 | url.searchParams.append('b', '2'); 121 | assert.equal(url.searchParams.get('b'), '2'); 122 | assert.deepEqual(url.searchParams.getAll('b'), ['2']); 123 | assert.equal(url.search, '?a=1&b=2'); 124 | assert.equal(url.href, 'http://example.com/?a=1&b=2'); 125 | 126 | url.searchParams.append('a', '3'); 127 | assert.equal(url.searchParams.get('a'), '1'); 128 | assert.deepEqual(url.searchParams.getAll('a'), ['1', '3']); 129 | assert.equal(url.search, '?a=1&b=2&a=3'); 130 | assert.equal(url.href, 'http://example.com/?a=1&b=2&a=3'); 131 | 132 | url.searchParams['delete']('a'); 133 | assert.equal(url.search, '?b=2'); 134 | assert.deepEqual(url.searchParams.getAll('a'), []); 135 | assert.equal(url.href, 'http://example.com/?b=2'); 136 | 137 | url.searchParams['delete']('b'); 138 | assert.deepEqual(url.searchParams.getAll('b'), []); 139 | assert.equal(url.href, 'http://example.com/'); 140 | 141 | url.href = 'http://example.com?m=9&n=3'; 142 | assert.equal(url.searchParams.has('a'), false); 143 | assert.equal(url.searchParams.has('b'), false); 144 | assert.equal(url.searchParams.get('m'), 9); 145 | assert.equal(url.searchParams.get('n'), 3); 146 | 147 | url.href = 'http://example.com'; 148 | url.searchParams.set('a', '1'); 149 | assert.deepEqual(url.searchParams.getAll('a'), ['1']); 150 | url.search = 'a=1&b=1&b=2&c=1'; 151 | url.searchParams.set('b', '3'); 152 | assert.deepEqual(url.searchParams.getAll('b'), ['3']); 153 | assert.equal(url.href, 'http://example.com/?a=1&b=3&c=1'); 154 | }); 155 | 156 | QUnit.test('Parameter Encoding', function(assert) { 157 | assert.expect(7); 158 | 159 | var url = new URL('http://example.com'); 160 | assert.equal(url.href, 'http://example.com/'); 161 | assert.equal(url.search, ''); 162 | url.searchParams.append('this\x00&that\x7f\xff', '1+2=3'); 163 | assert.equal(url.searchParams.get('this\x00&that\x7f\xff'), '1+2=3'); 164 | assert.equal(url.search, '?this%00%26that%7F%C3%BF=1%2B2%3D3'); 165 | assert.equal(url.href, 'http://example.com/?this%00%26that%7F%C3%BF=1%2B2%3D3'); 166 | url.search = ''; 167 | url.searchParams.append('a b', 'a b'); 168 | assert.equal(url.search, '?a++b=a++b'); 169 | assert.equal(url.searchParams.get('a b'), 'a b'); 170 | }); 171 | 172 | 173 | QUnit.test('Base URL', function(assert) { 174 | assert.expect(20); 175 | // fully qualified URL 176 | assert.equal(new URL('http://example.com', 'https://example.org').href, 'http://example.com/'); 177 | assert.equal(new URL('http://example.com/foo/bar', 'https://example.org').href, 'http://example.com/foo/bar'); 178 | 179 | // protocol relative 180 | assert.equal(new URL('//example.com', 'https://example.org').href, 'https://example.com/'); 181 | 182 | // path relative 183 | assert.equal(new URL('/foo/bar', 'https://example.org').href, 'https://example.org/foo/bar'); 184 | assert.equal(new URL('/foo/bar', 'https://example.org/baz/bat').href, 'https://example.org/foo/bar'); 185 | assert.equal(new URL('./bar', 'https://example.org').href, 'https://example.org/bar'); 186 | assert.equal(new URL('./bar', 'https://example.org/foo/').href, 'https://example.org/foo/bar'); 187 | assert.equal(new URL('bar', 'https://example.org/foo/').href, 'https://example.org/foo/bar'); 188 | assert.equal(new URL('../bar', 'https://example.org/foo/').href, 'https://example.org/bar'); 189 | assert.equal(new URL('../bar', 'https://example.org/foo/').href, 'https://example.org/bar'); 190 | assert.equal(new URL('../../bar', 'https://example.org/foo/baz/bat/').href, 'https://example.org/foo/bar'); 191 | assert.equal(new URL('../../bar', 'https://example.org/foo/baz/bat').href, 'https://example.org/bar'); 192 | assert.equal(new URL('../../bar', 'https://example.org/foo/baz/').href, 'https://example.org/bar'); 193 | assert.equal(new URL('../../bar', 'https://example.org/foo/').href, 'https://example.org/bar'); 194 | assert.equal(new URL('../../bar', 'https://example.org/foo/').href, 'https://example.org/bar'); 195 | 196 | // search/hash relative 197 | assert.equal(new URL('bar?ab#cd', 'https://example.org/foo/').href, 'https://example.org/foo/bar?ab#cd'); 198 | assert.equal(new URL('bar?ab#cd', 'https://example.org/foo').href, 'https://example.org/bar?ab#cd'); 199 | assert.equal(new URL('?ab#cd', 'https://example.org/foo').href, 'https://example.org/foo?ab#cd'); 200 | assert.equal(new URL('?ab', 'https://example.org/foo').href, 'https://example.org/foo?ab'); 201 | assert.equal(new URL('#cd', 'https://example.org/foo').href, 'https://example.org/foo#cd'); 202 | }); 203 | 204 | QUnit.test('URLSearchParams', function(assert) { 205 | var url = new URL('http://example.com?a=1&b=2'); 206 | assert.equal(String(new URLSearchParams()), ''); 207 | assert.equal(String(new URLSearchParams('')), ''); 208 | assert.equal(String(new URLSearchParams('a=1')), 'a=1'); 209 | assert.equal(String(new URLSearchParams('a=1&b=1')), 'a=1&b=1'); 210 | assert.equal(String(new URLSearchParams('a=1&b&a')), 'a=1&b=&a='); 211 | 212 | assert.equal(String(new URLSearchParams('?')), ''); 213 | assert.equal(String(new URLSearchParams('?a=1')), 'a=1'); 214 | assert.equal(String(new URLSearchParams('?a=1&b=1')), 'a=1&b=1'); 215 | assert.equal(String(new URLSearchParams('?a=1&b&a')), 'a=1&b=&a='); 216 | 217 | assert.equal(String(new URLSearchParams([])), ''); 218 | assert.equal(String(new URLSearchParams([['a', 1]])), 'a=1'); 219 | assert.equal(String(new URLSearchParams([['a', 1], ['b', 2], ['b', 3]])), 'a=1&b=2&b=3'); 220 | assert.throws(function() { new URLSearchParams([['a', 1], ['b']]); }); 221 | 222 | assert.equal(String(new URLSearchParams({})), ''); 223 | assert.equal(String(new URLSearchParams({a: 1})), 'a=1'); 224 | assert.equal(String(new URLSearchParams({a: 1, b: 2})), 'a=1&b=2'); 225 | 226 | assert.equal(String(new URLSearchParams(new URLSearchParams('?'))), ''); 227 | assert.equal(String(new URLSearchParams(new URLSearchParams('?a=1'))), 'a=1'); 228 | assert.equal(String(new URLSearchParams(new URLSearchParams('?a=1&b=1'))), 'a=1&b=1'); 229 | assert.equal(String(new URLSearchParams(new URLSearchParams('?a=1&b&a'))), 'a=1&b=&a='); 230 | }); 231 | 232 | QUnit.test('URLSearchParams mutation', function(assert) { 233 | var p = new URLSearchParams(); 234 | assert.equal(p.get('a'), null); 235 | assert.equal(p.get('b'), null); 236 | 237 | p.append('a', '1'); 238 | assert.equal(p.get('a'), '1'); 239 | assert.deepEqual(p.getAll('a'), ['1']); 240 | assert.equal(String(p), 'a=1'); 241 | 242 | p.append('b', '2'); 243 | assert.equal(p.get('b'), '2'); 244 | assert.deepEqual(p.getAll('b'), ['2']); 245 | assert.equal(String(p), 'a=1&b=2'); 246 | 247 | p.append('a', '3'); 248 | assert.equal(p.get('a'), '1'); 249 | assert.deepEqual(p.getAll('a'), ['1', '3']); 250 | assert.equal(String(p), 'a=1&b=2&a=3'); 251 | 252 | p['delete']('a'); 253 | assert.equal(String(p), 'b=2'); 254 | assert.deepEqual(p.getAll('a'), []); 255 | 256 | p['delete']('b'); 257 | assert.deepEqual(p.getAll('b'), []); 258 | 259 | p = new URLSearchParams('m=9&n=3'); 260 | assert.equal(p.has('a'), false); 261 | assert.equal(p.has('b'), false); 262 | assert.equal(p.get('m'), 9); 263 | assert.equal(p.get('n'), 3); 264 | 265 | p = new URLSearchParams(); 266 | p.set('a', '1'); 267 | assert.deepEqual(p.getAll('a'), ['1']); 268 | p = new URLSearchParams('a=1&b=1&b=2&c=1'); 269 | p.set('b', '3'); 270 | assert.deepEqual(p.getAll('b'), ['3']); 271 | assert.equal(String(p), 'a=1&b=3&c=1'); 272 | 273 | // Ensure copy constructor copies by value, not reference. 274 | var sp1 = new URLSearchParams('a=1'); 275 | assert.equal(String(sp1), 'a=1'); 276 | var sp2 = new URLSearchParams(sp1); 277 | assert.equal(String(sp2), 'a=1'); 278 | sp1.append('b', '2'); 279 | sp2.append('c', '3'); 280 | assert.equal(String(sp1), 'a=1&b=2'); 281 | assert.equal(String(sp2), 'a=1&c=3'); 282 | 283 | var sp3 = new URLSearchParams('a=1'); 284 | assert.equal(String(sp3), 'a=1'); 285 | var sp4 = new URLSearchParams(sp3); 286 | assert.equal(String(sp4), 'a=1'); 287 | sp4.set('a', 2); 288 | assert.equal(sp3.get('a'), 1); 289 | assert.equal(String(sp3), 'a=1'); 290 | assert.equal(sp4.get('a'), 2); 291 | assert.equal(String(sp4), 'a=2'); 292 | }); 293 | 294 | QUnit.test('URLSearchParams serialization', function(assert) { 295 | var p = new URLSearchParams(); 296 | p.append('this\x00&that\x7f\xff', '1+2=3'); 297 | assert.equal(p.get('this\x00&that\x7f\xff'), '1+2=3'); 298 | assert.equal(String(p), 'this%00%26that%7F%C3%BF=1%2B2%3D3'); 299 | p = new URLSearchParams(); 300 | p.append('a b', 'a b'); 301 | assert.equal(String(p), 'a++b=a++b'); 302 | assert.equal(p.get('a b'), 'a b'); 303 | }); 304 | 305 | QUnit.test('URLSearchParams iterable methods', function(assert) { 306 | var params = new URLSearchParams('a=1&b=2'); 307 | assert.deepEqual(toArray(params.entries()), [['a', '1'], ['b', '2']]); 308 | assert.deepEqual(toArray(params.keys()), ['a', 'b']); 309 | assert.deepEqual(toArray(params.values()), ['1', '2']); 310 | 311 | var a = [], thisArg = {}; 312 | params.forEach(function(key, value) { 313 | assert.equal(this, thisArg); 314 | a.push([key, value]); 315 | }, thisArg); 316 | assert.deepEqual(a, [['1', 'a'], ['2', 'b']]); 317 | 318 | if ('Symbol' in self && 'iterator' in self.Symbol) { 319 | assert.deepEqual(toArray(params[Symbol.iterator]()), [['a', '1'], ['b', '2']]); 320 | 321 | ['entries', 'keys', 'values'].forEach(function(m) { 322 | assert.equal(typeof params[m]()[Symbol.iterator], 'function', 323 | 'Iterator has @@iterator method'); 324 | var instance = params[m](); 325 | assert.equal(instance, instance[Symbol.iterator](), '@@iterator returns self'); 326 | }); 327 | } 328 | }); 329 | 330 | QUnit.test('URL contains native static methods', function(assert) { 331 | assert.ok(typeof URL.createObjectURL == 'function'); 332 | assert.ok(typeof URL.revokeObjectURL == 'function'); 333 | }); 334 | 335 | QUnit.test('Regression tests', function(assert) { 336 | // IE mangles the pathname when assigning to search with 'about:' URLs 337 | var p = new URL('about:blank').searchParams; 338 | p.append('a', 1); 339 | p.append('b', 2); 340 | assert.equal(p.toString(), 'a=1&b=2'); 341 | }); 342 | -------------------------------------------------------------------------------- /tests/xhr.html: -------------------------------------------------------------------------------- 1 | 2 | XMLHttpRequest Polyfill Unit Tests 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/xhr.js: -------------------------------------------------------------------------------- 1 | /*global QUnit*/ 2 | 3 | QUnit.test("Constants", function(assert) { 4 | assert.expect(7); 5 | 6 | // XMLHttpRequest 7 | assert.notEqual(XMLHttpRequest, null); 8 | assert.notEqual(new XMLHttpRequest(), null); 9 | assert.equal(XMLHttpRequest.UNSENT, 0); 10 | assert.equal(XMLHttpRequest.OPENED, 1); 11 | assert.equal(XMLHttpRequest.HEADERS_RECEIVED, 2); 12 | assert.equal(XMLHttpRequest.LOADING, 3); 13 | assert.equal(XMLHttpRequest.DONE, 4); 14 | }); 15 | -------------------------------------------------------------------------------- /timing.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | if (!('window' in global && 'document' in global)) 3 | return; 4 | 5 | //---------------------------------------------------------------------- 6 | // 7 | // Efficient Script Yielding 8 | // http://w3c.github.io/setImmediate/ 9 | // (Not widely adopted.) 10 | // 11 | //---------------------------------------------------------------------- 12 | 13 | (function() { 14 | if ('setImmediate' in global) 15 | return; 16 | 17 | function setImmediate(callback/*, args*/) { 18 | var params = [].slice.call(arguments, 1); 19 | return global.setTimeout(function() { 20 | callback.apply(null, params); 21 | }, 0); 22 | } 23 | 24 | function clearImmediate(handle) { 25 | global.clearTimeout(handle); 26 | } 27 | 28 | global.setImmediate = setImmediate; 29 | global.clearImmediate = clearImmediate; 30 | }()); 31 | }(this)); 32 | -------------------------------------------------------------------------------- /web.js.md: -------------------------------------------------------------------------------- 1 | Web Standards Bundle 2 | -------------------- 3 | [script](web.js) 4 | 5 | Bundled together; nearly every page I create needs at least some of 6 | these. These will change over time, and going forward I will only 7 | target IE8 and later. (Since IE7 and earlier did not support modifying 8 | built-in object prototypes, helper functions used instead that can be 9 | used if IE7 compatibility is needed.) 10 | 11 | Many of these require ES5 or ES2015. See [polyfill.js](polyfill.js.md) 12 | 13 | This bundles shims for: 14 | 15 | * HTML [script](html.js) - [living standard](https://html.spec.whatwg.org) 16 | * DOM [script](dom.js) - [living standard](https://dom.spec.whatwg.org) 17 | * Fetch [script](fetch.js) - [living standard](https://fetch.spec.whatwg.org) 18 | * URL [script](url.js) - [living standard](https://url.spec.whatwg.org) 19 | * XMLHttpRequest [script](xhr.js) - [living standard](https://xhr.spec.whatwg.org) 20 | * CSSOM [script](cssom.js) - [spec](https://dev.w3.org/csswg/cssom-view/) 21 | * Timing [script](timing.js) 22 | 23 | web.min.js is a minimized version, c/o http://javascript-minifier.com 24 | -------------------------------------------------------------------------------- /xhr.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | if (!('window' in global && 'document' in global)) 5 | return; 6 | 7 | //---------------------------------------------------------------------- 8 | // 9 | // XMLHttpRequest 10 | // https://xhr.spec.whatwg.org 11 | // 12 | //---------------------------------------------------------------------- 13 | 14 | // XMLHttpRequest interface 15 | // Needed for: IE7- 16 | global.XMLHttpRequest = global.XMLHttpRequest || function() { 17 | try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (_) { } 18 | try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (_) { } 19 | try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (_) { } 20 | throw Error("This browser does not support XMLHttpRequest."); 21 | }; 22 | 23 | // XMLHttpRequest interface constants 24 | // Needed for IE8- 25 | [ 26 | ['UNSENT', 0], 27 | ['OPENED', 1], 28 | ['HEADERS_RECEIVED', 2], 29 | ['LOADING', 3], 30 | ['DONE', 4], 31 | ].forEach(function(p) { if (!(p[0] in global.XMLHttpRequest)) global.XMLHttpRequest[p[0]] = p[1]; }); 32 | 33 | // FormData interface 34 | // Needed for: IE9- 35 | (function() { 36 | if ('FormData' in global) 37 | return; 38 | 39 | function FormData(form) { 40 | this._data = []; 41 | if (!form) return; 42 | for (var i = 0; i < form.elements.length; ++i) { 43 | var element = form.elements[i]; 44 | if (element.name !== '') 45 | this.append(element.name, element.value); 46 | } 47 | } 48 | 49 | FormData.prototype = { 50 | append: function(name, value /*, filename */) { 51 | if ('Blob' in global && value instanceof global.Blob) 52 | throw TypeError("Blob not supported"); 53 | name = String(name); 54 | this._data.push([name, value]); 55 | }, 56 | 57 | toString: function() { 58 | return this._data.map(function(pair) { 59 | return encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]); 60 | }).join('&'); 61 | } 62 | }; 63 | 64 | global.FormData = FormData; 65 | var send = global.XMLHttpRequest.prototype.send; 66 | global.XMLHttpRequest.prototype.send = function(body) { 67 | if (body instanceof FormData) { 68 | this.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 69 | arguments[0] = body.toString(); 70 | } 71 | return send.apply(this, arguments); 72 | }; 73 | }()); 74 | 75 | }(self)); 76 | --------------------------------------------------------------------------------