├── .gitignore ├── LICENSE ├── README.md ├── docs ├── .gitkeep ├── assets │ ├── IBMPlexMono-Bold-SlashedZero.woff2 │ ├── IBMPlexMono-BoldItalic-SlashedZero.woff2 │ ├── IBMPlexMono-Italic-SlashedZero.woff2 │ ├── IBMPlexMono-Regular-SlashedZero.woff2 │ ├── IBMPlexSans-Bold-SlashedZero.woff2 │ ├── IBMPlexSans-BoldItalic-SlashedZero.woff2 │ ├── IBMPlexSans-Italic-SlashedZero.woff2 │ ├── IBMPlexSans-Regular-SlashedZero.woff2 │ ├── IBMPlexSerif-Bold-SlashedZero.woff2 │ ├── IBMPlexSerif-BoldItalic-SlashedZero.woff2 │ ├── IBMPlexSerif-Italic-SlashedZero.woff2 │ ├── IBMPlexSerif-Regular-SlashedZero.woff2 │ ├── ecma-header.png │ ├── ecmarkup.css │ ├── ecmarkup.js │ └── print.css └── index.html ├── map-polyfill.js ├── package-lock.json ├── package.json ├── rationales.md ├── set-polyfill.js ├── spec ├── index.html ├── map │ ├── key-by.html │ ├── prototype-delete-all.html │ ├── prototype-every.html │ ├── prototype-filter.html │ ├── prototype-find-key.html │ ├── prototype-find.html │ ├── prototype-includes.html │ ├── prototype-key-of.html │ ├── prototype-map-keys.html │ ├── prototype-map-values.html │ ├── prototype-merge.html │ ├── prototype-reduce.html │ ├── prototype-some.html │ └── prototype-update.html ├── set │ ├── prototype-add-elements.html │ ├── prototype-every.html │ ├── prototype-filter.html │ ├── prototype-find.html │ ├── prototype-join.html │ ├── prototype-map.html │ ├── prototype-reduce.html │ ├── prototype-remove-elements.html │ └── prototype-some.html ├── weak-map │ └── prototype-delete-all.html └── weak-set │ ├── prototype-add-all.html │ └── prototype-delete-all.html └── test └── set ├── add_elements.spec.js ├── every.spec.js ├── filter.spec.js ├── find.spec.js ├── map.spec.js ├── reduce.spec.js ├── remove_elements.spec.js └── some.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | © Michał Wadas 2016-2018cd 2 | 3 | Usage of the works is permitted provided that this instrument is retained with the works, so that any entity that uses the works is notified of this instrument. 4 | 5 | DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # New collections (Set and Map) methods 2 | 3 | See [formal spec WIP](https://tc39.github.io/proposal-collection-methods/). 4 | 5 | # (Semi)relevant previous discussions 6 | 7 | * [Map#map and Map#filter](https://github.com/tc39/ecma262/pull/13) 8 | * [Map.prototype.map and Map.prototype.filter (spec) + Set](https://esdiscuss.org/notes/2014-11-19) 9 | * [Map: filter/map and more](https://esdiscuss.org/topic/map-filter-map-and-more) 10 | * [Original topic regarding this proposal](https://esdiscuss.org/topic/new-set-prototype-methods) 11 | * [Newer topic regarding this proposal](https://esdiscuss.org/topic/new-set-methods-again) 12 | 13 | 14 | # Motivations 15 | 16 | * it's consistent with already familiar and *widely used* `Array` API (reduced cognitive overhead) 17 | * easier refactoring 18 | * certain function become generic 19 | * reduces boilerplate code when dealing with common collection use cases 20 | * no new syntax 21 | * allow developers coming from other languages to use familiar APIs 22 | 23 | # Adoption 24 | 25 | **TBA** 26 | 27 | 28 | ## Quick reference for other languages similar data structures 29 | 30 | ### Set 31 | 32 | * [C# `HashSet`](https://msdn.microsoft.com/en-us/library/bb359438.aspx) 33 | * [F# - Collections.Set](https://msdn.microsoft.com/en-au/vstudio/ee340244(v=vs.89)) 34 | * [Haskell - Data.Set](http://hackage.haskell.org/package/containers-0.5.10.2/docs/Data-Set.html) 35 | * [Python - set/frozenset](https://docs.python.org/3.6/library/stdtypes.html#set) 36 | * [Hack - HH\Set](https://docs.hhvm.com/hack/reference/class/HH.Set/) 37 | * [Ruby - Set](https://ruby-doc.org/stdlib-2.5.0/libdoc/set/rdoc/Set.html) 38 | 39 | ### Map (known also as Dictionary) 40 | 41 | **TBA** 42 | 43 | # Proposal 44 | 45 | This proposal does not change grammar of language. 46 | 47 | New methods are added to `Set.prototype`. 48 | 49 | * Array-like methods. These methods replicates functionality of `Array.prototype` methods: 50 | * `Set.prototype.filter(predicate, thisArg)` 51 | * `Set.prototype.map(fn, thisArg)` 52 | * `Set.prototype.find(fn, thisArg)` 53 | * `Set.prototype.reduce(fn, initialValue)` 54 | * `Set.prototype.join(separator)` 55 | * `Set.prototype.some(predicate, thisArg)` 56 | * `Set.prototype.every(predicate, thisArg)` 57 | * New methods: 58 | * `Set.prototype.addAll(...elements)` - similar to `Array.prototype.push`. Adds all arguments to existing `Set`. 59 | * Alternative name: `.addEach` 60 | * `Set.prototype.deleteAll(...elements)` - reverse of `addAll`. Remove every `element` in `elements` from existing `Set`. 61 | * Alternative names: `.deleteEach` 62 | 63 | New methods are added to `Map.prototype`. 64 | 65 | * Array-like methods. These methods replicates functionality of `Array.prototype` methods: 66 | * `Map.prototype.filter(predicate, thisArg)` 67 | * `Map.prototype.mapValues(fn, thisArg)` 68 | * `Map.prototype.mapKeys(fn, thisArg)` 69 | * `Map.prototype.reduce(fn, initialValue)` 70 | * `Map.prototype.find(fn, thisArg)` 71 | * `Map.prototype.findKey(fn, thisArg)` 72 | * `Map.prototype.keyOf(searchElement)` 73 | * `Map.prototype.some(predicate, thisArg)` 74 | * `Map.prototype.every(predicate, thisArg)` 75 | * `Map.prototype.includes(searchElement)` 76 | * `Map.prototype.update(key, callbackfn, thunk)` 77 | 78 | * New methods: 79 | * `Map.prototype.deleteAll(...elements)` - similar to `Set.prototype.deleteAll`. 80 | * `Map.prototype.merge(...iterables)` - performs in-place update joining arbitrary number of iterables. 81 | * `Map.prototype.update(key, cb [,thunk])` - performs in-place update of single value. 82 | 83 | New method is added to `%Map%`: 84 | 85 | * `Map.keyBy(iterable, keyDerivative)` - 86 | 87 | New methods are added to `WeakSet.prototype`. 88 | 89 | * New methods: 90 | * `WeakSet.prototype.addAll(...elements)` - similar to `Set.prototype.addAll`. 91 | * `WeakSet.prototype.deleteAll(...elements)` - similar to `Set.prototype.deleteAll`. 92 | 93 | New method is added to `WeakMap.prototype`: 94 | 95 | * `WeakMap.prototype.deleteAll(...elements)` - similar to `Map.prototype.deleteAll`. 96 | 97 | ## Polyfill 98 | 99 | A polyfill is available in the [core-js](https://github.com/zloirock/core-js) library. 100 | You can find it in the [ECMAScript proposal section](https://github.com/zloirock/core-js#ecmascript-proposals) 101 | / **Stage 1 proposals** / **New Set and Map methods proposal**. 102 | 103 | For all new collection methods include this at the top of your entry point. 104 | ```js 105 | require('core-js/proposals/collection-methods'); 106 | 107 | const colors = new Map([['red', '#FF0000'], ['gold', '#FFD700']]); 108 | colors 109 | .mapKeys((value, key) => key.toUpperCase()) // Map { 'RED' => '#FF0000', 'GOLD' => '#FFD700' } 110 | .mapValues(value => value.toLowerCase()); // Map { 'RED' => '#ff0000', 'GOLD' => '#ffd700' } 111 | ``` 112 | For a specific method include the needed polyfill directly. 113 | ```js 114 | require('core-js/features/set/join'); 115 | 116 | const mySet = new Set(['Just', 'like', 'an', 'array']); 117 | mySet.join(' '); // Just like an array 118 | ``` 119 | 120 | ## Not included in this proposal but worth considering 121 | 122 | * `Set.prototype.flatMap`, `Set.prototype.flat` - should be added if [`Array.prototype.flatMap`](https://github.com/tc39/proposal-flatMap) is added to language 123 | 124 | # Why not `%IteratorPrototype%` methods 125 | 126 | See [stage 3 Iterator Helpers](https://github.com/tc39/proposal-iterator-helpers) proposal. 127 | 128 | # Rationales 129 | 130 | See [rationales.md](./rationales.md) for details. 131 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/.gitkeep -------------------------------------------------------------------------------- /docs/assets/IBMPlexMono-Bold-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexMono-Bold-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexMono-BoldItalic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexMono-BoldItalic-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexMono-Italic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexMono-Italic-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexMono-Regular-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexMono-Regular-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSans-Bold-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSans-Bold-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSans-BoldItalic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSans-BoldItalic-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSans-Italic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSans-Italic-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSans-Regular-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSans-Regular-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSerif-Bold-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSerif-Bold-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSerif-BoldItalic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSerif-BoldItalic-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSerif-Italic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSerif-Italic-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/IBMPlexSerif-Regular-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/IBMPlexSerif-Regular-SlashedZero.woff2 -------------------------------------------------------------------------------- /docs/assets/ecma-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-collection-methods/8fcbb53ce97efbc842232ecd782e6acf90bc086c/docs/assets/ecma-header.png -------------------------------------------------------------------------------- /docs/assets/ecmarkup.css: -------------------------------------------------------------------------------- 1 | /* IBM Plex fonts (Latin subset) have been downloaded from Google Fonts and modified to add support back in for the `zero` substitution (slashed zeroes) */ 2 | 3 | /* https://fonts.googleapis.com/css2?family=IBM%20Plex%20Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap */ 4 | @font-face { 5 | font-family: 'IBM Plex Serif'; 6 | font-style: normal; 7 | font-weight: 400; 8 | font-display: swap; 9 | src: local(IBM Plex Serif Regular), local(IBMPlexSerif-Regular), url(./IBMPlexSerif-Regular-SlashedZero.woff2) format('woff2'); 10 | } 11 | @font-face { 12 | font-family: 'IBM Plex Serif'; 13 | font-style: normal; 14 | font-weight: 700; 15 | font-display: swap; 16 | src: local(IBM Plex Serif Bold), local(IBMPlexSerif-Bold), url(./IBMPlexSerif-Bold-SlashedZero.woff2) format('woff2'); 17 | } 18 | @font-face { 19 | font-family: 'IBM Plex Serif'; 20 | font-style: italic; 21 | font-weight: 400; 22 | font-display: swap; 23 | src: local(IBM Plex Serif Italic), local(IBMPlexSerif-Italic), url(./IBMPlexSerif-Italic-SlashedZero.woff2) format('woff2'); 24 | } 25 | @font-face { 26 | font-family: 'IBM Plex Serif'; 27 | font-style: italic; 28 | font-weight: 700; 29 | font-display: swap; 30 | src: local(IBM Plex Serif Bold Italic), local(IBMPlexSerif-BoldItalic), url(./IBMPlexSerif-BoldItalic-SlashedZero.woff2) format('woff2'); 31 | } 32 | 33 | /* https://fonts.googleapis.com/css2?family=IBM%20Plex%20Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap */ 34 | @font-face { 35 | font-family: 'IBM Plex Sans'; 36 | font-style: normal; 37 | font-weight: 400; 38 | font-display: swap; 39 | src: local(IBM Plex Sans Regular), local(IBMPlexSans-Regular), url(./IBMPlexSans-Regular-SlashedZero.woff2) format('woff2'); 40 | } 41 | @font-face { 42 | font-family: 'IBM Plex Sans'; 43 | font-style: normal; 44 | font-weight: 700; 45 | font-display: swap; 46 | src: local(IBM Plex Sans Bold), local(IBMPlexSans-Bold), url(./IBMPlexSans-Bold-SlashedZero.woff2) format('woff2'); 47 | } 48 | @font-face { 49 | font-family: 'IBM Plex Sans'; 50 | font-style: italic; 51 | font-weight: 400; 52 | font-display: swap; 53 | src: local(IBM Plex Sans Italic), local(IBMPlexSans-Italic), url(./IBMPlexSans-Italic-SlashedZero.woff2) format('woff2'); 54 | } 55 | @font-face { 56 | font-family: 'IBM Plex Sans'; 57 | font-style: italic; 58 | font-weight: 700; 59 | font-display: swap; 60 | src: local(IBM Plex Sans Bold Italic), local(IBMPlexSans-BoldItalic), url(./IBMPlexSans-BoldItalic-SlashedZero.woff2) format('woff2'); 61 | } 62 | 63 | /* https://fonts.googleapis.com/css2?family=IBM%20Plex%20Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap */ 64 | @font-face { 65 | font-family: 'IBM Plex Mono'; 66 | font-style: normal; 67 | font-weight: 400; 68 | font-display: swap; 69 | src: local(IBM Plex Mono Regular), local(IBMPlexMono-Regular), url(./IBMPlexMono-Regular-SlashedZero.woff2) format('woff2'); 70 | } 71 | @font-face { 72 | font-family: 'IBM Plex Mono'; 73 | font-style: normal; 74 | font-weight: 700; 75 | font-display: swap; 76 | src: local(IBM Plex Mono Bold), local(IBMPlexMono-Bold), url(./IBMPlexMono-Bold-SlashedZero.woff2) format('woff2'); 77 | } 78 | @font-face { 79 | font-family: 'IBM Plex Mono'; 80 | font-style: italic; 81 | font-weight: 400; 82 | font-display: swap; 83 | src: local(IBM Plex Mono Italic), local(IBMPlexMono-Italic), url(./IBMPlexMono-Italic-SlashedZero.woff2) format('woff2'); 84 | } 85 | @font-face { 86 | font-family: 'IBM Plex Mono'; 87 | font-style: italic; 88 | font-weight: 700; 89 | font-display: swap; 90 | src: local(IBM Plex Mono Bold Italic), local(IBMPlexMono-BoldItalic), url(./IBMPlexMono-BoldItalic-SlashedZero.woff2) format('woff2'); 91 | } 92 | 93 | html { 94 | background-color: #fff; 95 | } 96 | 97 | body { 98 | display: flex; 99 | word-wrap: break-word; 100 | font-size: 18px; 101 | line-height: 1.5; 102 | font-family: 103 | IBM Plex Serif, 104 | serif; 105 | font-variant-numeric: slashed-zero; 106 | padding: 0; 107 | margin: 0; 108 | color: #111; 109 | } 110 | 111 | #spec-container { 112 | padding: 0 20px; 113 | max-width: 80rem; 114 | margin: 0 auto; 115 | flex-grow: 1; 116 | flex-basis: 66%; 117 | box-sizing: border-box; 118 | overflow: hidden; 119 | padding-bottom: 1em; 120 | } 121 | 122 | body.oldtoc { 123 | margin: 0 auto; 124 | } 125 | 126 | span[aria-hidden='true'] { 127 | font-size: 0; 128 | } 129 | 130 | a { 131 | text-decoration: none; 132 | color: #206ca7; 133 | } 134 | 135 | a:visited { 136 | color: #206ca7; 137 | } 138 | 139 | a:hover { 140 | text-decoration: underline; 141 | color: #239dee; 142 | } 143 | 144 | a.e-user-code, 145 | span.e-user-code { 146 | white-space: nowrap; 147 | } 148 | 149 | a.e-user-code::before, 150 | span.e-user-code::before { 151 | display: none; 152 | color: brown; 153 | background-color: white; 154 | border: 2pt solid brown; 155 | padding: 0.3ex; 156 | margin: 0 0.25em 0 0; 157 | line-height: normal; 158 | vertical-align: middle; 159 | text-transform: uppercase; 160 | font-family: 161 | IBM Plex Sans, 162 | sans-serif; 163 | font-weight: 900; 164 | font-size: x-small; 165 | } 166 | 167 | a.e-user-code:hover::before, 168 | span.e-user-code:hover::before { 169 | background-color: brown; 170 | color: white; 171 | } 172 | 173 | html.show-ao-annotations a.e-user-code::before, 174 | html.show-ao-annotations span.e-user-code::before { 175 | display: inline-block; 176 | } 177 | 178 | a.e-user-code::before, 179 | span.e-user-code::before { 180 | content: 'UC'; 181 | } 182 | 183 | code { 184 | font-weight: bold; 185 | font-family: 186 | Comic Code, 187 | IBM Plex Mono, 188 | monospace; 189 | white-space: pre; 190 | } 191 | 192 | pre code { 193 | font-weight: inherit; 194 | } 195 | 196 | pre code.hljs { 197 | background-color: #fff; 198 | margin: 0; 199 | padding: 0; 200 | } 201 | 202 | ol.toc { 203 | list-style: none; 204 | padding-left: 0; 205 | } 206 | 207 | ol.toc ol.toc { 208 | padding-left: 2ex; 209 | list-style: none; 210 | } 211 | 212 | var { 213 | border-radius: 5px; 214 | color: #218379; 215 | transition: all 0.25s ease-out; 216 | cursor: pointer; 217 | padding: 0 4px; 218 | margin: 0 -4px; 219 | mix-blend-mode: multiply; 220 | } 221 | var.field { 222 | font: inherit; 223 | color: inherit; 224 | } 225 | 226 | var.referenced { 227 | color: inherit; 228 | } 229 | 230 | var.referenced0 { 231 | background-color: #ffff6c; 232 | } 233 | var.referenced1 { 234 | background-color: #ffa4a8; 235 | } 236 | var.referenced2 { 237 | background-color: #96e885; 238 | } 239 | var.referenced3 { 240 | background-color: #3eeed2; 241 | } 242 | var.referenced4 { 243 | background-color: #eacfb6; 244 | } 245 | var.referenced5 { 246 | background-color: #82ddff; 247 | } 248 | var.referenced6 { 249 | background-color: #ffbcf2; 250 | } 251 | 252 | emu-const { 253 | font-family: 254 | IBM Plex Sans, 255 | sans-serif; 256 | font-variant: small-caps; 257 | text-transform: uppercase; 258 | } 259 | 260 | emu-val { 261 | font-weight: bold; 262 | } 263 | 264 | /* depth 1 */ 265 | emu-alg ol, 266 | /* depth 4 */ 267 | emu-alg ol ol ol ol, 268 | emu-alg ol.nested-thrice, 269 | emu-alg ol.nested-twice ol, 270 | emu-alg ol.nested-once ol ol { 271 | list-style-type: decimal; 272 | } 273 | 274 | /* depth 2 */ 275 | emu-alg ol ol, 276 | emu-alg ol.nested-once, 277 | /* depth 5 */ 278 | emu-alg ol ol ol ol ol, 279 | emu-alg ol.nested-four-times, 280 | emu-alg ol.nested-thrice ol, 281 | emu-alg ol.nested-twice ol ol, 282 | emu-alg ol.nested-once ol ol ol { 283 | list-style-type: lower-alpha; 284 | } 285 | 286 | /* depth 3 */ 287 | emu-alg ol ol ol, 288 | emu-alg ol.nested-twice, 289 | emu-alg ol.nested-once ol, 290 | /* depth 6 */ 291 | emu-alg ol ol ol ol ol ol, 292 | emu-alg ol.nested-lots, 293 | emu-alg ol.nested-four-times ol, 294 | emu-alg ol.nested-thrice ol ol, 295 | emu-alg ol.nested-twice ol ol ol, 296 | emu-alg ol.nested-once ol ol ol ol, 297 | /* depth 7+ */ 298 | emu-alg ol.nested-lots ol { 299 | list-style-type: lower-roman; 300 | } 301 | 302 | emu-eqn { 303 | display: block; 304 | margin-left: 4em; 305 | } 306 | 307 | emu-eqn.inline { 308 | display: inline; 309 | margin: 0; 310 | } 311 | 312 | emu-eqn div:first-child { 313 | margin-left: -2em; 314 | } 315 | 316 | emu-note { 317 | margin: 1em 0; 318 | display: flex; 319 | gap: 1em; 320 | flex-direction: row; 321 | color: inherit; 322 | border-left: 5px solid #52e052; 323 | background: #e9fbe9; 324 | padding: 10px 10px 10px 0; 325 | overflow-x: auto; 326 | } 327 | 328 | emu-note > span.note { 329 | white-space: nowrap; 330 | flex-grow: 0; 331 | flex-shrink: 1; 332 | text-transform: uppercase; 333 | padding-left: 5px; 334 | } 335 | 336 | emu-note[type='editor'] { 337 | border-left-color: #faa; 338 | } 339 | 340 | emu-note > div.note-contents { 341 | flex-grow: 1; 342 | flex-shrink: 1; 343 | overflow: auto; 344 | } 345 | 346 | emu-note > div.note-contents > p:first-of-type { 347 | margin-top: 0; 348 | } 349 | 350 | emu-note > div.note-contents > p:last-of-type { 351 | margin-bottom: 0; 352 | } 353 | 354 | emu-table:not(.code) td code { 355 | white-space: normal; 356 | } 357 | 358 | emu-figure { 359 | display: block; 360 | } 361 | 362 | emu-example { 363 | display: block; 364 | margin: 1em 3em; 365 | } 366 | 367 | emu-example figure figcaption { 368 | margin-top: 0.5em; 369 | text-align: left; 370 | } 371 | 372 | emu-figure figure, 373 | emu-example figure, 374 | emu-table figure { 375 | display: flex; 376 | flex-direction: column; 377 | align-items: center; 378 | } 379 | 380 | emu-production { 381 | display: block; 382 | } 383 | 384 | emu-grammar[type='example'] emu-production, 385 | emu-grammar[type='definition'] emu-production { 386 | margin-top: 1em; 387 | margin-bottom: 1em; 388 | margin-left: 5ex; 389 | } 390 | 391 | emu-grammar.inline, 392 | emu-production.inline, 393 | emu-grammar.inline emu-production emu-rhs, 394 | emu-production.inline emu-rhs, 395 | emu-grammar[collapsed] emu-production emu-rhs { 396 | display: inline; 397 | padding-left: 1ex; 398 | margin-left: 0; 399 | } 400 | 401 | emu-production[collapsed] emu-rhs { 402 | display: inline; 403 | padding-left: 0.5ex; 404 | margin-left: 0; 405 | } 406 | 407 | emu-grammar[collapsed] emu-production, 408 | emu-production[collapsed] { 409 | margin: 0; 410 | } 411 | 412 | emu-constraints { 413 | font-size: 0.75em; 414 | margin-right: 0.5ex; 415 | } 416 | 417 | emu-gann { 418 | margin-right: 0.5ex; 419 | } 420 | 421 | emu-gann emu-t:last-child, 422 | emu-gann emu-gprose:last-child, 423 | emu-gann emu-nt:last-child { 424 | margin-right: 0; 425 | } 426 | 427 | emu-geq { 428 | margin-left: 0.5ex; 429 | font-weight: bold; 430 | } 431 | 432 | emu-oneof { 433 | font-weight: bold; 434 | margin-left: 0.5ex; 435 | } 436 | 437 | emu-nt { 438 | display: inline-block; 439 | font-style: italic; 440 | white-space: nowrap; 441 | text-indent: 0; 442 | } 443 | 444 | emu-nt a, 445 | emu-nt a:visited { 446 | color: #333; 447 | } 448 | 449 | emu-rhs emu-nt { 450 | margin-right: 0.5ex; 451 | } 452 | 453 | emu-t { 454 | display: inline-block; 455 | font-family: 456 | IBM Plex Mono, 457 | monospace; 458 | font-weight: bold; 459 | white-space: nowrap; 460 | text-indent: 0; 461 | } 462 | 463 | emu-production emu-t { 464 | margin-right: 0.5ex; 465 | } 466 | 467 | emu-rhs { 468 | display: block; 469 | padding-left: 75px; 470 | text-indent: -25px; 471 | } 472 | 473 | emu-production:not([collapsed]) emu-rhs { 474 | border: 0.2ex dashed transparent; 475 | } 476 | 477 | emu-production:not([collapsed]) emu-rhs:hover { 478 | border-color: #888; 479 | background-color: #f0f0f0; 480 | } 481 | 482 | emu-mods { 483 | font-size: 0.85em; 484 | vertical-align: sub; 485 | font-style: normal; 486 | font-weight: normal; 487 | } 488 | 489 | emu-params, 490 | emu-opt { 491 | margin-right: 1ex; 492 | font-family: 493 | IBM Plex Mono, 494 | monospace; 495 | } 496 | 497 | emu-params, 498 | emu-constraints { 499 | color: #2aa198; 500 | } 501 | 502 | emu-opt { 503 | color: #b58900; 504 | } 505 | 506 | emu-gprose { 507 | font-size: 0.9em; 508 | font-family: 509 | IBM Plex Sans, 510 | sans-serif; 511 | } 512 | 513 | emu-production emu-gprose { 514 | margin-right: 1ex; 515 | } 516 | 517 | h1.shortname { 518 | color: #f60; 519 | font-size: 1.5em; 520 | margin: 0; 521 | } 522 | 523 | h1.version { 524 | color: #f60; 525 | font-size: 1.5em; 526 | } 527 | 528 | h1.title { 529 | color: #f60; 530 | } 531 | 532 | h1, 533 | h2, 534 | h3, 535 | h4, 536 | h5, 537 | h6 { 538 | position: relative; 539 | } 540 | 541 | h1 .secnum { 542 | text-decoration: none; 543 | margin-right: 5px; 544 | } 545 | 546 | h1 span.title { 547 | order: 2; 548 | } 549 | 550 | h1 { 551 | font-size: 2.67em; 552 | margin-bottom: 0; 553 | line-height: 1em; 554 | } 555 | h2 { 556 | font-size: 2em; 557 | } 558 | h3 { 559 | font-size: 1.56em; 560 | } 561 | h4 { 562 | font-size: 1.25em; 563 | } 564 | h5 { 565 | font-size: 1.11em; 566 | } 567 | h6 { 568 | font-size: 1em; 569 | } 570 | 571 | pre code.hljs { 572 | background: transparent; 573 | } 574 | 575 | emu-clause[id], 576 | emu-annex[id], 577 | emu-intro[id] { 578 | scroll-margin-top: 2ex; 579 | } 580 | 581 | emu-intro h1, 582 | emu-clause h1, 583 | emu-annex h1 { 584 | font-size: 2em; 585 | } 586 | emu-intro h2, 587 | emu-clause h2, 588 | emu-annex h2 { 589 | font-size: 1.56em; 590 | } 591 | emu-intro h3, 592 | emu-clause h3, 593 | emu-annex h3 { 594 | font-size: 1.25em; 595 | } 596 | emu-intro h4, 597 | emu-clause h4, 598 | emu-annex h4 { 599 | font-size: 1.11em; 600 | } 601 | emu-intro h5, 602 | emu-clause h5, 603 | emu-annex h5 { 604 | font-size: 1em; 605 | } 606 | emu-intro h6, 607 | emu-clause h6, 608 | emu-annex h6 { 609 | font-size: 0.9em; 610 | } 611 | emu-intro emu-intro h1, 612 | emu-clause emu-clause h1, 613 | emu-annex emu-annex h1 { 614 | font-size: 1.56em; 615 | } 616 | emu-intro emu-intro h2, 617 | emu-clause emu-clause h2, 618 | emu-annex emu-annex h2 { 619 | font-size: 1.25em; 620 | } 621 | emu-intro emu-intro h3, 622 | emu-clause emu-clause h3, 623 | emu-annex emu-annex h3 { 624 | font-size: 1.11em; 625 | } 626 | emu-intro emu-intro h4, 627 | emu-clause emu-clause h4, 628 | emu-annex emu-annex h4 { 629 | font-size: 1em; 630 | } 631 | emu-intro emu-intro h5, 632 | emu-clause emu-clause h5, 633 | emu-annex emu-annex h5 { 634 | font-size: 0.9em; 635 | } 636 | emu-intro emu-intro emu-intro h1, 637 | emu-clause emu-clause emu-clause h1, 638 | emu-annex emu-annex emu-annex h1 { 639 | font-size: 1.25em; 640 | } 641 | emu-intro emu-intro emu-intro h2, 642 | emu-clause emu-clause emu-clause h2, 643 | emu-annex emu-annex emu-annex h2 { 644 | font-size: 1.11em; 645 | } 646 | emu-intro emu-intro emu-intro h3, 647 | emu-clause emu-clause emu-clause h3, 648 | emu-annex emu-annex emu-annex h3 { 649 | font-size: 1em; 650 | } 651 | emu-intro emu-intro emu-intro h4, 652 | emu-clause emu-clause emu-clause h4, 653 | emu-annex emu-annex emu-annex h4 { 654 | font-size: 0.9em; 655 | } 656 | emu-intro emu-intro emu-intro emu-intro h1, 657 | emu-clause emu-clause emu-clause emu-clause h1, 658 | emu-annex emu-annex emu-annex emu-annex h1 { 659 | font-size: 1.11em; 660 | } 661 | emu-intro emu-intro emu-intro emu-intro h2, 662 | emu-clause emu-clause emu-clause emu-clause h2, 663 | emu-annex emu-annex emu-annex emu-annex h2 { 664 | font-size: 1em; 665 | } 666 | emu-intro emu-intro emu-intro emu-intro h3, 667 | emu-clause emu-clause emu-clause emu-clause h3, 668 | emu-annex emu-annex emu-annex emu-annex h3 { 669 | font-size: 0.9em; 670 | } 671 | emu-intro emu-intro emu-intro emu-intro emu-intro h1, 672 | emu-clause emu-clause emu-clause emu-clause emu-clause h1, 673 | emu-annex emu-annex emu-annex emu-annex emu-annex h1 { 674 | font-size: 1em; 675 | } 676 | emu-intro emu-intro emu-intro emu-intro emu-intro h2, 677 | emu-clause emu-clause emu-clause emu-clause emu-clause h2, 678 | emu-annex emu-annex emu-annex emu-annex emu-annex h2 { 679 | font-size: 0.9em; 680 | } 681 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro h1, 682 | emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, 683 | emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1 { 684 | font-size: 0.9em; 685 | } 686 | 687 | emu-clause, 688 | emu-intro, 689 | emu-annex { 690 | display: block; 691 | } 692 | 693 | /* these values are twice the font-size for the

titles for clauses */ 694 | emu-intro, 695 | emu-clause, 696 | emu-annex { 697 | margin-top: 4em; 698 | } 699 | emu-intro emu-intro, 700 | emu-clause emu-clause, 701 | emu-annex emu-annex { 702 | margin-top: 3.12em; 703 | } 704 | emu-intro emu-intro emu-intro, 705 | emu-clause emu-clause emu-clause, 706 | emu-annex emu-annex emu-annex { 707 | margin-top: 2.5em; 708 | } 709 | emu-intro emu-intro emu-intro emu-intro, 710 | emu-clause emu-clause emu-clause emu-clause, 711 | emu-annex emu-annex emu-annex emu-annex { 712 | margin-top: 2.22em; 713 | } 714 | emu-intro emu-intro emu-intro emu-intro emu-intro, 715 | emu-clause emu-clause emu-clause emu-clause emu-clause, 716 | emu-annex emu-annex emu-annex emu-annex emu-annex { 717 | margin-top: 2em; 718 | } 719 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro, 720 | emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause, 721 | emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex { 722 | margin-top: 1.8em; 723 | } 724 | 725 | #spec-container > emu-intro:first-of-type, 726 | #spec-container > emu-clause:first-of-type, 727 | #spec-container > emu-annex:first-of-type { 728 | margin-top: 0; 729 | } 730 | 731 | /* Figures and tables */ 732 | figure { 733 | display: block; 734 | overflow-x: auto; 735 | margin: 1.5em 0; 736 | } 737 | figure object { 738 | display: block; 739 | margin: 0 auto; 740 | } 741 | figure table.real-table { 742 | margin: 0 auto; 743 | } 744 | figure figcaption { 745 | display: block; 746 | color: #555555; 747 | font-weight: bold; 748 | text-align: center; 749 | } 750 | 751 | emu-table table { 752 | margin: 0 auto; 753 | } 754 | 755 | emu-table table, 756 | table.real-table { 757 | border-collapse: collapse; 758 | } 759 | 760 | emu-table td, 761 | emu-table th, 762 | table.real-table td, 763 | table.real-table th { 764 | border: 1px solid black; 765 | padding: 0.4em; 766 | vertical-align: baseline; 767 | } 768 | emu-table th, 769 | emu-table thead td, 770 | table.real-table th { 771 | background-color: #eeeeee; 772 | } 773 | 774 | emu-table td { 775 | background: #fff; 776 | } 777 | 778 | /* Note: the left content edges of table.lightweight-table >tbody >tr >td 779 | and div.display line up. */ 780 | table.lightweight-table { 781 | border-collapse: collapse; 782 | margin: 0 0 0 1.5em; 783 | } 784 | table.lightweight-table td, 785 | table.lightweight-table th { 786 | border: none; 787 | padding: 0 0.5em; 788 | vertical-align: baseline; 789 | } 790 | 791 | /* for non-clause-like link targets, apply a fading highlight 792 | and a persistent focus-associated highlight */ 793 | @keyframes highlight-target-bg { 794 | 0% { 795 | background-color: rgba(249, 241, 172, 1); 796 | } 797 | 100% { 798 | background-color: rgba(249, 241, 172, 0); 799 | } 800 | } 801 | #spec-container :target:not(emu-annex, emu-clause, emu-intro, emu-note, body) { 802 | animation: highlight-target-bg 2.5s ease-out; 803 | } 804 | #spec-container :target:focus-within:not(:has(:not(a))) { 805 | animation: none; 806 | background-color: rgba(249, 241, 172, 1); 807 | } 808 | 809 | /* diff styles */ 810 | ins { 811 | background-color: #e0f8e0; 812 | text-decoration: none; 813 | border-bottom: 1px solid #396; 814 | } 815 | 816 | del { 817 | background-color: #fee; 818 | } 819 | 820 | ins.block, 821 | del.block, 822 | emu-production > ins, 823 | emu-production > del, 824 | emu-grammar > ins, 825 | emu-grammar > del { 826 | display: block; 827 | } 828 | emu-rhs > ins, 829 | emu-rhs > del { 830 | display: inline; 831 | } 832 | 833 | tr.ins > td > ins { 834 | border-bottom: none; 835 | } 836 | 837 | tr.ins > td { 838 | background-color: #e0f8e0; 839 | } 840 | 841 | tr.del > td { 842 | background-color: #fee; 843 | } 844 | 845 | /* Menu Styles */ 846 | #menu-toggle { 847 | font-size: 2em; 848 | 849 | position: fixed; 850 | top: 0; 851 | left: 0; 852 | width: 1.5em; 853 | height: 1.5em; 854 | z-index: 3; 855 | visibility: hidden; 856 | color: #1567a2; 857 | background-color: #fff; 858 | 859 | line-height: 1.5em; 860 | text-align: center; 861 | -webkit-touch-callout: none; 862 | -webkit-user-select: none; 863 | -khtml-user-select: none; 864 | -moz-user-select: none; 865 | -ms-user-select: none; 866 | user-select: none; 867 | 868 | cursor: pointer; 869 | } 870 | 871 | #menu { 872 | display: flex; 873 | flex-direction: column; 874 | width: 33%; 875 | height: 100vh; 876 | max-width: 500px; 877 | box-sizing: border-box; 878 | background-color: #ddd; 879 | overflow: hidden; 880 | transition: opacity 0.1s linear; 881 | padding: 0 5px; 882 | position: fixed; 883 | left: 0; 884 | top: 0; 885 | border-right: 2px solid #bbb; 886 | 887 | z-index: 2; 888 | } 889 | 890 | .menu-spacer { 891 | flex-basis: 33%; 892 | max-width: 500px; 893 | flex-grow: 0; 894 | flex-shrink: 0; 895 | } 896 | 897 | #menu a { 898 | color: #1567a2; 899 | } 900 | 901 | #menu.active { 902 | display: flex; 903 | opacity: 1; 904 | z-index: 2; 905 | } 906 | 907 | #menu-pins { 908 | flex-grow: 1; 909 | display: none; 910 | } 911 | 912 | #menu-pins.active { 913 | display: block; 914 | } 915 | 916 | #menu-pins .unpin-all { 917 | border: none; 918 | background: #ccc; 919 | border-radius: 4px; 920 | height: 18px; 921 | font-size: 12px; 922 | margin: 0 5px 0 10px; 923 | font-family: IBM Plex Sans; 924 | } 925 | #menu-pins .unpin-all:hover { 926 | background: #ddd; 927 | } 928 | 929 | #menu-pins-list { 930 | margin: 0; 931 | padding: 0; 932 | counter-reset: pins-counter; 933 | } 934 | 935 | #menu-pins-list > li { 936 | display: flex; 937 | align-items: stretch; 938 | gap: 4px; 939 | margin: 3px 1px; 940 | border-radius: 4px; 941 | } 942 | 943 | #menu-pins-list > li > a { 944 | flex-grow: 1; 945 | align-self: center; 946 | } 947 | 948 | #menu-pins-list > li:before, 949 | #menu-pins-list > li > .unpin { 950 | flex-shrink: 0; 951 | flex-grow: 0; 952 | text-align: center; 953 | padding: 1px 3px; 954 | border-radius: 4px; 955 | background: none; 956 | border: none; 957 | } 958 | #menu-pins-list > li:before, 959 | #menu-pins-list > li > .unpin:hover { 960 | background: #ccc; 961 | } 962 | 963 | #menu-pins-list > li > .unpin, 964 | #menu-pins .unpin-all { 965 | cursor: pointer; 966 | } 967 | #menu-pins-list > li > .unpin:hover, 968 | #menu-pins .unpin-all:hover { 969 | color: #bb1212; 970 | } 971 | 972 | #menu-pins-list > li:before { 973 | content: counter(pins-counter); 974 | counter-increment: pins-counter; 975 | font-size: 16px; 976 | } 977 | 978 | #menu-toc > ol { 979 | padding: 0; 980 | flex-grow: 1; 981 | } 982 | 983 | #menu-toc > ol li { 984 | padding: 0; 985 | } 986 | 987 | #menu-toc > ol, 988 | #menu-toc > ol ol { 989 | list-style-type: none; 990 | margin: 0; 991 | padding: 0; 992 | } 993 | 994 | #menu-toc > ol ol { 995 | padding-left: 0.75em; 996 | } 997 | 998 | #menu-toc li { 999 | text-overflow: ellipsis; 1000 | overflow: hidden; 1001 | white-space: nowrap; 1002 | } 1003 | 1004 | #menu-toc .item-toggle { 1005 | display: inline-block; 1006 | transform: rotate(0deg); 1007 | transition: transform 0.1s ease; 1008 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMC42MDUiIGhlaWdodD0iMTUuNTU1Ij48cGF0aCBmaWxsPSIjODg5IiBkPSJtMi44MjggMTUuNTU1IDcuNzc3LTcuNzc5TDIuODI4IDAgMCAyLjgyOGw0Ljk0OSA0Ljk0OEwwIDEyLjcyN2wyLjgyOCAyLjgyOHoiLz48L3N2Zz4); 1009 | background-repeat: no-repeat; 1010 | background-position: center; 1011 | background-size: auto 50%; 1012 | text-align: center; 1013 | width: 1em; 1014 | 1015 | color: transparent; 1016 | 1017 | -webkit-touch-callout: none; 1018 | -webkit-user-select: none; 1019 | -khtml-user-select: none; 1020 | -moz-user-select: none; 1021 | -ms-user-select: none; 1022 | user-select: none; 1023 | 1024 | cursor: pointer; 1025 | } 1026 | 1027 | #menu-toc .item-toggle-none { 1028 | display: inline-block; 1029 | width: 1em; 1030 | } 1031 | 1032 | #menu-toc li.active > .item-toggle, 1033 | #menu-toc li.revealed > .item-toggle { 1034 | transform: rotate(90deg); 1035 | } 1036 | 1037 | #menu-toc li > ol { 1038 | display: none; 1039 | } 1040 | 1041 | #menu-toc li.active > ol { 1042 | display: block; 1043 | } 1044 | 1045 | #menu-toc li.revealed > a { 1046 | background-color: #ccc; 1047 | font-weight: bold; 1048 | } 1049 | 1050 | #menu-toc li.revealed-leaf > a { 1051 | color: #206ca7; 1052 | } 1053 | 1054 | #menu-toc li.revealed > ol { 1055 | display: block; 1056 | } 1057 | 1058 | #menu-toc li > a { 1059 | padding: 2px 5px; 1060 | } 1061 | 1062 | #menu > * { 1063 | margin-bottom: 5px; 1064 | } 1065 | 1066 | .menu-pane-header { 1067 | padding: 2px 8px; 1068 | background-color: #bbb; 1069 | font-weight: bold; 1070 | letter-spacing: 1px; 1071 | flex-grow: 0; 1072 | flex-shrink: 0; 1073 | font-size: 80%; 1074 | user-select: none; 1075 | } 1076 | 1077 | .menu-pane-header emu-opt, 1078 | .menu-pane-header emu-t, 1079 | .menu-pane-header emu-nt { 1080 | margin-right: 0px; 1081 | display: inline; 1082 | color: inherit; 1083 | } 1084 | 1085 | .menu-pane-header emu-rhs { 1086 | display: inline; 1087 | padding-left: 0px; 1088 | text-indent: 0px; 1089 | } 1090 | 1091 | .menu-pane-header emu-geq { 1092 | margin-left: 0px; 1093 | } 1094 | 1095 | a.menu-pane-header-production { 1096 | color: inherit; 1097 | } 1098 | 1099 | .menu-pane-header-production { 1100 | text-transform: none; 1101 | letter-spacing: 1.5px; 1102 | padding-left: 0.5em; 1103 | } 1104 | 1105 | #menu-toc { 1106 | display: flex; 1107 | flex-direction: column; 1108 | width: 100%; 1109 | overflow: hidden; 1110 | flex-grow: 1; 1111 | } 1112 | 1113 | #menu-toc ol.toc { 1114 | overflow-x: hidden; 1115 | overflow-y: auto; 1116 | } 1117 | 1118 | #menu-search { 1119 | position: relative; 1120 | flex-grow: 0; 1121 | flex-shrink: 0; 1122 | width: 100%; 1123 | 1124 | display: flex; 1125 | flex-direction: column; 1126 | 1127 | max-height: 300px; 1128 | } 1129 | 1130 | #menu-trace-list { 1131 | display: none; 1132 | } 1133 | 1134 | #menu-search-box { 1135 | box-sizing: border-box; 1136 | display: block; 1137 | width: 100%; 1138 | margin: 5px 0 0 0; 1139 | font-size: 1em; 1140 | padding: 2px; 1141 | background-color: #bbb; 1142 | border: 1px solid #999; 1143 | } 1144 | 1145 | #menu-search-results { 1146 | overflow-x: hidden; 1147 | overflow-y: auto; 1148 | } 1149 | 1150 | li.menu-search-result-clause:before { 1151 | content: 'clause'; 1152 | width: 40px; 1153 | display: inline-block; 1154 | text-align: right; 1155 | padding-right: 1ex; 1156 | color: #666; 1157 | font-size: 75%; 1158 | } 1159 | li.menu-search-result-op:before { 1160 | content: 'op'; 1161 | width: 40px; 1162 | display: inline-block; 1163 | text-align: right; 1164 | padding-right: 1ex; 1165 | color: #666; 1166 | font-size: 75%; 1167 | } 1168 | 1169 | li.menu-search-result-prod:before { 1170 | content: 'prod'; 1171 | width: 40px; 1172 | display: inline-block; 1173 | text-align: right; 1174 | padding-right: 1ex; 1175 | color: #666; 1176 | font-size: 75%; 1177 | } 1178 | 1179 | li.menu-search-result-term:before { 1180 | content: 'term'; 1181 | width: 40px; 1182 | display: inline-block; 1183 | text-align: right; 1184 | padding-right: 1ex; 1185 | color: #666; 1186 | font-size: 75%; 1187 | } 1188 | 1189 | #menu-search-results ul { 1190 | padding: 0 5px; 1191 | margin: 0; 1192 | } 1193 | 1194 | #menu-search-results li { 1195 | white-space: nowrap; 1196 | text-overflow: ellipsis; 1197 | } 1198 | 1199 | #menu-trace-list { 1200 | counter-reset: item; 1201 | margin: 0 0 0 20px; 1202 | padding: 0; 1203 | } 1204 | #menu-trace-list li { 1205 | display: block; 1206 | white-space: nowrap; 1207 | } 1208 | 1209 | #menu-trace-list li .secnum:after { 1210 | content: ' '; 1211 | } 1212 | #menu-trace-list li:before { 1213 | content: counter(item) ' '; 1214 | background-color: #222; 1215 | counter-increment: item; 1216 | color: #999; 1217 | width: 20px; 1218 | height: 20px; 1219 | line-height: 20px; 1220 | display: inline-block; 1221 | text-align: center; 1222 | margin: 2px 4px 2px 0; 1223 | } 1224 | 1225 | @media (max-width: 1000px) { 1226 | body { 1227 | margin: 0; 1228 | display: block; 1229 | } 1230 | 1231 | #menu { 1232 | display: none; 1233 | padding-top: 3em; 1234 | width: 450px; 1235 | } 1236 | 1237 | #menu.active { 1238 | position: fixed; 1239 | height: 100%; 1240 | left: 0; 1241 | top: 0; 1242 | right: 300px; 1243 | } 1244 | 1245 | #menu-toggle { 1246 | visibility: visible; 1247 | } 1248 | 1249 | #spec-container { 1250 | padding: 0 5px; 1251 | } 1252 | 1253 | #references-pane-spacer { 1254 | display: none; 1255 | } 1256 | } 1257 | 1258 | @media only screen and (max-width: 800px) { 1259 | #menu { 1260 | width: 100%; 1261 | } 1262 | 1263 | h1 .secnum:empty { 1264 | margin: 0; 1265 | padding: 0; 1266 | } 1267 | } 1268 | 1269 | /* Toolbox */ 1270 | .toolbox-container { 1271 | position: absolute; 1272 | display: none; 1273 | padding-bottom: 7px; 1274 | } 1275 | 1276 | .toolbox-container.active { 1277 | display: inline-block; 1278 | } 1279 | 1280 | .toolbox { 1281 | position: relative; 1282 | background: #ddd; 1283 | border: 1px solid #aaa; 1284 | color: #eee; 1285 | padding: 5px 7px; 1286 | border-radius: 3px; 1287 | } 1288 | 1289 | .toolbox a { 1290 | text-decoration: none; 1291 | padding: 0 3px; 1292 | white-space: nowrap; 1293 | } 1294 | 1295 | .toolbox a:hover { 1296 | text-decoration: underline; 1297 | } 1298 | 1299 | .toolbox:after, 1300 | .toolbox:before { 1301 | top: 100%; 1302 | left: 15px; 1303 | border: solid transparent; 1304 | content: ' '; 1305 | height: 0; 1306 | width: 0; 1307 | position: absolute; 1308 | pointer-events: none; 1309 | } 1310 | 1311 | .toolbox:after { 1312 | border-color: rgba(0, 0, 0, 0); 1313 | border-top-color: #ddd; 1314 | border-width: 10px; 1315 | margin-left: -10px; 1316 | } 1317 | .toolbox:before { 1318 | border-color: rgba(204, 204, 204, 0); 1319 | border-top-color: #aaa; 1320 | border-width: 12px; 1321 | margin-left: -12px; 1322 | } 1323 | 1324 | #references-pane-container { 1325 | position: fixed; 1326 | bottom: 0; 1327 | left: 0; 1328 | right: 0; 1329 | display: none; 1330 | background-color: #ddd; 1331 | z-index: 1; 1332 | } 1333 | 1334 | #references-pane-table-container { 1335 | overflow-x: hidden; 1336 | overflow-y: auto; 1337 | min-height: 35px; 1338 | max-height: 85vh; 1339 | } 1340 | 1341 | #references-pane { 1342 | flex-grow: 1; 1343 | overflow: hidden; 1344 | display: flex; 1345 | flex-direction: column; 1346 | } 1347 | 1348 | #references-pane > .menu-pane-header { 1349 | cursor: row-resize; 1350 | } 1351 | 1352 | #references-pane-container.active { 1353 | display: flex; 1354 | } 1355 | 1356 | #references-pane-close:after { 1357 | content: '\2716'; 1358 | float: right; 1359 | cursor: pointer; 1360 | } 1361 | 1362 | #references-pane table tbody { 1363 | vertical-align: baseline; 1364 | } 1365 | 1366 | #references-pane table tr td:first-child { 1367 | text-align: right; 1368 | padding-right: 5px; 1369 | } 1370 | 1371 | [normative-optional], 1372 | [deprecated], 1373 | [legacy] { 1374 | border-left: 5px solid #ff6600; 1375 | padding: 0.5em; 1376 | background: #ffeedd; 1377 | } 1378 | 1379 | .attributes-tag { 1380 | text-transform: uppercase; 1381 | color: #884400; 1382 | } 1383 | 1384 | .attributes-tag a { 1385 | color: #884400; 1386 | } 1387 | 1388 | /* Shortcuts help dialog */ 1389 | 1390 | #shortcuts-help { 1391 | position: fixed; 1392 | left: 5%; 1393 | margin: 0 auto; 1394 | right: 5%; 1395 | z-index: 10; 1396 | top: 10%; 1397 | top: calc(5vw + 5vh); 1398 | padding: 30px 90px; 1399 | max-width: 500px; 1400 | outline: solid 10000px rgba(255, 255, 255, 0.6); 1401 | border-radius: 5px; 1402 | border-width: 1px 1px 0 1px; 1403 | background-color: #ddd; 1404 | display: none; 1405 | } 1406 | 1407 | #shortcuts-help.active { 1408 | display: block; 1409 | } 1410 | 1411 | #shortcuts-help ul { 1412 | padding: 0; 1413 | } 1414 | 1415 | #shortcuts-help li { 1416 | display: flex; 1417 | justify-content: space-between; 1418 | } 1419 | 1420 | #shortcuts-help code { 1421 | padding: 3px 10px; 1422 | border-radius: 3px; 1423 | border-width: 1px 1px 0 1px; 1424 | border-color: #bbb; 1425 | background-color: #eee; 1426 | box-shadow: inset 0 -1px 0 #ccc; 1427 | } 1428 | -------------------------------------------------------------------------------- /docs/assets/ecmarkup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let sdoBox = { 3 | init() { 4 | this.$alternativeId = null; 5 | this.$outer = document.createElement('div'); 6 | this.$outer.classList.add('toolbox-container'); 7 | this.$container = document.createElement('div'); 8 | this.$container.classList.add('toolbox'); 9 | this.$displayLink = document.createElement('a'); 10 | this.$displayLink.setAttribute('href', '#'); 11 | this.$displayLink.textContent = 'Syntax-Directed Operations'; 12 | this.$displayLink.addEventListener('click', e => { 13 | e.preventDefault(); 14 | e.stopPropagation(); 15 | referencePane.showSDOs(sdoMap[this.$alternativeId] || {}, this.$alternativeId); 16 | }); 17 | this.$container.appendChild(this.$displayLink); 18 | this.$outer.appendChild(this.$container); 19 | document.body.appendChild(this.$outer); 20 | }, 21 | 22 | activate(el) { 23 | clearTimeout(this.deactiveTimeout); 24 | Toolbox.deactivate(); 25 | this.$alternativeId = el.id; 26 | let numSdos = Object.keys(sdoMap[this.$alternativeId] || {}).length; 27 | this.$displayLink.textContent = 'Syntax-Directed Operations (' + numSdos + ')'; 28 | this.$outer.classList.add('active'); 29 | let top = el.offsetTop - this.$outer.offsetHeight; 30 | let left = el.offsetLeft + 50 - 10; // 50px = padding-left(=75px) + text-indent(=-25px) 31 | this.$outer.setAttribute('style', 'left: ' + left + 'px; top: ' + top + 'px'); 32 | if (top < document.body.scrollTop) { 33 | this.$container.scrollIntoView(); 34 | } 35 | }, 36 | 37 | deactivate() { 38 | clearTimeout(this.deactiveTimeout); 39 | this.$outer.classList.remove('active'); 40 | }, 41 | }; 42 | 43 | document.addEventListener('DOMContentLoaded', () => { 44 | if (typeof sdoMap == 'undefined') { 45 | console.error('could not find sdo map'); 46 | return; 47 | } 48 | sdoBox.init(); 49 | 50 | let insideTooltip = false; 51 | sdoBox.$outer.addEventListener('pointerenter', () => { 52 | insideTooltip = true; 53 | }); 54 | sdoBox.$outer.addEventListener('pointerleave', () => { 55 | insideTooltip = false; 56 | sdoBox.deactivate(); 57 | }); 58 | 59 | sdoBox.deactiveTimeout = null; 60 | [].forEach.call(document.querySelectorAll('emu-grammar[type=definition] emu-rhs'), node => { 61 | node.addEventListener('pointerenter', function () { 62 | sdoBox.activate(this); 63 | }); 64 | 65 | node.addEventListener('pointerleave', () => { 66 | sdoBox.deactiveTimeout = setTimeout(() => { 67 | if (!insideTooltip) { 68 | sdoBox.deactivate(); 69 | } 70 | }, 500); 71 | }); 72 | }); 73 | 74 | document.addEventListener( 75 | 'keydown', 76 | debounce(e => { 77 | if (e.code === 'Escape') { 78 | sdoBox.deactivate(); 79 | } 80 | }), 81 | ); 82 | }); 83 | 84 | 'use strict'; 85 | function Search(menu) { 86 | this.menu = menu; 87 | this.$search = document.getElementById('menu-search'); 88 | this.$searchBox = document.getElementById('menu-search-box'); 89 | this.$searchResults = document.getElementById('menu-search-results'); 90 | 91 | this.loadBiblio(); 92 | 93 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 94 | 95 | this.$searchBox.addEventListener( 96 | 'keydown', 97 | debounce(this.searchBoxKeydown.bind(this), { stopPropagation: true }), 98 | ); 99 | this.$searchBox.addEventListener( 100 | 'keyup', 101 | debounce(this.searchBoxKeyup.bind(this), { stopPropagation: true }), 102 | ); 103 | 104 | // Perform an initial search if the box is not empty. 105 | if (this.$searchBox.value) { 106 | this.search(this.$searchBox.value); 107 | } 108 | } 109 | 110 | Search.prototype.loadBiblio = function () { 111 | if (typeof biblio === 'undefined') { 112 | console.error('could not find biblio'); 113 | this.biblio = { refToClause: {}, entries: [] }; 114 | } else { 115 | this.biblio = biblio; 116 | this.biblio.clauses = this.biblio.entries.filter(e => e.type === 'clause'); 117 | this.biblio.byId = this.biblio.entries.reduce((map, entry) => { 118 | map[entry.id] = entry; 119 | return map; 120 | }, {}); 121 | let refParentClause = Object.create(null); 122 | this.biblio.refParentClause = refParentClause; 123 | let refsByClause = this.biblio.refsByClause; 124 | Object.keys(refsByClause).forEach(clause => { 125 | refsByClause[clause].forEach(ref => { 126 | refParentClause[ref] = clause; 127 | }); 128 | }); 129 | } 130 | }; 131 | 132 | Search.prototype.documentKeydown = function (e) { 133 | if (e.key === '/') { 134 | e.preventDefault(); 135 | e.stopPropagation(); 136 | this.triggerSearch(); 137 | } 138 | }; 139 | 140 | Search.prototype.searchBoxKeydown = function (e) { 141 | e.stopPropagation(); 142 | e.preventDefault(); 143 | if (e.keyCode === 191 && e.target.value.length === 0) { 144 | e.preventDefault(); 145 | } else if (e.keyCode === 13) { 146 | e.preventDefault(); 147 | this.selectResult(); 148 | } 149 | }; 150 | 151 | Search.prototype.searchBoxKeyup = function (e) { 152 | if (e.keyCode === 13 || e.keyCode === 9) { 153 | return; 154 | } 155 | 156 | this.search(e.target.value); 157 | }; 158 | 159 | Search.prototype.triggerSearch = function () { 160 | if (this.menu.isVisible()) { 161 | this._closeAfterSearch = false; 162 | } else { 163 | this._closeAfterSearch = true; 164 | this.menu.show(); 165 | } 166 | 167 | this.$searchBox.focus(); 168 | this.$searchBox.select(); 169 | }; 170 | // bit 12 - Set if the result starts with searchString 171 | // bits 8-11: 8 - number of chunks multiplied by 2 if cases match, otherwise 1. 172 | // bits 1-7: 127 - length of the entry 173 | // General scheme: prefer case sensitive matches with fewer chunks, and otherwise 174 | // prefer shorter matches. 175 | function relevance(result) { 176 | let relevance = 0; 177 | 178 | relevance = Math.max(0, 8 - result.match.chunks) << 7; 179 | 180 | if (result.match.caseMatch) { 181 | relevance *= 2; 182 | } 183 | 184 | if (result.match.prefix) { 185 | relevance += 2048; 186 | } 187 | 188 | relevance += Math.max(0, 255 - result.key.length); 189 | 190 | return relevance; 191 | } 192 | 193 | Search.prototype.search = function (searchString) { 194 | if (searchString === '') { 195 | this.displayResults([]); 196 | this.hideSearch(); 197 | return; 198 | } else { 199 | this.showSearch(); 200 | } 201 | 202 | if (searchString.length === 1) { 203 | this.displayResults([]); 204 | return; 205 | } 206 | 207 | let results; 208 | 209 | if (/^[\d.]*$/.test(searchString)) { 210 | results = this.biblio.clauses 211 | .filter(clause => clause.number.substring(0, searchString.length) === searchString) 212 | .map(clause => ({ key: getKey(clause), entry: clause })); 213 | } else { 214 | results = []; 215 | 216 | for (let i = 0; i < this.biblio.entries.length; i++) { 217 | let entry = this.biblio.entries[i]; 218 | let key = getKey(entry); 219 | if (!key) { 220 | // biblio entries without a key aren't searchable 221 | continue; 222 | } 223 | 224 | let match = fuzzysearch(searchString, key); 225 | if (match) { 226 | results.push({ key, entry, match }); 227 | } 228 | } 229 | 230 | results.forEach(result => { 231 | result.relevance = relevance(result, searchString); 232 | }); 233 | 234 | results = results.sort((a, b) => b.relevance - a.relevance); 235 | } 236 | 237 | if (results.length > 50) { 238 | results = results.slice(0, 50); 239 | } 240 | 241 | this.displayResults(results); 242 | }; 243 | Search.prototype.hideSearch = function () { 244 | this.$search.classList.remove('active'); 245 | }; 246 | 247 | Search.prototype.showSearch = function () { 248 | this.$search.classList.add('active'); 249 | }; 250 | 251 | Search.prototype.selectResult = function () { 252 | let $first = this.$searchResults.querySelector('li:first-child a'); 253 | 254 | if ($first) { 255 | document.location = $first.getAttribute('href'); 256 | } 257 | 258 | this.$searchBox.value = ''; 259 | this.$searchBox.blur(); 260 | this.displayResults([]); 261 | this.hideSearch(); 262 | 263 | if (this._closeAfterSearch) { 264 | this.menu.hide(); 265 | } 266 | }; 267 | 268 | Search.prototype.displayResults = function (results) { 269 | if (results.length > 0) { 270 | this.$searchResults.classList.remove('no-results'); 271 | 272 | let html = ''; 306 | 307 | this.$searchResults.innerHTML = html; 308 | } else { 309 | this.$searchResults.innerHTML = ''; 310 | this.$searchResults.classList.add('no-results'); 311 | } 312 | }; 313 | 314 | function getKey(item) { 315 | if (item.key) { 316 | return item.key; 317 | } 318 | switch (item.type) { 319 | case 'clause': 320 | return item.title || item.titleHTML; 321 | case 'production': 322 | return item.name; 323 | case 'op': 324 | return item.aoid; 325 | case 'term': 326 | return item.term; 327 | case 'table': 328 | case 'figure': 329 | case 'example': 330 | case 'note': 331 | return item.caption; 332 | case 'step': 333 | return item.id; 334 | default: 335 | throw new Error("Can't get key for " + item.type); 336 | } 337 | } 338 | 339 | function Menu() { 340 | this.$toggle = document.getElementById('menu-toggle'); 341 | this.$menu = document.getElementById('menu'); 342 | this.$toc = document.querySelector('menu-toc > ol'); 343 | this.$pins = document.querySelector('#menu-pins'); 344 | this.$pinList = document.getElementById('menu-pins-list'); 345 | this.$toc = document.querySelector('#menu-toc > ol'); 346 | this.$specContainer = document.getElementById('spec-container'); 347 | this.search = new Search(this); 348 | 349 | this._pinnedIds = {}; 350 | this.loadPinEntries(); 351 | 352 | // unpin all button 353 | document 354 | .querySelector('#menu-pins .unpin-all') 355 | .addEventListener('click', this.unpinAll.bind(this)); 356 | 357 | // individual unpinning buttons 358 | this.$pinList.addEventListener('click', this.pinListClick.bind(this)); 359 | 360 | // toggle menu 361 | this.$toggle.addEventListener('click', this.toggle.bind(this)); 362 | 363 | // keydown events for pinned clauses 364 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 365 | 366 | // toc expansion 367 | let tocItems = this.$menu.querySelectorAll('#menu-toc li'); 368 | for (let i = 0; i < tocItems.length; i++) { 369 | let $item = tocItems[i]; 370 | $item.addEventListener('click', event => { 371 | $item.classList.toggle('active'); 372 | event.stopPropagation(); 373 | }); 374 | } 375 | 376 | // close toc on toc item selection 377 | let tocLinks = this.$menu.querySelectorAll('#menu-toc li > a'); 378 | for (let i = 0; i < tocLinks.length; i++) { 379 | let $link = tocLinks[i]; 380 | $link.addEventListener('click', event => { 381 | this.toggle(); 382 | event.stopPropagation(); 383 | }); 384 | } 385 | 386 | // update active clause on scroll 387 | window.addEventListener('scroll', debounce(this.updateActiveClause.bind(this))); 388 | this.updateActiveClause(); 389 | 390 | // prevent menu scrolling from scrolling the body 391 | this.$toc.addEventListener('wheel', e => { 392 | let target = e.currentTarget; 393 | let offTop = e.deltaY < 0 && target.scrollTop === 0; 394 | if (offTop) { 395 | e.preventDefault(); 396 | } 397 | let offBottom = e.deltaY > 0 && target.offsetHeight + target.scrollTop >= target.scrollHeight; 398 | 399 | if (offBottom) { 400 | e.preventDefault(); 401 | } 402 | }); 403 | } 404 | 405 | Menu.prototype.documentKeydown = function (e) { 406 | e.stopPropagation(); 407 | if (e.keyCode === 80) { 408 | this.togglePinEntry(); 409 | } else if (e.keyCode >= 48 && e.keyCode < 58) { 410 | this.selectPin((e.keyCode - 9) % 10); 411 | } 412 | }; 413 | 414 | Menu.prototype.updateActiveClause = function () { 415 | this.setActiveClause(findActiveClause(this.$specContainer)); 416 | }; 417 | 418 | Menu.prototype.setActiveClause = function (clause) { 419 | this.$activeClause = clause; 420 | this.revealInToc(this.$activeClause); 421 | }; 422 | 423 | Menu.prototype.revealInToc = function (path) { 424 | let current = this.$toc.querySelectorAll('li.revealed'); 425 | for (let i = 0; i < current.length; i++) { 426 | current[i].classList.remove('revealed'); 427 | current[i].classList.remove('revealed-leaf'); 428 | } 429 | 430 | current = this.$toc; 431 | let index = 0; 432 | outer: while (index < path.length) { 433 | let children = current.children; 434 | for (let i = 0; i < children.length; i++) { 435 | if ('#' + path[index].id === children[i].children[1].hash) { 436 | children[i].classList.add('revealed'); 437 | if (index === path.length - 1) { 438 | children[i].classList.add('revealed-leaf'); 439 | let rect = children[i].getBoundingClientRect(); 440 | // this.$toc.getBoundingClientRect().top; 441 | let tocRect = this.$toc.getBoundingClientRect(); 442 | if (rect.top + 10 > tocRect.bottom) { 443 | this.$toc.scrollTop = 444 | this.$toc.scrollTop + (rect.top - tocRect.bottom) + (rect.bottom - rect.top); 445 | } else if (rect.top < tocRect.top) { 446 | this.$toc.scrollTop = this.$toc.scrollTop - (tocRect.top - rect.top); 447 | } 448 | } 449 | current = children[i].querySelector('ol'); 450 | index++; 451 | continue outer; 452 | } 453 | } 454 | console.log('could not find location in table of contents', path); 455 | break; 456 | } 457 | }; 458 | 459 | function findActiveClause(root, path) { 460 | path = path || []; 461 | 462 | let visibleClauses = getVisibleClauses(root, path); 463 | let midpoint = Math.floor(window.innerHeight / 2); 464 | 465 | for (let [$clause, path] of visibleClauses) { 466 | let { top: clauseTop, bottom: clauseBottom } = $clause.getBoundingClientRect(); 467 | let isFullyVisibleAboveTheFold = 468 | clauseTop > 0 && clauseTop < midpoint && clauseBottom < window.innerHeight; 469 | if (isFullyVisibleAboveTheFold) { 470 | return path; 471 | } 472 | } 473 | 474 | visibleClauses.sort(([, pathA], [, pathB]) => pathB.length - pathA.length); 475 | for (let [$clause, path] of visibleClauses) { 476 | let { top: clauseTop, bottom: clauseBottom } = $clause.getBoundingClientRect(); 477 | let $header = $clause.querySelector('h1'); 478 | let clauseStyles = getComputedStyle($clause); 479 | let marginTop = Math.max( 480 | 0, 481 | parseInt(clauseStyles['margin-top']), 482 | parseInt(getComputedStyle($header)['margin-top']), 483 | ); 484 | let marginBottom = Math.max(0, parseInt(clauseStyles['margin-bottom'])); 485 | let crossesMidpoint = 486 | clauseTop - marginTop <= midpoint && clauseBottom + marginBottom >= midpoint; 487 | if (crossesMidpoint) { 488 | return path; 489 | } 490 | } 491 | 492 | return path; 493 | } 494 | 495 | function getVisibleClauses(root, path) { 496 | let childClauses = getChildClauses(root); 497 | path = path || []; 498 | 499 | let result = []; 500 | 501 | let seenVisibleClause = false; 502 | for (let $clause of childClauses) { 503 | let { top: clauseTop, bottom: clauseBottom } = $clause.getBoundingClientRect(); 504 | let isPartiallyVisible = 505 | (clauseTop > 0 && clauseTop < window.innerHeight) || 506 | (clauseBottom > 0 && clauseBottom < window.innerHeight) || 507 | (clauseTop < 0 && clauseBottom > window.innerHeight); 508 | 509 | if (isPartiallyVisible) { 510 | seenVisibleClause = true; 511 | let innerPath = path.concat($clause); 512 | result.push([$clause, innerPath]); 513 | result.push(...getVisibleClauses($clause, innerPath)); 514 | } else if (seenVisibleClause) { 515 | break; 516 | } 517 | } 518 | 519 | return result; 520 | } 521 | 522 | function* getChildClauses(root) { 523 | for (let el of root.children) { 524 | switch (el.nodeName) { 525 | // descend into 526 | case 'EMU-IMPORT': 527 | yield* getChildClauses(el); 528 | break; 529 | 530 | // accept , , and 531 | case 'EMU-CLAUSE': 532 | case 'EMU-INTRO': 533 | case 'EMU-ANNEX': 534 | yield el; 535 | } 536 | } 537 | } 538 | 539 | Menu.prototype.toggle = function () { 540 | this.$menu.classList.toggle('active'); 541 | }; 542 | 543 | Menu.prototype.show = function () { 544 | this.$menu.classList.add('active'); 545 | }; 546 | 547 | Menu.prototype.hide = function () { 548 | this.$menu.classList.remove('active'); 549 | }; 550 | 551 | Menu.prototype.isVisible = function () { 552 | return this.$menu.classList.contains('active'); 553 | }; 554 | 555 | Menu.prototype.showPins = function () { 556 | this.$pins.classList.add('active'); 557 | }; 558 | 559 | Menu.prototype.hidePins = function () { 560 | this.$pins.classList.remove('active'); 561 | }; 562 | 563 | Menu.prototype.addPinEntry = function (id) { 564 | let entry = this.search.biblio.byId[id]; 565 | if (!entry) { 566 | // id was deleted after pin (or something) so remove it 567 | delete this._pinnedIds[id]; 568 | this.persistPinEntries(); 569 | return; 570 | } 571 | 572 | let text; 573 | if (entry.type === 'clause') { 574 | let prefix; 575 | if (entry.number) { 576 | prefix = entry.number + ' '; 577 | } else { 578 | prefix = ''; 579 | } 580 | text = `${prefix}${entry.titleHTML}`; 581 | } else { 582 | text = getKey(entry); 583 | } 584 | 585 | let link = `${text}`; 586 | this.$pinList.innerHTML += `
  • ${link}
  • `; 587 | 588 | if (Object.keys(this._pinnedIds).length === 0) { 589 | this.showPins(); 590 | } 591 | this._pinnedIds[id] = true; 592 | this.persistPinEntries(); 593 | }; 594 | 595 | Menu.prototype.removePinEntry = function (id) { 596 | let item = this.$pinList.querySelector(`li[data-section-id="${id}"]`); 597 | this.$pinList.removeChild(item); 598 | delete this._pinnedIds[id]; 599 | if (Object.keys(this._pinnedIds).length === 0) { 600 | this.hidePins(); 601 | } 602 | 603 | this.persistPinEntries(); 604 | }; 605 | 606 | Menu.prototype.unpinAll = function () { 607 | for (let id of Object.keys(this._pinnedIds)) { 608 | this.removePinEntry(id); 609 | } 610 | }; 611 | 612 | Menu.prototype.pinListClick = function (event) { 613 | if (event?.target?.classList.contains('unpin')) { 614 | let id = event.target.parentNode.dataset.sectionId; 615 | if (id) { 616 | this.removePinEntry(id); 617 | } 618 | } 619 | }; 620 | 621 | Menu.prototype.persistPinEntries = function () { 622 | try { 623 | if (!window.localStorage) return; 624 | } catch (e) { 625 | return; 626 | } 627 | 628 | localStorage.pinEntries = JSON.stringify(Object.keys(this._pinnedIds)); 629 | }; 630 | 631 | Menu.prototype.loadPinEntries = function () { 632 | try { 633 | if (!window.localStorage) return; 634 | } catch (e) { 635 | return; 636 | } 637 | 638 | let pinsString = window.localStorage.pinEntries; 639 | if (!pinsString) return; 640 | let pins = JSON.parse(pinsString); 641 | for (let i = 0; i < pins.length; i++) { 642 | this.addPinEntry(pins[i]); 643 | } 644 | }; 645 | 646 | Menu.prototype.togglePinEntry = function (id) { 647 | if (!id) { 648 | id = this.$activeClause[this.$activeClause.length - 1].id; 649 | } 650 | 651 | if (this._pinnedIds[id]) { 652 | this.removePinEntry(id); 653 | } else { 654 | this.addPinEntry(id); 655 | } 656 | }; 657 | 658 | Menu.prototype.selectPin = function (num) { 659 | if (num >= this.$pinList.children.length) return; 660 | document.location = this.$pinList.children[num].children[0].href; 661 | }; 662 | 663 | let menu; 664 | 665 | document.addEventListener('DOMContentLoaded', init); 666 | 667 | function debounce(fn, opts) { 668 | opts = opts || {}; 669 | let timeout; 670 | return function (e) { 671 | if (opts.stopPropagation) { 672 | e.stopPropagation(); 673 | } 674 | let args = arguments; 675 | if (timeout) { 676 | clearTimeout(timeout); 677 | } 678 | timeout = setTimeout(() => { 679 | timeout = null; 680 | fn.apply(this, args); 681 | }, 150); 682 | }; 683 | } 684 | 685 | let CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; 686 | function findContainer($elem) { 687 | let parentClause = $elem.parentNode; 688 | while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { 689 | parentClause = parentClause.parentNode; 690 | } 691 | return parentClause; 692 | } 693 | 694 | function findLocalReferences(parentClause, name) { 695 | let vars = parentClause.querySelectorAll('var'); 696 | let references = []; 697 | 698 | for (let i = 0; i < vars.length; i++) { 699 | let $var = vars[i]; 700 | 701 | if ($var.innerHTML === name) { 702 | references.push($var); 703 | } 704 | } 705 | 706 | return references; 707 | } 708 | 709 | let REFERENCED_CLASSES = Array.from({ length: 7 }, (x, i) => `referenced${i}`); 710 | function chooseHighlightIndex(parentClause) { 711 | let counts = REFERENCED_CLASSES.map($class => parentClause.getElementsByClassName($class).length); 712 | // Find the earliest index with the lowest count. 713 | let minCount = Infinity; 714 | let index = null; 715 | for (let i = 0; i < counts.length; i++) { 716 | if (counts[i] < minCount) { 717 | minCount = counts[i]; 718 | index = i; 719 | } 720 | } 721 | return index; 722 | } 723 | 724 | function toggleFindLocalReferences($elem) { 725 | let parentClause = findContainer($elem); 726 | let references = findLocalReferences(parentClause, $elem.innerHTML); 727 | if ($elem.classList.contains('referenced')) { 728 | references.forEach($reference => { 729 | $reference.classList.remove('referenced', ...REFERENCED_CLASSES); 730 | }); 731 | } else { 732 | let index = chooseHighlightIndex(parentClause); 733 | references.forEach($reference => { 734 | $reference.classList.add('referenced', `referenced${index}`); 735 | }); 736 | } 737 | } 738 | 739 | function installFindLocalReferences() { 740 | document.addEventListener('click', e => { 741 | if (e.target.nodeName === 'VAR') { 742 | toggleFindLocalReferences(e.target); 743 | } 744 | }); 745 | } 746 | 747 | document.addEventListener('DOMContentLoaded', installFindLocalReferences); 748 | 749 | // The following license applies to the fuzzysearch function 750 | // The MIT License (MIT) 751 | // Copyright © 2015 Nicolas Bevacqua 752 | // Copyright © 2016 Brian Terlson 753 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 754 | // this software and associated documentation files (the "Software"), to deal in 755 | // the Software without restriction, including without limitation the rights to 756 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 757 | // the Software, and to permit persons to whom the Software is furnished to do so, 758 | // subject to the following conditions: 759 | 760 | // The above copyright notice and this permission notice shall be included in all 761 | // copies or substantial portions of the Software. 762 | 763 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 764 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 765 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 766 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 767 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 768 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 769 | function fuzzysearch(searchString, haystack, caseInsensitive) { 770 | let tlen = haystack.length; 771 | let qlen = searchString.length; 772 | let chunks = 1; 773 | let finding = false; 774 | 775 | if (qlen > tlen) { 776 | return false; 777 | } 778 | 779 | if (qlen === tlen) { 780 | if (searchString === haystack) { 781 | return { caseMatch: true, chunks: 1, prefix: true }; 782 | } else if (searchString.toLowerCase() === haystack.toLowerCase()) { 783 | return { caseMatch: false, chunks: 1, prefix: true }; 784 | } else { 785 | return false; 786 | } 787 | } 788 | 789 | let j = 0; 790 | outer: for (let i = 0; i < qlen; i++) { 791 | let nch = searchString[i]; 792 | while (j < tlen) { 793 | let targetChar = haystack[j++]; 794 | if (targetChar === nch) { 795 | finding = true; 796 | continue outer; 797 | } 798 | if (finding) { 799 | chunks++; 800 | finding = false; 801 | } 802 | } 803 | 804 | if (caseInsensitive) { 805 | return false; 806 | } 807 | 808 | return fuzzysearch(searchString.toLowerCase(), haystack.toLowerCase(), true); 809 | } 810 | 811 | return { caseMatch: !caseInsensitive, chunks, prefix: j <= qlen }; 812 | } 813 | 814 | let referencePane = { 815 | init() { 816 | this.$container = document.createElement('div'); 817 | this.$container.setAttribute('id', 'references-pane-container'); 818 | 819 | let $spacer = document.createElement('div'); 820 | $spacer.setAttribute('id', 'references-pane-spacer'); 821 | $spacer.classList.add('menu-spacer'); 822 | 823 | this.$pane = document.createElement('div'); 824 | this.$pane.setAttribute('id', 'references-pane'); 825 | 826 | this.$container.appendChild($spacer); 827 | this.$container.appendChild(this.$pane); 828 | 829 | this.$header = document.createElement('div'); 830 | this.$header.classList.add('menu-pane-header'); 831 | this.$headerText = document.createElement('span'); 832 | this.$header.appendChild(this.$headerText); 833 | this.$headerRefId = document.createElement('a'); 834 | this.$header.appendChild(this.$headerRefId); 835 | this.$header.addEventListener('pointerdown', e => { 836 | this.dragStart(e); 837 | }); 838 | 839 | this.$closeButton = document.createElement('span'); 840 | this.$closeButton.setAttribute('id', 'references-pane-close'); 841 | this.$closeButton.addEventListener('click', () => { 842 | this.deactivate(); 843 | }); 844 | this.$header.appendChild(this.$closeButton); 845 | 846 | this.$pane.appendChild(this.$header); 847 | this.$tableContainer = document.createElement('div'); 848 | this.$tableContainer.setAttribute('id', 'references-pane-table-container'); 849 | 850 | this.$table = document.createElement('table'); 851 | this.$table.setAttribute('id', 'references-pane-table'); 852 | 853 | this.$tableBody = this.$table.createTBody(); 854 | 855 | this.$tableContainer.appendChild(this.$table); 856 | this.$pane.appendChild(this.$tableContainer); 857 | 858 | if (menu != null) { 859 | menu.$specContainer.appendChild(this.$container); 860 | } 861 | }, 862 | 863 | activate() { 864 | this.$container.classList.add('active'); 865 | }, 866 | 867 | deactivate() { 868 | this.$container.classList.remove('active'); 869 | this.state = null; 870 | }, 871 | 872 | showReferencesFor(entry) { 873 | this.activate(); 874 | this.state = { type: 'ref', id: entry.id }; 875 | this.$headerText.textContent = 'References to '; 876 | let newBody = document.createElement('tbody'); 877 | let previousId; 878 | let previousCell; 879 | let dupCount = 0; 880 | this.$headerRefId.innerHTML = getKey(entry); 881 | this.$headerRefId.setAttribute('href', makeLinkToId(entry.id)); 882 | this.$headerRefId.style.display = 'inline'; 883 | (entry.referencingIds || []) 884 | .map(id => { 885 | let cid = menu.search.biblio.refParentClause[id]; 886 | let clause = menu.search.biblio.byId[cid]; 887 | if (clause == null) { 888 | throw new Error('could not find clause for id ' + cid); 889 | } 890 | return { id, clause }; 891 | }) 892 | .sort((a, b) => sortByClauseNumber(a.clause, b.clause)) 893 | .forEach(record => { 894 | if (previousId === record.clause.id) { 895 | previousCell.innerHTML += ` (${dupCount + 2})`; 896 | dupCount++; 897 | } else { 898 | let row = newBody.insertRow(); 899 | let cell = row.insertCell(); 900 | cell.innerHTML = record.clause.number; 901 | cell = row.insertCell(); 902 | cell.innerHTML = `${record.clause.titleHTML}`; 903 | previousCell = cell; 904 | previousId = record.clause.id; 905 | dupCount = 0; 906 | } 907 | }, this); 908 | this.$table.removeChild(this.$tableBody); 909 | this.$tableBody = newBody; 910 | this.$table.appendChild(this.$tableBody); 911 | this.autoSize(); 912 | }, 913 | 914 | showSDOs(sdos, alternativeId) { 915 | let rhs = document.getElementById(alternativeId); 916 | let parentName = rhs.parentNode.getAttribute('name'); 917 | let colons = rhs.parentNode.querySelector('emu-geq'); 918 | rhs = rhs.cloneNode(true); 919 | rhs.querySelectorAll('emu-params,emu-constraints').forEach(e => { 920 | e.remove(); 921 | }); 922 | rhs.querySelectorAll('[id]').forEach(e => { 923 | e.removeAttribute('id'); 924 | }); 925 | rhs.querySelectorAll('a').forEach(e => { 926 | e.parentNode.replaceChild(document.createTextNode(e.textContent), e); 927 | }); 928 | 929 | this.$headerText.innerHTML = `Syntax-Directed Operations for
    ${parentName} ${colons.outerHTML} `; 930 | this.$headerText.querySelector('a').append(rhs); 931 | this.showSDOsBody(sdos, alternativeId); 932 | }, 933 | 934 | showSDOsBody(sdos, alternativeId) { 935 | this.activate(); 936 | this.state = { type: 'sdo', id: alternativeId, html: this.$headerText.innerHTML }; 937 | this.$headerRefId.style.display = 'none'; 938 | let newBody = document.createElement('tbody'); 939 | Object.keys(sdos).forEach(sdoName => { 940 | let pair = sdos[sdoName]; 941 | let clause = pair.clause; 942 | let ids = pair.ids; 943 | let first = ids[0]; 944 | let row = newBody.insertRow(); 945 | let cell = row.insertCell(); 946 | cell.innerHTML = clause; 947 | cell = row.insertCell(); 948 | let html = '' + sdoName + ''; 949 | for (let i = 1; i < ids.length; ++i) { 950 | html += ' (' + (i + 1) + ')'; 951 | } 952 | cell.innerHTML = html; 953 | }); 954 | this.$table.removeChild(this.$tableBody); 955 | this.$tableBody = newBody; 956 | this.$table.appendChild(this.$tableBody); 957 | this.autoSize(); 958 | }, 959 | 960 | autoSize() { 961 | this.$tableContainer.style.height = 962 | Math.min(250, this.$table.getBoundingClientRect().height) + 'px'; 963 | }, 964 | 965 | dragStart(pointerDownEvent) { 966 | let startingMousePos = pointerDownEvent.clientY; 967 | let startingHeight = this.$tableContainer.getBoundingClientRect().height; 968 | let moveListener = pointerMoveEvent => { 969 | if (pointerMoveEvent.buttons === 0) { 970 | removeListeners(); 971 | return; 972 | } 973 | let desiredHeight = startingHeight - (pointerMoveEvent.clientY - startingMousePos); 974 | this.$tableContainer.style.height = Math.max(0, desiredHeight) + 'px'; 975 | }; 976 | let listenerOptions = { capture: true, passive: true }; 977 | let removeListeners = () => { 978 | document.removeEventListener('pointermove', moveListener, listenerOptions); 979 | this.$header.removeEventListener('pointerup', removeListeners, listenerOptions); 980 | this.$header.removeEventListener('pointercancel', removeListeners, listenerOptions); 981 | }; 982 | document.addEventListener('pointermove', moveListener, listenerOptions); 983 | this.$header.addEventListener('pointerup', removeListeners, listenerOptions); 984 | this.$header.addEventListener('pointercancel', removeListeners, listenerOptions); 985 | }, 986 | }; 987 | 988 | let Toolbox = { 989 | init() { 990 | this.$outer = document.createElement('div'); 991 | this.$outer.classList.add('toolbox-container'); 992 | this.$container = document.createElement('div'); 993 | this.$container.classList.add('toolbox'); 994 | this.$outer.appendChild(this.$container); 995 | this.$permalink = document.createElement('a'); 996 | this.$permalink.textContent = 'Permalink'; 997 | this.$pinLink = document.createElement('a'); 998 | this.$pinLink.textContent = 'Pin'; 999 | this.$pinLink.setAttribute('href', '#'); 1000 | this.$pinLink.addEventListener('click', e => { 1001 | e.preventDefault(); 1002 | e.stopPropagation(); 1003 | menu.togglePinEntry(this.entry.id); 1004 | this.$pinLink.textContent = menu._pinnedIds[this.entry.id] ? 'Unpin' : 'Pin'; 1005 | }); 1006 | 1007 | this.$refsLink = document.createElement('a'); 1008 | this.$refsLink.setAttribute('href', '#'); 1009 | this.$refsLink.addEventListener('click', e => { 1010 | e.preventDefault(); 1011 | e.stopPropagation(); 1012 | referencePane.showReferencesFor(this.entry); 1013 | }); 1014 | this.$container.appendChild(this.$permalink); 1015 | this.$container.appendChild(document.createTextNode(' ')); 1016 | this.$container.appendChild(this.$pinLink); 1017 | this.$container.appendChild(document.createTextNode(' ')); 1018 | this.$container.appendChild(this.$refsLink); 1019 | document.body.appendChild(this.$outer); 1020 | }, 1021 | 1022 | activate(el, entry, target) { 1023 | if (el === this._activeEl) return; 1024 | sdoBox.deactivate(); 1025 | this.active = true; 1026 | this.entry = entry; 1027 | this.$pinLink.textContent = menu._pinnedIds[entry.id] ? 'Unpin' : 'Pin'; 1028 | this.$outer.classList.add('active'); 1029 | this.top = el.offsetTop - this.$outer.offsetHeight; 1030 | this.left = el.offsetLeft - 10; 1031 | this.$outer.setAttribute('style', 'left: ' + this.left + 'px; top: ' + this.top + 'px'); 1032 | this.updatePermalink(); 1033 | this.updateReferences(); 1034 | this._activeEl = el; 1035 | if (this.top < document.body.scrollTop && el === target) { 1036 | // don't scroll unless it's a small thing (< 200px) 1037 | this.$outer.scrollIntoView(); 1038 | } 1039 | }, 1040 | 1041 | updatePermalink() { 1042 | this.$permalink.setAttribute('href', makeLinkToId(this.entry.id)); 1043 | }, 1044 | 1045 | updateReferences() { 1046 | this.$refsLink.textContent = `References (${(this.entry.referencingIds || []).length})`; 1047 | }, 1048 | 1049 | activateIfMouseOver(e) { 1050 | let ref = this.findReferenceUnder(e.target); 1051 | if (ref && (!this.active || e.pageY > this._activeEl.offsetTop)) { 1052 | let entry = menu.search.biblio.byId[ref.id]; 1053 | this.activate(ref.element, entry, e.target); 1054 | } else if ( 1055 | this.active && 1056 | (e.pageY < this.top || e.pageY > this._activeEl.offsetTop + this._activeEl.offsetHeight) 1057 | ) { 1058 | this.deactivate(); 1059 | } 1060 | }, 1061 | 1062 | findReferenceUnder(el) { 1063 | while (el) { 1064 | let parent = el.parentNode; 1065 | if (el.nodeName === 'EMU-RHS' || el.nodeName === 'EMU-PRODUCTION') { 1066 | return null; 1067 | } 1068 | if ( 1069 | el.nodeName === 'H1' && 1070 | parent.nodeName.match(/EMU-CLAUSE|EMU-ANNEX|EMU-INTRO/) && 1071 | parent.id 1072 | ) { 1073 | return { element: el, id: parent.id }; 1074 | } else if (el.nodeName === 'EMU-NT') { 1075 | if ( 1076 | parent.nodeName === 'EMU-PRODUCTION' && 1077 | parent.id && 1078 | parent.id[0] !== '_' && 1079 | parent.firstElementChild === el 1080 | ) { 1081 | // return the LHS non-terminal element 1082 | return { element: el, id: parent.id }; 1083 | } 1084 | return null; 1085 | } else if ( 1086 | el.nodeName.match(/EMU-(?!CLAUSE|XREF|ANNEX|INTRO)|DFN/) && 1087 | el.id && 1088 | el.id[0] !== '_' 1089 | ) { 1090 | if ( 1091 | el.nodeName === 'EMU-FIGURE' || 1092 | el.nodeName === 'EMU-TABLE' || 1093 | el.nodeName === 'EMU-EXAMPLE' 1094 | ) { 1095 | // return the figcaption element 1096 | return { element: el.children[0].children[0], id: el.id }; 1097 | } else { 1098 | return { element: el, id: el.id }; 1099 | } 1100 | } 1101 | el = parent; 1102 | } 1103 | }, 1104 | 1105 | deactivate() { 1106 | this.$outer.classList.remove('active'); 1107 | this._activeEl = null; 1108 | this.active = false; 1109 | }, 1110 | }; 1111 | 1112 | function sortByClauseNumber(clause1, clause2) { 1113 | let c1c = clause1.number.split('.'); 1114 | let c2c = clause2.number.split('.'); 1115 | 1116 | for (let i = 0; i < c1c.length; i++) { 1117 | if (i >= c2c.length) { 1118 | return 1; 1119 | } 1120 | 1121 | let c1 = c1c[i]; 1122 | let c2 = c2c[i]; 1123 | let c1cn = Number(c1); 1124 | let c2cn = Number(c2); 1125 | 1126 | if (Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 1127 | if (c1 > c2) { 1128 | return 1; 1129 | } else if (c1 < c2) { 1130 | return -1; 1131 | } 1132 | } else if (!Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 1133 | return -1; 1134 | } else if (Number.isNaN(c1cn) && !Number.isNaN(c2cn)) { 1135 | return 1; 1136 | } else if (c1cn > c2cn) { 1137 | return 1; 1138 | } else if (c1cn < c2cn) { 1139 | return -1; 1140 | } 1141 | } 1142 | 1143 | if (c1c.length === c2c.length) { 1144 | return 0; 1145 | } 1146 | return -1; 1147 | } 1148 | 1149 | function makeLinkToId(id) { 1150 | let hash = '#' + id; 1151 | if (typeof idToSection === 'undefined' || !idToSection[id]) { 1152 | return hash; 1153 | } 1154 | let targetSec = idToSection[id]; 1155 | return (targetSec === 'index' ? './' : targetSec + '.html') + hash; 1156 | } 1157 | 1158 | function doShortcut(e) { 1159 | if (!(e.target instanceof HTMLElement)) { 1160 | return; 1161 | } 1162 | let target = e.target; 1163 | let name = target.nodeName.toLowerCase(); 1164 | if (name === 'textarea' || name === 'input' || name === 'select' || target.isContentEditable) { 1165 | return; 1166 | } 1167 | if (e.altKey || e.ctrlKey || e.metaKey) { 1168 | return; 1169 | } 1170 | if (e.key === 'm' && usesMultipage) { 1171 | let pathParts = location.pathname.split('/'); 1172 | let hash = location.hash; 1173 | if (pathParts[pathParts.length - 2] === 'multipage') { 1174 | if (hash === '') { 1175 | let sectionName = pathParts[pathParts.length - 1]; 1176 | if (sectionName.endsWith('.html')) { 1177 | sectionName = sectionName.slice(0, -5); 1178 | } 1179 | if (idToSection['sec-' + sectionName] !== undefined) { 1180 | hash = '#sec-' + sectionName; 1181 | } 1182 | } 1183 | location = pathParts.slice(0, -2).join('/') + '/' + hash; 1184 | } else { 1185 | location = 'multipage/' + hash; 1186 | } 1187 | } else if (e.key === 'u') { 1188 | document.documentElement.classList.toggle('show-ao-annotations'); 1189 | } else if (e.key === '?') { 1190 | document.getElementById('shortcuts-help').classList.toggle('active'); 1191 | } 1192 | } 1193 | 1194 | function init() { 1195 | if (document.getElementById('menu') == null) { 1196 | return; 1197 | } 1198 | menu = new Menu(); 1199 | let $container = document.getElementById('spec-container'); 1200 | $container.addEventListener( 1201 | 'mouseover', 1202 | debounce(e => { 1203 | Toolbox.activateIfMouseOver(e); 1204 | }), 1205 | ); 1206 | document.addEventListener( 1207 | 'keydown', 1208 | debounce(e => { 1209 | if (e.code === 'Escape') { 1210 | if (Toolbox.active) { 1211 | Toolbox.deactivate(); 1212 | } 1213 | document.getElementById('shortcuts-help').classList.remove('active'); 1214 | } 1215 | }), 1216 | ); 1217 | } 1218 | 1219 | document.addEventListener('keypress', doShortcut); 1220 | 1221 | document.addEventListener('DOMContentLoaded', () => { 1222 | Toolbox.init(); 1223 | referencePane.init(); 1224 | }); 1225 | 1226 | // preserve state during navigation 1227 | 1228 | function getTocPath(li) { 1229 | let path = []; 1230 | let pointer = li; 1231 | while (true) { 1232 | let parent = pointer.parentElement; 1233 | if (parent == null) { 1234 | return null; 1235 | } 1236 | let index = [].indexOf.call(parent.children, pointer); 1237 | if (index == -1) { 1238 | return null; 1239 | } 1240 | path.unshift(index); 1241 | pointer = parent.parentElement; 1242 | if (pointer == null) { 1243 | return null; 1244 | } 1245 | if (pointer.id === 'menu-toc') { 1246 | break; 1247 | } 1248 | if (pointer.tagName !== 'LI') { 1249 | return null; 1250 | } 1251 | } 1252 | return path; 1253 | } 1254 | 1255 | function activateTocPath(path) { 1256 | try { 1257 | let pointer = document.getElementById('menu-toc'); 1258 | for (let index of path) { 1259 | pointer = pointer.querySelector('ol').children[index]; 1260 | } 1261 | pointer.classList.add('active'); 1262 | } catch (e) { 1263 | // pass 1264 | } 1265 | } 1266 | 1267 | function getActiveTocPaths() { 1268 | return [...menu.$menu.querySelectorAll('.active')].map(getTocPath).filter(p => p != null); 1269 | } 1270 | 1271 | function initTOCExpansion(visibleItemLimit) { 1272 | // Initialize to a reasonable amount of TOC expansion: 1273 | // * Expand any full-breadth nesting level up to visibleItemLimit. 1274 | // * Expand any *single-item* level while under visibleItemLimit (even if that pushes over it). 1275 | 1276 | // Limit to initialization by bailing out if any parent item is already expanded. 1277 | const tocItems = Array.from(document.querySelectorAll('#menu-toc li')); 1278 | if (tocItems.some(li => li.classList.contains('active') && li.querySelector('li'))) { 1279 | return; 1280 | } 1281 | 1282 | const selfAndSiblings = maybe => Array.from(maybe?.parentNode.children ?? []); 1283 | let currentLevelItems = selfAndSiblings(tocItems[0]); 1284 | let availableCount = visibleItemLimit - currentLevelItems.length; 1285 | while (availableCount > 0 && currentLevelItems.length) { 1286 | const nextLevelItems = currentLevelItems.flatMap(li => selfAndSiblings(li.querySelector('li'))); 1287 | availableCount -= nextLevelItems.length; 1288 | if (availableCount > 0 || currentLevelItems.length === 1) { 1289 | // Expand parent items of the next level down (i.e., current-level items with children). 1290 | for (const ol of new Set(nextLevelItems.map(li => li.parentNode))) { 1291 | ol.closest('li').classList.add('active'); 1292 | } 1293 | } 1294 | currentLevelItems = nextLevelItems; 1295 | } 1296 | } 1297 | 1298 | function initState() { 1299 | if (typeof menu === 'undefined' || window.navigating) { 1300 | return; 1301 | } 1302 | const storage = typeof sessionStorage !== 'undefined' ? sessionStorage : Object.create(null); 1303 | if (storage.referencePaneState != null) { 1304 | let state = JSON.parse(storage.referencePaneState); 1305 | if (state != null) { 1306 | if (state.type === 'ref') { 1307 | let entry = menu.search.biblio.byId[state.id]; 1308 | if (entry != null) { 1309 | referencePane.showReferencesFor(entry); 1310 | } 1311 | } else if (state.type === 'sdo') { 1312 | let sdos = sdoMap[state.id]; 1313 | if (sdos != null) { 1314 | referencePane.$headerText.innerHTML = state.html; 1315 | referencePane.showSDOsBody(sdos, state.id); 1316 | } 1317 | } 1318 | delete storage.referencePaneState; 1319 | } 1320 | } 1321 | 1322 | if (storage.activeTocPaths != null) { 1323 | document.querySelectorAll('#menu-toc li.active').forEach(li => li.classList.remove('active')); 1324 | let active = JSON.parse(storage.activeTocPaths); 1325 | active.forEach(activateTocPath); 1326 | delete storage.activeTocPaths; 1327 | } else { 1328 | initTOCExpansion(20); 1329 | } 1330 | 1331 | if (storage.searchValue != null) { 1332 | let value = JSON.parse(storage.searchValue); 1333 | menu.search.$searchBox.value = value; 1334 | menu.search.search(value); 1335 | delete storage.searchValue; 1336 | } 1337 | 1338 | if (storage.tocScroll != null) { 1339 | let tocScroll = JSON.parse(storage.tocScroll); 1340 | menu.$toc.scrollTop = tocScroll; 1341 | delete storage.tocScroll; 1342 | } 1343 | } 1344 | 1345 | document.addEventListener('DOMContentLoaded', initState); 1346 | 1347 | window.addEventListener('pageshow', initState); 1348 | 1349 | window.addEventListener('beforeunload', () => { 1350 | if (!window.sessionStorage || typeof menu === 'undefined') { 1351 | return; 1352 | } 1353 | sessionStorage.referencePaneState = JSON.stringify(referencePane.state || null); 1354 | sessionStorage.activeTocPaths = JSON.stringify(getActiveTocPaths()); 1355 | sessionStorage.searchValue = JSON.stringify(menu.search.$searchBox.value); 1356 | sessionStorage.tocScroll = JSON.stringify(menu.$toc.scrollTop); 1357 | }); 1358 | 1359 | 'use strict'; 1360 | 1361 | // Manually prefix algorithm step list items with hidden counter representations 1362 | // corresponding with their markers so they get selected and copied with content. 1363 | // We read list-style-type to avoid divergence with the style sheet, but 1364 | // for efficiency assume that all lists at the same nesting depth use the same 1365 | // style (except for those associated with replacement steps). 1366 | // We also precompute some initial items for each supported style type. 1367 | // https://w3c.github.io/csswg-drafts/css-counter-styles/ 1368 | 1369 | const lowerLetters = Array.from({ length: 26 }, (_, i) => 1370 | String.fromCharCode('a'.charCodeAt(0) + i), 1371 | ); 1372 | // Implement the lower-alpha 'alphabetic' algorithm, 1373 | // adjusting for indexing from 0 rather than 1. 1374 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#simple-alphabetic 1375 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#alphabetic-system 1376 | const lowerAlphaTextForIndex = i => { 1377 | let S = ''; 1378 | for (const N = lowerLetters.length; i >= 0; i--) { 1379 | S = lowerLetters[i % N] + S; 1380 | i = Math.floor(i / N); 1381 | } 1382 | return S; 1383 | }; 1384 | 1385 | const weightedLowerRomanSymbols = Object.entries({ 1386 | m: 1000, 1387 | cm: 900, 1388 | d: 500, 1389 | cd: 400, 1390 | c: 100, 1391 | xc: 90, 1392 | l: 50, 1393 | xl: 40, 1394 | x: 10, 1395 | ix: 9, 1396 | v: 5, 1397 | iv: 4, 1398 | i: 1, 1399 | }); 1400 | // Implement the lower-roman 'additive' algorithm, 1401 | // adjusting for indexing from 0 rather than 1. 1402 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#simple-numeric 1403 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#additive-system 1404 | const lowerRomanTextForIndex = i => { 1405 | let value = i + 1; 1406 | let S = ''; 1407 | for (const [symbol, weight] of weightedLowerRomanSymbols) { 1408 | if (!value) break; 1409 | if (weight > value) continue; 1410 | const reps = Math.floor(value / weight); 1411 | S += symbol.repeat(reps); 1412 | value -= weight * reps; 1413 | } 1414 | return S; 1415 | }; 1416 | 1417 | // Memoize pure index-to-text functions with an exposed cache for fast retrieval. 1418 | const makeCounter = (pureGetTextForIndex, precomputeCount = 30) => { 1419 | const cache = Array.from({ length: precomputeCount }, (_, i) => pureGetTextForIndex(i)); 1420 | const getTextForIndex = i => { 1421 | if (i >= cache.length) cache[i] = pureGetTextForIndex(i); 1422 | return cache[i]; 1423 | }; 1424 | return { getTextForIndex, cache }; 1425 | }; 1426 | 1427 | const counterByStyle = { 1428 | __proto__: null, 1429 | decimal: makeCounter(i => String(i + 1)), 1430 | 'lower-alpha': makeCounter(lowerAlphaTextForIndex), 1431 | 'upper-alpha': makeCounter(i => lowerAlphaTextForIndex(i).toUpperCase()), 1432 | 'lower-roman': makeCounter(lowerRomanTextForIndex), 1433 | 'upper-roman': makeCounter(i => lowerRomanTextForIndex(i).toUpperCase()), 1434 | }; 1435 | const fallbackCounter = makeCounter(() => '?'); 1436 | const counterByDepth = []; 1437 | 1438 | function addStepNumberText( 1439 | ol, 1440 | depth = 0, 1441 | special = [...ol.classList].some(c => c.startsWith('nested-')), 1442 | ) { 1443 | let counter = !special && counterByDepth[depth]; 1444 | if (!counter) { 1445 | const counterStyle = getComputedStyle(ol)['list-style-type']; 1446 | counter = counterByStyle[counterStyle]; 1447 | if (!counter) { 1448 | console.warn('unsupported list-style-type', { 1449 | ol, 1450 | counterStyle, 1451 | id: ol.closest('[id]')?.getAttribute('id'), 1452 | }); 1453 | counterByStyle[counterStyle] = fallbackCounter; 1454 | counter = fallbackCounter; 1455 | } 1456 | if (!special) { 1457 | counterByDepth[depth] = counter; 1458 | } 1459 | } 1460 | const { cache, getTextForIndex } = counter; 1461 | let i = (Number(ol.getAttribute('start')) || 1) - 1; 1462 | for (const li of ol.children) { 1463 | const marker = document.createElement('span'); 1464 | marker.textContent = `${i < cache.length ? cache[i] : getTextForIndex(i)}. `; 1465 | marker.setAttribute('aria-hidden', 'true'); 1466 | const attributesContainer = li.querySelector('.attributes-tag'); 1467 | if (attributesContainer == null) { 1468 | li.prepend(marker); 1469 | } else { 1470 | attributesContainer.insertAdjacentElement('afterend', marker); 1471 | } 1472 | for (const sublist of li.querySelectorAll(':scope > ol')) { 1473 | addStepNumberText(sublist, depth + 1, special); 1474 | } 1475 | i++; 1476 | } 1477 | } 1478 | 1479 | document.addEventListener('DOMContentLoaded', () => { 1480 | document.querySelectorAll('emu-alg > ol').forEach(ol => { 1481 | addStepNumberText(ol); 1482 | }); 1483 | }); 1484 | 1485 | 'use strict'; 1486 | 1487 | // Update superscripts to not suffer misinterpretation when copied and pasted as plain text. 1488 | // For example, 1489 | // * Replace `103` with 1490 | // `103` 1491 | // so it gets pasted as `10**3` rather than `103`. 1492 | // * Replace `10-x` with 1493 | // `10-x` 1494 | // so it gets pasted as `10**-x` rather than `10-x`. 1495 | // * Replace `2a + 1` with 1496 | // `2**(a + 1)` 1497 | // so it gets pasted as `2**(a + 1)` rather than `2a + 1`. 1498 | 1499 | function makeExponentPlainTextSafe(sup) { 1500 | // Change a only if it appears to be an exponent: 1501 | // * text-only and contains only mathematical content (not e.g. `1st`) 1502 | // * contains only s and internal links (e.g. 1503 | // `2(_y_)`) 1504 | const isText = [...sup.childNodes].every(node => node.nodeType === 3); 1505 | const text = sup.textContent; 1506 | if (isText) { 1507 | if (!/^[0-9. 𝔽ℝℤ()=*×/÷±+\u2212-]+$/u.test(text)) { 1508 | return; 1509 | } 1510 | } else { 1511 | if (sup.querySelector('*:not(var, emu-xref, :scope emu-xref a)')) { 1512 | return; 1513 | } 1514 | } 1515 | 1516 | let prefix = '**'; 1517 | let suffix = ''; 1518 | 1519 | // Add wrapping parentheses unless they are already present 1520 | // or this is a simple (possibly signed) integer or single-variable exponent. 1521 | const skipParens = 1522 | /^[±+\u2212-]?(?:[0-9]+|\p{ID_Start}\p{ID_Continue}*)$/u.test(text) || 1523 | // Split on parentheses and remember them; the resulting parts must 1524 | // start and end empty (i.e., with open/close parentheses) 1525 | // and increase depth to 1 only at the first parenthesis 1526 | // to e.g. wrap `(a+1)*(b+1)` but not `((a+1)*(b+1))`. 1527 | text 1528 | .trim() 1529 | .split(/([()])/g) 1530 | .reduce((depth, s, i, parts) => { 1531 | if (s === '(') { 1532 | return depth > 0 || i === 1 ? depth + 1 : NaN; 1533 | } else if (s === ')') { 1534 | return depth > 0 ? depth - 1 : NaN; 1535 | } else if (s === '' || (i > 0 && i < parts.length - 1)) { 1536 | return depth; 1537 | } 1538 | return NaN; 1539 | }, 0) === 0; 1540 | if (!skipParens) { 1541 | prefix += '('; 1542 | suffix += ')'; 1543 | } 1544 | 1545 | sup.insertAdjacentHTML('beforebegin', ``); 1546 | if (suffix) { 1547 | sup.insertAdjacentHTML('afterend', ``); 1548 | } 1549 | } 1550 | 1551 | document.addEventListener('DOMContentLoaded', () => { 1552 | document.querySelectorAll('sup:not(.text)').forEach(sup => { 1553 | makeExponentPlainTextSafe(sup); 1554 | }); 1555 | }); 1556 | 1557 | let sdoMap = JSON.parse(`{}`); 1558 | let biblio = JSON.parse(`{"refsByClause":{},"entries":[{"type":"clause","id":"formalities","titleHTML":"Formal informations","number":"1"},{"type":"clause","id":"Set.prototype.filter","title":"Set.prototype.filter( callbackfn [ , thisArg ] )","titleHTML":"Set.prototype.filter( callbackfn [ , thisArg ] )","number":"2.1"},{"type":"clause","id":"Set.prototype.map","title":"Set.prototype.map( callbackfn [ , thisArg ] )","titleHTML":"Set.prototype.map( callbackfn [ , thisArg ] )","number":"2.2"},{"type":"clause","id":"Set.prototype.find","title":"Set.prototype.find( predicate [ , thisArg ] )","titleHTML":"Set.prototype.find( predicate [ , thisArg ] )","number":"2.3"},{"type":"clause","id":"Set.prototype.some","title":"Set.prototype.some( predicate [ , thisArg ] )","titleHTML":"Set.prototype.some( predicate [ , thisArg ] )","number":"2.4"},{"type":"clause","id":"Set.prototype.every","title":"Set.prototype.every( predicate [ , thisArg ] )","titleHTML":"Set.prototype.every( predicate [ , thisArg ] )","number":"2.5"},{"type":"clause","id":"Set.prototype.reduce","title":"Set.prototype.reduce( callbackfn [ , initialValue ] )","titleHTML":"Set.prototype.reduce( callbackfn [ , initialValue ] )","number":"2.6"},{"type":"clause","id":"Set.prototype.join","title":"Set.prototype.join( [ separator ] )","titleHTML":"Set.prototype.join( [ separator ] )","number":"2.7"},{"type":"clause","id":"Set.prototype.add-elements","title":"Set.prototype.addAll(...items)","titleHTML":"Set.prototype.addAll(...items)","number":"2.8"},{"type":"clause","id":"Set.prototype.remove-elements","title":"Set.prototype.deleteAll(...items)","titleHTML":"Set.prototype.deleteAll(...items)","number":"2.9"},{"type":"clause","id":"Set.prototype","titleHTML":"Set.prototype methods","number":"2"},{"type":"clause","id":"Map.prototype.mapValues","title":"Map.prototype.mapValues( callbackfn, [ thisArg ] )","titleHTML":"Map.prototype.mapValues( callbackfn, [ thisArg ] )","number":"3.1"},{"type":"clause","id":"Map.prototype.mapKeys","title":"Map.prototype.mapKeys( callbackfn, [ thisArg ] )","titleHTML":"Map.prototype.mapKeys( callbackfn, [ thisArg ] )","number":"3.2"},{"type":"clause","id":"Map.prototype.filter","title":"Map.prototype.filter( callbackfn, [ thisArg ] )","titleHTML":"Map.prototype.filter( callbackfn, [ thisArg ] )","number":"3.3"},{"type":"clause","id":"Map.prototype.find","title":"Map.prototype.find( callbackfn [ , thisArg ] )","titleHTML":"Map.prototype.find( callbackfn [ , thisArg ] )","number":"3.4"},{"type":"clause","id":"Map.prototype.findKey","title":"Map.prototype.findKey( callbackfn [ , thisArg ] )","titleHTML":"Map.prototype.findKey( callbackfn [ , thisArg ] )","number":"3.5"},{"type":"clause","id":"Map.prototype.keyOf","title":"Map.prototype.keyOf( searchElement )","titleHTML":"Map.prototype.keyOf( searchElement )","number":"3.6"},{"type":"clause","id":"Map.prototype.some","title":"Map.prototype.some( callbackfn [ , thisArg ] )","titleHTML":"Map.prototype.some( callbackfn [ , thisArg ] )","number":"3.7"},{"type":"clause","id":"Map.prototype.every","title":"Map.prototype.every( predicate [ , thisArg ] )","titleHTML":"Map.prototype.every( predicate [ , thisArg ] )","number":"3.8"},{"type":"clause","id":"Map.prototype.includes","title":"Map.prototype.includes( searchElement )","titleHTML":"Map.prototype.includes( searchElement )","number":"3.9"},{"type":"clause","id":"Map.prototype.reduce","title":"Map.prototype.reduce( callbackfn [ , initialValue ] )","titleHTML":"Map.prototype.reduce( callbackfn [ , initialValue ] )","number":"3.10"},{"type":"clause","id":"Map.prototype.merge","title":"Map.prototype.merge(... iterables )","titleHTML":"Map.prototype.merge(... iterables )","number":"3.11"},{"type":"clause","id":"Map.prototype.deleteAll","title":"Map.prototype.deleteAll(...items)","titleHTML":"Map.prototype.deleteAll(...items)","number":"3.12"},{"type":"clause","id":"Map.prototype.update","title":"Map.prototype.update(key, callback [, thunk])","titleHTML":"Map.prototype.update(key, callback [, thunk])","number":"3.13"},{"type":"clause","id":"Map.prototype","titleHTML":"Map.prototype methods","number":"3"},{"type":"clause","id":"Map.keyBy","title":"Map.keyBy( iterable, callbackfn)","titleHTML":"Map.keyBy( iterable, callbackfn)","number":"4.1"},{"type":"clause","id":"Map","titleHTML":"Map static methods","number":"4"},{"type":"clause","id":"WeakSet.prototype.addAll","title":"WeakSet.prototype.addAll(...items)","titleHTML":"WeakSet.prototype.addAll(...items)","number":"5"},{"type":"clause","id":"WeakSet.prototype.deleteAll","title":"WeakSet.prototype.deleteAll(...items)","titleHTML":"WeakSet.prototype.deleteAll(...items)","number":"6"},{"type":"clause","id":"WeakMap.prototype.deleteAll","title":"WeakMap.prototype.deleteAll(...items)","titleHTML":"WeakMap.prototype.deleteAll(...items)","number":"7"},{"type":"clause","id":"sec-copyright-and-software-license","title":"Copyright & Software License","titleHTML":"Copyright & Software License","number":"A"}]}`); 1559 | ;let usesMultipage = false -------------------------------------------------------------------------------- /docs/assets/print.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | font-size: 10pt; 4 | background: #fff; 5 | color: #000; 6 | } 7 | 8 | .title { 9 | font-family: Verdana; 10 | } 11 | 12 | p { 13 | text-align: justify; 14 | text-rendering: optimizeLegibility; 15 | text-wrap: pretty; 16 | overflow-wrap: break-word; 17 | hyphens: auto; 18 | orphans: 2; 19 | widows: 2; 20 | } 21 | 22 | h1 { 23 | text-wrap: balance; 24 | line-height: 1.4; 25 | } 26 | 27 | emu-note p, 28 | emu-table td p { 29 | text-align: left; 30 | hyphens: manual; 31 | overflow: hidden; 32 | } 33 | 34 | emu-table td, 35 | emu-table th { 36 | overflow-wrap: break-word; 37 | } 38 | 39 | emu-table table { 40 | table-layout: auto; 41 | width: 100%; 42 | } 43 | 44 | emu-figure img { 45 | max-width: 100%; 46 | height: auto; 47 | } 48 | 49 | #spec-container { 50 | max-width: none; 51 | } 52 | 53 | #toc a, 54 | #toc var { 55 | color: #000; 56 | } 57 | 58 | #toc a[href] { 59 | background: #fff; 60 | padding-right: 0.5em; 61 | } 62 | #toc a[href]::after { 63 | content: /* leader(dotted) */ target-counter(attr(href), page); 64 | float: right; 65 | padding-left: 0.5em; 66 | background: #fff; 67 | } 68 | /* NOTE: hacks because Paged.js doesn't support leader() in content directives */ 69 | #toc ol { 70 | overflow-x: hidden; 71 | } 72 | #toc ol li:before { 73 | float: left; 74 | width: 0; 75 | white-space: nowrap; 76 | content: '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' 77 | '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' 78 | '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .'; 79 | } 80 | 81 | /* skip the Introduction since it's before the first emu-clause (and therefore doesn't have a proper page number) */ 82 | #toc > ol > li:first-child { 83 | display: none; 84 | } 85 | 86 | #toc > ol > li { 87 | margin-top: 1ex; 88 | } 89 | 90 | #toc, 91 | #spec-container > emu-intro, 92 | #spec-container > emu-annex { 93 | break-before: recto; 94 | break-after: always; 95 | } 96 | 97 | /* according to Ecma guidelines, we're actually not supposed to break before every clause (only the first), but Paged.js fails if we do that */ 98 | /* so instead, just break before any of the clauses that have sub-clauses */ 99 | #spec-container > emu-clause:has(emu-clause:not([example])) { 100 | break-before: always; 101 | } 102 | 103 | #spec-container > emu-clause:first-of-type { 104 | break-before: recto; 105 | } 106 | 107 | emu-note, 108 | emu-note p, 109 | emu-table tr, 110 | emu-table th, 111 | emu-table td, 112 | emu-alg li, 113 | pre, 114 | h1, 115 | #metadata-block { 116 | break-inside: avoid; 117 | } 118 | 119 | emu-table thead, 120 | h1, 121 | figcaption, 122 | emu-alg > ol > li:first-child { 123 | break-after: avoid; 124 | } 125 | 126 | emu-grammar + emu-alg, 127 | figcaption + emu-table, 128 | figcaption + span[id] + emu-table, 129 | emu-alg > ol > li:last-child { 130 | break-before: avoid; 131 | } 132 | 133 | a[data-print-href]::after { 134 | content: ' <' attr(href) '>'; 135 | color: initial; 136 | } 137 | 138 | emu-table thead { 139 | display: table-header-group; 140 | } 141 | emu-table tfoot { 142 | display: table-footer-group; 143 | } 144 | 145 | @page { 146 | size: A4; 147 | } 148 | 149 | @page { 150 | @top-center { 151 | content: url(./ecma-header.png); 152 | } 153 | } 154 | @page :first { 155 | @top-center { 156 | content: none; 157 | } 158 | } 159 | 160 | :root { 161 | --page-number-style: decimal; 162 | } 163 | 164 | #toc { 165 | page: toc; 166 | } 167 | @page toc { 168 | --page-number-style: lower-roman; 169 | } 170 | emu-intro { 171 | page: intro; 172 | } 173 | @page intro { 174 | --page-number-style: lower-roman; 175 | } 176 | 177 | #toc { 178 | counter-reset: page 1; 179 | } 180 | #spec-container > emu-clause:first-of-type { 181 | counter-reset: page 1; 182 | } 183 | 184 | @page :left { 185 | @bottom-left { 186 | content: counter(page, var(--page-number-style)); 187 | } 188 | } 189 | @page :right { 190 | @bottom-right { 191 | content: counter(page, var(--page-number-style)); 192 | } 193 | } 194 | 195 | @page :first { 196 | @bottom-left { 197 | content: ''; 198 | } 199 | @bottom-right { 200 | content: ''; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | New collections methods
    33 |
      34 |
    • Toggle shortcuts help?
    • 35 |
    • Toggle "can call user code" annotationsu
    • 36 | 37 |
    • Jump to search box/
    • 38 |
    • Toggle pinning of the current clausep
    • 39 |
    • Jump to nth pin1-9
    • 40 |

    New collections methods

    44 | 45 | 46 | 47 |

    1 Formal informations

    48 | 54 |
    55 | 56 | 57 |

    2 Set.prototype methods

    58 | 59 | 60 | 61 |

    2.1 Set.prototype.filter( callbackfn [ , thisArg ] )

    62 |

    When the filter method is called, the following steps are taken:

    1. Let O be the this value.
    2. Perform ? RequireInternalSlot(O, [[SetData]]).
    3. If IsCallable(callbackfn) is false, throw a TypeError exception.
    4. Let resultSetData be a new empty List.
    5. Let entries be O.[[SetData]]
    6. Let thisSize be the number of elements in O.[[SetData]].
    7. Let index be 0.
    8. Repeat, while index < thisSize,
      1. Let e be O.[[SetData]][index].
      2. Set index to index + 1.
      3. If e is not empty, then
        1. Let selected be ToBoolean(? Call(callbackfn, thisArg, « kValue, kValue, O »)).
        2. If selected is true, then:
          1. Append e to resultSetData.
    9. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
    10. Set result.[[SetData]] to resultSetData.
    11. Return result.
    63 |
    64 | 65 | 66 |

    2.2 Set.prototype.map( callbackfn [ , thisArg ] )

    67 |

    When the map method is called, the following steps are taken:

    1. Let O be the this value.
    2. Perform ? RequireInternalSlot(O, [[SetData]]).
    3. If IsCallable(callbackfn) is false, throw a TypeError exception.
    4. Let resultSetData be a new empty List.
    5. Let entries be O.[[SetData]]
    6. Let thisSize be the number of elements in O.[[SetData]].
    7. Let index be 0.
    8. Repeat, while index < thisSize,
      1. Let e be O.[[SetData]][index].
      2. Set index to index + 1.
      3. If e is not empty, then
        1. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, kValue, O »).
        2. Append mappedValue to resultSetData.
    9. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
    10. Set result.[[SetData]] to resultSetData.
    11. Return result.
    68 |
    69 | 70 | 71 |

    2.3 Set.prototype.find( predicate [ , thisArg ] )

    72 |

    When the find method is called, the following steps are taken:

    1. Let O be the this value.
    2. Perform ? RequireInternalSlot(O, [[SetData]]).
    3. If IsCallable(predicate) is false, throw a TypeError exception.
    4. Let entries be O.[[SetData]]
    5. Let thisSize be the number of elements in O.[[SetData]].
    6. Let index be 0.
    7. Repeat, while index < thisSize,
      1. Let e be O.[[SetData]][index].
      2. Set index to index + 1.
      3. If e is not empty, then
        1. Let testResult be ToBoolean(? Call(predicate, thisArg, « e, e, O »))
        2. If testResult is true, then return e
    8. Return undefined.
    73 |
    74 | 75 | 76 |

    2.4 Set.prototype.some( predicate [ , thisArg ] )

    77 |

    When the some method is called, the following steps are taken:

    1. Let set be the this value.
      1. If Type(set) is not Object, throw a TypeError exception.
      2. If set does not have a [[SetData]] internal slot, throw a TypeError exception.
    2. If IsCallable(predicate) is false, throw a TypeError exception.
    3. Let entries be O.[[SetData]]
    4. Let thisSize be the number of elements in O.[[SetData]].
    5. Let index be 0.
    6. Repeat, while index < thisSize,
      1. Let e be O.[[SetData]][index].
      2. Set index to index + 1.
      3. If e is not empty, then
        1. Let testResult be ToBoolean(? Call(predicate, thisArg, « e, e, O »))
        2. If testResult is true, then return testResult
    7. Return false. 78 | 79 |
    80 |
    81 | 82 | 83 |

    2.5 Set.prototype.every( predicate [ , thisArg ] )

    84 |

    When the every method is called, the following steps are taken:

    1. Let O be the this value.
    2. Perform ? RequireInternalSlot(O, [[SetData]]).
    3. If IsCallable(predicate) is false, throw a TypeError exception.
    4. Let entries be O.[[SetData]]
    5. Let thisSize be the number of elements in O.[[SetData]].
    6. Let index be 0.
    7. Repeat, while index < thisSize,
      1. Let e be O.[[SetData]][index].
      2. Set index to index + 1.
      3. If e is not empty, then
        1. Let testResult be ToBoolean(? Call(predicate, thisArg, « e, e, set »))
        2. If testResult is false, then return false
    8. Return true. 85 | 86 |
    87 |
    88 | 89 | 90 |

    2.6 Set.prototype.reduce( callbackfn [ , initialValue ] )

    91 |

    When the reduce method is called, the following steps are taken:

    1. Let set be the this value.
      1. If Type(set) is not Object, throw a TypeError exception.
      2. If set does not have a [[SetData]] internal slot, throw a TypeError exception.
    2. If IsCallable(callbackfn) is false, throw a TypeError exception.
    3. Let entries be the List that is set.[[SetData]].
    4. Let accumulator be undefined
    5. Let first be true.
    6. If initialValue is present, then
      1. Set accumulator to initialValue
    7. For each e that is an element of entries, in original insertion order, do:
      1. If e is not empty, then
        1. If first be true and initialValue is not present:
          1. Set accumulator to e
        2. Else,
          1. Set accumulator to ? Call(callbackfn, undefined, « accumulator, e, e, map »)
        3. Set first to false
    8. If first is true and initialValue is not present, throw TypeError exception.
    9. Return accumulator.
    92 |
    93 | 94 | 95 |

    2.7 Set.prototype.join( [ separator ] )

    96 |

    When the join method is called, the following steps are taken:

    1. Let set be the this value.
      1. If Type(set) is not Object, throw a TypeError exception.
      2. If set does not have a [[SetData]] internal slot, throw a TypeError exception.
    2. If separator is undefined, let sep be the single-element String ",".
    3. Else, let sep be ? ToString(separator).
    4. Let R be the empty String.
    5. Let k be false.
    6. Let entries be O.[[SetData]]
    7. Let thisSize be the number of elements in O.[[SetData]].
    8. Let index be 0.
    9. Repeat, while index < thisSize,
      1. Let e be O.[[SetData]][index].
      2. Set index to index + 1.
      3. If e is not empty, then
        1. If k is true, let R be the string-concatenation of R and sep.
        2. Set k to true.
        3. Let next be ? ToString(e).
        4. Set R to the string-concatenation of R and next.
    10. Return R.
    97 |
    98 | 99 | 100 |

    2.8 Set.prototype.addAll(...items)

    101 |

    When the addAll method is called, the following steps are taken:

    1. Let len be the actual number of arguments passed to this function.
    2. Let items be the List of arguments passed to this function.
    3. Let set be the this value.
      1. If Type(set) is not Object, throw a TypeError exception.
      2. If set does not have a [[SetData]] internal slot, throw a TypeError exception.
    4. Let elements be CreateArrayFromList(items).
    5. Let adder be ? Get(set, "add")
    6. Return ? AddEntriesFromIterable(set, elements, adder). 102 | 103 |

    The length property of the addAll method is 0.

    104 |
    105 | 106 | 107 |

    2.9 Set.prototype.deleteAll(...items)

    108 |

    When the deleteAll method is called, the following steps are taken:

    1. Let len be the actual number of arguments passed to this function.
    2. Let items be the List of arguments passed to this function.
    3. Let set be the this value.
      1. If Type(set) is not Object, throw a TypeError exception.
    4. Let k be 0.
    5. Let remover be ? Get(set, "delete")
    6. If IsCallable(remover) is false, throw a TypeError exception.
    7. Let allDeleted be true.
    8. Repeat while k < len
      1. Let value be items[k].
      2. Let wasDeleted be Call(remover, set, value)
      3. Set allDeleted to be allDeleted && wasDeleted
      4. Increase k by 1.
    9. Return ToBoolean(allDeleted); 109 | 110 |
    111 |
    112 |
    113 | 114 | 115 | 116 |

    3 Map.prototype methods

    117 | 118 | 119 |

    3.1 Map.prototype.mapValues( callbackfn, [ thisArg ] )

    120 | Editor's Note
    121 | This code serves as placeholder until formal spec is written for this method. 122 |
    function mapValues(callbackFn, thisArg) {
    123 |     assert(isObject(this), 'this is not an object');
    124 |     assert(isCallable(callbackFn), 'callback is not a function');
    125 |     const Ctor = SpeciesConstructor(this, Map);
    126 |     const retObj = new Ctor();
    127 |     const _set = retObj.set;
    128 |     for(const [key, value] of this) {
    129 |         const newValue = Reflect.apply(callbackFn, thisArg, [value, key, this])
    130 |         Reflect.apply(_set, retObj, [key, newValue]);
    131 |     }
    132 |     return retObj;
    133 | }
    134 |
    135 | 136 | 137 |

    3.2 Map.prototype.mapKeys( callbackfn, [ thisArg ] )

    138 | Editor's Note
    139 | This code serves as placeholder until formal spec is written for this method. 140 |
    function mapKeys(callbackFn, thisArg) {
    141 |     assert(isObject(this), 'this is not an object');
    142 |     assert(isCallable(callbackFn), 'callback is not a function');
    143 |     const Ctor = SpeciesConstructor(this, Map);
    144 |     const retObj = new Ctor();
    145 |     const _set = retObj.set;
    146 |     for(const [key, value] of this) {
    147 |         const newKey = Reflect.apply(callbackFn, thisArg, [value, key, this])
    148 |         Reflect.apply(_set, retObj, [newKey, value]);
    149 |     }
    150 |     return retObj;
    151 | }
    152 |
    153 | 154 | 155 |

    3.3 Map.prototype.filter( callbackfn, [ thisArg ] )

    156 | Editor's Note
    157 | This code serves as placeholder until formal spec is written for this method. 158 |
    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. If IsCallable(callbackfn) is false, throw a TypeError exception.
    3. If thisArg was supplied, let T be thisArg; else let T be undefined.
    4. Let Ctr be ? SpeciesConstructor(map, %Map%)
    5. Let newMap be ? Construct(Ctr)
    6. Let adder be ? Get(newSet, "set")
      1. If IsCallable(adder) is false, throw a TypeError.
    7. Let entries be the List that is map.[[MapData]]
    8. Let newMapData be new empty List.
    9. For each Record { [[Key]], [[Value]] } p that is an element of entries, do
      1. If p.[[Key]] is not empty, then
        1. Let key be p.[[Key]]
        2. Let value be p.[[Value]]
        3. Let selected be ToBoolean(? Call(callbackfn, T, « value, key, map »))
        4. If selected is true, then
          1. Let pair be CreateArrayFromList(« value, key »)
          2. Append pair as last element to newMapData.
    10. Return ? AddEntriesFromIterable(map, CreateArrayFromList(newMapData), adder).
    159 |
    160 | 161 | 162 |

    3.4 Map.prototype.find( callbackfn [ , thisArg ] )

    163 |

    When the find method is called, the following steps are taken:

    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. If IsCallable(callbackfn) is false, throw a TypeError exception.
    3. If thisArg was supplied, let T be thisArg; else let T be undefined.
    4. Let entries be the List that is map.[[MapData]].
    5. For each Record { [[Key]], [[Value]] } e that is an element of entries, in original key insertion order, do:
      1. If e.[[Key]] is not empty, then
        1. Let testResult be ToBoolean(? Call(callbackfn, T, « e.[[Value]], e.[[Key]], map »)).
        2. If testResult is true, then return e.[[Value]].
    6. Return undefined. 164 | 165 |
    166 |
    167 | 168 | 169 |

    3.5 Map.prototype.findKey( callbackfn [ , thisArg ] )

    170 |

    When the findKey method is called, the following steps are taken:

    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. If IsCallable(callbackfn) is false, throw a TypeError exception.
    3. If thisArg was supplied, let T be thisArg; else let T be undefined.
    4. Let entries be the List that is map.[[MapData]].
    5. For each Record { [[Key]], [[Value]] } e that is an element of entries, in original key insertion order, do:
      1. If e.[[Key]] is not empty, then
        1. Let testResult be ToBoolean(? Call(callbackfn, T, « e.[[Value]], e.[[Key]], map »)).
        2. If testResult is true, then return e.[[Key]].
    6. Return undefined. 171 | 172 |
    173 |
    174 | 175 | 176 |

    3.6 Map.prototype.keyOf( searchElement )

    177 |

    When the keyOf method is called, the following steps are taken:

    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. Let entries be the List that is map.[[MapData]].
    3. For each Record { [[Key]], [[Value]] } e that is an element of entries, in original key insertion order, do:
      1. If e.[[Key]] is not empty, then
        1. Let same be the result of performing Strict Equality Comparison searchElement === e.[[Value]].
        2. If same is true, then return e.[[Key]].
    4. Return undefined. 178 | 179 |
    180 |
    181 | 182 | 183 |

    3.7 Map.prototype.some( callbackfn [ , thisArg ] )

    184 |

    When the some method is called, the following steps are taken:

    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. If IsCallable(callbackfn) is false, throw a TypeError exception.
    3. If thisArg was supplied, let T be thisArg; else let T be undefined.
    4. Let entries be the List that is map.[[MapData]].
    5. For each Record { [[Key]], [[Value]] } e that is an element of entries, in original key insertion order, do:
      1. If e.[[Key]] is not empty, then
        1. Let testResult be ToBoolean(? Call(callbackfn, T, « e.[[Value]], e.[[Key]], map »))
        2. If testResult is true, then return true
    6. Return false. 185 | 186 |
    187 |
    188 | 189 | 190 |

    3.8 Map.prototype.every( predicate [ , thisArg ] )

    191 |

    When the every method is called, the following steps are taken:

    1. Let M be the this value.
    2. Perform ? RequireInternalSlot(M, [[MapData]]).
    3. If IsCallable(predicate) is **false*, throw a *TypeError exception.
    4. Let entries be the List that is map.[[MapData]].
    5. For each Record { [[Key]], [[Value]] } e of M.[[MapData]], do
      1. If e.[[Key]] is not empty, then
        1. Let testResult be ToBoolean(? Call(predicate, T, « e.[[Value]], e.[[Key]], map »))
        2. If testResult is false, then return false
    6. Return true. 192 | 193 |
    194 |
    195 | 196 | 197 |

    3.9 Map.prototype.includes( searchElement )

    198 |

    When the includes method is called, the following steps are taken:

    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. Let entries be the List that is map.[[MapData]].
    3. For each Record { [[Key]], [[Value]] } e that is an element of entries, in original key insertion order, do:
      1. If e.[[Key]] is not empty, then
        1. If SameValueZero(searchElement, e.[[Value]]) is true, return true.
    4. Return false. 199 | 200 |
    201 |
    202 | 203 | 204 |

    3.10 Map.prototype.reduce( callbackfn [ , initialValue ] )

    205 |

    When the reduce method is called, the following steps are taken:

    1. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    2. If IsCallable(callbackfn) is false, throw a TypeError exception.
    3. Let entries be the List that is map.[[MapData]].
    4. Let accumulator be undefined
    5. Let first be true.
    6. If initialValue is present, then
      1. Set accumulator to initialValue
    7. For each Record { [[Key]], [[Value]] } e that is an element of entries, in original key insertion order, do:
      1. If e.[[Key]] is not empty, then
        1. If first be true and initialValue is not present:
          1. Set accumulator to e.[[Value]]
        2. Else,
          1. Set accumulator to ? Call(callbackfn, undefined, « accumulator, e.[[Value]], e.[[Key]], map »)
        3. Set first to false
    8. If first is true and initialValue is not present, throw TypeError exception.
    9. Return accumulator.
    206 |
    207 | 208 | 209 |

    3.11 Map.prototype.merge(... iterables )

    210 | Editor's Note
    211 | This code serves as placeholder until formal spec is written for this method. 212 |
    function merge(...iterables) {
    213 |     assert(isObject(this), 'this is not an object');
    214 |     assert(this instanceof Map, 'this is not a Map instance');
    215 |     for(const iterable of iterables) {
    216 |         for(const [key, value] of iterable) {
    217 |             this.set(key, value);
    218 |         }
    219 |     }
    220 | 
    221 |     return this;
    222 | }
    223 |
    224 | 225 | 226 |

    3.12 Map.prototype.deleteAll(...items)

    227 |

    When the deleteAll method is called, the following steps are taken:

    The length property of the deleteAll method is 0.

    1. Let len be the actual number of arguments passed to this function.
    2. Let items be the List of arguments passed to this function.
    3. Let map be the this value.
      1. If Type(map) is not Object, throw a TypeError exception.
      2. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    4. Let k be 0.
    5. Let remover be ? Get(map, "delete")
    6. If IsCallable(remover) is false, throw a TypeError exception.
    7. Repeat while k < len
      1. Let value be items[k].
      2. Call(remover, map, value)
      3. Increase k by 1.
    8. Return map. 228 | 229 |
    230 |
    231 | 232 | 233 |

    3.13 Map.prototype.update(key, callback [, thunk])

    234 | Editor's Note
    235 | This code serves as placeholder until formal spec is written for this method. 236 |
    function update(key, cb, thunk) {
    237 |     assert(isObject(this), 'this is not an object');
    238 |     assert(isCallable(callbackFn), 'callback is not a function');
    239 |     asset(isMap(this), 'this is not a Map instance');
    240 |     const isPresentInMap = this.has(key);
    241 |     if (isPresentInMap === false && arguments.length < 3) {
    242 |         throw new Error('Invalid!');
    243 |     }
    244 |     const value = isPresentInMap ? this.get(key) : thunk(key, this);
    245 |     const newValue = cb(value, key, this);
    246 |     this.set(key, newValue);
    247 |     return this;
    248 | }
    249 |
    250 |
    251 | 252 | 253 |

    4 Map static methods

    254 | 255 |

    4.1 Map.keyBy( iterable, callbackfn)

    256 |

    When the keyBy method is called, the following steps are taken:

    1. Let C be the this value.
    2. If IsConstructor(C) is true
      1. Let map be ? Construct(C)
    3. Else,
      1. Throw a TypeError.
    4. If map does not have a [[MapData]] internal slot, throw a TypeError exception.
    5. If IsCallable(callbackfn) is false, throw a TypeError exception.
    6. Let set be ? Get(map, "set")
    7. Let iteratorRecord be ? GetIterator(iterable).
    8. Repeat,
      1. Let next be ? IteratorStep(iteratorRecord).
      2. If next is false, return map.
      3. Let item be ? IteratorValue(next).
      4. Let key be Call(callbackfn, null, « item »)
      5. Let status be Call(set, map, « key.[[Value]], value.[[Value]] »).
      6. If status is an abrupt completion, return ? IteratorClose(iteratorRecord, status).
    257 |
    258 |
    259 | 260 | 261 | 262 |

    5 WeakSet.prototype.addAll(...items)

    263 |

    When the addAll method is called, the following steps are taken:

    1. Let len be the actual number of arguments passed to this function.
    2. Let items be the List of arguments passed to this function.
    3. Let weakset be the this value.
      1. If Type(weakset) is not Object, throw a TypeError exception.
    4. Let k be 0.
    5. Let adder be ? Get(weakset, "add")
    6. If IsCallable(adder) is false, throw a TypeError exception.
    7. Repeat while k < len
      1. Let kValue be items[k].
      2. Call(adder, weakset, kValue)
      3. Increase k by 1.
    8. Return weakset. 264 | 265 |

    The length property of the addAll method is 0.

    266 |
    267 | 268 | 269 |

    6 WeakSet.prototype.deleteAll(...items)

    270 |

    When the deleteAll method is called, the following steps are taken:

    1. Let len be the actual number of arguments passed to this function.
    2. Let items be the List of arguments passed to this function.
    3. Let weakset be the this value.
      1. If Type(weakset) is not Object, throw a TypeError exception.
    4. Let k be 0.
    5. Let remover be ? Get(weakset, "delete")
    6. If IsCallable(remover) is false, throw a TypeError exception.
    7. Repeat while k < len
      1. Let value be items[k].
      2. Call(remover, weakset, value)
      3. Increase k by 1.
    8. Return weakset. 271 | 272 |

    The length property of the deleteAll method is 0.

    273 |
    274 | 275 | 276 |

    7 WeakMap.prototype.deleteAll(...items)

    277 |

    When the deleteAll method is called, the following steps are taken:

    1. Let len be the actual number of arguments passed to this function.
    2. Let items be the List of arguments passed to this function.
    3. Let weakmap be the this value.
      1. If Type(weakmap) is not Object, throw a TypeError exception.
    4. Let k be 0.
    5. Let remover be ? Get(weakmap, "delete")
    6. If IsCallable(remover) is false, throw a TypeError exception.
    7. Repeat while k < len
      1. Let value be items[k].
      2. Call(remover, weakmap, value)
      3. Increase k by 1.
    8. Return weakmap. 278 | 279 |

    The length property of the deleteAll method is 0.

    280 |
    281 |

    A Copyright & Software License

    282 | 283 |

    Copyright Notice

    284 |

    © 2024 Michał Wadas, Ecma International

    285 | 286 |

    Software License

    287 |

    All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

    288 | 289 |

    Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    290 | 291 |
      292 |
    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    2. 293 |
    3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    4. 294 |
    5. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.
    6. 295 |
    296 | 297 |

    THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    298 | 299 |
    300 |
    -------------------------------------------------------------------------------- /map-polyfill.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function assert(val, msg) { 4 | if (!val) { 5 | throw new TypeError(msg); 6 | } 7 | } 8 | 9 | function isObject(x) { 10 | return (typeof x === 'function' || typeof x === 'object'); 11 | } 12 | 13 | function SpeciesConstructor(O, defaultConstructor) { 14 | assert(isObject(O), 'O is not Object'); 15 | 16 | const C = O.constructor; 17 | 18 | if (typeof C === 'undefined') { 19 | return defaultConstructor; 20 | } 21 | 22 | if (!isObject(C)) { 23 | throw new TypeError('O.constructor is not an Object'); 24 | } 25 | 26 | const S = C[Symbol.species]; 27 | 28 | if (S == null) { 29 | return defaultConstructor; 30 | } 31 | 32 | if (typeof S === 'function' && !!S.prototype) { 33 | return S; 34 | } 35 | 36 | throw new TypeError('no constructor found'); 37 | } 38 | 39 | function isCallable(x) { 40 | try { 41 | Function.prototype.toString.call(x); 42 | return true; 43 | } catch(e) { 44 | return false; 45 | } 46 | } 47 | 48 | function filter(callbackFn, thisArg) { 49 | assert(isObject(this), 'this is not an object'); 50 | assert(isCallable(callbackFn), 'callback is not a function'); 51 | const Ctor = SpeciesConstructor(this, Map); 52 | const retObj = new Ctor(); 53 | const _set = retObj.set; 54 | for(const [key, value] of this) { 55 | if (Reflect.apply(callbackFn, thisArg, [value, key, this])) { 56 | Reflect.apply(_set, retObj, [key, value]); 57 | } 58 | } 59 | return retObj; 60 | } 61 | 62 | function mapValues(callbackFn, thisArg) { 63 | assert(isObject(this), 'this is not an object'); 64 | assert(isCallable(callbackFn), 'callback is not a function'); 65 | const Ctor = SpeciesConstructor(this, Map); 66 | const retObj = new Ctor(); 67 | const _set = retObj.set; 68 | for(const [key, value] of this) { 69 | const newValue = Reflect.apply(callbackFn, thisArg, [value, key, this]) 70 | Reflect.apply(_set, retObj, [key, newValue]); 71 | } 72 | return retObj; 73 | } 74 | 75 | function mapKeys(callbackFn, thisArg) { 76 | assert(isObject(this), 'this is not an object'); 77 | assert(isCallable(callbackFn), 'callback is not a function'); 78 | const Ctor = SpeciesConstructor(this, Map); 79 | const retObj = new Ctor(); 80 | const _set = retObj.set; 81 | for(const [key, value] of this) { 82 | const newKey = Reflect.apply(callbackFn, thisArg, [value, key, this]) 83 | Reflect.apply(_set, retObj, [newKey, value]); 84 | } 85 | return retObj; 86 | } 87 | 88 | function merge(iterable) { 89 | assert(isObject(this), 'this is not an object'); 90 | const Ctor = SpeciesConstructor(this, Map); 91 | return new Ctor([...this, ...iterable]); 92 | } 93 | 94 | function groupBy(iterable, keyDerivative) { 95 | assert(isObject(this), 'this is not an object'); 96 | assert(isCallable(this), 'this is not a function'); 97 | assert(Map === this || Map.isPrototypeOf(this), 'this is not a Map constructor'); 98 | const retObj = new this(); 99 | for(const element of iterable) { 100 | const derivedKey = Reflect.apply(keyDerivative, null, [element]); 101 | if (!retObj.has(derivedKey)) { 102 | retObj.set(derivedKey, []); 103 | } 104 | const arr = retObj.get(derivedKey); 105 | arr.push(element); 106 | } 107 | return retObj; 108 | } 109 | 110 | function keyBy(iterable, keyDerivative) { 111 | assert(isObject(this), 'this is not an object'); 112 | assert(isCallable(this), 'this is not a function'); 113 | assert(Map === this || Map.isPrototypeOf(this), 'this is not a Map constructor'); 114 | const retObj = new this(); 115 | for(const element of iterable) { 116 | const derivedKey = Reflect.apply(keyDerivative, null, [element]); 117 | retObj.set(derivedKey, value); 118 | } 119 | return retObj; 120 | } 121 | 122 | assert(typeof Map === 'function', 'Map does not exist'); 123 | const prototypeMethods = { 124 | mapValues, 125 | mapKeys, 126 | filter, 127 | merge, 128 | }; 129 | const staticMethods = { 130 | groupBy, 131 | keyBy 132 | }; 133 | for (const [key, value] of Object.entries(prototypeMethods)) { 134 | Object.defineProperty(Map.prototype, key, { 135 | configurable: true, 136 | value 137 | }); 138 | } 139 | for (const [key, value] of Object.entries(staticMethods)) { 140 | Object.defineProperty(Map, key, { 141 | configurable: true, 142 | value 143 | }); 144 | } 145 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "proposal-set-methods", 4 | "description": "New collection methods proposal for JavaScript", 5 | "license": "MIT", 6 | "scripts": { 7 | "spec": "ecmarkup --assets=external spec/index.html docs/index.html", 8 | "watch": "npx ecmarkup --watch --assets=external spec/index.html docs/index.html", 9 | "test": "mocha -r ./set-polyfill.js -r ./map-polyfill.js test/*/*.spec.js", 10 | "format": "emu-format --write spec/**/*.html" 11 | }, 12 | "type": "module", 13 | "version": "1.0.0", 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/Ginden/set-methods.git" 17 | }, 18 | "keywords": [], 19 | "author": "Michał Wadas ", 20 | "bugs": { 21 | "url": "https://github.com/tc39/proposal-collection-methods/issues" 22 | }, 23 | "homepage": "https://github.com/tc39/proposal-collection-methods#readme", 24 | "dependencies": { 25 | "chai": "^5.1.1", 26 | "mocha": "^10.7.0" 27 | }, 28 | "devDependencies": { 29 | "ecmarkup": "^19.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rationales.md: -------------------------------------------------------------------------------- 1 | # Set prototype methods 2 | 3 | ## Set.prototype.filter 4 | 5 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.filter](https://tc39.github.io/ecma262/#sec-array.prototype.filter) as close as reasonable. 6 | 7 | ## Set.prototype.map 8 | 9 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.map](https://tc39.github.io/ecma262/#sec-array.prototype.map) as close as reasonable. 10 | 11 | 12 | ## Set.prototype.find 13 | 14 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.find](https://tc39.github.io/ecma262/#sec-array.prototype.find) as close as reasonable. 15 | * [Problematic behaviour](https://github.com/tc39/proposal-collection-methods/issues/6) 16 | 17 | ## Set.prototype.reduce 18 | 19 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.reduce](https://tc39.github.io/ecma262/#sec-array.prototype.reduce) as close as reasonable. 20 | * Static linters can check if function used by Set.prototype.reduce is [associative](https://en.wikipedia.org/wiki/Associative_property) and [commutative](https://en.wikipedia.org/wiki/Commutative_property). Otherwise, results can be surprising to user. 21 | 22 | ## Set.prototype.join 23 | 24 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.join](https://tc39.github.io/ecma262/#sec-array.prototype.join) as close as reasonable. 25 | 26 | ## Set.prototype.some 27 | 28 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.some](https://tc39.github.io/ecma262/#sec-array.prototype.some) as close as reasonable. 29 | 30 | 31 | ## Set.prototype.every 32 | 33 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.every](https://tc39.github.io/ecma262/#sec-array.prototype.every) as close as reasonable. 34 | 35 | 36 | ## Set.prototype.addAll 37 | 38 | * `Set.prototype.add` can't be extended due to pattern of `arr.forEach(set.add, set)` and `arr.forEach(set.add.bind(set))`. 39 | * Doesn't create new set like [`Set.prototype.union`](https://tc39.github.io/proposal-set-methods/#Set.prototype.union) 40 | 41 | 42 | ## Set.prototype.deleteAll 43 | 44 | * `Set.prototype.delete` can't be extended due to pattern of `arr.forEach(set.delete, set)` and `arr.forEach(set.delete.bind(set))`. 45 | 46 | 47 | 48 | # Map prototype methods 49 | 50 | ## Map.prototype.filter 51 | 52 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.filter](https://tc39.github.io/ecma262/#sec-array.prototype.filter) as close as reasonable. 53 | 54 | 55 | ## Map.prototype.mapValues 56 | 57 | TBA 58 | 59 | 60 | ## Map.prototype.mapKeys 61 | 62 | TBA 63 | 64 | 65 | ## Map.prototype.reduce 66 | 67 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.reduce](https://tc39.github.io/ecma262/#sec-array.prototype.reduce) as close as reasonable. 68 | 69 | 70 | ## Map.prototype.find 71 | 72 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.find](https://tc39.github.io/ecma262/#sec-array.prototype.find). 73 | * This methods find value and returns it. It can be reasonable to return pair `[key, value]` or merge it with `findKey`. 74 | 75 | ## Map.prototype.findKey 76 | * This methods find value and returns its key. It can be reasonable to return pair `[key, value]` or merge it with `find`. 77 | 78 | ## Map.prototype.keyOf(searchElement) 79 | 80 | * This methods returns first key where `SameValueZero(p.[[Value]], searchElement)` is *true*. 81 | 82 | ## Map.prototype.some 83 | 84 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.some](https://tc39.github.io/ecma262/#sec-array.prototype.some) as close as reasonable. 85 | 86 | 87 | ## Map.prototype.every 88 | 89 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.some](https://tc39.github.io/ecma262/#sec-array.prototype.some) as close as reasonable. 90 | 91 | ## Map.prototype.includes 92 | 93 | * Added to make all collections similar in capabilities. Should mimic [Array.prototype.includes](https://tc39.github.io/ecma262/#sec-array.prototype.includes) as close as reasonable. 94 | 95 | 96 | ## Map.prototype.update 97 | 98 | TBA 99 | 100 | 101 | ## Map.prototype.deleteAll 102 | 103 | TBA 104 | 105 | 106 | ## Map.prototype.merge 107 | 108 | TBA 109 | 110 | 111 | # Map static methods 112 | 113 | ## Map.groupBy 114 | 115 | TBA 116 | 117 | ## Map.keyBy 118 | 119 | TBA 120 | -------------------------------------------------------------------------------- /set-polyfill.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var originalSet = Set; 4 | 5 | function assert(val, msg) { 6 | if (!val) { 7 | throw new TypeError(msg); 8 | } 9 | } 10 | 11 | function isObject(x) { 12 | return (typeof x === 'function' || typeof x === 'object'); 13 | } 14 | 15 | function SpeciesConstructor(O, defaultConstructor) { 16 | assert(isObject(O), 'O is not Object'); 17 | 18 | var C = O.constructor; 19 | 20 | if (typeof C === 'undefined') { 21 | return defaultConstructor; 22 | } 23 | 24 | if (!isObject(C)) { 25 | throw new TypeError('O.constructor is not an Object'); 26 | } 27 | 28 | var S = C[Symbol.species]; 29 | 30 | if (S == null) { 31 | return defaultConstructor; 32 | } 33 | 34 | if (typeof S === 'function' && !!S.prototype) { 35 | return S; 36 | } 37 | 38 | throw new TypeError('no constructor found'); 39 | } 40 | 41 | function isCallable(x) { 42 | try { 43 | Function.prototype.toString.call(x); 44 | return true; 45 | } catch(e) { 46 | return false; 47 | } 48 | } 49 | 50 | function isSet(obj) { 51 | const methodNames = ['has', 'add', 'forEach', 'delete', 'keys', 'values', 'entries']; 52 | return !!( 53 | obj && 54 | methodNames.every(key=>key in obj && typeof obj[key] === 'function') && 55 | 'size' in obj && 56 | typeof obj.size === 'number' && 57 | Number.isInteger(obj.size) 58 | ); 59 | } 60 | 61 | function filter(predicate, thisArg = null) { 62 | assert(typeof predicate === 'function'); 63 | assert(isSet(this)); 64 | const ret = new (SpeciesConstructor(this)); 65 | for (const element of this) { 66 | if (Reflect.apply(predicate, thisArg, [element, element, this])) { 67 | ret.add(element); 68 | } 69 | } 70 | return ret; 71 | } 72 | 73 | function map(mapFunction, thisArg = null) { 74 | assert(typeof mapFunction === 'function'); 75 | assert(isSet(this)); 76 | const ret = new (SpeciesConstructor(this)); 77 | for (const element of this) { 78 | ret.add(Reflect.apply(mapFunction, thisArg, [element, element, this])) 79 | } 80 | return ret; 81 | } 82 | 83 | function some(predicate, thisArg = null) { 84 | assert(typeof predicate === 'function'); 85 | assert(isSet(this)); 86 | for (const element of this) { 87 | if (Reflect.apply(predicate, thisArg, [element, element, this])) { 88 | return true; 89 | } 90 | } 91 | return false; 92 | } 93 | 94 | function find(predicate, thisArg = null) { 95 | assert(typeof predicate === 'function'); 96 | assert(isSet(this)); 97 | for (const element of this) { 98 | if (Reflect.apply(predicate, thisArg, [element, element, this])) { 99 | return element; 100 | } 101 | } 102 | return undefined; 103 | } 104 | 105 | function every(predicate, thisArg = null) { 106 | assert(typeof predicate === 'function'); 107 | assert(isSet(this)); 108 | for (const element of this) { 109 | if (!Reflect.apply(predicate, thisArg, [element, element, this])) { 110 | return false; 111 | } 112 | } 113 | return true; 114 | } 115 | 116 | 117 | 118 | function addAll(...args) { 119 | assert(isSet(this)); 120 | for (const element of args) { 121 | this.add(element); 122 | } 123 | return this; 124 | } 125 | 126 | function deleteAll(...items) { 127 | const len = items.length; 128 | const set = this; 129 | assert(isObject(set), 'set is not an Object'); 130 | 131 | let k = 0; 132 | const remover = set.delete; 133 | assert(isCallable(remover), 'remover is not a function'); 134 | 135 | while (k < len) { 136 | const value = items[k]; 137 | Reflect.apply(remover, set, [value]); 138 | k++; 139 | } 140 | return this; 141 | } 142 | 143 | assert(typeof Set === 'function', 'Set does not exist'); 144 | const prototypeMethods = [ 145 | ['map', map], 146 | ['filter', filter], 147 | ['some', some], 148 | ['every', every], 149 | ['find', find], 150 | ['addAll', addAll], 151 | ['deleteAll', deleteAll], 152 | ]; 153 | const staticMethods = [ 154 | ['isSet', isSet] 155 | ]; 156 | for (const [key, value] of prototypeMethods) { 157 | Object.defineProperty(Set.prototype, key, { 158 | configurable: true, 159 | value 160 | }); 161 | } 162 | for (const [key, value] of staticMethods) { 163 | Object.defineProperty(Set, key, { 164 | configurable: true, 165 | value 166 | }); 167 | } 168 | -------------------------------------------------------------------------------- /spec/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 |

    Formal informations

    13 | 19 |
    20 | 21 | 22 |

    Set.prototype methods

    23 | 24 | 25 | 26 |

    Set.prototype.filter( _callbackfn_ [ , _thisArg_ ] )

    27 | 28 |
    29 | 30 | 31 |

    Set.prototype.map( _callbackfn_ [ , _thisArg_ ] )

    32 | 33 |
    34 | 35 | 36 |

    Set.prototype.find( _predicate_ [ , _thisArg_ ] )

    37 | 38 |
    39 | 40 | 41 |

    Set.prototype.some( _predicate_ [ , _thisArg_ ] )

    42 | 43 |
    44 | 45 | 46 |

    Set.prototype.every( _predicate_ [ , _thisArg_ ] )

    47 | 48 |
    49 | 50 | 51 |

    Set.prototype.reduce( _callbackfn_ [ , _initialValue_ ] )

    52 | 53 |
    54 | 55 | 56 |

    Set.prototype.join( [ _separator_ ] )

    57 | 58 |
    59 | 60 | 61 |

    Set.prototype.addAll(..._items_)

    62 | 63 |
    64 | 65 | 66 |

    Set.prototype.deleteAll(..._items_)

    67 | 68 |
    69 |
    70 | 71 | 72 | 73 |

    Map.prototype methods

    74 | 75 | 76 |

    Map.prototype.mapValues( _callbackfn_, [ _thisArg_ ] )

    77 | 78 |
    79 | 80 | 81 |

    Map.prototype.mapKeys( _callbackfn_, [ _thisArg_ ] )

    82 | 83 |
    84 | 85 | 86 |

    Map.prototype.filter( _callbackfn_, [ _thisArg_ ] )

    87 | 88 |
    89 | 90 | 91 |

    Map.prototype.find( _callbackfn_ [ , _thisArg_ ] )

    92 | 93 |
    94 | 95 | 96 |

    Map.prototype.findKey( _callbackfn_ [ , _thisArg_ ] )

    97 | 98 |
    99 | 100 | 101 |

    Map.prototype.keyOf( _searchElement_ )

    102 | 103 |
    104 | 105 | 106 |

    Map.prototype.some( _callbackfn_ [ , _thisArg_ ] )

    107 | 108 |
    109 | 110 | 111 |

    Map.prototype.every( _predicate_ [ , _thisArg_ ] )

    112 | 113 |
    114 | 115 | 116 |

    Map.prototype.includes( _searchElement_ )

    117 | 118 |
    119 | 120 | 121 |

    Map.prototype.reduce( _callbackfn_ [ , _initialValue_ ] )

    122 | 123 |
    124 | 125 | 126 |

    Map.prototype.merge(... _iterables_ )

    127 | 128 |
    129 | 130 | 131 |

    Map.prototype.deleteAll(..._items_)

    132 | 133 |
    134 | 135 | 136 |

    Map.prototype.update(_key_, _callback_ [, _thunk_])

    137 | 138 |
    139 |
    140 | 141 | 142 |

    Map static methods

    143 | 144 |

    Map.keyBy( _iterable_, _callbackfn_)

    145 | 146 |
    147 |
    148 | 149 | 150 | 151 |

    WeakSet.prototype.addAll(..._items_)

    152 | 153 |
    154 | 155 | 156 |

    WeakSet.prototype.deleteAll(..._items_)

    157 | 158 |
    159 | 160 | 161 |

    WeakMap.prototype.deleteAll(..._items_)

    162 | 163 |
    164 | -------------------------------------------------------------------------------- /spec/map/key-by.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `keyBy` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _C_ be the *this* value. 6 | 1. If IsConstructor(_C_) is *true* 7 | 1. Let _map_ be ? Construct(_C_) 8 | 1. Else, 9 | 1. Throw a *TypeError*. 10 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 11 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 12 | 1. Let _set_ be ? Get(_map_, "set") 13 | 1. Let _iteratorRecord_ be ? GetIterator(_iterable_). 14 | 1. Repeat, 15 | 1. Let _next_ be ? IteratorStep(_iteratorRecord_). 16 | 1. If _next_ is *false*, return _map_. 17 | 1. Let _item_ be ? IteratorValue(_next_). 18 | 1. Let _key_ be Call(_callbackfn_, *null*, « _item_ ») 19 | 1. Let _status_ be Call(_set_, _map_, « _key_.[[Value]], _value_.[[Value]] »). 20 | 1. If _status_ is an abrupt completion, return ? IteratorClose(_iteratorRecord_, _status_). 21 | 22 | -------------------------------------------------------------------------------- /spec/map/prototype-delete-all.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `deleteAll` method is called, the following steps are taken:

    3 | 4 |

    The `length` property of the `deleteAll` method is *0*.

    5 | 6 | 7 | 1. Let _len_ be the actual number of arguments passed to this function. 8 | 1. Let _items_ be the List of arguments passed to this function. 9 | 1. Let _map_ be the *this* value. 10 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 11 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 12 | 1. Let _k_ be 0. 13 | 1. Let _remover_ be ? Get(_map_, "delete") 14 | 1. If IsCallable(_remover_) is *false*, throw a *TypeError* exception. 15 | 1. Repeat while _k_ < _len_ 16 | 1. Let _value_ be _items_[_k_]. 17 | 1. Call(_remover_, _map_, _value_) 18 | 1. Increase _k_ by 1. 19 | 1. Return _map_. 20 | 21 | 22 | -------------------------------------------------------------------------------- /spec/map/prototype-every.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `every` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _M_ be the *this* value. 6 | 1. Perform ? RequireInternalSlot(_M_, [[MapData]]). 7 | 1. If IsCallable(_predicate_) is **false**, throw a *TypeError* exception. 8 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 9 | 1. For each Record { [[Key]], [[Value]] } _e_ of _M_.[[MapData]], do 10 | 1. If _e_.[[Key]] is not ~empty~, then 11 | 1. Let _testResult_ be ToBoolean(? Call(_predicate_, _T_, « _e_.[[Value]], _e_.[[Key]], _map_ »)) 12 | 1. If _testResult_ is *false*, then return *false* 13 | 1. Return *true*. 14 | 15 | 16 | -------------------------------------------------------------------------------- /spec/map/prototype-filter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | This code serves as placeholder until formal spec is written for this method. 4 | 5 | 6 | 7 | 1. Let _map_ be the *this* value. 8 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 9 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 10 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 11 | 1. If _thisArg_ was supplied, let _T_ be _thisArg_; else let _T_ be *undefined*. 12 | 1. Let _Ctr_ be ? SpeciesConstructor(_map_, %Map%) 13 | 1. Let _newMap_ be ? Construct(_Ctr_) 14 | 1. Let _adder_ be ? Get(_newSet_, "set") 15 | 1. If IsCallable(_adder_) is *false*, throw a TypeError. 16 | 1. Let _entries_ be the List that is _map_.[[MapData]] 17 | 1. Let _newMapData_ be new empty List. 18 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of entries, do 19 | 1. If _p_.[[Key]] is not empty, then 20 | 1. Let _key_ be _p_.[[Key]] 21 | 1. Let _value_ be _p_.[[Value]] 22 | 1. Let _selected_ be ToBoolean(? Call(_callbackfn_, _T_, « _value_, _key_, _map_ »)) 23 | 1. If _selected_ is *true*, then 24 | 1. Let _pair_ be CreateArrayFromList(« _value_, _key_ ») 25 | 1. Append _pair_ as last element to _newMapData_. 26 | 1. Return ? AddEntriesFromIterable(_map_, CreateArrayFromList(_newMapData_), _adder_). 27 | 28 | -------------------------------------------------------------------------------- /spec/map/prototype-find-key.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `findKey` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _map_ be the *this* value. 6 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 7 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 8 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 9 | 1. If _thisArg_ was supplied, let _T_ be _thisArg_; else let _T_ be *undefined*. 10 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 11 | 1. For each Record { [[Key]], [[Value]] } _e_ that is an element of _entries_, in original key insertion order, do: 12 | 1. If _e_.[[Key]] is not ~empty~, then 13 | 1. Let _testResult_ be ToBoolean(? Call(_callbackfn_, _T_, « _e_.[[Value]], _e_.[[Key]], _map_ »)). 14 | 1. If _testResult_ is *true*, then return _e_.[[Key]]. 15 | 1. Return *undefined*. 16 | 17 | 18 | -------------------------------------------------------------------------------- /spec/map/prototype-find.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `find` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _map_ be the *this* value. 6 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 7 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 8 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 9 | 1. If _thisArg_ was supplied, let _T_ be _thisArg_; else let _T_ be *undefined*. 10 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 11 | 1. For each Record { [[Key]], [[Value]] } _e_ that is an element of _entries_, in original key insertion order, do: 12 | 1. If _e_.[[Key]] is not ~empty~, then 13 | 1. Let _testResult_ be ToBoolean(? Call(_callbackfn_, _T_, « _e_.[[Value]], _e_.[[Key]], _map_ »)). 14 | 1. If _testResult_ is *true*, then return _e_.[[Value]]. 15 | 1. Return *undefined*. 16 | 17 | 18 | -------------------------------------------------------------------------------- /spec/map/prototype-includes.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `includes` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _map_ be the *this* value. 6 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 7 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 8 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 9 | 1. For each Record { [[Key]], [[Value]] } _e_ that is an element of _entries_, in original key insertion order, do: 10 | 1. If _e_.[[Key]] is not ~empty~, then 11 | 1. If SameValueZero(_searchElement_, _e_.[[Value]]) is *true*, return *true*. 12 | 1. Return *false*. 13 | 14 | 15 | -------------------------------------------------------------------------------- /spec/map/prototype-key-of.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `keyOf` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _map_ be the *this* value. 6 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 7 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 8 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 9 | 1. For each Record { [[Key]], [[Value]] } _e_ that is an element of _entries_, in original key insertion order, do: 10 | 1. If _e_.[[Key]] is not ~empty~, then 11 | 1. Let _same_ be the result of performing Strict Equality Comparison _searchElement_ === _e_.[[Value]]. 12 | 1. If _same_ is *true*, then return _e_.[[Key]]. 13 | 1. Return *undefined*. 14 | 15 | 16 | -------------------------------------------------------------------------------- /spec/map/prototype-map-keys.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | This code serves as placeholder until formal spec is written for this method. 4 | 5 | 6 |
    
     7 | function mapKeys(callbackFn, thisArg) {
     8 |     assert(isObject(this), 'this is not an object');
     9 |     assert(isCallable(callbackFn), 'callback is not a function');
    10 |     const Ctor = SpeciesConstructor(this, Map);
    11 |     const retObj = new Ctor();
    12 |     const _set = retObj.set;
    13 |     for(const [key, value] of this) {
    14 |         const newKey = Reflect.apply(callbackFn, thisArg, [value, key, this])
    15 |         Reflect.apply(_set, retObj, [newKey, value]);
    16 |     }
    17 |     return retObj;
    18 | }
    19 | 
    20 | -------------------------------------------------------------------------------- /spec/map/prototype-map-values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | This code serves as placeholder until formal spec is written for this method. 4 | 5 | 6 |
    
     7 | function mapValues(callbackFn, thisArg) {
     8 |     assert(isObject(this), 'this is not an object');
     9 |     assert(isCallable(callbackFn), 'callback is not a function');
    10 |     const Ctor = SpeciesConstructor(this, Map);
    11 |     const retObj = new Ctor();
    12 |     const _set = retObj.set;
    13 |     for(const [key, value] of this) {
    14 |         const newValue = Reflect.apply(callbackFn, thisArg, [value, key, this])
    15 |         Reflect.apply(_set, retObj, [key, newValue]);
    16 |     }
    17 |     return retObj;
    18 | }
    19 | 
    20 | -------------------------------------------------------------------------------- /spec/map/prototype-merge.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | This code serves as placeholder until formal spec is written for this method. 4 | 5 | 6 |
    
     7 | function merge(...iterables) {
     8 |     assert(isObject(this), 'this is not an object');
     9 |     assert(this instanceof Map, 'this is not a Map instance');
    10 |     for(const iterable of iterables) {
    11 |         for(const [key, value] of iterable) {
    12 |             this.set(key, value);
    13 |         }
    14 |     }
    15 | 
    16 |     return this;
    17 | }
    18 | 
    19 | -------------------------------------------------------------------------------- /spec/map/prototype-reduce.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `reduce` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _map_ be the *this* value. 6 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 7 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 8 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 9 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 10 | 1. Let _accumulator_ be *undefined* 11 | 1. Let _first_ be *true*. 12 | 1. If _initialValue_ is present, then 13 | 1. Set _accumulator_ to _initialValue_ 14 | 1. For each Record { [[Key]], [[Value]] } _e_ that is an element of _entries_, in original key insertion order, do: 15 | 1. If _e_.[[Key]] is not ~empty~, then 16 | 1. If _first_ be *true* and _initialValue_ is not present: 17 | 1. Set _accumulator_ to _e_.[[Value]] 18 | 1. Else, 19 | 1. Set _accumulator_ to ? Call(_callbackfn_, undefined, « _accumulator_, _e_.[[Value]], _e_.[[Key]], _map_ ») 20 | 1. Set _first_ to *false* 21 | 1. If _first_ is *true* and _initialValue_ is not present, throw *TypeError* exception. 22 | 1. Return _accumulator_. 23 | 24 | -------------------------------------------------------------------------------- /spec/map/prototype-some.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `some` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _map_ be the *this* value. 6 | 1. If Type(_map_) is not Object, throw a *TypeError* exception. 7 | 1. If _map_ does not have a ~[[MapData]]~ internal slot, throw a *TypeError* exception. 8 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 9 | 1. If _thisArg_ was supplied, let _T_ be _thisArg_; else let _T_ be *undefined*. 10 | 1. Let _entries_ be the List that is _map_.[[MapData]]. 11 | 1. For each Record { [[Key]], [[Value]] } _e_ that is an element of _entries_, in original key insertion order, do: 12 | 1. If _e_.[[Key]] is not ~empty~, then 13 | 1. Let _testResult_ be ToBoolean(? Call(_callbackfn_, _T_, « _e_.[[Value]], _e_.[[Key]], _map_ »)) 14 | 1. If _testResult_ is *true*, then return *true* 15 | 1. Return *false*. 16 | 17 | 18 | -------------------------------------------------------------------------------- /spec/map/prototype-update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | This code serves as placeholder until formal spec is written for this method. 4 | 5 | 6 |
    
     7 | function update(key, cb, thunk) {
     8 |     assert(isObject(this), 'this is not an object');
     9 |     assert(isCallable(callbackFn), 'callback is not a function');
    10 |     asset(isMap(this), 'this is not a Map instance');
    11 |     const isPresentInMap = this.has(key);
    12 |     if (isPresentInMap === false && arguments.length < 3) {
    13 |         throw new Error('Invalid!');
    14 |     }
    15 |     const value = isPresentInMap ? this.get(key) : thunk(key, this);
    16 |     const newValue = cb(value, key, this);
    17 |     this.set(key, newValue);
    18 |     return this;
    19 | }
    20 | 
    21 | -------------------------------------------------------------------------------- /spec/set/prototype-add-elements.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `addAll` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _len_ be the actual number of arguments passed to this function. 6 | 1. Let _items_ be the List of arguments passed to this function. 7 | 1. Let _set_ be the *this* value. 8 | 1. If Type(_set_) is not Object, throw a *TypeError* exception. 9 | 1. If _set_ does not have a ~[[SetData]]~ internal slot, throw a *TypeError* exception. 10 | 1. Let _elements_ be CreateArrayFromList(items). 11 | 1. Let _adder_ be ? Get(_set_, "add") 12 | 1. Return ? AddEntriesFromIterable(_set_, _elements_, _adder_). 13 | 14 | 15 | 16 |

    The `length` property of the `addAll` method is *0*.

    17 | -------------------------------------------------------------------------------- /spec/set/prototype-every.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `every` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _O_ be the *this* value. 6 | 1. Perform ? RequireInternalSlot(_O_, [[SetData]]). 7 | 1. If IsCallable(_predicate_) is *false*, throw a *TypeError* exception. 8 | 1. Let _entries_ be _O_.[[SetData]] 9 | 1. Let _thisSize_ be the number of elements in _O_.[[SetData]]. 10 | 1. Let _index_ be 0. 11 | 1. Repeat, while _index_ < _thisSize_, 12 | 1. Let _e_ be _O_.[[SetData]][_index_]. 13 | 1. Set _index_ to _index_ + 1. 14 | 1. If _e_ is not `empty`, then 15 | 1. Let _testResult_ be ToBoolean(? Call(_predicate_, _thisArg_, « _e_, _e_, _set_ »)) 16 | 1. If _testResult_ is *false*, then return *false* 17 | 1. Return *true*. 18 | 19 | 20 | -------------------------------------------------------------------------------- /spec/set/prototype-filter.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `filter` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _O_ be the *this* value. 6 | 1. Perform ? RequireInternalSlot(_O_, [[SetData]]). 7 | 1. If IsCallable(_callbackfn_) is *false*, throw a *TypeError* exception. 8 | 1. Let _resultSetData_ be a new empty List. 9 | 1. Let _entries_ be _O_.[[SetData]] 10 | 1. Let _thisSize_ be the number of elements in _O_.[[SetData]]. 11 | 1. Let _index_ be 0. 12 | 1. Repeat, while _index_ < _thisSize_, 13 | 1. Let _e_ be _O_.[[SetData]][_index_]. 14 | 1. Set _index_ to _index_ + 1. 15 | 1. If _e_ is not ~empty~, then 16 | 1. Let _selected_ be ToBoolean(? Call(_callbackfn_, _thisArg_, « _kValue_, _kValue_, _O_ »)). 17 | 1. If _selected_ is *true*, then: 18 | 1. Append _e_ to _resultSetData_. 19 | 1. Let _result_ be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »). 20 | 1. Set _result_.[[SetData]] to _resultSetData_. 21 | 1. Return _result_. 22 | 23 | -------------------------------------------------------------------------------- /spec/set/prototype-find.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `find` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _O_ be the *this* value. 6 | 1. Perform ? RequireInternalSlot(_O_, [[SetData]]). 7 | 1. If IsCallable(_predicate_) is false, throw a *TypeError* exception. 8 | 1. Let _entries_ be _O_.[[SetData]] 9 | 1. Let _thisSize_ be the number of elements in _O_.[[SetData]]. 10 | 1. Let _index_ be 0. 11 | 1. Repeat, while _index_ < _thisSize_, 12 | 1. Let _e_ be _O_.[[SetData]][_index_]. 13 | 1. Set _index_ to _index_ + 1. 14 | 1. If _e_ is not ~empty~, then 15 | 1. Let _testResult_ be ToBoolean(? Call(_predicate_, _thisArg_, « _e_, _e_, _O_ »)) 16 | 1. If _testResult_ is *true*, then return _e_ 17 | 1. Return *undefined*. 18 | 19 | -------------------------------------------------------------------------------- /spec/set/prototype-join.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `join` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _set_ be the *this* value. 6 | 1. If Type(_set_) is not Object, throw a *TypeError* exception. 7 | 1. If _set_ does not have a [[SetData]] internal slot, throw a *TypeError* exception. 8 | 1. If _separator_ is *undefined*, let _sep_ be the single-element String `","`. 9 | 1. Else, let _sep_ be ? ToString(_separator_). 10 | 1. Let _R_ be the empty String. 11 | 1. Let _k_ be *false*. 12 | 1. Let _entries_ be _O_.[[SetData]] 13 | 1. Let _thisSize_ be the number of elements in _O_.[[SetData]]. 14 | 1. Let _index_ be 0. 15 | 1. Repeat, while _index_ < _thisSize_, 16 | 1. Let _e_ be _O_.[[SetData]][_index_]. 17 | 1. Set _index_ to _index_ + 1. 18 | 1. If _e_ is not ~empty~, then 19 | 1. If _k_ is *true*, let _R_ be the string-concatenation of _R_ and _sep_. 20 | 1. Set _k_ to *true*. 21 | 1. Let _next_ be ? ToString(_e_). 22 | 1. Set _R_ to the string-concatenation of _R_ and _next_. 23 | 1. Return _R_. 24 | 25 | -------------------------------------------------------------------------------- /spec/set/prototype-map.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `map` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _O_ be the *this* value. 6 | 1. Perform ? RequireInternalSlot(_O_, [[SetData]]). 7 | 1. If IsCallable(_callbackfn_) is *false*, throw a *TypeError* exception. 8 | 1. Let _resultSetData_ be a new empty List. 9 | 1. Let _entries_ be _O_.[[SetData]] 10 | 1. Let _thisSize_ be the number of elements in _O_.[[SetData]]. 11 | 1. Let _index_ be 0. 12 | 1. Repeat, while _index_ < _thisSize_, 13 | 1. Let _e_ be _O_.[[SetData]][_index_]. 14 | 1. Set _index_ to _index_ + 1. 15 | 1. If _e_ is not ~empty~, then 16 | 1. Let _mappedValue_ be ? Call(_callbackfn_, _thisArg_, « _kValue_, _kValue_, _O_ »). 17 | 1. Append _mappedValue_ to _resultSetData_. 18 | 1. Let _result_ be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »). 19 | 1. Set _result_.[[SetData]] to _resultSetData_. 20 | 1. Return _result_. 21 | 22 | -------------------------------------------------------------------------------- /spec/set/prototype-reduce.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `reduce` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _set_ be the *this* value. 6 | 1. If Type(_set_) is not Object, throw a *TypeError* exception. 7 | 1. If _set_ does not have a [[SetData]] internal slot, throw a *TypeError* exception. 8 | 1. If IsCallable(_callbackfn_) is false, throw a *TypeError* exception. 9 | 1. Let _entries_ be the List that is _set_.[[SetData]]. 10 | 1. Let _accumulator_ be *undefined* 11 | 1. Let _first_ be *true*. 12 | 1. If _initialValue_ is present, then 13 | 1. Set _accumulator_ to _initialValue_ 14 | 1. For each _e_ that is an element of _entries_, in original insertion order, do: 15 | 1. If _e_ is not `empty`, then 16 | 1. If _first_ be *true* and _initialValue_ is not present: 17 | 1. Set _accumulator_ to _e_ 18 | 1. Else, 19 | 1. Set _accumulator_ to ? Call(_callbackfn_, undefined, « _accumulator_, _e_, _e_, _map_ ») 20 | 1. Set _first_ to *false* 21 | 1. If _first_ is *true* and _initialValue_ is not present, throw *TypeError* exception. 22 | 1. Return _accumulator_. 23 | 24 | -------------------------------------------------------------------------------- /spec/set/prototype-remove-elements.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `deleteAll` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _len_ be the actual number of arguments passed to this function. 6 | 1. Let _items_ be the List of arguments passed to this function. 7 | 1. Let _set_ be the *this* value. 8 | 1. If Type(_set_) is not Object, throw a *TypeError* exception. 9 | 1. Let _k_ be 0. 10 | 1. Let _remover_ be ? Get(_set_, "delete") 11 | 1. If IsCallable(_remover_) is *false*, throw a *TypeError* exception. 12 | 1. Let _allDeleted_ be *true*. 13 | 1. Repeat while _k_ < _len_ 14 | 1. Let _value_ be _items_[_k_]. 15 | 1. Let _wasDeleted_ be Call(_remover_, _set_, _value_) 16 | 1. Set `allDeleted` to be `allDeleted` && `wasDeleted` 17 | 1. Increase _k_ by 1. 18 | 1. Return ToBoolean(`allDeleted`); 19 | 20 | 21 | -------------------------------------------------------------------------------- /spec/set/prototype-some.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `some` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _set_ be the *this* value. 6 | 1. If Type(_set_) is not Object, throw a *TypeError* exception. 7 | 1. If _set_ does not have a [[SetData]] internal slot, throw a *TypeError* exception. 8 | 1. If IsCallable(_predicate_) is false, throw a *TypeError* exception. 9 | 1. Let _entries_ be _O_.[[SetData]] 10 | 1. Let _thisSize_ be the number of elements in _O_.[[SetData]]. 11 | 1. Let _index_ be 0. 12 | 1. Repeat, while _index_ < _thisSize_, 13 | 1. Let _e_ be _O_.[[SetData]][_index_]. 14 | 1. Set _index_ to _index_ + 1. 15 | 1. If _e_ is not ~empty~, then 16 | 1. Let _testResult_ be ToBoolean(? Call(_predicate_, _thisArg_, « _e_, _e_, _O_ »)) 17 | 1. If _testResult_ is *true*, then return _testResult_ 18 | 1. Return *false*. 19 | 20 | 21 | -------------------------------------------------------------------------------- /spec/weak-map/prototype-delete-all.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `deleteAll` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _len_ be the actual number of arguments passed to this function. 6 | 1. Let _items_ be the List of arguments passed to this function. 7 | 1. Let _weakmap_ be the *this* value. 8 | 1. If Type(_weakmap_) is not Object, throw a *TypeError* exception. 9 | 1. Let _k_ be 0. 10 | 1. Let _remover_ be ? Get(_weakmap_, "delete") 11 | 1. If IsCallable(_remover_) is *false*, throw a *TypeError* exception. 12 | 1. Repeat while _k_ < _len_ 13 | 1. Let _value_ be _items_[_k_]. 14 | 1. Call(_remover_, _weakmap_, _value_) 15 | 1. Increase _k_ by 1. 16 | 1. Return _weakmap_. 17 | 18 | 19 | 20 |

    The `length` property of the `deleteAll` method is *0*.

    21 | -------------------------------------------------------------------------------- /spec/weak-set/prototype-add-all.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `addAll` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _len_ be the actual number of arguments passed to this function. 6 | 1. Let _items_ be the List of arguments passed to this function. 7 | 1. Let _weakset_ be the *this* value. 8 | 1. If Type(_weakset_) is not Object, throw a *TypeError* exception. 9 | 1. Let _k_ be 0. 10 | 1. Let _adder_ be ? Get(_weakset_, "add") 11 | 1. If IsCallable(_adder_) is *false*, throw a *TypeError* exception. 12 | 1. Repeat while _k_ < _len_ 13 | 1. Let _kValue_ be _items_[_k_]. 14 | 1. Call(_adder_, _weakset_, _kValue_) 15 | 1. Increase _k_ by 1. 16 | 1. Return _weakset_. 17 | 18 | 19 | 20 |

    The `length` property of the `addAll` method is *0*.

    21 | -------------------------------------------------------------------------------- /spec/weak-set/prototype-delete-all.html: -------------------------------------------------------------------------------- 1 | 2 |

    When the `deleteAll` method is called, the following steps are taken:

    3 | 4 | 5 | 1. Let _len_ be the actual number of arguments passed to this function. 6 | 1. Let _items_ be the List of arguments passed to this function. 7 | 1. Let _weakset_ be the *this* value. 8 | 1. If Type(_weakset_) is not Object, throw a *TypeError* exception. 9 | 1. Let _k_ be 0. 10 | 1. Let _remover_ be ? Get(_weakset_, "delete") 11 | 1. If IsCallable(_remover_) is *false*, throw a *TypeError* exception. 12 | 1. Repeat while _k_ < _len_ 13 | 1. Let _value_ be _items_[_k_]. 14 | 1. Call(_remover_, _weakset_, _value_) 15 | 1. Increase _k_ by 1. 16 | 1. Return _weakset_. 17 | 18 | 19 | 20 |

    The `length` property of the `deleteAll` method is *0*.

    21 | -------------------------------------------------------------------------------- /test/set/add_elements.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe.skip('Set.prototype.addAll', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.addAll); 7 | assert.isFunction(Set.prototype.addAll); 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /test/set/every.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe.skip('Set.prototype.every', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.every); 7 | assert.isFunction(Set.prototype.every); 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /test/set/filter.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {assert} from 'chai'; 4 | 5 | describe.skip('Set.prototype.filter', () => { 6 | it('Should be present', () => { 7 | const set = new Set(); 8 | assert.isFunction(set.filter); 9 | assert.isFunction(Set.prototype.filter); 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /test/set/find.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe.skip('Set.prototype.find', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.find); 7 | assert.isFunction(Set.prototype.find); 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /test/set/map.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe.skip('Set.prototype.map', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.map); 7 | assert.isFunction(Set.prototype.map); 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /test/set/reduce.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe.skip('Set.prototype.reduce', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.reduce); 7 | assert.isFunction(Set.prototype.reduce); 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /test/set/remove_elements.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe('Set.prototype.deleteAll', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.deleteAll); 7 | assert.isFunction(Set.prototype.deleteAll); 8 | }); 9 | 10 | it('Should remove elements from Set', () => { 11 | const set = new Set([1, 2, 3]); 12 | const actual = set.deleteAll(1, 2); 13 | const expected = new Set([3]); 14 | assert.deepEqual(actual, expected); 15 | }); 16 | 17 | it('Should not remove elements if they dont exist', () => { 18 | const set = new Set([1, 2, 3]); 19 | const actual = set.deleteAll(4, 5); 20 | const expected = new Set([1, 2, 3]); 21 | assert.deepEqual(actual, expected); 22 | }); 23 | }) 24 | -------------------------------------------------------------------------------- /test/set/some.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai'; 2 | 3 | describe.skip('Set.prototype.some', () => { 4 | it('Should be present', () => { 5 | const set = new Set(); 6 | assert.isFunction(set.some); 7 | assert.isFunction(Set.prototype.some); 8 | }) 9 | }) 10 | --------------------------------------------------------------------------------