├── .gitignore ├── LICENSE ├── README.md ├── index.html ├── package.json ├── spec.css ├── spec.emu └── spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Caridy Patiño 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Module Reform (REJECTED) 2 | 3 | ECMAScript Proposal specs for the reform to preserve the order of execution for dynamic modules. 4 | 5 | ## Champions 6 | 7 | * [@caridy](https://github.com/caridy). 8 | * [@dherman](https://github.com/dherman). 9 | 10 | This proposal was rejected in favor of preserving the current semantics. You can read more about this decision in the [tc39-notes](https://github.com/rwaldron/tc39-notes/blob/master/es8/2017-03/mar-23.md#10iia-dynamic-module-reform-for-stage-2). The last slide from [Update 2 - March 2017](https://docs.google.com/presentation/d/1EYOysPhgjXtgmuNoZ_wUCMElZ8GKLxJmCLeF0EvUXkc/edit#slide=id.g1f1f22a6f5_1_31) shows why this proposal is faulty. 11 | 12 | ## Slides 13 | 14 | * [Update 1 - Nov 2016](https://docs.google.com/presentation/d/1EYOysPhgjXtgmuNoZ_wUCMElZ8GKLxJmCLeF0EvUXkc/edit#slide=id.p) 15 | * [Update 2 - March 2017](https://docs.google.com/presentation/d/1EYOysPhgjXtgmuNoZ_wUCMElZ8GKLxJmCLeF0EvUXkc/edit#slide=id.g1f19d52d1f_0_0) 16 | 17 | ## Rationale 18 | 19 | ES module spec requires all Module Records to know all the exports during `ModuleDeclarationInstantiation()`. This is needed to guarantee that the linking process (bindings from one module environment record to another) can be accomplish, and works very well for Source Text Module Records, but forces other dynamic modules (like Node CJS modules) to be evaluated during the `ModuleDeclarationInstantiation()` phase. As a result, it is impossible to preserve the order of evaluation. 20 | 21 | ## Preserving the order of evaluation for Dynamic Module Records 22 | 23 | This proposal introduces the `"pending"` resolution value when calling `ResolveExport()` for a Dynamic Module Record. Additionally, this proposal introduces a new internal slot `[[PendingImportEntries]]` on Source Text Module Records that is used to track the import entries that are coming from Dynamic Module Records that hasn't been evaluated yet. As a result, calling `ResolveExport()` on the Dynamic Module Record during the `ModuleDeclarationInstantiation()` phase can resolve to `"pending"` to signal that the validation or assertion about the bindings should be deferred to the `ModuleEvaluation()` phase for the Source Text Module Record importing from a Dynamic Module Record. 24 | 25 | These changes allow us to identify, during the linking phase, an import binding that cannot be created yet and does not require explicit assertion during this phase. As a result, we can defer the evaluation of a Dynamic Module Record to preserve the execution order, and we do so under the assumption that the imported bindings from the Dynamic Module Record will gets populated after it is evaluated. 26 | 27 | ## Enabling Circular Dependencies with Dynamic Module Records 28 | 29 | This change also enable us to match the semantics of NCJS when it comes to circular dependencies. The following example illustrates this: 30 | 31 | ```js 32 | // even.js 33 | module.exports = function even(n) { 34 | ... 35 | require('odd')(n - 1) 36 | ... 37 | } 38 | 39 | // odd.js 40 | module.exports = function odd(n) { 41 | ... 42 | require('even')(n - 1) 43 | ... 44 | } 45 | ``` 46 | 47 | These CJS modules will work in Node independently of which one is imported first, the same applies if both are written as ESM. But if one of them is ESM and the other is CJS, based on the currenct spec, we might get a static error depending on which one is imported first. E.g.: 48 | 49 | ```js 50 | // even.js (ESM): 51 | import odd from "odd"; 52 | export default function even() { 53 | ... 54 | odd(n - 1) 55 | ... 56 | } 57 | 58 | // odd.js (DM) 59 | module.exports = function odd(n) { 60 | ... 61 | require('even')(n - 1) 62 | ... 63 | } 64 | ``` 65 | 66 | If the binding for `odd` in `even.js` is not created until after `odd.js` is evaluated, the NCJS semantics are preserved, and this example works independently of which one is imported first. 67 | 68 | ## SyntaxError in Source Text Module Records 69 | 70 | * Throw during `ModuleDeclarationInstantiation` if the indirect import entry resolves to *null* or *ambiguous*. 71 | * Throw during `ModuleEvaluation` if a pending import entry resolves to *null* or *ambiguous* or *pending* after evaluating all dependencies, and before evaluating the source text. 72 | 73 | ## Compromises 74 | 75 | With this proposal, the statically verifiable mechanism introduced by ESM can only be enforced in a ES Module that is importing from another ES Module, and any interaction with Dynamic Module Records will be deferred to the `ModuleEvaluation()` phase for Dynamic Module Records that were not previously evaluated. 76 | 77 | ## Additional Information 78 | 79 | Most of the discussion around this topic is condenced in the meetings notes from TC39 Sept 2016 Meeting: 80 | 81 | * [Meeting Notes](https://esdiscuss.org/notes/2016-09-28) 82 | * [ES Modules Slides](https://esdiscuss.org/notes/2016-09/ES-Modules-Compat.pdf) 83 | 84 | ## Spec 85 | 86 | You can view the spec rendered as [HTML](https://rawgit.com/tc39/proposal-dynamic-modules/master/index.html). 87 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | dynamic modules reform 9 | 94 | 1030 | 2079 | 2080 | 2081 | 2082 | 2083 | 2084 | 2104 |
2105 |

Stage 1 Draft / January 26, 2017

2106 |

dynamic modules reform

2107 | 2108 | 2109 | 2110 |
2111 |
Table 1: Additional Fields of Source Text Module Records
2112 | 2113 | 2114 | 2115 | 2119 | 2123 | 2127 | 2128 | 2129 | 2133 | 2137 | 2142 | 2143 | 2144 | 2147 | 2150 | 2153 | 2154 | 2155 | 2159 | 2163 | 2168 | 2169 | 2170 |
2116 | Field Name 2117 | 2118 | 2120 | Value Type 2121 | 2122 | 2124 | Meaning 2125 | 2126 |
2130 | [[ImportEntries]] 2131 | 2132 | 2134 | List of ImportEntry Records 2135 | 2136 | 2138 | A 2139 | List of ImportEntry records derived from the code of this module. 2140 | 2141 |
2145 | [[PendingImportEntries]] 2146 | 2148 | List of ImportEntry Records 2149 | 2151 | A List of ImportEntry records that are pending to be validated. 2152 |
2156 | [[LocalExportEntries]] 2157 | 2158 | 2160 | List of ExportEntry Records 2161 | 2162 | 2164 | A 2165 | List of ExportEntry records derived from the code of this module that correspond to declarations that occur within the module. 2166 | 2167 |
2171 |
2172 |
2173 | 2174 | 2175 | 2176 |

1[[Get]] (P, Receiver)

2177 |

When the [[Get]] internal method of a module namespace exotic object O is called with property key P and 2178 | ECMAScript language value Receiver, the following steps are taken:

2179 | 2180 |
    2181 |
  1. Assert: 2182 | IsPropertyKey(P) is 2183 | true.
  2. 2184 |
  3. If 2185 | Type(P) is Symbol, then 2186 |
      2187 |
    1. Return ?  2188 | OrdinaryGet(O, P, Receiver).
    2. 2189 |
    2190 |
  4. 2191 |
  5. Let exports be O.[[Exports]].
  6. 2192 |
  7. If P is not an element of exports, return 2193 | undefined.
  8. 2194 |
  9. Let m be O.[[Module]].
  10. 2195 |
  11. Let binding be ! m.ResolveExport(P, « », « »).
  12. 2196 |
  13. Assert: binding is neither null nor "ambiguous".
  14. 2197 |
  15. If binding is "pending" or binding is null or resolution is "ambiguous", throw a SyntaxError exception.
  16. 2198 |
  17. Let targetModule be binding.[[Module]].
  18. 2199 |
  19. Assert: targetModule is not 2200 | undefined.
  20. 2201 |
  21. Let targetEnv be targetModule.[[Environment]].
  22. 2202 |
  23. If targetEnv is 2203 | undefined, throw a 2204 | ReferenceError exception.
  24. 2205 |
  25. Let targetEnvRec be targetEnv's 2206 | EnvironmentRecord.
  26. 2207 |
  27. Return ? targetEnvRec.GetBindingValue(binding.[[BindingName]], 2208 | true). 2209 |
  28. 2210 |
2211 |
2212 | Note 2213 |
2214 |

ResolveExport is idempotent and side-effect free. An implementation might choose to pre-compute or cache the ResolveExport results for the [[Exports]] of each module namespace exotic object.

2215 |
2216 |
2217 |
2218 | 2219 | 2220 | 2221 |

2ResolveExport( exportName, resolveSet, exportStarSet ) Concrete Method

2222 |

The ResolveExport concrete method of a 2223 | Source Text Module Record with arguments exportName, resolveSet, and exportStarSet performs the following steps:

2224 | 2225 |
    2226 |
  1. Let module be this 2227 | Source Text Module Record.
  2. 2228 |
  3. For each 2229 | Record {[[Module]], [[ExportName]]} r in resolveSet, do: 2230 |
      2231 |
    1. If module and r.[[Module]] are the same 2232 | Module Record and 2233 | SameValue(exportName, r.[[ExportName]]) is 2234 | true, then 2235 |
        2236 |
      1. Assert: This is a circular import request.
      2. 2237 |
      3. Return 2238 | null.
      4. 2239 |
      2240 |
    2. 2241 |
    2242 |
  4. 2243 |
  5. Append the 2244 | Record {[[Module]]: module, [[ExportName]]: exportName} to resolveSet.
  6. 2245 |
  7. For each ExportEntry 2246 | Record e in module.[[LocalExportEntries]], do 2247 |
      2248 |
    1. If 2249 | SameValue(exportName, e.[[ExportName]]) is 2250 | true, then 2251 |
        2252 |
      1. Assert: module provides the direct binding for this export.
      2. 2253 |
      3. Return 2254 | Record{[[Module]]: module, [[BindingName]]: e.[[LocalName]]}.
      4. 2255 |
      2256 |
    2. 2257 |
    2258 |
  8. 2259 |
  9. For each ExportEntry 2260 | Record e in module.[[IndirectExportEntries]], do 2261 |
      2262 |
    1. If 2263 | SameValue(exportName, e.[[ExportName]]) is 2264 | true, then 2265 |
        2266 |
      1. Assert: module imports a specific binding for this export.
      2. 2267 |
      3. Let importedModule be ?  2268 | HostResolveImportedModule(module, e.[[ModuleRequest]]).
      4. 2269 |
      5. Return ? importedModule.ResolveExport(e.[[ImportName]], resolveSet, exportStarSet).
      6. 2270 |
      2271 |
    2. 2272 |
    2273 |
  10. 2274 |
  11. If 2275 | SameValue(exportName, "default") is 2276 | true, then 2277 |
      2278 |
    1. Assert: A default export was not explicitly defined by this module.
    2. 2279 |
    3. Return 2280 | null.
    4. 2281 |
    5. NOTE A default export cannot be provided by an export *.
    6. 2282 |
    2283 |
  12. 2284 |
  13. If exportStarSet contains module, return 2285 | null.
  14. 2286 |
  15. Append module to exportStarSet.
  16. 2287 |
  17. Let starResolution be 2288 | null.
  18. 2289 |
  19. For each ExportEntry 2290 | Record e in module.[[StarExportEntries]], do 2291 |
      2292 |
    1. Let importedModule be ?  2293 | HostResolveImportedModule(module, e.[[ModuleRequest]]).
    2. 2294 |
    3. Let resolution be ? importedModule.ResolveExport(exportName, resolveSet, exportStarSet).
    4. 2295 |
    5. If resolution is "ambiguous", return "ambiguous".
    6. 2296 |
    7. If resolution is "pending", return "pending".
    8. 2297 |
    9. If resolution is not 2298 | null, then 2299 |
        2300 |
      1. If starResolution is 2301 | null, let starResolution be resolution.
      2. 2302 |
      3. Else, 2303 |
          2304 |
        1. Assert: There is more than one * import that includes the requested name.
        2. 2305 |
        3. If resolution.[[Module]] and starResolution.[[Module]] are not the same 2306 | Module Record or 2307 | SameValue(resolution.[[BindingName]], starResolution.[[BindingName]]) is 2308 | false, return "ambiguous".
        4. 2309 |
        2310 |
      4. 2311 |
      2312 |
    10. 2313 |
    2314 |
  20. 2315 |
  21. Return starResolution. 2316 |
  22. 2317 |
2318 |
2319 | Note 2320 |
2321 |

ResolveExport attempts to resolve an imported binding to the actual defining module and local binding name. The defining module may be the module represented by the 2322 | Module Record this method was invoked on or some other module that is imported by that module. The parameter resolveSet is use to detect unresolved circular import/export paths. If a pair consisting of specific 2323 | Module Record and exportName is reached that is already in resolveSet, an import circularity has been encountered. Before recursively calling ResolveExport, a pair consisting of module and exportName is added to resolveSet.

2324 |

If a defining module is found a 2325 | Record {[[Module]], [[BindingName]]} is returned. This record identifies the resolved binding of the originally requested export. If no definition was found or the request is found to be circular, 2326 | null is returned. If the request is found to be ambiguous, the string "ambiguous" is returned. If the request is found to be pending, the string "pending" is returned.

2327 |
2328 |
2329 |
2330 | 2331 | 2332 | 2333 | 2334 |

3ModuleDeclarationInstantiation( ) Concrete Method

2335 |

The ModuleDeclarationInstantiation concrete method of a 2336 | Source Text Module Record performs the following steps:

2337 | 2338 |
    2339 |
  1. Let module be this 2340 | Source Text Module Record.
  2. 2341 |
  3. Let realm be module.[[Realm]].
  4. 2342 |
  5. Assert: realm is not 2343 | undefined.
  6. 2344 |
  7. Let code be module.[[ECMAScriptCode]].
  8. 2345 |
  9. If module.[[Environment]] is not 2346 | undefined, return 2347 | NormalCompletion( 2348 | empty).
  10. 2349 |
  11. Let env be 2350 | NewModuleEnvironment(realm.[[GlobalEnv]]).
  12. 2351 |
  13. Set module.[[Environment]] to env.
  14. 2352 |
  15. For each String required that is an element of module.[[RequestedModules]] do, 2353 |
      2354 |
    1. NOTE: Before instantiating a module, all of the modules it requested must be available. An implementation may perform this test at any time prior to this point.
    2. 2355 |
    3. Let requiredModule be ?  2356 | HostResolveImportedModule(module, required).
    4. 2357 |
    5. Perform ? requiredModule.ModuleDeclarationInstantiation().
    6. 2358 |
    2359 |
  16. 2360 |
  17. For each ExportEntry 2361 | Record e in module.[[IndirectExportEntries]], do 2362 |
      2363 |
    1. Let resolution be ? module.ResolveExport(e.[[ExportName]], « », « »).
    2. 2364 |
    3. If resolution is 2365 | null or resolution is "ambiguous", throw a 2366 | SyntaxError exception.
    4. 2367 |
    2368 |
  18. 2369 |
  19. Assert: All named exports from module are resolvable.
  20. 2370 |
  21. Let envRec be env's 2371 | EnvironmentRecord.
  22. 2372 |
  23. For each ImportEntry 2373 | Record in in module.[[ImportEntries]], do 2374 |
      2375 |
    1. Let importedModule be !  2376 | HostResolveImportedModule(module, in.[[ModuleRequest]]).
    2. 2377 |
    3. NOTE: The above call cannot fail because imported module requests are a subset of module.[[RequestedModules]], and these have been resolved earlier in this algorithm.
    4. 2378 |
    5. If in.[[ImportName]] is "*", then 2379 |
        2380 |
      1. Let namespace be ?  2381 | GetModuleNamespace(importedModule).
      2. 2382 |
      3. Perform ! envRec.CreateImmutableBinding(in.[[LocalName]], 2383 | true).
      4. 2384 |
      5. Call envRec.InitializeBinding(in.[[LocalName]], namespace).
      6. 2385 |
      2386 |
    6. 2387 |
    7. Else, 2388 |
        2389 |
      1. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]], « », « »).
      2. 2390 |
      3. If resolution is 2391 | null or resolution is "ambiguous", throw a 2392 | SyntaxError exception.
      4. 2393 |
      5. If resolution is "pending", then append in to module.[[PendingImportEntries]]. 2394 |
      6. 2395 |
      7. Else, call envRec.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]).
      8. 2396 |
      2397 |
    8. 2398 |
    2399 |
  24. 2400 |
  25. Let varDeclarations be the VarScopedDeclarations of code.
  26. 2401 |
  27. Let declaredVarNames be a new empty 2402 | List.
  28. 2403 |
  29. For each element d in varDeclarations do 2404 |
      2405 |
    1. For each element dn of the BoundNames of d do 2406 |
        2407 |
      1. If dn is not an element of declaredVarNames, then 2408 |
          2409 |
        1. Perform ! envRec.CreateMutableBinding(dn, 2410 | false).
        2. 2411 |
        3. Call envRec.InitializeBinding(dn, 2412 | undefined).
        4. 2413 |
        5. Append dn to declaredVarNames.
        6. 2414 |
        2415 |
      2. 2416 |
      2417 |
    2. 2418 |
    2419 |
  30. 2420 |
  31. Let lexDeclarations be the LexicallyScopedDeclarations of code.
  32. 2421 |
  33. For each element d in lexDeclarations do 2422 |
      2423 |
    1. For each element dn of the BoundNames of d do 2424 |
        2425 |
      1. If IsConstantDeclaration of d is 2426 | true, then 2427 |
          2428 |
        1. Perform ! envRec.CreateImmutableBinding(dn, 2429 | true).
        2. 2430 |
        2431 |
      2. 2432 |
      3. Else, 2433 |
          2434 |
        1. Perform ! envRec.CreateMutableBinding(dn, 2435 | false).
        2. 2436 |
        2437 |
      4. 2438 |
      5. If d is a 2439 | GeneratorDeclaration production or a 2440 | FunctionDeclaration production, then 2441 |
          2442 |
        1. Let fo be the result of performing InstantiateFunctionObject for d with argument env.
        2. 2443 |
        3. Call envRec.InitializeBinding(dn, fo).
        4. 2444 |
        2445 |
      6. 2446 |
      2447 |
    2. 2448 |
    2449 |
  34. 2450 |
  35. Return 2451 | NormalCompletion( 2452 | empty). 2453 |
  36. 2454 |
2455 |
2456 |
2457 | 2458 | 2459 | 2460 |

4ModuleEvaluation() Concrete Method

2461 |

The ModuleEvaluation concrete method of a 2462 | Source Text Module Record performs the following steps:

2463 | 2464 |
    2465 |
  1. Let module be this 2466 | Source Text Module Record.
  2. 2467 |
  3. Assert: ModuleDeclarationInstantiation has already been invoked on module and successfully completed.
  4. 2468 |
  5. Assert: module.[[Realm]] is not 2469 | undefined.
  6. 2470 |
  7. If module.[[Evaluated]] is 2471 | true, return 2472 | undefined.
  8. 2473 |
  9. Set module.[[Evaluated]] to 2474 | true.
  10. 2475 |
  11. For each String required that is an element of module.[[RequestedModules]] do, 2476 |
      2477 |
    1. Let requiredModule be !  2478 | HostResolveImportedModule(module, required).
    2. 2479 |
    3. NOTE: ModuleDeclarationInstantiation must be completed prior to invoking this method, so every requested module is guaranteed to resolve successfully.
    4. 2480 |
    5. Perform ? requiredModule.ModuleEvaluation().
    6. 2481 |
    2482 |
  12. 2483 |
  13. Let env be module.[[Environment]].
  14. 2484 |
  15. Let envRec be env's EnvironmentRecord.
  16. 2485 |
  17. For each ImportEntry Record in in module.[[PendingImportEntries]], do 2486 |
      2487 |
    1. Let resolution be ? importedModule.ResolveExport(in.[[ImportName]], « », « »).
    2. 2488 |
    3. If resolution is null or resolution is "ambiguous" or resultion is "pending", throw a SyntaxError exception.
    4. 2489 |
    5. Remove ImportEntry Record in from module.[[PendingImportEntries]].
    6. 2490 |
    7. Call envRec.CreateImportBinding(in.[[LocalName]], resolution.[[Module].
    8. 2491 |
    2492 |
  18. 2493 |
  19. Let moduleCxt be a new ECMAScript code 2494 | execution context.
  20. 2495 |
  21. Set the Function of moduleCxt to 2496 | null.
  22. 2497 |
  23. Set the 2498 | Realm of moduleCxt to module.[[Realm]].
  24. 2499 |
  25. Set the ScriptOrModule of moduleCxt to module.
  26. 2500 |
  27. Assert: module has been linked and declarations in its 2501 | module environment have been instantiated.
  28. 2502 |
  29. Set the VariableEnvironment of moduleCxt to module.[[Environment]].
  30. 2503 |
  31. Set the LexicalEnvironment of moduleCxt to module.[[Environment]].
  32. 2504 |
  33. Suspend the currently 2505 | running execution context.
  34. 2506 |
  35. Push moduleCxt on to the 2507 | execution context stack; moduleCxt is now the 2508 | running execution context.
  36. 2509 |
  37. Let result be the result of evaluating module.[[ECMAScriptCode]].
  38. 2510 |
  39. Suspend moduleCxt and remove it from the 2511 | execution context stack.
  40. 2512 |
  41. Resume the context that is now on the top of the 2513 | execution context stack as the 2514 | running execution context.
  42. 2515 |
  43. Return 2516 | Completion(result). 2517 |
  44. 2518 |
2519 |
2520 |
2521 | 2522 | 2523 | 2524 |

5ParseModule ( sourceText, realm, hostDefined )

2525 |

The abstract operation ParseModule with arguments sourceText, realm, and hostDefined creates a 2526 | Source Text Module Record based upon the result of parsing sourceText as a 2527 | Module. ParseModule performs the following steps:

2528 | 2529 |
    2530 |
  1. Assert: sourceText is an ECMAScript source text (see clause 2531 | 10).
  2. 2532 |
  3. Parse sourceText using 2533 | Module as the 2534 | goal symbol and analyze the parse result for any Early Error conditions. If the parse was successful and no early errors were found, let body be the resulting parse tree. Otherwise, let body be a 2535 | List of one or more 2536 | SyntaxError or 2537 | ReferenceError objects representing the parsing errors and/or early errors. Parsing and 2538 | early error detection may be interweaved in an implementation dependent manner. If more than one parsing error or 2539 | early error is present, the number and ordering of error objects in the list is implementation dependent, but at least one must be present.
  4. 2540 |
  5. If body is a 2541 | List of errors, then return body.
  6. 2542 |
  7. Let requestedModules be the ModuleRequests of body.
  8. 2543 |
  9. Let importEntries be ImportEntries of body.
  10. 2544 |
  11. Let pendingImportEntries be a new empty List. 2545 |
  12. 2546 |
  13. Let importedBoundNames be 2547 | ImportedLocalNames(importEntries).
  14. 2548 |
  15. Let indirectExportEntries be a new empty 2549 | List.
  16. 2550 |
  17. Let localExportEntries be a new empty 2551 | List.
  18. 2552 |
  19. Let starExportEntries be a new empty 2553 | List.
  20. 2554 |
  21. Let exportEntries be ExportEntries of body.
  22. 2555 |
  23. For each record ee in exportEntries, do 2556 |
      2557 |
    1. If ee.[[ModuleRequest]] is 2558 | null, then 2559 |
        2560 |
      1. If ee.[[LocalName]] is not an element of importedBoundNames, then 2561 |
          2562 |
        1. Append ee to localExportEntries.
        2. 2563 |
        2564 |
      2. 2565 |
      3. Else, 2566 |
          2567 |
        1. Let ie be the element of importEntries whose [[LocalName]] is the same as ee.[[LocalName]].
        2. 2568 |
        3. If ie.[[ImportName]] is "*", then 2569 |
            2570 |
          1. Assert: This is a re-export of an imported module namespace object.
          2. 2571 |
          3. Append ee to localExportEntries.
          4. 2572 |
          2573 |
        4. 2574 |
        5. Else this is a re-export of a single name, 2575 |
            2576 |
          1. Append to indirectExportEntries the 2577 | Record {[[ModuleRequest]]: ie.[[ModuleRequest]], [[ImportName]]: ie.[[ImportName]], [[LocalName]]: 2578 | null, [[ExportName]]: ee.[[ExportName]] }.
          2. 2579 |
          2580 |
        6. 2581 |
        2582 |
      4. 2583 |
      2584 |
    2. 2585 |
    3. Else if ee.[[ImportName]] is "*", then 2586 |
        2587 |
      1. Append ee to starExportEntries.
      2. 2588 |
      2589 |
    4. 2590 |
    5. Else, 2591 |
        2592 |
      1. Append ee to indirectExportEntries.
      2. 2593 |
      2594 |
    6. 2595 |
    2596 |
  24. 2597 |
  25. Return 2598 | Source Text Module Record {[[Realm]]: realm, [[Environment]]: 2599 | undefined, [[HostDefined]]: hostDefined, [[Namespace]]: 2600 | undefined, [[Evaluated]]: 2601 | false, [[ECMAScriptCode]]: body, [[RequestedModules]]: requestedModules, [[ImportEntries]]: importEntries, [[PendingImportEntries]]: pendingImportEntries, [[LocalExportEntries]]: localExportEntries, [[StarExportEntries]]: starExportEntries, [[IndirectExportEntries]]: indirectExportEntries}. 2602 |
  26. 2603 |
2604 |
2605 | Note 2606 |
2607 |

An implementation may parse module source text and analyze it for Early Error conditions prior to the evaluation of ParseModule for that module source text. However, the reporting of any errors must be deferred until the point where this specification actually performs ParseModule upon that source text.

2608 |
2609 |
2610 |
2611 | 2612 |

ACopyright & Software License

2613 | 2614 |

Copyright Notice

2615 |

© 2017 Caridy Patiño

2616 | 2617 |

Software License

2618 |

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 http://www.ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

2619 | 2620 |

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

2621 | 2622 |
    2623 |
  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. 2624 |
  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. 2625 |
  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. 2626 |
2627 | 2628 |

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.

2629 | 2630 |
2631 |
2632 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecma-proposal-dynamic-modules", 3 | "version": "0.0.0", 4 | "description": "ECMAScript spec proposal to support dynamic modules", 5 | "scripts": { 6 | "build": "ecmarkup spec.emu --js=spec.js --css=spec.css | js-beautify -f - --type=html -t > index.html", 7 | "pr1epublish": "npm run build && echo >&2 'no publishing' && exit 255" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/caridy/proposal-dynamic-modules.git" 12 | }, 13 | "keywords": [ 14 | "es", 15 | "module", 16 | "cjs", 17 | "node", 18 | "ES8", 19 | "ES2017", 20 | "ECMAScript", 21 | "ESNext", 22 | "spec", 23 | "proposal" 24 | ], 25 | "author": "Caridy Patiño ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/caridy/proposal-dynamic-modules/issues" 29 | }, 30 | "homepage": "https://github.com/caridy/proposal-dynamic-modules#readme", 31 | "dependencies": {}, 32 | "devDependencies": { 33 | "ecmarkup": "^3.4.0", 34 | "js-beautify": "^1.6.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spec.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 | 171 | emu-grammar.inline, emu-production.inline, 172 | emu-grammar.inline emu-production emu-rhs, emu-production.inline emu-rhs { 173 | display: inline; 174 | } 175 | 176 | emu-grammar[collapsed] emu-production, emu-production[collapsed] { 177 | margin: 0; 178 | } 179 | 180 | emu-grammar[collapsed] emu-production emu-rhs, emu-production[collapsed] emu-rhs { 181 | display: inline; 182 | padding-left: 1ex; 183 | } 184 | 185 | emu-constraints { 186 | font-size: .75em; 187 | margin-right: 1ex; 188 | } 189 | 190 | emu-gann { 191 | margin-right: 1ex; 192 | } 193 | 194 | emu-gann emu-t:last-child, 195 | emu-gann emu-nt:last-child { 196 | margin-right: 0; 197 | } 198 | 199 | emu-geq { 200 | margin-left: 1ex; 201 | font-weight: bold; 202 | } 203 | 204 | emu-oneof { 205 | font-weight: bold; 206 | margin-left: 1ex; 207 | } 208 | 209 | emu-nt { 210 | display: inline-block; 211 | font-style: italic; 212 | white-space: nowrap; 213 | text-indent: 0; 214 | } 215 | 216 | emu-nt a, emu-nt a:visited { 217 | color: #333; 218 | } 219 | 220 | emu-rhs emu-nt { 221 | margin-right: 1ex; 222 | } 223 | 224 | emu-t { 225 | display: inline-block; 226 | font-family: monospace; 227 | font-weight: bold; 228 | white-space: nowrap; 229 | text-indent: 0; 230 | } 231 | 232 | emu-production emu-t { 233 | margin-right: 1ex; 234 | } 235 | 236 | emu-rhs { 237 | display: block; 238 | padding-left: 75px; 239 | text-indent: -25px; 240 | } 241 | 242 | emu-mods { 243 | font-size: .85em; 244 | vertical-align: sub; 245 | font-style: normal; 246 | font-weight: normal; 247 | } 248 | 249 | emu-production[collapsed] emu-mods { 250 | display: none; 251 | } 252 | 253 | emu-params, emu-opt { 254 | margin-right: 1ex; 255 | font-family: monospace; 256 | } 257 | 258 | emu-params, emu-constraints { 259 | color: #2aa198; 260 | } 261 | 262 | emu-opt { 263 | color: #b58900; 264 | } 265 | 266 | emu-gprose { 267 | font-size: 0.9em; 268 | font-family: Helvetica, Arial, sans-serif; 269 | } 270 | 271 | h1.shortname { 272 | color: #f60; 273 | font-size: 1.5em; 274 | margin: 0; 275 | } 276 | 277 | h1.version { 278 | color: #f60; 279 | font-size: 1.5em; 280 | margin: 0; 281 | } 282 | 283 | h1.title { 284 | margin-top: 0; 285 | color: #f60; 286 | } 287 | 288 | h1.first { 289 | margin-top: 0; 290 | } 291 | 292 | h1, h2, h3, h4, h5, h6 { 293 | position: relative; 294 | } 295 | 296 | h1 .secnum { 297 | text-decoration: none; 298 | margin-right: 10px; 299 | } 300 | 301 | h1 span.title { 302 | order: 2; 303 | } 304 | 305 | 306 | h1 { font-size: 2.67em; margin-top: 2em; margin-bottom: 0; line-height: 1em;} 307 | h2 { font-size: 2em; } 308 | h3 { font-size: 1.56em; } 309 | h4 { font-size: 1.25em; } 310 | h5 { font-size: 1.11em; } 311 | h6 { font-size: 1em; } 312 | 313 | h1:hover span.utils { 314 | display: block; 315 | } 316 | 317 | span.utils { 318 | font-size: 18px; 319 | line-height: 18px; 320 | display: none; 321 | position: absolute; 322 | top: 100%; 323 | left: 0; 324 | right: 0; 325 | font-weight: normal; 326 | } 327 | 328 | span.utils:before { 329 | content: "⤷"; 330 | display: inline-block; 331 | padding: 0 5px; 332 | } 333 | 334 | span.utils > * { 335 | display: inline-block; 336 | margin-right: 20px; 337 | } 338 | 339 | h1 span.utils span.anchor a, 340 | h2 span.utils span.anchor a, 341 | h3 span.utils span.anchor a, 342 | h4 span.utils span.anchor a, 343 | h5 span.utils span.anchor a, 344 | h6 span.utils span.anchor a { 345 | text-decoration: none; 346 | font-variant: small-caps; 347 | } 348 | 349 | h1 span.utils span.anchor a:hover, 350 | h2 span.utils span.anchor a:hover, 351 | h3 span.utils span.anchor a:hover, 352 | h4 span.utils span.anchor a:hover, 353 | h5 span.utils span.anchor a:hover, 354 | h6 span.utils span.anchor a:hover { 355 | color: #333; 356 | } 357 | 358 | emu-intro h1, emu-clause h1, emu-annex h1 { font-size: 2em; } 359 | emu-intro h2, emu-clause h2, emu-annex h2 { font-size: 1.56em; } 360 | emu-intro h3, emu-clause h3, emu-annex h3 { font-size: 1.25em; } 361 | emu-intro h4, emu-clause h4, emu-annex h4 { font-size: 1.11em; } 362 | emu-intro h5, emu-clause h5, emu-annex h5 { font-size: 1em; } 363 | emu-intro h6, emu-clause h6, emu-annex h6 { font-size: 0.9em; } 364 | emu-intro emu-intro h1, emu-clause emu-clause h1, emu-annex emu-annex h1 { font-size: 1.56em; } 365 | emu-intro emu-intro h2, emu-clause emu-clause h2, emu-annex emu-annex h2 { font-size: 1.25em; } 366 | emu-intro emu-intro h3, emu-clause emu-clause h3, emu-annex emu-annex h3 { font-size: 1.11em; } 367 | emu-intro emu-intro h4, emu-clause emu-clause h4, emu-annex emu-annex h4 { font-size: 1em; } 368 | emu-intro emu-intro h5, emu-clause emu-clause h5, emu-annex emu-annex h5 { font-size: 0.9em; } 369 | 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; } 370 | 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; } 371 | emu-intro emu-intro emu-intro h3, emu-clause emu-clause emu-clause h3, emu-annex emu-annex emu-annex h3 { font-size: 1em; } 372 | 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; } 373 | 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; } 374 | 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; } 375 | 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; } 376 | 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; } 377 | 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; } 378 | 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 } 379 | 380 | emu-clause, emu-intro, emu-annex { 381 | display: block; 382 | } 383 | 384 | /* Figures and tables */ 385 | figure { display: block; margin: 1em 0 3em 0; } 386 | figure object { display: block; margin: 0 auto; } 387 | figure table.real-table { margin: 0 auto; } 388 | figure figcaption { 389 | display: block; 390 | color: #555555; 391 | font-weight: bold; 392 | text-align: center; 393 | } 394 | 395 | emu-table table { 396 | margin: 0 auto; 397 | } 398 | 399 | emu-table table, table.real-table { 400 | border-collapse: collapse; 401 | } 402 | 403 | emu-table td, emu-table th, table.real-table td, table.real-table th { 404 | border: 1px solid black; 405 | padding: 0.4em; 406 | vertical-align: baseline; 407 | } 408 | emu-table th, emu-table thead td, table.real-table th { 409 | background-color: #eeeeee; 410 | } 411 | 412 | /* Note: the left content edges of table.lightweight-table >tbody >tr >td 413 | and div.display line up. */ 414 | table.lightweight-table { 415 | border-collapse: collapse; 416 | margin: 0 0 0 1.5em; 417 | } 418 | table.lightweight-table td, table.lightweight-table th { 419 | border: none; 420 | padding: 0 0.5em; 421 | vertical-align: baseline; 422 | } 423 | 424 | /* diff styles */ 425 | ins { 426 | background-color: #e0f8e0; 427 | text-decoration: none; 428 | border-bottom: 1px solid #396; 429 | } 430 | 431 | del { 432 | background-color: #fee; 433 | } 434 | 435 | ins.block, del.block, 436 | emu-production > ins, emu-production > del, 437 | emu-grammar > ins, emu-grammar > del { 438 | display: block; 439 | } 440 | 441 | /* Menu Styles */ 442 | #menu-toggle { 443 | font-size: 2em; 444 | 445 | position: fixed; 446 | top: 0; 447 | left: 0; 448 | width: 1.5em; 449 | height: 1.5em; 450 | z-index: 3; 451 | visibility: hidden; 452 | color: #1567a2; 453 | background-color: #fff; 454 | 455 | line-height: 1.5em; 456 | text-align: center; 457 | -webkit-touch-callout: none; 458 | -webkit-user-select: none; 459 | -khtml-user-select: none; 460 | -moz-user-select: none; 461 | -ms-user-select: none; 462 | user-select: none;; 463 | 464 | cursor: pointer; 465 | } 466 | 467 | #menu { 468 | display: flex; 469 | flex-direction: column; 470 | width: 33%; height: 100vh; 471 | max-width: 500px; 472 | box-sizing: border-box; 473 | background-color: #ddd; 474 | overflow: hidden; 475 | transition: opacity 0.1s linear; 476 | padding: 0 5px; 477 | position: fixed; 478 | left: 0; top: 0; 479 | border-right: 2px solid #bbb; 480 | 481 | z-index: 2; 482 | } 483 | 484 | #menu-spacer { 485 | flex-basis: 33%; 486 | max-width: 500px; 487 | flex-grow: 0; 488 | flex-shrink: 0; 489 | } 490 | 491 | #menu a { 492 | color: #1567a2; 493 | } 494 | 495 | #menu.active { 496 | display: flex; 497 | opacity: 1; 498 | z-index: 2; 499 | } 500 | 501 | #menu-pins { 502 | flex-grow: 1; 503 | display: none; 504 | } 505 | 506 | #menu-pins.active { 507 | display: block; 508 | } 509 | 510 | #menu-pins-list { 511 | margin: 0; 512 | padding: 0; 513 | counter-reset: pins-counter; 514 | } 515 | 516 | #menu-pins-list > li:before { 517 | content: counter(pins-counter); 518 | counter-increment: pins-counter; 519 | display: inline-block; 520 | width: 25px; 521 | text-align: center; 522 | border: 1px solid #bbb; 523 | padding: 2px; 524 | margin: 4px; 525 | box-sizing: border-box; 526 | line-height: 1em; 527 | background-color: #ccc; 528 | border-radius: 4px; 529 | } 530 | #menu-toc > ol { 531 | padding: 0; 532 | flex-grow: 1; 533 | } 534 | 535 | #menu-toc > ol li { 536 | padding: 0; 537 | } 538 | 539 | #menu-toc > ol , #menu-toc > ol ol { 540 | list-style-type: none; 541 | margin: 0; 542 | padding: 0; 543 | } 544 | 545 | #menu-toc > ol ol { 546 | padding-left: 0.75em; 547 | } 548 | 549 | #menu-toc li { 550 | text-overflow: ellipsis; 551 | overflow: hidden; 552 | white-space: nowrap; 553 | } 554 | 555 | #menu-toc .item-toggle { 556 | display: inline-block; 557 | transform: rotate(-45deg) translate(-5px, -5px); 558 | transition: transform 0.1s ease; 559 | text-align: center; 560 | width: 20px; 561 | 562 | color: #aab; 563 | 564 | -webkit-touch-callout: none; 565 | -webkit-user-select: none; 566 | -khtml-user-select: none; 567 | -moz-user-select: none; 568 | -ms-user-select: none; 569 | user-select: none;; 570 | 571 | cursor: pointer; 572 | } 573 | 574 | #menu-toc .item-toggle-none { 575 | display: inline-block; 576 | width: 20px; 577 | } 578 | 579 | #menu-toc li.active > .item-toggle { 580 | transform: rotate(45deg) translate(-5px, -5px); 581 | } 582 | 583 | #menu-toc li > ol { 584 | display: none; 585 | } 586 | 587 | #menu-toc li.active > ol { 588 | display: block; 589 | } 590 | 591 | #menu-toc li.revealed > a { 592 | background-color: #bbb; 593 | font-weight: bold; 594 | /* 595 | background-color: #222; 596 | color: #c6d8e4; 597 | */ 598 | } 599 | 600 | #menu-toc li.revealed-leaf> a { 601 | color: #206ca7; 602 | /* 603 | background-color: #222; 604 | color: #c6d8e4; 605 | */ 606 | } 607 | 608 | #menu-toc li.revealed > .item-toggle { 609 | transform: rotate(45deg) translate(-5px, -5px); 610 | } 611 | 612 | #menu-toc li.revealed > ol { 613 | display: block; 614 | } 615 | 616 | #menu-toc li > a { 617 | padding: 2px 5px; 618 | } 619 | 620 | #menu > * { 621 | margin-bottom: 5px; 622 | } 623 | 624 | .menu-pane-header { 625 | padding: 0 5px; 626 | text-transform: uppercase; 627 | background-color: #aaa; 628 | color: #335; 629 | font-weight: bold; 630 | letter-spacing: 2px; 631 | flex-grow: 0; 632 | flex-shrink: 0; 633 | font-size: 0.8em; 634 | } 635 | 636 | #menu-toc { 637 | display: flex; 638 | flex-direction: column; 639 | width: 100%; 640 | overflow: hidden; 641 | flex-grow: 1; 642 | } 643 | 644 | #menu-toc ol.toc { 645 | overflow-x: hidden; 646 | overflow-y: auto; 647 | } 648 | 649 | #menu-search { 650 | position: relative; 651 | flex-grow: 0; 652 | flex-shrink: 0; 653 | width: 100%; 654 | 655 | display: flex; 656 | flex-direction: column; 657 | 658 | max-height: 300px; 659 | } 660 | 661 | #menu-trace-list { 662 | display: none; 663 | } 664 | 665 | #menu-search-box { 666 | box-sizing: border-box; 667 | display: block; 668 | width: 100%; 669 | margin: 5px 0 0 0; 670 | font-size: 1em; 671 | padding: 2px; 672 | background-color: #bbb; 673 | border: 1px solid #999; 674 | } 675 | 676 | #menu-search-results { 677 | overflow-x: hidden; 678 | overflow-y: auto; 679 | } 680 | 681 | li.menu-search-result-clause:before { 682 | content: 'clause'; 683 | width: 40px; 684 | display: inline-block; 685 | text-align: right; 686 | padding-right: 1ex; 687 | color: #666; 688 | font-size: 75%; 689 | } 690 | li.menu-search-result-op:before { 691 | content: 'op'; 692 | width: 40px; 693 | display: inline-block; 694 | text-align: right; 695 | padding-right: 1ex; 696 | color: #666; 697 | font-size: 75%; 698 | } 699 | 700 | li.menu-search-result-prod:before { 701 | content: 'prod'; 702 | width: 40px; 703 | display: inline-block; 704 | text-align: right; 705 | padding-right: 1ex; 706 | color: #666; 707 | font-size: 75% 708 | } 709 | 710 | 711 | li.menu-search-result-term:before { 712 | content: 'term'; 713 | width: 40px; 714 | display: inline-block; 715 | text-align: right; 716 | padding-right: 1ex; 717 | color: #666; 718 | font-size: 75% 719 | } 720 | 721 | #menu-search-results ul { 722 | padding: 0 5px; 723 | margin: 0; 724 | } 725 | 726 | #menu-search-results li { 727 | white-space: nowrap; 728 | text-overflow: ellipsis; 729 | } 730 | 731 | 732 | #menu-trace-list { 733 | counter-reset: item; 734 | margin: 0 0 0 20px; 735 | padding: 0; 736 | } 737 | #menu-trace-list li { 738 | display: block; 739 | white-space: nowrap; 740 | } 741 | 742 | #menu-trace-list li .secnum:after { 743 | content: " "; 744 | } 745 | #menu-trace-list li:before { 746 | content: counter(item) " "; 747 | background-color: #222; 748 | counter-increment: item; 749 | color: #999; 750 | width: 20px; 751 | height: 20px; 752 | line-height: 20px; 753 | display: inline-block; 754 | text-align: center; 755 | margin: 2px 4px 2px 0; 756 | } 757 | 758 | @media (max-width: 1000px) { 759 | body { 760 | margin: 0; 761 | display: block; 762 | } 763 | 764 | #menu { 765 | display: none; 766 | padding-top: 3em; 767 | width: 450px; 768 | } 769 | 770 | #menu.active { 771 | position: fixed; 772 | height: 100%; 773 | left: 0; 774 | top: 0; 775 | right: 300px; 776 | } 777 | 778 | #menu-toggle { 779 | visibility: visible; 780 | } 781 | 782 | #spec-container { 783 | padding: 0 5px; 784 | } 785 | 786 | #references-pane-spacer { 787 | display: none; 788 | } 789 | } 790 | 791 | @media only screen and (max-width: 800px) { 792 | #menu { 793 | width: 100%; 794 | } 795 | 796 | h1 .secnum:empty { 797 | margin: 0; padding: 0; 798 | } 799 | } 800 | 801 | 802 | /* Toolbox */ 803 | .toolbox { 804 | position: absolute; 805 | background: #ddd; 806 | border: 1px solid #aaa; 807 | display: none; 808 | color: #eee; 809 | padding: 5px; 810 | border-radius: 3px; 811 | } 812 | 813 | .toolbox.active { 814 | display: inline-block; 815 | } 816 | 817 | .toolbox a { 818 | text-decoration: none; 819 | padding: 0 5px; 820 | } 821 | 822 | .toolbox a:hover { 823 | text-decoration: underline; 824 | } 825 | 826 | .toolbox:after, .toolbox:before { 827 | top: 100%; 828 | left: 15px; 829 | border: solid transparent; 830 | content: " "; 831 | height: 0; 832 | width: 0; 833 | position: absolute; 834 | pointer-events: none; 835 | } 836 | 837 | .toolbox:after { 838 | border-color: rgba(0, 0, 0, 0); 839 | border-top-color: #ddd; 840 | border-width: 10px; 841 | margin-left: -10px; 842 | } 843 | .toolbox:before { 844 | border-color: rgba(204, 204, 204, 0); 845 | border-top-color: #aaa; 846 | border-width: 12px; 847 | margin-left: -12px; 848 | } 849 | 850 | #references-pane-container { 851 | position: fixed; 852 | bottom: 0; 853 | left: 0; 854 | right: 0; 855 | height: 250px; 856 | display: none; 857 | background-color: #ddd; 858 | z-index: 1; 859 | } 860 | 861 | #references-pane-table-container { 862 | overflow-x: hidden; 863 | overflow-y: auto; 864 | } 865 | 866 | #references-pane-spacer { 867 | flex-basis: 33%; 868 | max-width: 500px; 869 | } 870 | 871 | #references-pane { 872 | flex-grow: 1; 873 | overflow: hidden; 874 | display: flex; 875 | flex-direction: column; 876 | } 877 | 878 | #references-pane-container.active { 879 | display: flex; 880 | } 881 | 882 | #references-pane-close:after { 883 | content: '✖'; 884 | float: right; 885 | cursor: pointer; 886 | } 887 | 888 | #references-pane table tr td:first-child { 889 | text-align: right; 890 | padding-right: 5px; 891 | } 892 | -------------------------------------------------------------------------------- /spec.emu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
  7 | title: dynamic modules reform
  8 | stage: 1
  9 | contributors: Caridy Patiño
 10 | 
11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 23 | 26 | 27 | 28 | 31 | 34 | 37 | 38 | 39 | 42 | 45 | 48 | 49 | 50 | 53 | 56 | 59 | 60 | 61 |
18 | Field Name 19 | 21 | Value Type 22 | 24 | Meaning 25 |
29 | [[ImportEntries]] 30 | 32 | List of ImportEntry Records 33 | 35 | A List of ImportEntry records derived from the code of this module. 36 |
40 | [[PendingImportEntries]] 41 | 43 | List of ImportEntry Records 44 | 46 | A List of ImportEntry records that are pending to be validated. 47 |
51 | [[LocalExportEntries]] 52 | 54 | List of ExportEntry Records 55 | 57 | A List of ExportEntry records derived from the code of this module that correspond to declarations that occur within the module. 58 |
62 |
63 | 64 | 65 | 66 |

[[Get]] (_P_, _Receiver_)

67 |

When the [[Get]] internal method of a module namespace exotic object _O_ is called with property key _P_ and ECMAScript language value _Receiver_, the following steps are taken:

68 | 69 | 1. Assert: IsPropertyKey(_P_) is *true*. 70 | 1. If Type(_P_) is Symbol, then 71 | 1. Return ? OrdinaryGet(_O_, _P_, _Receiver_). 72 | 1. Let _exports_ be _O_.[[Exports]]. 73 | 1. If _P_ is not an element of _exports_, return *undefined*. 74 | 1. Let _m_ be _O_.[[Module]]. 75 | 1. Let _binding_ be ! _m_.ResolveExport(_P_, « », « »). 76 | 1. Assert: _binding_ is neither *null* nor `"ambiguous"`. 77 | 1. If _binding_ is `"pending"` or _binding_ is *null* or resolution is `"ambiguous"`, throw a *SyntaxError* exception. 78 | 1. Let _targetModule_ be _binding_.[[Module]]. 79 | 1. Assert: _targetModule_ is not *undefined*. 80 | 1. Let _targetEnv_ be _targetModule_.[[Environment]]. 81 | 1. If _targetEnv_ is *undefined*, throw a *ReferenceError* exception. 82 | 1. Let _targetEnvRec_ be _targetEnv_'s EnvironmentRecord. 83 | 1. Return ? _targetEnvRec_.GetBindingValue(_binding_.[[BindingName]], *true*). 84 | 85 | 86 |

ResolveExport is idempotent and side-effect free. An implementation might choose to pre-compute or cache the ResolveExport results for the [[Exports]] of each module namespace exotic object.

87 |
88 |
89 | 90 | 91 | 92 |

ResolveExport( _exportName_, _resolveSet_, _exportStarSet_ ) Concrete Method

93 |

The ResolveExport concrete method of a Source Text Module Record with arguments _exportName_, _resolveSet_, and _exportStarSet_ performs the following steps:

94 | 95 | 1. Let _module_ be this Source Text Module Record. 96 | 1. For each Record {[[Module]], [[ExportName]]} _r_ in _resolveSet_, do: 97 | 1. If _module_ and _r_.[[Module]] are the same Module Record and SameValue(_exportName_, _r_.[[ExportName]]) is *true*, then 98 | 1. Assert: This is a circular import request. 99 | 1. Return *null*. 100 | 1. Append the Record {[[Module]]: _module_, [[ExportName]]: _exportName_} to _resolveSet_. 101 | 1. For each ExportEntry Record _e_ in _module_.[[LocalExportEntries]], do 102 | 1. If SameValue(_exportName_, _e_.[[ExportName]]) is *true*, then 103 | 1. Assert: _module_ provides the direct binding for this export. 104 | 1. Return Record{[[Module]]: _module_, [[BindingName]]: _e_.[[LocalName]]}. 105 | 1. For each ExportEntry Record _e_ in _module_.[[IndirectExportEntries]], do 106 | 1. If SameValue(_exportName_, _e_.[[ExportName]]) is *true*, then 107 | 1. Assert: _module_ imports a specific binding for this export. 108 | 1. Let _importedModule_ be ? HostResolveImportedModule(_module_, _e_.[[ModuleRequest]]). 109 | 1. Return ? _importedModule_.ResolveExport(_e_.[[ImportName]], _resolveSet_, _exportStarSet_). 110 | 1. If SameValue(_exportName_, `"default"`) is *true*, then 111 | 1. Assert: A `default` export was not explicitly defined by this module. 112 | 1. Return *null*. 113 | 1. NOTE A `default` export cannot be provided by an `export *`. 114 | 1. If _exportStarSet_ contains _module_, return *null*. 115 | 1. Append _module_ to _exportStarSet_. 116 | 1. Let _starResolution_ be *null*. 117 | 1. For each ExportEntry Record _e_ in _module_.[[StarExportEntries]], do 118 | 1. Let _importedModule_ be ? HostResolveImportedModule(_module_, _e_.[[ModuleRequest]]). 119 | 1. Let _resolution_ be ? _importedModule_.ResolveExport(_exportName_, _resolveSet_, _exportStarSet_). 120 | 1. If _resolution_ is `"ambiguous"`, return `"ambiguous"`. 121 | 1. If _resolution_ is `"pending"`, return `"pending"`. 122 | 1. If _resolution_ is not *null*, then 123 | 1. If _starResolution_ is *null*, let _starResolution_ be _resolution_. 124 | 1. Else, 125 | 1. Assert: There is more than one `*` import that includes the requested name. 126 | 1. If _resolution_.[[Module]] and _starResolution_.[[Module]] are not the same Module Record or SameValue(_resolution_.[[BindingName]], _starResolution_.[[BindingName]]) is *false*, return `"ambiguous"`. 127 | 1. Return _starResolution_. 128 | 129 | 130 |

ResolveExport attempts to resolve an imported binding to the actual defining module and local binding name. The defining module may be the module represented by the Module Record this method was invoked on or some other module that is imported by that module. The parameter _resolveSet_ is use to detect unresolved circular import/export paths. If a pair consisting of specific Module Record and _exportName_ is reached that is already in _resolveSet_, an import circularity has been encountered. Before recursively calling ResolveExport, a pair consisting of _module_ and _exportName_ is added to _resolveSet_.

131 |

If a defining module is found a Record {[[Module]], [[BindingName]]} is returned. This record identifies the resolved binding of the originally requested export. If no definition was found or the request is found to be circular, *null* is returned. If the request is found to be ambiguous, the string `"ambiguous"` is returned. If the request is found to be pending, the string `"pending"` is returned.

132 |
133 |
134 | 135 | 136 | 137 | 138 |

ModuleDeclarationInstantiation( ) Concrete Method

139 |

The ModuleDeclarationInstantiation concrete method of a Source Text Module Record performs the following steps:

140 | 141 | 1. Let _module_ be this Source Text Module Record. 142 | 1. Let _realm_ be _module_.[[Realm]]. 143 | 1. Assert: _realm_ is not *undefined*. 144 | 1. Let _code_ be _module_.[[ECMAScriptCode]]. 145 | 1. If _module_.[[Environment]] is not *undefined*, return NormalCompletion(~empty~). 146 | 1. Let _env_ be NewModuleEnvironment(_realm_.[[GlobalEnv]]). 147 | 1. Set _module_.[[Environment]] to _env_. 148 | 1. For each String _required_ that is an element of _module_.[[RequestedModules]] do, 149 | 1. NOTE: Before instantiating a module, all of the modules it requested must be available. An implementation may perform this test at any time prior to this point. 150 | 1. Let _requiredModule_ be ? HostResolveImportedModule(_module_, _required_). 151 | 1. Perform ? _requiredModule_.ModuleDeclarationInstantiation(). 152 | 1. For each ExportEntry Record _e_ in _module_.[[IndirectExportEntries]], do 153 | 1. Let _resolution_ be ? _module_.ResolveExport(_e_.[[ExportName]], « », « »). 154 | 1. If _resolution_ is *null* or _resolution_ is `"ambiguous"`, throw a *SyntaxError* exception. 155 | 1. Assert: All named exports from _module_ are resolvable. 156 | 1. Let _envRec_ be _env_'s EnvironmentRecord. 157 | 1. For each ImportEntry Record _in_ in _module_.[[ImportEntries]], do 158 | 1. Let _importedModule_ be ! HostResolveImportedModule(_module_, _in_.[[ModuleRequest]]). 159 | 1. NOTE: The above call cannot fail because imported module requests are a subset of _module_.[[RequestedModules]], and these have been resolved earlier in this algorithm. 160 | 1. If _in_.[[ImportName]] is `"*"`, then 161 | 1. Let _namespace_ be ? GetModuleNamespace(_importedModule_). 162 | 1. Perform ! _envRec_.CreateImmutableBinding(_in_.[[LocalName]], *true*). 163 | 1. Call _envRec_.InitializeBinding(_in_.[[LocalName]], _namespace_). 164 | 1. Else, 165 | 1. Let _resolution_ be ? _importedModule_.ResolveExport(_in_.[[ImportName]], « », « »). 166 | 1. If _resolution_ is *null* or _resolution_ is `"ambiguous"`, throw a *SyntaxError* exception. 167 | 1. If _resolution_ is `"pending"`, then append _in_ to _module_.[[PendingImportEntries]]. 168 | 1. Else, call _envRec_.CreateImportBinding(_in_.[[LocalName]], _resolution_.[[Module]], _resolution_.[[BindingName]]). 169 | 1. Let _varDeclarations_ be the VarScopedDeclarations of _code_. 170 | 1. Let _declaredVarNames_ be a new empty List. 171 | 1. For each element _d_ in _varDeclarations_ do 172 | 1. For each element _dn_ of the BoundNames of _d_ do 173 | 1. If _dn_ is not an element of _declaredVarNames_, then 174 | 1. Perform ! _envRec_.CreateMutableBinding(_dn_, *false*). 175 | 1. Call _envRec_.InitializeBinding(_dn_, *undefined*). 176 | 1. Append _dn_ to _declaredVarNames_. 177 | 1. Let _lexDeclarations_ be the LexicallyScopedDeclarations of _code_. 178 | 1. For each element _d_ in _lexDeclarations_ do 179 | 1. For each element _dn_ of the BoundNames of _d_ do 180 | 1. If IsConstantDeclaration of _d_ is *true*, then 181 | 1. Perform ! _envRec_.CreateImmutableBinding(_dn_, *true*). 182 | 1. Else, 183 | 1. Perform ! _envRec_.CreateMutableBinding(_dn_, *false*). 184 | 1. If _d_ is a |GeneratorDeclaration| production or a |FunctionDeclaration| production, then 185 | 1. Let _fo_ be the result of performing InstantiateFunctionObject for _d_ with argument _env_. 186 | 1. Call _envRec_.InitializeBinding(_dn_, _fo_). 187 | 1. Return NormalCompletion(~empty~). 188 | 189 |
190 | 191 | 192 | 193 |

ModuleEvaluation() Concrete Method

194 |

The ModuleEvaluation concrete method of a Source Text Module Record performs the following steps:

195 | 196 | 1. Let _module_ be this Source Text Module Record. 197 | 1. Assert: ModuleDeclarationInstantiation has already been invoked on _module_ and successfully completed. 198 | 1. Assert: _module_.[[Realm]] is not *undefined*. 199 | 1. If _module_.[[Evaluated]] is *true*, return *undefined*. 200 | 1. Set _module_.[[Evaluated]] to *true*. 201 | 1. For each String _required_ that is an element of _module_.[[RequestedModules]] do, 202 | 1. Let _requiredModule_ be ! HostResolveImportedModule(_module_, _required_). 203 | 1. NOTE: ModuleDeclarationInstantiation must be completed prior to invoking this method, so every requested module is guaranteed to resolve successfully. 204 | 1. Perform ? _requiredModule_.ModuleEvaluation(). 205 | 1. Let _env_ be _module_.[[Environment]]. 206 | 1. Let _envRec_ be _env_'s EnvironmentRecord. 207 | 1. For each ImportEntry Record _in_ in _module_.[[PendingImportEntries]], do 208 | 1. Let _resolution_ be ? _importedModule_.ResolveExport(_in_.[[ImportName]], « », « »). 209 | 1. If _resolution_ is *null* or _resolution_ is `"ambiguous"` or _resultion_ is `"pending"`, throw a *SyntaxError* exception. 210 | 1. Remove ImportEntry Record _in_ from _module_.[[PendingImportEntries]]. 211 | 1. Call _envRec_.CreateImportBinding(_in_.[[LocalName]], _resolution_.[[Module]. 212 | 1. Let _moduleCxt_ be a new ECMAScript code execution context. 213 | 1. Set the Function of _moduleCxt_ to *null*. 214 | 1. Set the Realm of _moduleCxt_ to _module_.[[Realm]]. 215 | 1. Set the ScriptOrModule of _moduleCxt_ to _module_. 216 | 1. Assert: _module_ has been linked and declarations in its module environment have been instantiated. 217 | 1. Set the VariableEnvironment of _moduleCxt_ to _module_.[[Environment]]. 218 | 1. Set the LexicalEnvironment of _moduleCxt_ to _module_.[[Environment]]. 219 | 1. Suspend the currently running execution context. 220 | 1. Push _moduleCxt_ on to the execution context stack; _moduleCxt_ is now the running execution context. 221 | 1. Let _result_ be the result of evaluating _module_.[[ECMAScriptCode]]. 222 | 1. Suspend _moduleCxt_ and remove it from the execution context stack. 223 | 1. Resume the context that is now on the top of the execution context stack as the running execution context. 224 | 1. Return Completion(_result_). 225 | 226 |
227 | 228 | 229 | 230 |

ParseModule ( _sourceText_, _realm_, _hostDefined_ )

231 |

The abstract operation ParseModule with arguments _sourceText_, _realm_, and _hostDefined_ creates a Source Text Module Record based upon the result of parsing _sourceText_ as a |Module|. ParseModule performs the following steps:

232 | 233 | 1. Assert: _sourceText_ is an ECMAScript source text (see clause ). 234 | 1. Parse _sourceText_ using |Module| as the goal symbol and analyze the parse result for any Early Error conditions. If the parse was successful and no early errors were found, let _body_ be the resulting parse tree. Otherwise, let _body_ be a List of one or more *SyntaxError* or *ReferenceError* objects representing the parsing errors and/or early errors. Parsing and early error detection may be interweaved in an implementation dependent manner. If more than one parsing error or early error is present, the number and ordering of error objects in the list is implementation dependent, but at least one must be present. 235 | 1. If _body_ is a List of errors, then return _body_. 236 | 1. Let _requestedModules_ be the ModuleRequests of _body_. 237 | 1. Let _importEntries_ be ImportEntries of _body_. 238 | 1. Let _pendingImportEntries_ be a new empty List. 239 | 1. Let _importedBoundNames_ be ImportedLocalNames(_importEntries_). 240 | 1. Let _indirectExportEntries_ be a new empty List. 241 | 1. Let _localExportEntries_ be a new empty List. 242 | 1. Let _starExportEntries_ be a new empty List. 243 | 1. Let _exportEntries_ be ExportEntries of _body_. 244 | 1. For each record _ee_ in _exportEntries_, do 245 | 1. If _ee_.[[ModuleRequest]] is *null*, then 246 | 1. If _ee_.[[LocalName]] is not an element of _importedBoundNames_, then 247 | 1. Append _ee_ to _localExportEntries_. 248 | 1. Else, 249 | 1. Let _ie_ be the element of _importEntries_ whose [[LocalName]] is the same as _ee_.[[LocalName]]. 250 | 1. If _ie_.[[ImportName]] is `"*"`, then 251 | 1. Assert: This is a re-export of an imported module namespace object. 252 | 1. Append _ee_ to _localExportEntries_. 253 | 1. Else this is a re-export of a single name, 254 | 1. Append to _indirectExportEntries_ the Record {[[ModuleRequest]]: _ie_.[[ModuleRequest]], [[ImportName]]: _ie_.[[ImportName]], [[LocalName]]: *null*, [[ExportName]]: _ee_.[[ExportName]] }. 255 | 1. Else if _ee_.[[ImportName]] is `"*"`, then 256 | 1. Append _ee_ to _starExportEntries_. 257 | 1. Else, 258 | 1. Append _ee_ to _indirectExportEntries_. 259 | 1. Return Source Text Module Record {[[Realm]]: _realm_, [[Environment]]: *undefined*, [[HostDefined]]: _hostDefined_, [[Namespace]]: *undefined*, [[Evaluated]]: *false*, [[ECMAScriptCode]]: _body_, [[RequestedModules]]: _requestedModules_, [[ImportEntries]]: _importEntries_, [[PendingImportEntries]]: _pendingImportEntries_, [[LocalExportEntries]]: _localExportEntries_, [[StarExportEntries]]: _starExportEntries_, [[IndirectExportEntries]]: _indirectExportEntries_}. 260 | 261 | 262 |

An implementation may parse module source text and analyze it for Early Error conditions prior to the evaluation of ParseModule for that module source text. However, the reporting of any errors must be deferred until the point where this specification actually performs ParseModule upon that source text.

263 |
264 |
-------------------------------------------------------------------------------- /spec.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 = '' 205 | 206 | this.$searchResults.innerHTML = html; 207 | } else { 208 | this.$searchResults.innerHTML = ''; 209 | this.$searchResults.classList.add('no-results'); 210 | } 211 | } 212 | 213 | 214 | function Menu() { 215 | this.$toggle = document.getElementById('menu-toggle'); 216 | this.$menu = document.getElementById('menu'); 217 | this.$toc = document.querySelector('menu-toc > ol'); 218 | this.$pins = document.querySelector('#menu-pins'); 219 | this.$pinList = document.getElementById('menu-pins-list'); 220 | this.$toc = document.querySelector('#menu-toc > ol'); 221 | this.$specContainer = document.getElementById('spec-container'); 222 | this.search = new Search(this); 223 | 224 | this._pinnedIds = {}; 225 | this.loadPinEntries(); 226 | 227 | // toggle menu 228 | this.$toggle.addEventListener('click', this.toggle.bind(this)); 229 | 230 | // keydown events for pinned clauses 231 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 232 | 233 | // toc expansion 234 | var tocItems = this.$menu.querySelectorAll('#menu-toc li'); 235 | for (var i = 0; i < tocItems.length; i++) { 236 | var $item = tocItems[i]; 237 | $item.addEventListener('click', function($item, event) { 238 | $item.classList.toggle('active'); 239 | event.stopPropagation(); 240 | }.bind(null, $item)); 241 | } 242 | 243 | // close toc on toc item selection 244 | var tocLinks = this.$menu.querySelectorAll('#menu-toc li > a'); 245 | for (var i = 0; i < tocLinks.length; i++) { 246 | var $link = tocLinks[i]; 247 | $link.addEventListener('click', function(event) { 248 | this.toggle(); 249 | event.stopPropagation(); 250 | }.bind(this)); 251 | } 252 | 253 | // update active clause on scroll 254 | window.addEventListener('scroll', debounce(this.updateActiveClause.bind(this))); 255 | this.updateActiveClause(); 256 | 257 | // prevent menu scrolling from scrolling the body 258 | this.$toc.addEventListener('wheel', function (e) { 259 | var target = e.currentTarget; 260 | var offTop = e.deltaY < 0 && target.scrollTop === 0; 261 | if (offTop) { 262 | e.preventDefault(); 263 | } 264 | var offBottom = e.deltaY > 0 265 | && target.offsetHeight + target.scrollTop >= target.scrollHeight; 266 | 267 | if (offBottom) { 268 | e.preventDefault(); 269 | } 270 | }) 271 | } 272 | 273 | Menu.prototype.documentKeydown = function (e) { 274 | e.stopPropagation(); 275 | if (e.keyCode === 80) { 276 | this.togglePinEntry(); 277 | } else if (e.keyCode > 48 && e.keyCode < 58) { 278 | this.selectPin(e.keyCode - 49); 279 | } 280 | } 281 | 282 | Menu.prototype.updateActiveClause = function () { 283 | this.setActiveClause(findActiveClause(this.$specContainer)) 284 | } 285 | 286 | Menu.prototype.setActiveClause = function (clause) { 287 | this.$activeClause = clause; 288 | this.revealInToc(this.$activeClause); 289 | } 290 | 291 | Menu.prototype.revealInToc = function (path) { 292 | var current = this.$toc.querySelectorAll('li.revealed'); 293 | for (var i = 0; i < current.length; i++) { 294 | current[i].classList.remove('revealed'); 295 | current[i].classList.remove('revealed-leaf'); 296 | } 297 | 298 | var current = this.$toc; 299 | var index = 0; 300 | while (index < path.length) { 301 | var children = current.children; 302 | for (var i = 0; i < children.length; i++) { 303 | if ('#' + path[index].id === children[i].children[1].getAttribute('href') ) { 304 | children[i].classList.add('revealed'); 305 | if (index === path.length - 1) { 306 | children[i].classList.add('revealed-leaf'); 307 | var rect = children[i].getBoundingClientRect(); 308 | this.$toc.getBoundingClientRect().top 309 | var tocRect = this.$toc.getBoundingClientRect(); 310 | if (rect.top + 10 > tocRect.bottom) { 311 | this.$toc.scrollTop = this.$toc.scrollTop + (rect.top - tocRect.bottom) + (rect.bottom - rect.top); 312 | } else if (rect.top < tocRect.top) { 313 | this.$toc.scrollTop = this.$toc.scrollTop - (tocRect.top - rect.top); 314 | } 315 | } 316 | current = children[i].querySelector('ol'); 317 | index++; 318 | break; 319 | } 320 | } 321 | 322 | } 323 | } 324 | 325 | function findActiveClause(root, path) { 326 | var clauses = new ClauseWalker(root); 327 | var $clause; 328 | var found = false; 329 | var path = path || []; 330 | 331 | while ($clause = clauses.nextNode()) { 332 | var rect = $clause.getBoundingClientRect(); 333 | var $header = $clause.children[0]; 334 | var marginTop = parseInt(getComputedStyle($header)["margin-top"]); 335 | 336 | if ((rect.top - marginTop) <= 0 && rect.bottom > 0) { 337 | found = true; 338 | return findActiveClause($clause, path.concat($clause)) || path; 339 | } 340 | } 341 | 342 | return path; 343 | } 344 | 345 | function ClauseWalker(root) { 346 | var previous; 347 | var treeWalker = document.createTreeWalker( 348 | root, 349 | NodeFilter.SHOW_ELEMENT, 350 | { 351 | acceptNode: function (node) { 352 | if (previous === node.parentNode) { 353 | return NodeFilter.FILTER_REJECT; 354 | } else { 355 | previous = node; 356 | } 357 | if (node.nodeName === 'EMU-CLAUSE' || node.nodeName === 'EMU-INTRO' || node.nodeName === 'EMU-ANNEX') { 358 | return NodeFilter.FILTER_ACCEPT; 359 | } else { 360 | return NodeFilter.FILTER_SKIP; 361 | } 362 | } 363 | }, 364 | false 365 | ); 366 | 367 | return treeWalker; 368 | } 369 | 370 | Menu.prototype.toggle = function () { 371 | this.$menu.classList.toggle('active'); 372 | } 373 | 374 | Menu.prototype.show = function () { 375 | this.$menu.classList.add('active'); 376 | } 377 | 378 | Menu.prototype.hide = function () { 379 | this.$menu.classList.remove('active'); 380 | } 381 | 382 | Menu.prototype.isVisible = function() { 383 | return this.$menu.classList.contains('active'); 384 | } 385 | 386 | Menu.prototype.showPins = function () { 387 | this.$pins.classList.add('active'); 388 | } 389 | 390 | Menu.prototype.hidePins = function () { 391 | this.$pins.classList.remove('active'); 392 | } 393 | 394 | Menu.prototype.addPinEntry = function (id) { 395 | var entry = this.search.biblio.byId[id]; 396 | if (!entry) { 397 | // id was deleted after pin (or something) so remove it 398 | delete this._pinnedIds[id]; 399 | this.persistPinEntries(); 400 | return; 401 | } 402 | 403 | if (entry.type === 'clause') { 404 | var prefix; 405 | if (entry.number) { 406 | prefix = entry.number + ' '; 407 | } else { 408 | prefix = ''; 409 | } 410 | this.$pinList.innerHTML += '
  • ' + prefix + entry.titleHTML + '
  • '; 411 | } else { 412 | this.$pinList.innerHTML += '
  • ' + entry.key + '
  • '; 413 | } 414 | 415 | if (Object.keys(this._pinnedIds).length === 0) { 416 | this.showPins(); 417 | } 418 | this._pinnedIds[id] = true; 419 | this.persistPinEntries(); 420 | } 421 | 422 | Menu.prototype.removePinEntry = function (id) { 423 | var item = this.$pinList.querySelector('a[href="#' + id + '"]').parentNode; 424 | this.$pinList.removeChild(item); 425 | delete this._pinnedIds[id]; 426 | if (Object.keys(this._pinnedIds).length === 0) { 427 | this.hidePins(); 428 | } 429 | 430 | this.persistPinEntries(); 431 | } 432 | 433 | Menu.prototype.persistPinEntries = function () { 434 | try { 435 | if (!window.localStorage) return; 436 | } catch (e) { 437 | return; 438 | } 439 | 440 | localStorage.pinEntries = JSON.stringify(Object.keys(this._pinnedIds)); 441 | } 442 | 443 | Menu.prototype.loadPinEntries = function () { 444 | try { 445 | if (!window.localStorage) return; 446 | } catch (e) { 447 | return; 448 | } 449 | 450 | var pinsString = window.localStorage.pinEntries; 451 | if (!pinsString) return; 452 | var pins = JSON.parse(pinsString); 453 | for(var i = 0; i < pins.length; i++) { 454 | this.addPinEntry(pins[i]); 455 | } 456 | } 457 | 458 | Menu.prototype.togglePinEntry = function (id) { 459 | if (!id) { 460 | id = this.$activeClause[this.$activeClause.length - 1].id; 461 | } 462 | 463 | if (this._pinnedIds[id]) { 464 | this.removePinEntry(id); 465 | } else { 466 | this.addPinEntry(id); 467 | } 468 | } 469 | 470 | Menu.prototype.selectPin = function (num) { 471 | document.location = this.$pinList.children[num].children[0].href; 472 | } 473 | 474 | var menu; 475 | function init() { 476 | menu = new Menu(); 477 | var $container = document.getElementById('spec-container'); 478 | $container.addEventListener('mouseover', debounce(function (e) { 479 | Toolbox.activateIfMouseOver(e); 480 | })); 481 | } 482 | 483 | document.addEventListener('DOMContentLoaded', init); 484 | 485 | function debounce(fn, opts) { 486 | opts = opts || {}; 487 | var timeout; 488 | return function(e) { 489 | if (opts.stopPropagation) { 490 | e.stopPropagation(); 491 | } 492 | var args = arguments; 493 | if (timeout) { 494 | clearTimeout(timeout); 495 | } 496 | timeout = setTimeout(function() { 497 | timeout = null; 498 | fn.apply(this, args); 499 | }.bind(this), 150); 500 | } 501 | } 502 | 503 | var CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; 504 | function findLocalReferences ($elem) { 505 | var name = $elem.innerHTML; 506 | var references = []; 507 | 508 | var parentClause = $elem.parentNode; 509 | while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { 510 | parentClause = parentClause.parentNode; 511 | } 512 | 513 | if(!parentClause) return; 514 | 515 | var vars = parentClause.querySelectorAll('var'); 516 | 517 | for (var i = 0; i < vars.length; i++) { 518 | var $var = vars[i]; 519 | 520 | if ($var.innerHTML === name) { 521 | references.push($var); 522 | } 523 | } 524 | 525 | return references; 526 | } 527 | 528 | function toggleFindLocalReferences($elem) { 529 | var references = findLocalReferences($elem); 530 | if ($elem.classList.contains('referenced')) { 531 | references.forEach(function ($reference) { 532 | $reference.classList.remove('referenced'); 533 | }); 534 | } else { 535 | references.forEach(function ($reference) { 536 | $reference.classList.add('referenced'); 537 | }); 538 | } 539 | } 540 | 541 | function installFindLocalReferences () { 542 | document.addEventListener('click', function (e) { 543 | if (e.target.nodeName === 'VAR') { 544 | toggleFindLocalReferences(e.target); 545 | } 546 | }); 547 | } 548 | 549 | document.addEventListener('DOMContentLoaded', installFindLocalReferences); 550 | 551 | 552 | 553 | 554 | // The following license applies to the fuzzysearch function 555 | // The MIT License (MIT) 556 | // Copyright © 2015 Nicolas Bevacqua 557 | // Copyright © 2016 Brian Terlson 558 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 559 | // this software and associated documentation files (the "Software"), to deal in 560 | // the Software without restriction, including without limitation the rights to 561 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 562 | // the Software, and to permit persons to whom the Software is furnished to do so, 563 | // subject to the following conditions: 564 | 565 | // The above copyright notice and this permission notice shall be included in all 566 | // copies or substantial portions of the Software. 567 | 568 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 569 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 570 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 571 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 572 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 573 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 574 | function fuzzysearch (searchString, haystack, caseInsensitive) { 575 | var tlen = haystack.length; 576 | var qlen = searchString.length; 577 | var chunks = 1; 578 | var finding = false; 579 | var prefix = true; 580 | 581 | if (qlen > tlen) { 582 | return false; 583 | } 584 | 585 | if (qlen === tlen) { 586 | if (searchString === haystack) { 587 | return { caseMatch: true, chunks: 1, prefix: true }; 588 | } else if (searchString.toLowerCase() === haystack.toLowerCase()) { 589 | return { caseMatch: false, chunks: 1, prefix: true }; 590 | } else { 591 | return false; 592 | } 593 | } 594 | 595 | outer: for (var i = 0, j = 0; i < qlen; i++) { 596 | var nch = searchString[i]; 597 | while (j < tlen) { 598 | var targetChar = haystack[j++]; 599 | if (targetChar === nch) { 600 | finding = true; 601 | continue outer; 602 | } 603 | if (finding) { 604 | chunks++; 605 | finding = false; 606 | } 607 | } 608 | 609 | if (caseInsensitive) { return false } 610 | 611 | return fuzzysearch(searchString.toLowerCase(), haystack.toLowerCase(), true); 612 | } 613 | 614 | return { caseMatch: !caseInsensitive, chunks: chunks, prefix: j <= qlen }; 615 | } 616 | 617 | var Toolbox = { 618 | init: function () { 619 | this.$container = document.createElement('div'); 620 | this.$container.classList.add('toolbox'); 621 | this.$permalink = document.createElement('a'); 622 | this.$permalink.textContent = 'Permalink'; 623 | this.$pinLink = document.createElement('a'); 624 | this.$pinLink.textContent = 'Pin'; 625 | this.$pinLink.setAttribute('href', '#'); 626 | this.$pinLink.addEventListener('click', function (e) { 627 | e.preventDefault(); 628 | e.stopPropagation(); 629 | menu.togglePinEntry(this.entry.id); 630 | }.bind(this)); 631 | 632 | this.$refsLink = document.createElement('a'); 633 | this.$refsLink.setAttribute('href', '#'); 634 | this.$refsLink.addEventListener('click', function (e) { 635 | e.preventDefault(); 636 | e.stopPropagation(); 637 | referencePane.showReferencesFor(this.entry); 638 | }.bind(this)); 639 | this.$container.appendChild(this.$permalink); 640 | this.$container.appendChild(this.$pinLink); 641 | this.$container.appendChild(this.$refsLink); 642 | document.body.appendChild(this.$container); 643 | }, 644 | 645 | activate: function (el, entry, target) { 646 | if (el === this._activeEl) return; 647 | this.active = true; 648 | this.entry = entry; 649 | this.$container.classList.add('active'); 650 | this.top = el.offsetTop - this.$container.offsetHeight - 10; 651 | this.left = el.offsetLeft; 652 | this.$container.setAttribute('style', 'left: ' + this.left + 'px; top: ' + this.top + 'px'); 653 | this.updatePermalink(); 654 | this.updateReferences(); 655 | this._activeEl = el; 656 | if (this.top < document.body.scrollTop && el === target) { 657 | // don't scroll unless it's a small thing (< 200px) 658 | this.$container.scrollIntoView(); 659 | } 660 | }, 661 | 662 | updatePermalink: function () { 663 | this.$permalink.setAttribute('href', '#' + this.entry.id); 664 | }, 665 | 666 | updateReferences: function () { 667 | this.$refsLink.textContent = 'References (' + this.entry.referencingIds.length + ')'; 668 | }, 669 | 670 | activateIfMouseOver: function (e) { 671 | var ref = this.findReferenceUnder(e.target); 672 | if (ref && (!this.active || e.pageY > this._activeEl.offsetTop)) { 673 | var entry = menu.search.biblio.byId[ref.id]; 674 | this.activate(ref.element, entry, e.target); 675 | } else if (this.active && ((e.pageY < this.top) || e.pageY > (this._activeEl.offsetTop + this._activeEl.offsetHeight))) { 676 | this.deactivate(); 677 | } 678 | }, 679 | 680 | findReferenceUnder: function (el) { 681 | while (el) { 682 | var parent = el.parentNode; 683 | if (el.nodeName === 'H1' && parent.nodeName.match(/EMU-CLAUSE|EMU-ANNEX|EMU-INTRO/) && parent.id) { 684 | return { element: el, id: parent.id }; 685 | } else if (el.nodeName.match(/EMU-(?!CLAUSE|XREF|ANNEX|INTRO)|DFN/) && 686 | el.id && el.id[0] !== '_') { 687 | if (el.nodeName === 'EMU-FIGURE' || el.nodeName === 'EMU-TABLE' || el.nodeName === 'EMU-EXAMPLE') { 688 | // return the figcaption element 689 | return { element: el.children[0].children[0], id: el.id }; 690 | } else if (el.nodeName === 'EMU-PRODUCTION') { 691 | // return the LHS non-terminal element 692 | return { element: el.children[0], id: el.id }; 693 | } else { 694 | return { element: el, id: el.id }; 695 | } 696 | } 697 | el = parent; 698 | } 699 | }, 700 | 701 | deactivate: function () { 702 | this.$container.classList.remove('active'); 703 | this._activeEl = null; 704 | this.activeElBounds = null; 705 | this.active = false; 706 | } 707 | } 708 | 709 | var referencePane = { 710 | init: function() { 711 | this.$container = document.createElement('div'); 712 | this.$container.setAttribute('id', 'references-pane-container'); 713 | 714 | var $spacer = document.createElement('div'); 715 | $spacer.setAttribute('id', 'references-pane-spacer'); 716 | 717 | this.$pane = document.createElement('div'); 718 | this.$pane.setAttribute('id', 'references-pane'); 719 | 720 | this.$container.appendChild($spacer); 721 | this.$container.appendChild(this.$pane); 722 | 723 | this.$header = document.createElement('div'); 724 | this.$header.classList.add('menu-pane-header'); 725 | this.$header.textContent = 'References to '; 726 | this.$headerRefId = document.createElement('a'); 727 | this.$header.appendChild(this.$headerRefId); 728 | this.$closeButton = document.createElement('span'); 729 | this.$closeButton.setAttribute('id', 'references-pane-close'); 730 | this.$closeButton.addEventListener('click', function (e) { 731 | this.deactivate(); 732 | }.bind(this)); 733 | this.$header.appendChild(this.$closeButton); 734 | 735 | this.$pane.appendChild(this.$header); 736 | var tableContainer = document.createElement('div'); 737 | tableContainer.setAttribute('id', 'references-pane-table-container'); 738 | 739 | this.$table = document.createElement('table'); 740 | this.$table.setAttribute('id', 'references-pane-table'); 741 | 742 | this.$tableBody = this.$table.createTBody(); 743 | 744 | tableContainer.appendChild(this.$table); 745 | this.$pane.appendChild(tableContainer); 746 | 747 | menu.$specContainer.appendChild(this.$container); 748 | }, 749 | 750 | activate: function () { 751 | this.$container.classList.add('active'); 752 | }, 753 | 754 | deactivate: function () { 755 | this.$container.classList.remove('active'); 756 | }, 757 | 758 | showReferencesFor(entry) { 759 | this.activate(); 760 | var newBody = document.createElement('tbody'); 761 | var previousId; 762 | var previousCell; 763 | var dupCount = 0; 764 | this.$headerRefId.textContent = '#' + entry.id; 765 | this.$headerRefId.setAttribute('href', '#' + entry.id); 766 | entry.referencingIds.map(function (id) { 767 | var target = document.getElementById(id); 768 | var cid = findParentClauseId(target); 769 | var clause = menu.search.biblio.byId[cid]; 770 | var dupCount = 0; 771 | return { id: id, clause: clause } 772 | }).sort(function (a, b) { 773 | return sortByClauseNumber(a.clause, b.clause); 774 | }).forEach(function (record, i) { 775 | if (previousId === record.clause.id) { 776 | previousCell.innerHTML += ' (' + (dupCount + 2) + ')'; 777 | dupCount++; 778 | } else { 779 | var row = newBody.insertRow(); 780 | var cell = row.insertCell(); 781 | cell.innerHTML = record.clause.number; 782 | cell = row.insertCell(); 783 | cell.innerHTML = '' + record.clause.titleHTML + ''; 784 | previousCell = cell; 785 | previousId = record.clause.id; 786 | dupCount = 0; 787 | } 788 | }, this); 789 | this.$table.removeChild(this.$tableBody); 790 | this.$tableBody = newBody; 791 | this.$table.appendChild(this.$tableBody); 792 | } 793 | } 794 | function findParentClauseId(node) { 795 | while (node && node.nodeName !== 'EMU-CLAUSE' && node.nodeName !== 'EMU-INTRO' && node.nodeName !== 'EMU-ANNEX') { 796 | node = node.parentNode; 797 | } 798 | if (!node) return null; 799 | return node.getAttribute('id'); 800 | } 801 | 802 | function sortByClauseNumber(c1, c2) { 803 | var c1c = c1.number.split('.'); 804 | var c2c = c2.number.split('.'); 805 | 806 | for (var i = 0; i < c1c.length; i++) { 807 | if (i >= c2c.length) { 808 | return 1; 809 | } 810 | 811 | var c1 = c1c[i]; 812 | var c2 = c2c[i]; 813 | var c1cn = Number(c1); 814 | var c2cn = Number(c2); 815 | 816 | if (Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 817 | if (c1 > c2) { 818 | return 1; 819 | } else if (c1 < c2) { 820 | return -1; 821 | } 822 | } else if (!Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 823 | return -1; 824 | } else if (Number.isNaN(c1cn) && !Number.isNaN(c2cn)) { 825 | return 1; 826 | } else if(c1cn > c2cn) { 827 | return 1; 828 | } else if (c1cn < c2cn) { 829 | return -1; 830 | } 831 | } 832 | 833 | if (c1c.length === c2c.length) { 834 | return 0; 835 | } 836 | return -1; 837 | } 838 | 839 | document.addEventListener('DOMContentLoaded', function () { 840 | Toolbox.init(); 841 | referencePane.init(); 842 | }) 843 | var CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; 844 | function findLocalReferences ($elem) { 845 | var name = $elem.innerHTML; 846 | var references = []; 847 | 848 | var parentClause = $elem.parentNode; 849 | while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { 850 | parentClause = parentClause.parentNode; 851 | } 852 | 853 | if(!parentClause) return; 854 | 855 | var vars = parentClause.querySelectorAll('var'); 856 | 857 | for (var i = 0; i < vars.length; i++) { 858 | var $var = vars[i]; 859 | 860 | if ($var.innerHTML === name) { 861 | references.push($var); 862 | } 863 | } 864 | 865 | return references; 866 | } 867 | 868 | function toggleFindLocalReferences($elem) { 869 | var references = findLocalReferences($elem); 870 | if ($elem.classList.contains('referenced')) { 871 | references.forEach(function ($reference) { 872 | $reference.classList.remove('referenced'); 873 | }); 874 | } else { 875 | references.forEach(function ($reference) { 876 | $reference.classList.add('referenced'); 877 | }); 878 | } 879 | } 880 | 881 | function installFindLocalReferences () { 882 | document.addEventListener('click', function (e) { 883 | if (e.target.nodeName === 'VAR') { 884 | toggleFindLocalReferences(e.target); 885 | } 886 | }); 887 | } 888 | 889 | document.addEventListener('DOMContentLoaded', installFindLocalReferences); 890 | --------------------------------------------------------------------------------