├── .gitignore ├── .travis.yml ├── README.md ├── deploy.sh ├── ecmarkup.css ├── ecmarkup.js ├── github_deploy_key.enc ├── index.html ├── package.json └── spec ├── biblio.json ├── index.html └── relativetimeformat.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: off 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - "8" 7 | 8 | script: 9 | - bash ./deploy.sh 10 | 11 | env: 12 | global: 13 | - ENCRYPTION_LABEL: "33173ec242dd" 14 | - GH_USER_NAME: "littledan" 15 | - GH_USER_EMAIL: "littledan@igalia.com" 16 | - PRIVATE_KEY_FILE_NAME: "github_deploy_key.enc" 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Intl.RelativeTimeFormat` API Specification [draft] 2 | 3 | ## Overview 4 | 5 | ### Motivation 6 | 7 | Due to common use, relative time–formatted values exist in a majority of websites and are available for the majority of frameworks (e.g., React, via [react-intl](https://github.com/yahoo/react-intl) and [react-globalize](https://github.com/globalizejs/react-globalize); Ember, via [ember-intl](https://github.com/ember-intl/ember-intl)). Popular localization libraries like [Moment.js](https://momentjs.com/), [Format.js](https://formatjs.io/), [Globalize](http://globalizejs.com/), and [others](https://github.com/rxaviers/javascript-globalization) have implemented a formatting process for relative time values as well. 8 | 9 | It is highly probable that the majority of current relative time formatting implementations require a large portion of CLDR raw or compiled data to format relative time values. Bringing this into the platform will improve performance of the web and developer productivity as they no longer have to bring extra weight to format relative time values. 10 | 11 | ### Usage examples 12 | 13 | The following example shows how to create a relative time formatter using the English language. 14 | 15 | > Units : "year", "quarter", "month", "week", "day", "hour", "minute" and "second". 16 | 17 | ```js 18 | // Create a relative time formatter in your locale 19 | // with default values explicitly passed in. 20 | const rtf = new Intl.RelativeTimeFormat("en", { 21 | localeMatcher: "best fit", // other values: "lookup" 22 | numeric: "always", // other values: "auto" 23 | style: "long", // other values: "short" or "narrow" 24 | }); 25 | 26 | 27 | // Format relative time using negative value (-1). 28 | rtf.format(-1, "day"); 29 | // > "1 day ago" 30 | 31 | // Format relative time using positive value (1). 32 | rtf.format(1, "day"); 33 | // > "in 1 day" 34 | 35 | ``` 36 | 37 | > Note: If `numeric:auto` option is passed, it will produce the string `yesterday` or `tomorrow` instead of `1 day ago` or `in 1 day`, this allows to not always have to use numeric values in the output. 38 | 39 | ```js 40 | // Create a relative time formatter in your locale 41 | // with numeric: "auto" option value passed in. 42 | const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); 43 | 44 | // Format relative time using negative value (-1). 45 | rtf.format(-1, "day"); 46 | // > "yesterday" 47 | 48 | // Format relative time using positive day unit (1). 49 | rtf.format(1, "day"); 50 | // > "tomorrow" 51 | ``` 52 | 53 | ### Implementation Status 54 | 55 | __Stage 4__ 56 | 57 | Implementation Progress 58 | 59 | - [V8 v7.1.179](https://bugs.chromium.org/p/v8/issues/detail?id=7869), shipped in Chrome 71 60 | - Shipped in Firefox 65 61 | - [Polyfills](#polyfills) are available 62 | - [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RelativeTimeFormat#Browser_compatibility) 63 | 64 | Backpointers 65 | 66 | - https://github.com/tc39/ecma402/issues/35 67 | 68 | #### Polyfills 69 | 70 | There're several polyfills available which are listed in the comparison table below. The functionality of all polyfills is the same in terms of the API: they only differ in their implementation details like the way the polyfill is imported or the way locales are loaded or whether the implementation passes the [Official ECMAScript Conformance Test](https://github.com/tc39/test262) for the complete coverage of all possible edge cases. 71 | 72 | Polyfill | [`intl-relative-time-format`](https://www.npmjs.com/package/intl-relative-time-format) | [`@formatjs/intl-relativetimeformat`](https://www.npmjs.com/package/@formatjs/intl-relativetimeformat) | [`relative-time-format`](https://www.npmjs.com/package/relative-time-format) 73 | --- | --- | --- | --- 74 | Requirements | [Requirements](https://www.npmjs.com/package/intl-relative-time-format#dependencies--browser-support): [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat), [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/PluralRules), [`Intl.getCanonicalLocales`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_ObjectsGlobal_Objects/Intl/getCanonicalLocales), `Object.is`, `WeakMap` and others | [Requirements](https://www.npmjs.com/package/@formatjs/intl-relativetimeformat#requirements): [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat), [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/PluralRules), [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) | No requirements 75 | Core bundle size (gzipped) |  |  |  76 | | Passes the [Official ECMAScript Conformance Test](https://github.com/tc39/test262) | ✔️ [Yes](https://github.com/wessberg/intl-relative-time-format#description) | ✔️ [Yes](https://www.npmjs.com/package/@formatjs/intl-relativetimeformat#tests) | [No](https://github.com/catamphetamine/relative-time-format#test262) 77 | 78 | #### Authors 79 | 80 | - Caridy Patiño (@caridy) 81 | - Eric Ferraiuolo (@ericf) 82 | - Zibi Braniecki (@zbraniecki) 83 | - Rafael Xavier (@rxaviers) 84 | - Daniel Ehrenberg (@littledan) 85 | 86 | #### Reviewers 87 | 88 | TBD 89 | 90 | ## Proposal 91 | 92 | `Intl.RelativeTimeFormat` is a low level API to facilitate libraries and frameworks to format relative time in a localized fashion by providing internationalized messages for date and time fields, using customary word or phrase when available. 93 | 94 | ### Spec 95 | 96 | You can view the [spec text](spec/relativetimeformat.html) or rendered as [HTML](https://rawgit.com/tc39/proposal-intl-relative-time/master/index.html). 97 | 98 | ### Technical Design 99 | 100 | This proposal is based on the ICU Relative Date Time Formatter and on the Unicode CLDR Calendar Fields Relative values: 101 | 102 | - http://icu-project.org/apiref/icu4j/com/ibm/icu/text/RelativeDateTimeFormatter.html 103 | - https://unicode.org/reports/tr35/tr35-dates.html#Calendar_Fields 104 | 105 | It is also based on the LDML spec, C.11 Language Plural Rules: 106 | 107 | - https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules 108 | 109 | #### Prior Art 110 | 111 | ##### Java 112 | 113 | - [ICU](http://icu-project.org/apiref/icu4j/com/ibm/icu/text/RelativeDateTimeFormatter.html): `com.ibm.icu.impl.RelativeDateFormat` 114 | - `org.ocpsoft.prettytime.PrettyTime` 115 | 116 | ##### Ruby 117 | 118 | ```ruby 119 | include ActionView::Helpers::DateHelper 120 | def index 121 | @friendly_date = time_ago_in_words(Date.today - 1) 122 | end 123 | ``` 124 | 125 | #### Naming 126 | 127 | For consistency with `Intl.NumberFormat` and `Intl.DateTimeFormat`, we have chosen a similar form for this new feature. The creation of an `Intl.RelativeTimeFormat` instance is an expensive operation that requires resolution of locale data, and most likely, libraries will attempt to cache those instances, just like they do for `Intl.NumberFormat` and `Intl.DateTimeFormat`. 128 | 129 | We have also chosen `style` as the primary form of switching between different formatting forms for consistency with `Intl.NumberFormat` and `Intl.DateTimeFormat`. 130 | 131 | Since this new feature does format a provided value just like instances of `Intl.NumberFormat`, and `Intl.DateTimeFormat`, we have chosen the same form by providing a `format(value)` method of the instance, which returns a formatted string value. 132 | 133 | #### Take number instead of date object for input 134 | 135 | Relative time is used to display date distances, therefore the natural form of input should intuitively be a date object. Although, in this API we chose to take a number instead due to the following reasons: 136 | 137 | 1. Basically, taking a number as input for the format method instead of a date object significantly simplifies the scope of this proposal while it still fully addresses the main objective which is to provide i18n building blocks to address this problem realm. 138 | 2. Taking a date object means we should implement the comparison logic (relative time is about date distance between target and source dates). The source date is usually *now*, but not always. We would have to address modifying that. See [#4](https://github.com/tc39/proposal-intl-relative-time/issues/4). 139 | 3. Taking a date object also means we should allow for different calendar calculations, which implies `Date` should support it. See [#6](https://github.com/tc39/proposal-intl-relative-time/issues/6) and [#13](https://github.com/tc39/proposal-intl-relative-time/issues/13). 140 | 4. Taking a date object suggests we should be able to implement a *bestFit* algorithm, which has its own API challenges with respect to standardizing an approach that works for all cases. See [#7](https://github.com/tc39/proposal-intl-relative-time/issues/7), [#14](https://github.com/tc39/proposal-intl-relative-time/issues/14), and [#15](https://github.com/tc39/proposal-intl-relative-time/issues/15). We'd probably need to provide a flag for users to fill, with no default setting, to choose between options for calendar calculation. 141 | 142 | #### Take number as input rather than exposing the underlying database 143 | 144 | An idea has been floated, in the context of "the extensible web", of just exposing the engine's copy of the CLDR database rather than a higher-level interface would be better. In the case of this specification, there is already a JS object model ready to go--the locale database is represented internally in the spec as a JavaScript object. 145 | 146 | However, we opted not to go that route for a couple reasons: 147 | - As described above, the API is already fairly low-level, taking numbers rather than dates. 148 | - Although there are clearly use cases for different policies about rounding dates into units, we haven't come across a use case for seeing the underlying data. 149 | - This new API is analogous to previous APIs, which should be useful for people learning the system. 150 | - CLDR changes schema over time; if the data model improves, implementations can transparently upgrade users to better results with the same API. However, if we freeze things to the current logic, the old data model would need to be emulated. 151 | 152 | #### Difference between this and `UnitFormat` 153 | 154 | The fundamental difference between `RelativeTimeFormat` and `UnitFormat` is that `RelativeTimeFormat` displays a relative unit (e.g., `5 days ago` or `in 5 days`) while `UnitFormat` displays an absolute unit (e.g., `-5 meters` or `5 meters`). Note that `RelativeTimeFormat` uses different internationalized messages based on the value sign direction, while `UnitFormat` uses the same internationalized message for all values. 155 | 156 | ##### Countdowns, e.g., 15 days, 0 hours, 27 minutes, and 52 seconds 157 | 158 | A countdown for example is a mix of `UnitFormat` and `ListFormat`, and is not a `RelativeTimeFormat`. 159 | 160 | #### NumberFormat options (e.g., useGrouping, maximumFractionDigits) 161 | 162 | RelativeTimeFormat messages may include number parts (e.g., the `1,000` in `1,000 days ago`), which are formatted using the NumberFormat default options. 163 | 164 | In this design, we didn't find any use case that could justify allowing to change/override these NumberFormat default options. Therefore, RelativeTimeFormat doesn't include any NumberFormat option. 165 | 166 | ### API 167 | 168 | #### `Intl.RelativeTimeFormat([locales[, options]])` 169 | 170 | The `Intl.RelativeTimeFormat` object is a constructor for objects that enable language-sensitive relative time formatting. 171 | 172 | #### `locales` 173 | 174 | Optional. A string with a BCP 47 language tag, or an array of such strings. For the general form and interpretation of the `locales` argument, see the [`Intl` page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation). 175 | 176 | #### `options` 177 | 178 | Optional. An object with some or all of the following properties: 179 | 180 | ##### `localeMatcher` 181 | 182 | The locale matching algorithm to use. Possible values are `"lookup"` and `"best fit"`; the default is `"best fit"`. For information about this option, see the [`Intl` page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_negotiation). 183 | 184 | ##### `numeric` 185 | The format of output message. Possible values are `"always"` (default, e.g., `1 day ago`), or `"auto"` (e.g., `yesterday`). `"auto"` allows to not always have to use numeric values in the output. 186 | 187 | ##### `style` 188 | 189 | The length of the internationalized message. Possible values are: `"long"` (default, e.g., `in 1 month`); `"short"` (e.g., `in 1 mo.`), or `"narrow"` (e.g., `in 1 mo.`). The narrow style could be similar to the short style for some locales. 190 | 191 | #### Example 192 | 193 | ```js 194 | // Create a relative time formatter in your locale. 195 | let rtf = new Intl.RelativeTimeFormat("en", { 196 | localeMatcher: "best fit", // other values: "lookup" 197 | numeric: "always", // other values: "auto" 198 | style: "long", // other values: "short" or "narrow" 199 | }); 200 | ``` 201 | 202 | ### `Intl.RelativeTimeFormat.prototype.format(value, unit)` 203 | 204 | The `Intl.RelativeTimeFormat.prototype.format` method formats a `value` and `unit` according to the locale and formatting options of this `Intl.RelativeTimeFormat` object. 205 | 206 | While this method automatically provides the correct plural forms, the grammatical form is otherwise as neutral as possible. It is the caller's responsibility to handle cut-off logic such as deciding between displaying "in 7 days" or "in 1 week". This API does not support relative dates involving compound units. e.g "in 5 days and 4 hours". 207 | 208 | #### `value` 209 | 210 | Numeric value to use in the internationalized relative time message. 211 | 212 | #### `unit` 213 | 214 | Unit to use in the relative time internationalized message. Possible values are: `"year"`, `"quarter"`, `"month"`, `"week"`, `"day"`, `"hour"`, `"minute"`, `"second"`. Plural forms are also permitted. 215 | 216 | #### Example 217 | 218 | ```js 219 | const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); 220 | 221 | // Format relative time using the day unit. 222 | rtf.format(-1, "day"); 223 | // > "yesterday" 224 | 225 | rtf.format(2.15, "day"); 226 | // > "in 2.15 days" 227 | 228 | rtf.format(100, "day"); 229 | // > "in 100 days" 230 | 231 | rtf.format(0, "day"); 232 | // > "today" 233 | 234 | rtf.format(-0, "day"); 235 | // > "today" 236 | ``` 237 | 238 | Additionally, by combining the class option `style` and `unit`, you can achieve any of the following results: 239 | 240 | ```text 241 | last year 242 | this year 243 | next year 244 | in 1 year 245 | in 2 years 246 | 1 year ago 247 | 2 years ago 248 | yr. 249 | last yr. 250 | this yr. 251 | next yr. 252 | in 1 yr. 253 | in 2 yr. 254 | 1 yr. ago 255 | 2 yr. ago 256 | last quarter 257 | this quarter 258 | next quarter 259 | in 1 quarter 260 | in 2 quarters 261 | 1 quarter ago 262 | 2 quarters ago 263 | last qtr. 264 | this qtr. 265 | next qtr. 266 | in 1 qtr. 267 | in 2 qtrs. 268 | 1 qtr. ago 269 | 2 qtrs. ago 270 | last month 271 | this month 272 | next month 273 | in 1 month 274 | in 2 months 275 | 1 month ago 276 | 2 months ago 277 | last mo. 278 | this mo. 279 | next mo. 280 | in 1 mo. 281 | in 2 mo. 282 | 1 mo. ago 283 | 2 mo. ago 284 | last week 285 | this week 286 | next week 287 | in 1 week 288 | in 2 weeks 289 | 1 week ago 290 | 2 weeks ago 291 | last wk. 292 | this wk. 293 | next wk. 294 | in 1 wk. 295 | in 2 wk. 296 | 1 wk. ago 297 | 2 wk. ago 298 | in 1 day 299 | in 2 days 300 | 1 day ago 301 | 2 days ago 302 | yesterday 303 | today 304 | tomorrow 305 | in 1 hour 306 | in 2 hours 307 | 1 hour ago 308 | 2 hours ago 309 | in 1 hr. 310 | in 2 hr. 311 | 1 hr. ago 312 | 2 hr. ago 313 | in 1 minute 314 | in 2 minutes 315 | 1 minute ago 316 | 2 minutes ago 317 | in 1 min. 318 | in 2 min. 319 | 1 min. ago 320 | 2 min. ago 321 | in 1 second 322 | in 2 seconds 323 | 1 second ago 324 | 2 seconds ago 325 | in 1 sec. 326 | in 1 sec. 327 | 1 sec. ago 328 | 2 sec. ago 329 | now 330 | ``` 331 | 332 | ### `Intl.RelativeTimeFormat.prototype.formatToParts(value, unit)` 333 | 334 | The `Intl.RelativeTimeFormat.prototype.formatToParts` method is a version of the `format` method which it returns an array of objects which represent "parts" of the object, separating the formatted number into its consituent parts and separating it from other surrounding text. These objects have two properties: `type` a NumberFormat formatToParts type, and `value`, which is the String which is the component of the output. If a "part" came from NumberFormat, it will have a `unit` property which indicates the unit being formatted; literals which are part of the larger frame will not have this property. 335 | 336 | #### Example 337 | 338 | ```js 339 | const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); 340 | 341 | // Format relative time using the day unit. 342 | rtf.formatToParts(-1, "day"); 343 | // > [{ type: "literal", value: "yesterday"}] 344 | 345 | rtf.formatToParts(100, "day"); 346 | // > [{ type: "literal", value: "in " }, { type: "integer", value: "100", unit: "day" }, { type: "literal", value: " days" }] 347 | ``` 348 | 349 | ## Development 350 | 351 | ### Render Spec 352 | 353 | ``` 354 | npm install 355 | npm run build 356 | open index.html 357 | ``` 358 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ev 4 | 5 | # Enable SSH authentication 6 | 7 | ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" 8 | ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" 9 | ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} 10 | ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} 11 | 12 | $(npm bin)/set-up-ssh --key "$ENCRYPTED_KEY" \ 13 | --iv "$ENCRYPTED_IV" \ 14 | --path-encrypted-key "$PRIVATE_KEY_FILE_NAME" 15 | 16 | # Update the content from the `gh-pages` branch 17 | 18 | $(npm bin)/update-branch --commands "mkdir out && ecmarkup spec/index.html out/index.html" \ 19 | --commit-message "Update gh-pages [skip ci]" \ 20 | --directory "out" \ 21 | --distribution-branch "gh-pages" \ 22 | --source-branch "master" 23 | 24 | -------------------------------------------------------------------------------- /ecmarkup.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | font-size: 18px; 4 | line-height: 1.5; 5 | font-family: Cambria, Palatino Linotype, Palatino, Liberation Serif, serif; 6 | padding: 0; 7 | margin: 0; 8 | color: #111; 9 | } 10 | 11 | #spec-container { 12 | padding: 0 20px; 13 | flex-grow: 1; 14 | flex-basis: 66%; 15 | box-sizing: border-box; 16 | overflow: hidden; 17 | } 18 | 19 | body.oldtoc { 20 | margin: 0 auto; 21 | } 22 | 23 | a { 24 | text-decoration: none; 25 | color: #206ca7; 26 | } 27 | 28 | a:visited { 29 | color: #206ca7; 30 | } 31 | 32 | a:hover { 33 | text-decoration: underline; 34 | color: #239dee; 35 | } 36 | 37 | 38 | code { 39 | font-weight: bold; 40 | font-family: Consolas, Monaco, monospace; 41 | white-space: pre; 42 | } 43 | 44 | pre code { 45 | font-weight: inherit; 46 | } 47 | 48 | pre code.hljs { 49 | background-color: #fff; 50 | margin: 0; 51 | padding: 0; 52 | } 53 | 54 | ol.toc { 55 | list-style: none; 56 | padding-left: 0; 57 | } 58 | 59 | ol.toc ol.toc { 60 | padding-left: 2ex; 61 | list-style: none; 62 | } 63 | 64 | var { 65 | color: #2aa198; 66 | transition: background-color 0.25s ease; 67 | cursor: pointer; 68 | } 69 | 70 | var.referenced { 71 | background-color: #ffff33; 72 | } 73 | 74 | emu-const { 75 | font-family: sans-serif; 76 | } 77 | 78 | emu-val { 79 | font-weight: bold; 80 | } 81 | emu-alg ol, emu-alg ol ol ol ol { 82 | list-style-type: decimal; 83 | } 84 | 85 | emu-alg ol ol, emu-alg ol ol ol ol ol { 86 | list-style-type: lower-alpha; 87 | } 88 | 89 | emu-alg ol ol ol, ol ol ol ol ol ol { 90 | list-style-type: lower-roman; 91 | } 92 | 93 | emu-eqn { 94 | display: block; 95 | margin-left: 4em; 96 | } 97 | 98 | emu-eqn.inline { 99 | display: inline; 100 | margin: 0; 101 | } 102 | 103 | emu-eqn div:first-child { 104 | margin-left: -2em; 105 | } 106 | 107 | emu-note { 108 | margin: 1em 0; 109 | color: #666; 110 | border-left: 5px solid #ccc; 111 | display: flex; 112 | flex-direction: row; 113 | } 114 | 115 | emu-note > span.note { 116 | flex-basis: 100px; 117 | min-width: 100px; 118 | flex-grow: 0; 119 | flex-shrink: 1; 120 | text-transform: uppercase; 121 | padding-left: 5px; 122 | } 123 | 124 | emu-note[type=editor] { 125 | border-left-color: #faa; 126 | } 127 | 128 | emu-note > div.note-contents { 129 | flex-grow: 1; 130 | flex-shrink: 1; 131 | } 132 | 133 | emu-note > div.note-contents > p:first-child { 134 | margin-top: 0; 135 | } 136 | 137 | emu-note > div.note-contents > p:last-child { 138 | margin-bottom: 0; 139 | } 140 | 141 | emu-figure { 142 | display: block; 143 | } 144 | 145 | emu-example { 146 | display: block; 147 | margin: 1em 3em; 148 | } 149 | 150 | emu-example figure figcaption { 151 | margin-top: 0.5em; 152 | text-align: left; 153 | } 154 | 155 | emu-figure figure, 156 | emu-example figure, 157 | emu-table figure { 158 | display: flex; 159 | flex-direction: column; 160 | align-items: center; 161 | } 162 | 163 | emu-production { 164 | display: block; 165 | margin-top: 1em; 166 | margin-bottom: 1em; 167 | margin-left: 5ex; 168 | } 169 | 170 | emu-grammar.inline, emu-production.inline, 171 | emu-grammar.inline emu-production emu-rhs, emu-production.inline emu-rhs, 172 | emu-grammar[collapsed] emu-production emu-rhs, emu-production[collapsed] emu-rhs { 173 | display: inline; 174 | padding-left: 1ex; 175 | margin-left: 0; 176 | } 177 | 178 | emu-grammar[collapsed] emu-production, emu-production[collapsed] { 179 | margin: 0; 180 | } 181 | 182 | emu-constraints { 183 | font-size: .75em; 184 | margin-right: 1ex; 185 | } 186 | 187 | emu-gann { 188 | margin-right: 1ex; 189 | } 190 | 191 | emu-gann emu-t:last-child, 192 | emu-gann emu-nt:last-child { 193 | margin-right: 0; 194 | } 195 | 196 | emu-geq { 197 | margin-left: 1ex; 198 | font-weight: bold; 199 | } 200 | 201 | emu-oneof { 202 | font-weight: bold; 203 | margin-left: 1ex; 204 | } 205 | 206 | emu-nt { 207 | display: inline-block; 208 | font-style: italic; 209 | white-space: nowrap; 210 | text-indent: 0; 211 | } 212 | 213 | emu-nt a, emu-nt a:visited { 214 | color: #333; 215 | } 216 | 217 | emu-rhs emu-nt { 218 | margin-right: 1ex; 219 | } 220 | 221 | emu-t { 222 | display: inline-block; 223 | font-family: monospace; 224 | font-weight: bold; 225 | white-space: nowrap; 226 | text-indent: 0; 227 | } 228 | 229 | emu-production emu-t { 230 | margin-right: 1ex; 231 | } 232 | 233 | emu-rhs { 234 | display: block; 235 | padding-left: 75px; 236 | text-indent: -25px; 237 | } 238 | 239 | emu-mods { 240 | font-size: .85em; 241 | vertical-align: sub; 242 | font-style: normal; 243 | font-weight: normal; 244 | } 245 | 246 | emu-production[collapsed] emu-mods { 247 | display: none; 248 | } 249 | 250 | emu-params, emu-opt { 251 | margin-right: 1ex; 252 | font-family: monospace; 253 | } 254 | 255 | emu-params, emu-constraints { 256 | color: #2aa198; 257 | } 258 | 259 | emu-opt { 260 | color: #b58900; 261 | } 262 | 263 | emu-gprose { 264 | font-size: 0.9em; 265 | font-family: Helvetica, Arial, sans-serif; 266 | } 267 | 268 | h1.shortname { 269 | color: #f60; 270 | font-size: 1.5em; 271 | margin: 0; 272 | } 273 | 274 | h1.version { 275 | color: #f60; 276 | font-size: 1.5em; 277 | margin: 0; 278 | } 279 | 280 | h1.title { 281 | margin-top: 0; 282 | color: #f60; 283 | } 284 | 285 | h1.first { 286 | margin-top: 0; 287 | } 288 | 289 | h1, h2, h3, h4, h5, h6 { 290 | position: relative; 291 | } 292 | 293 | h1 .secnum { 294 | text-decoration: none; 295 | margin-right: 10px; 296 | } 297 | 298 | h1 span.title { 299 | order: 2; 300 | } 301 | 302 | 303 | h1 { font-size: 2.67em; margin-top: 2em; margin-bottom: 0; line-height: 1em;} 304 | h2 { font-size: 2em; } 305 | h3 { font-size: 1.56em; } 306 | h4 { font-size: 1.25em; } 307 | h5 { font-size: 1.11em; } 308 | h6 { font-size: 1em; } 309 | 310 | h1:hover span.utils { 311 | display: block; 312 | } 313 | 314 | span.utils { 315 | font-size: 18px; 316 | line-height: 18px; 317 | display: none; 318 | position: absolute; 319 | top: 100%; 320 | left: 0; 321 | right: 0; 322 | font-weight: normal; 323 | } 324 | 325 | span.utils:before { 326 | content: "⤷"; 327 | display: inline-block; 328 | padding: 0 5px; 329 | } 330 | 331 | span.utils > * { 332 | display: inline-block; 333 | margin-right: 20px; 334 | } 335 | 336 | h1 span.utils span.anchor a, 337 | h2 span.utils span.anchor a, 338 | h3 span.utils span.anchor a, 339 | h4 span.utils span.anchor a, 340 | h5 span.utils span.anchor a, 341 | h6 span.utils span.anchor a { 342 | text-decoration: none; 343 | font-variant: small-caps; 344 | } 345 | 346 | h1 span.utils span.anchor a:hover, 347 | h2 span.utils span.anchor a:hover, 348 | h3 span.utils span.anchor a:hover, 349 | h4 span.utils span.anchor a:hover, 350 | h5 span.utils span.anchor a:hover, 351 | h6 span.utils span.anchor a:hover { 352 | color: #333; 353 | } 354 | 355 | emu-intro h1, emu-clause h1, emu-annex h1 { font-size: 2em; } 356 | emu-intro h2, emu-clause h2, emu-annex h2 { font-size: 1.56em; } 357 | emu-intro h3, emu-clause h3, emu-annex h3 { font-size: 1.25em; } 358 | emu-intro h4, emu-clause h4, emu-annex h4 { font-size: 1.11em; } 359 | emu-intro h5, emu-clause h5, emu-annex h5 { font-size: 1em; } 360 | emu-intro h6, emu-clause h6, emu-annex h6 { font-size: 0.9em; } 361 | emu-intro emu-intro h1, emu-clause emu-clause h1, emu-annex emu-annex h1 { font-size: 1.56em; } 362 | emu-intro emu-intro h2, emu-clause emu-clause h2, emu-annex emu-annex h2 { font-size: 1.25em; } 363 | emu-intro emu-intro h3, emu-clause emu-clause h3, emu-annex emu-annex h3 { font-size: 1.11em; } 364 | emu-intro emu-intro h4, emu-clause emu-clause h4, emu-annex emu-annex h4 { font-size: 1em; } 365 | emu-intro emu-intro h5, emu-clause emu-clause h5, emu-annex emu-annex h5 { font-size: 0.9em; } 366 | emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex h1 { font-size: 1.25em; } 367 | emu-intro emu-intro emu-intro h2, emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex h2 { font-size: 1.11em; } 368 | emu-intro emu-intro emu-intro h3, emu-clause emu-clause emu-clause h3, emu-annex emu-annex emu-annex h3 { font-size: 1em; } 369 | emu-intro emu-intro emu-intro h4, emu-clause emu-clause emu-clause h4, emu-annex emu-annex emu-annex h4 { font-size: 0.9em; } 370 | emu-intro emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex h1 { font-size: 1.11em; } 371 | emu-intro emu-intro emu-intro emu-intro h2, emu-clause emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex emu-annex h2 { font-size: 1em; } 372 | emu-intro emu-intro emu-intro emu-intro h3, emu-clause emu-clause emu-clause emu-clause h3, emu-annex emu-annex emu-annex emu-annex h3 { font-size: 0.9em; } 373 | emu-intro emu-intro emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex h1 { font-size: 1em; } 374 | emu-intro emu-intro emu-intro emu-intro emu-intro h2, emu-clause emu-clause emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex emu-annex emu-annex h2 { font-size: 0.9em; } 375 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1 { font-size: 0.9em } 376 | 377 | emu-clause, emu-intro, emu-annex { 378 | display: block; 379 | } 380 | 381 | /* Figures and tables */ 382 | figure { display: block; margin: 1em 0 3em 0; } 383 | figure object { display: block; margin: 0 auto; } 384 | figure table.real-table { margin: 0 auto; } 385 | figure figcaption { 386 | display: block; 387 | color: #555555; 388 | font-weight: bold; 389 | text-align: center; 390 | } 391 | 392 | emu-table table { 393 | margin: 0 auto; 394 | } 395 | 396 | emu-table table, table.real-table { 397 | border-collapse: collapse; 398 | } 399 | 400 | emu-table td, emu-table th, table.real-table td, table.real-table th { 401 | border: 1px solid black; 402 | padding: 0.4em; 403 | vertical-align: baseline; 404 | } 405 | emu-table th, emu-table thead td, table.real-table th { 406 | background-color: #eeeeee; 407 | } 408 | 409 | /* Note: the left content edges of table.lightweight-table >tbody >tr >td 410 | and div.display line up. */ 411 | table.lightweight-table { 412 | border-collapse: collapse; 413 | margin: 0 0 0 1.5em; 414 | } 415 | table.lightweight-table td, table.lightweight-table th { 416 | border: none; 417 | padding: 0 0.5em; 418 | vertical-align: baseline; 419 | } 420 | 421 | /* diff styles */ 422 | ins { 423 | background-color: #e0f8e0; 424 | text-decoration: none; 425 | border-bottom: 1px solid #396; 426 | } 427 | 428 | del { 429 | background-color: #fee; 430 | } 431 | 432 | ins.block, del.block, 433 | emu-production > ins, emu-production > del, 434 | emu-grammar > ins, emu-grammar > del { 435 | display: block; 436 | } 437 | 438 | tr.ins > td > ins { 439 | border-bottom: none; 440 | } 441 | 442 | tr.ins > td { 443 | background-color: #e0f8e0; 444 | } 445 | 446 | tr.del > td { 447 | background-color: #fee; 448 | } 449 | 450 | /* Menu Styles */ 451 | #menu-toggle { 452 | font-size: 2em; 453 | 454 | position: fixed; 455 | top: 0; 456 | left: 0; 457 | width: 1.5em; 458 | height: 1.5em; 459 | z-index: 3; 460 | visibility: hidden; 461 | color: #1567a2; 462 | background-color: #fff; 463 | 464 | line-height: 1.5em; 465 | text-align: center; 466 | -webkit-touch-callout: none; 467 | -webkit-user-select: none; 468 | -khtml-user-select: none; 469 | -moz-user-select: none; 470 | -ms-user-select: none; 471 | user-select: none;; 472 | 473 | cursor: pointer; 474 | } 475 | 476 | #menu { 477 | display: flex; 478 | flex-direction: column; 479 | width: 33%; height: 100vh; 480 | max-width: 500px; 481 | box-sizing: border-box; 482 | background-color: #ddd; 483 | overflow: hidden; 484 | transition: opacity 0.1s linear; 485 | padding: 0 5px; 486 | position: fixed; 487 | left: 0; top: 0; 488 | border-right: 2px solid #bbb; 489 | 490 | z-index: 2; 491 | } 492 | 493 | #menu-spacer { 494 | flex-basis: 33%; 495 | max-width: 500px; 496 | flex-grow: 0; 497 | flex-shrink: 0; 498 | } 499 | 500 | #menu a { 501 | color: #1567a2; 502 | } 503 | 504 | #menu.active { 505 | display: flex; 506 | opacity: 1; 507 | z-index: 2; 508 | } 509 | 510 | #menu-pins { 511 | flex-grow: 1; 512 | display: none; 513 | } 514 | 515 | #menu-pins.active { 516 | display: block; 517 | } 518 | 519 | #menu-pins-list { 520 | margin: 0; 521 | padding: 0; 522 | counter-reset: pins-counter; 523 | } 524 | 525 | #menu-pins-list > li:before { 526 | content: counter(pins-counter); 527 | counter-increment: pins-counter; 528 | display: inline-block; 529 | width: 25px; 530 | text-align: center; 531 | border: 1px solid #bbb; 532 | padding: 2px; 533 | margin: 4px; 534 | box-sizing: border-box; 535 | line-height: 1em; 536 | background-color: #ccc; 537 | border-radius: 4px; 538 | } 539 | #menu-toc > ol { 540 | padding: 0; 541 | flex-grow: 1; 542 | } 543 | 544 | #menu-toc > ol li { 545 | padding: 0; 546 | } 547 | 548 | #menu-toc > ol , #menu-toc > ol ol { 549 | list-style-type: none; 550 | margin: 0; 551 | padding: 0; 552 | } 553 | 554 | #menu-toc > ol ol { 555 | padding-left: 0.75em; 556 | } 557 | 558 | #menu-toc li { 559 | text-overflow: ellipsis; 560 | overflow: hidden; 561 | white-space: nowrap; 562 | } 563 | 564 | #menu-toc .item-toggle { 565 | display: inline-block; 566 | transform: rotate(-45deg) translate(-5px, -5px); 567 | transition: transform 0.1s ease; 568 | text-align: center; 569 | width: 20px; 570 | 571 | color: #aab; 572 | 573 | -webkit-touch-callout: none; 574 | -webkit-user-select: none; 575 | -khtml-user-select: none; 576 | -moz-user-select: none; 577 | -ms-user-select: none; 578 | user-select: none;; 579 | 580 | cursor: pointer; 581 | } 582 | 583 | #menu-toc .item-toggle-none { 584 | display: inline-block; 585 | width: 20px; 586 | } 587 | 588 | #menu-toc li.active > .item-toggle { 589 | transform: rotate(45deg) translate(-5px, -5px); 590 | } 591 | 592 | #menu-toc li > ol { 593 | display: none; 594 | } 595 | 596 | #menu-toc li.active > ol { 597 | display: block; 598 | } 599 | 600 | #menu-toc li.revealed > a { 601 | background-color: #bbb; 602 | font-weight: bold; 603 | /* 604 | background-color: #222; 605 | color: #c6d8e4; 606 | */ 607 | } 608 | 609 | #menu-toc li.revealed-leaf> a { 610 | color: #206ca7; 611 | /* 612 | background-color: #222; 613 | color: #c6d8e4; 614 | */ 615 | } 616 | 617 | #menu-toc li.revealed > .item-toggle { 618 | transform: rotate(45deg) translate(-5px, -5px); 619 | } 620 | 621 | #menu-toc li.revealed > ol { 622 | display: block; 623 | } 624 | 625 | #menu-toc li > a { 626 | padding: 2px 5px; 627 | } 628 | 629 | #menu > * { 630 | margin-bottom: 5px; 631 | } 632 | 633 | .menu-pane-header { 634 | padding: 0 5px; 635 | text-transform: uppercase; 636 | background-color: #aaa; 637 | color: #335; 638 | font-weight: bold; 639 | letter-spacing: 2px; 640 | flex-grow: 0; 641 | flex-shrink: 0; 642 | font-size: 0.8em; 643 | } 644 | 645 | #menu-toc { 646 | display: flex; 647 | flex-direction: column; 648 | width: 100%; 649 | overflow: hidden; 650 | flex-grow: 1; 651 | } 652 | 653 | #menu-toc ol.toc { 654 | overflow-x: hidden; 655 | overflow-y: auto; 656 | } 657 | 658 | #menu-search { 659 | position: relative; 660 | flex-grow: 0; 661 | flex-shrink: 0; 662 | width: 100%; 663 | 664 | display: flex; 665 | flex-direction: column; 666 | 667 | max-height: 300px; 668 | } 669 | 670 | #menu-trace-list { 671 | display: none; 672 | } 673 | 674 | #menu-search-box { 675 | box-sizing: border-box; 676 | display: block; 677 | width: 100%; 678 | margin: 5px 0 0 0; 679 | font-size: 1em; 680 | padding: 2px; 681 | background-color: #bbb; 682 | border: 1px solid #999; 683 | } 684 | 685 | #menu-search-results { 686 | overflow-x: hidden; 687 | overflow-y: auto; 688 | } 689 | 690 | li.menu-search-result-clause:before { 691 | content: 'clause'; 692 | width: 40px; 693 | display: inline-block; 694 | text-align: right; 695 | padding-right: 1ex; 696 | color: #666; 697 | font-size: 75%; 698 | } 699 | li.menu-search-result-op:before { 700 | content: 'op'; 701 | width: 40px; 702 | display: inline-block; 703 | text-align: right; 704 | padding-right: 1ex; 705 | color: #666; 706 | font-size: 75%; 707 | } 708 | 709 | li.menu-search-result-prod:before { 710 | content: 'prod'; 711 | width: 40px; 712 | display: inline-block; 713 | text-align: right; 714 | padding-right: 1ex; 715 | color: #666; 716 | font-size: 75% 717 | } 718 | 719 | 720 | li.menu-search-result-term:before { 721 | content: 'term'; 722 | width: 40px; 723 | display: inline-block; 724 | text-align: right; 725 | padding-right: 1ex; 726 | color: #666; 727 | font-size: 75% 728 | } 729 | 730 | #menu-search-results ul { 731 | padding: 0 5px; 732 | margin: 0; 733 | } 734 | 735 | #menu-search-results li { 736 | white-space: nowrap; 737 | text-overflow: ellipsis; 738 | } 739 | 740 | 741 | #menu-trace-list { 742 | counter-reset: item; 743 | margin: 0 0 0 20px; 744 | padding: 0; 745 | } 746 | #menu-trace-list li { 747 | display: block; 748 | white-space: nowrap; 749 | } 750 | 751 | #menu-trace-list li .secnum:after { 752 | content: " "; 753 | } 754 | #menu-trace-list li:before { 755 | content: counter(item) " "; 756 | background-color: #222; 757 | counter-increment: item; 758 | color: #999; 759 | width: 20px; 760 | height: 20px; 761 | line-height: 20px; 762 | display: inline-block; 763 | text-align: center; 764 | margin: 2px 4px 2px 0; 765 | } 766 | 767 | @media (max-width: 1000px) { 768 | body { 769 | margin: 0; 770 | display: block; 771 | } 772 | 773 | #menu { 774 | display: none; 775 | padding-top: 3em; 776 | width: 450px; 777 | } 778 | 779 | #menu.active { 780 | position: fixed; 781 | height: 100%; 782 | left: 0; 783 | top: 0; 784 | right: 300px; 785 | } 786 | 787 | #menu-toggle { 788 | visibility: visible; 789 | } 790 | 791 | #spec-container { 792 | padding: 0 5px; 793 | } 794 | 795 | #references-pane-spacer { 796 | display: none; 797 | } 798 | } 799 | 800 | @media only screen and (max-width: 800px) { 801 | #menu { 802 | width: 100%; 803 | } 804 | 805 | h1 .secnum:empty { 806 | margin: 0; padding: 0; 807 | } 808 | } 809 | 810 | 811 | /* Toolbox */ 812 | .toolbox { 813 | position: absolute; 814 | background: #ddd; 815 | border: 1px solid #aaa; 816 | display: none; 817 | color: #eee; 818 | padding: 5px; 819 | border-radius: 3px; 820 | } 821 | 822 | .toolbox.active { 823 | display: inline-block; 824 | } 825 | 826 | .toolbox a { 827 | text-decoration: none; 828 | padding: 0 5px; 829 | } 830 | 831 | .toolbox a:hover { 832 | text-decoration: underline; 833 | } 834 | 835 | .toolbox:after, .toolbox:before { 836 | top: 100%; 837 | left: 15px; 838 | border: solid transparent; 839 | content: " "; 840 | height: 0; 841 | width: 0; 842 | position: absolute; 843 | pointer-events: none; 844 | } 845 | 846 | .toolbox:after { 847 | border-color: rgba(0, 0, 0, 0); 848 | border-top-color: #ddd; 849 | border-width: 10px; 850 | margin-left: -10px; 851 | } 852 | .toolbox:before { 853 | border-color: rgba(204, 204, 204, 0); 854 | border-top-color: #aaa; 855 | border-width: 12px; 856 | margin-left: -12px; 857 | } 858 | 859 | #references-pane-container { 860 | position: fixed; 861 | bottom: 0; 862 | left: 0; 863 | right: 0; 864 | height: 250px; 865 | display: none; 866 | background-color: #ddd; 867 | z-index: 1; 868 | } 869 | 870 | #references-pane-table-container { 871 | overflow-x: hidden; 872 | overflow-y: auto; 873 | } 874 | 875 | #references-pane-spacer { 876 | flex-basis: 33%; 877 | max-width: 500px; 878 | } 879 | 880 | #references-pane { 881 | flex-grow: 1; 882 | overflow: hidden; 883 | display: flex; 884 | flex-direction: column; 885 | } 886 | 887 | #references-pane-container.active { 888 | display: flex; 889 | } 890 | 891 | #references-pane-close:after { 892 | content: '✖'; 893 | float: right; 894 | cursor: pointer; 895 | } 896 | 897 | #references-pane table tr td:first-child { 898 | text-align: right; 899 | padding-right: 5px; 900 | } 901 | -------------------------------------------------------------------------------- /ecmarkup.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function Search(menu) { 4 | this.menu = menu; 5 | this.$search = document.getElementById('menu-search'); 6 | this.$searchBox = document.getElementById('menu-search-box'); 7 | this.$searchResults = document.getElementById('menu-search-results'); 8 | 9 | this.loadBiblio(); 10 | 11 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 12 | 13 | this.$searchBox.addEventListener('keydown', debounce(this.searchBoxKeydown.bind(this), { stopPropagation: true })); 14 | this.$searchBox.addEventListener('keyup', debounce(this.searchBoxKeyup.bind(this), { stopPropagation: true })); 15 | } 16 | 17 | Search.prototype.loadBiblio = function () { 18 | var $biblio = document.getElementById('menu-search-biblio'); 19 | if (!$biblio) { 20 | this.biblio = []; 21 | } else { 22 | this.biblio = JSON.parse($biblio.textContent); 23 | this.biblio.clauses = this.biblio.filter(function (e) { return e.type === 'clause' }); 24 | this.biblio.byId = this.biblio.reduce(function (map, entry) { 25 | map[entry.id] = entry; 26 | return map; 27 | }, {}); 28 | } 29 | } 30 | 31 | Search.prototype.documentKeydown = function (e) { 32 | if (e.keyCode === 191) { 33 | e.preventDefault(); 34 | e.stopPropagation(); 35 | this.triggerSearch(); 36 | } 37 | } 38 | 39 | Search.prototype.searchBoxKeydown = function (e) { 40 | e.stopPropagation(); 41 | e.preventDefault(); 42 | if (e.keyCode === 191 && e.target.value.length === 0) { 43 | e.preventDefault(); 44 | } else if (e.keyCode === 13) { 45 | e.preventDefault(); 46 | this.selectResult(); 47 | } 48 | } 49 | 50 | Search.prototype.searchBoxKeyup = function (e) { 51 | if (e.keyCode === 13 || e.keyCode === 9) { 52 | return; 53 | } 54 | 55 | this.search(e.target.value); 56 | } 57 | 58 | 59 | Search.prototype.triggerSearch = function (e) { 60 | if (this.menu.isVisible()) { 61 | this._closeAfterSearch = false; 62 | } else { 63 | this._closeAfterSearch = true; 64 | this.menu.show(); 65 | } 66 | 67 | this.$searchBox.focus(); 68 | this.$searchBox.select(); 69 | } 70 | // bit 12 - Set if the result starts with searchString 71 | // bits 8-11: 8 - number of chunks multiplied by 2 if cases match, otherwise 1. 72 | // bits 1-7: 127 - length of the entry 73 | // General scheme: prefer case sensitive matches with fewer chunks, and otherwise 74 | // prefer shorter matches. 75 | function relevance(result, searchString) { 76 | var relevance = 0; 77 | 78 | relevance = Math.max(0, 8 - result.match.chunks) << 7; 79 | 80 | if (result.match.caseMatch) { 81 | relevance *= 2; 82 | } 83 | 84 | if (result.match.prefix) { 85 | relevance += 2048 86 | } 87 | 88 | relevance += Math.max(0, 255 - result.entry.key.length); 89 | 90 | return relevance; 91 | } 92 | 93 | Search.prototype.search = function (searchString) { 94 | var s = Date.now(); 95 | 96 | if (searchString === '') { 97 | this.displayResults([]); 98 | this.hideSearch(); 99 | return; 100 | } else { 101 | this.showSearch(); 102 | } 103 | 104 | if (searchString.length === 1) { 105 | this.displayResults([]); 106 | return; 107 | } 108 | 109 | var results; 110 | 111 | if (/^[\d\.]*$/.test(searchString)) { 112 | results = this.biblio.clauses.filter(function (clause) { 113 | return clause.number.substring(0, searchString.length) === searchString; 114 | }).map(function (clause) { 115 | return { entry: clause }; 116 | }); 117 | } else { 118 | results = []; 119 | 120 | for (var i = 0; i < this.biblio.length; i++) { 121 | var entry = this.biblio[i]; 122 | 123 | var match = fuzzysearch(searchString, entry.key); 124 | if (match) { 125 | results.push({ entry: entry, match: match }); 126 | } 127 | } 128 | 129 | results.forEach(function (result) { 130 | result.relevance = relevance(result, searchString); 131 | }); 132 | 133 | results = results.sort(function (a, b) { return b.relevance - a.relevance }); 134 | 135 | } 136 | 137 | if (results.length > 50) { 138 | results = results.slice(0, 50); 139 | } 140 | 141 | this.displayResults(results); 142 | } 143 | Search.prototype.hideSearch = function () { 144 | this.$search.classList.remove('active'); 145 | } 146 | 147 | Search.prototype.showSearch = function () { 148 | this.$search.classList.add('active'); 149 | } 150 | 151 | Search.prototype.selectResult = function () { 152 | var $first = this.$searchResults.querySelector('li:first-child a'); 153 | 154 | if ($first) { 155 | document.location = $first.getAttribute('href'); 156 | } 157 | 158 | this.$searchBox.value = ''; 159 | this.$searchBox.blur(); 160 | this.displayResults([]); 161 | this.hideSearch(); 162 | 163 | if (this._closeAfterSearch) { 164 | this.menu.hide(); 165 | } 166 | } 167 | 168 | Search.prototype.displayResults = function (results) { 169 | if (results.length > 0) { 170 | this.$searchResults.classList.remove('no-results'); 171 | 172 | var html = '
19 | The abstract operation InitializeRelativeTimeFormat accepts the arguments relativeTimeFormat (which must be an object), locales, and options. It initializes relativeTimeFormat as a RelativeTimeFormat object. It performas the following steps: 20 | 21 |
22 | 23 |"localeMatcher"
, "string"
, «"lookup"
, "best fit"
», "best fit"
)."style"
, "string"
, «"long"
, "short"
, "narrow"
», "long"
)."useGrouping"
, "minimumIntegerDigits"
, "style"
, "cardinal"
).
31 | When the
"second"
, "minute"
, "hour"
, "day"
, "week"
, "month"
, "quarter"
, "year"
, throw a RangeError exception."past"
."future"
.
43 | The MakePartsList abstract operation is called with arguments parts, a pattern String, unit, a String, and value a String.
44 |
45 |
MakePartsList("AA{0}BB", "hour", "15") --< 49 | 50 | Output (List of Records): 51 | [ 52 | {type: 'literal', value: 'AA'}, 53 | {type: 'hour', value: '15'}, 54 | {type: 'literal', value: 'BB'}, 55 | ] 56 |57 | 58 |
"length"
)."literal"
, [[Value]]: literal } as a new element of the list result."0"
."literal"
, [[Value]]: literal } as a new element of the list result.66 | The FormatRelativeTime abstract operation is called with arguments relativeTimeFormat (which must be an object initialized as a RelativeTimeFormat), value (which must be a Number value), and unit (which must be a String denoting the value unit) and performs the following steps: 67 | 68 |
69 | 70 |79 |
80 | 81 |
90 | The RelativeTimeFormat constructor is a standard built-in property of the Intl object. Behaviour common to all service constructor properties of the Intl object is specified in
98 | When the
111 | The Intl.RelativeTimeFormat constructor has the following properties: 112 | 113 |
114 | 115 |
119 | The value of
123 | This property has the attributes { [[Writable]]:
132 | When the
140 | The value of the
149 | The value of the [[AvailableLocales]] internal slot is implementation defined within the constraints described in
154 | The value of the [[RelevantExtensionKeys]] internal slot is []. 155 | 156 |
157 | 158 |
164 | The value of the [[LocaleData]] internal slot is implementation defined within the constraints described in
"second"
, "minute"
, "hour"
, "day"
, "week"
, "month"
, "quarter"
and "year"
. Additional property keys may exist with the previous names concatenated with the strings "-narrow"
or "-short"
. The values corresponding to these property keys are objects which contain these two categories of properties:"future"
and "past"
properties, which are objects with a property for each of the [[PluralCategories]] in the Locale. The value corresponding to those properties is a pattern which may contain "{0}"
to be replaced by a formatted number.187 | The Intl.RelativeTimeFormat prototype object is itself an ordinary object. %RelativeTimeFormatPrototype% is not an Intl.RelativeTimeFormat instance and does not have an [[InitializedRelatimveTimeFormat]] internal slot or any of the other internal slots of Intl.RelativeTimeFormat instance objects. 188 | 189 |
190 |
191 | In the following descriptions of functions that are properties or [[Get]] attributes of properties of
199 | The initial value of
208 | The initial value of the @@toStringTag property is the string value "Object"
.
209 |
210 |
212 | This property has the attributes { [[Writable]]:
221 | When the
233 | When the
245 | This function provides access to the locale and formatting options computed during initialization of the object. 246 | 247 |
248 |
249 | The function returns a new object whose properties and attributes are set as if constructed by an object literal assigning to each of the following properties the value of the corresponding internal slot of this RelativeTimeFormat object (see
259 | Intl.RelativeTimeFormat instances inherit properties from
264 | Intl.RelativeTimeFormat instances and other objects that have been successfully initialized as a RelativeTimeFormat have [[InitializedIntlObject]] and [[InitializedRelativeTimeFormat]] internal slots whose values are
269 | Objects that have been successfully initialized as a RelativeTimeFormat object also have several internal slots that are computed by the constructor: 270 | 271 |
272 | 273 |"long"
, "short"
, or "narrow"
, identifying the relative time format style used.8 | title: Intl.RelativeTimeFormat Spec Proposal 9 | stage: 3 10 | contributors: Caridy Patiño, Eric Ferraiuolo, Zibi Braniecki, Rafael Xavier, Daniel Ehrenberg 11 | status: draft 12 | copyright: false 13 | location: https://rawgit.com/tc39/proposal-intl-relative-time/master/index.html 14 |15 |
12 | The abstract operation InitializeRelativeTimeFormat accepts the arguments _relativeTimeFormat_ (which must be an object), _locales_, and _options_. It initializes _relativeTimeFormat_ as a RelativeTimeFormat object. 13 |
14 |The following steps are taken:
15 | 16 |69 | When the FormatRelativeTime abstract operation is called with arguments _relativeTimeFormat_, _value_, and _unit_ it returns a String value representing _value_ (which must be a Number value) according to the effective locale and the formatting options of _relativeTimeFormat_. 70 |
71 | 72 |
112 | The MakePartsList abstract operation is called with arguments _pattern_, a pattern String, _unit_, a String, and _parts_, a List of Records representing a formatted Number.
113 |
116 | MakePartsList("AA{0}BB", "hour", « { [[Type]]: "integer", [[Value]]: "15" } » )
117 |
118 | Output (List of Records):
119 | «
120 | { [[Type]]: "literal", [[Value]]: "AA"},
121 | { [[Type]]: "integer", [[Value]]: "15", [[Unit]]: "hour"},
122 | { [[Type]]: "literal", [[Value]]: "BB"}
123 | »
124 |
125 |
144 | The FormatRelativeTime abstract operation is called with arguments _relativeTimeFormat_ (which must be an object initialized as a RelativeTimeFormat), _value_ (which must be a Number value), and _unit_ (which must be a String denoting the value unit) and performs the following steps: 145 |
146 | 147 |160 | The FormatRelativeTimeToParts abstract operation is called with arguments _relativeTimeFormat_ (which must be an object initialized as a RelativeTimeFormat), _value_ (which must be a Number value), and _unit_ (which must be a String denoting the value unit) and performs the following steps: 161 |
162 | 163 |
184 | The RelativeTimeFormat constructor is the %RelativeTimeFormat% intrinsic object and a standard built-in property of the Intl object. Behaviour common to all service constructor properties of the Intl object is specified in
191 | When the *Intl.RelativeTimeFormat* function is called with optional arguments the following steps are taken: 192 |
193 | 194 |206 | The Intl.RelativeTimeFormat constructor has the following properties: 207 |
208 | 209 |213 | The value of `Intl.RelativeTimeFormat.prototype` is %RelativeTimeFormatPrototype%. 214 |
215 |216 | This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *false* }. 217 |
218 |224 | When the `supportedLocalesOf` method of %RelativeTimeFormat% is called, the following steps are taken: 225 |
226 | 227 |
238 | The value of the [[AvailableLocales]] internal slot is implementation defined within the constraints described in
242 | The value of the [[RelevantExtensionKeys]] internal slot is « `"nu"` ». 243 |
244 | 245 |
250 | The value of the [[LocaleData]] internal slot is implementation defined within the constraints described in
275 | The Intl.RelativeTimeFormat prototype object is itself an ordinary object. %RelativeTimeFormatPrototype% is not an Intl.RelativeTimeFormat instance and does not have an [[InitializedRelativeTimeFormat]] internal slot or any of the other internal slots of Intl.RelativeTimeFormat instance objects. 276 |
277 | 278 |282 | The initial value of `Intl.RelativeTimeFormat.prototype.constructor` is %RelativeTimeFormat%. 283 |
284 |290 | The initial value of the @@toStringTag property is the string value `"Intl.RelativeTimeFormat"`. 291 |
292 |293 | This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }. 294 |
295 |301 | When the `format` method is called with arguments _value_ and _unit_, the following steps are taken: 302 |
303 | 304 |318 | When the `formatToParts` method is called with arguments _value_ and _unit_, the following steps are taken: 319 |
320 | 321 |335 | This function provides access to the locale and options computed during initialization of the object. 336 |
337 | 338 |Internal Slot | 357 |Property | 358 |
---|---|
[[Locale]] | 362 |`"locale"` | 363 |
[[Style]] | 366 |`"style"` | 367 |
[[Numeric]] | 370 |`"numeric"` | 371 |
[[NumberingSystem]] | 374 |`"numberingSystem"` | 375 |
385 | Intl.RelativeTimeFormat instances are ordinary objects that inherit properties from %RelativeTimeFormatPrototype%. 386 |
387 | 388 |389 | Intl.RelativeTimeFormat instances have an [[InitializedRelativeTimeFormat]] internal slot. 390 |
391 | 392 |393 | Intl.RelativeTimeFormat instances also have several internal slots that are computed by the constructor: 394 |
395 | 396 |