Skip to main content

@storyteller-platform/uri-fragment

A JavaScript library for parsing, building, and serializing URI fragments.

Supports:

  • Anchor fragments (#paragraph01)
  • Media fragments (#t=3.2, #xywh=25,25,50,50)
  • Text fragments (:~:text=the%20end)
  • Character fragments (#char=100, #char=100,150)
  • EPUB CFI (#epubcfi(/6/4!/4/10/3:10))
  • XPointer (#xpointer(id("introduction")))
  • KOReader crengine "xpointer", a non-standard fragment to represent KOReader's crengine's non-standard "xpointer" implementation (has almost nothing to do with the W3C XPointer spec). (#crengine(/body/DocFragment/body/div/p[4]/text()[1].64))

Also supports mixing fragments where supported by the spec(s). For example, any URI fragment may contain one identifier and any number of directives, e.g., #paragraph01:~:text=first&text=second. Also, media identifiers may contain multiple dimensions, e.g., #t=15,75&xywh=percent:25,25,50,50.

Installation​

npm:

npm install @storyteller-platform/uri-fragment

yarn:

yarn add @storyteller-platform/uri-fragment

deno:

deno install npm:@storyteller-platform/uri-fragment

About​

URI's may terminate with a URI fragment. Different media types have different specifications regarding how to interpret these fragments, as well as (sometimes complex) grammars for parsing them. Standard URI parsers (e.g., new URL()) do not parse the fragment, returning it as is.

The goal of this library is to accept a fragment parsed by a URI parser and further parse it into a semantically useful object that can be inspected and operated on.

Usage​

Parsing​

// Parsing a text directive
const url = new URL("https://example.com/example#:~:text=permission,.")

const fragment = new UriFragment(url.hash)

console.log(fragment.directive.text[0]?.decodedTextStart)

// => "permission"

console.log(fragment.directive.text[0]?.decodedTextEnd)

// => "."
// Parsing a media identifier
const url = new URL(
"https://example.com/video.mp4#t=01:30,02:15&xywh=percent:25,25,50,50",
)

const fragment = new UriFragment(url.hash)

console.log(fragment.identifier?.kind)

// => "media"

console.log(fragment.identifier?.media.temporal?.parsedTimeStart)

// => 90

console.log(fragment.identifier?.media.spatial?.xCoord)

// => 25

Building​

const fragment = new UriFragment()

fragment.directive.appendTextDirective("permission,.")

const media = new MediaDimensions()

media.temporal = new MediaTemporalDimension()

media.temporal.timeStart = "3.5"

fragment.setIdentifier("media", { media })

console.log(fragment.toString())

// => `#t=3.5:~:text=permission,.`

Development​

This package lives in the Storyteller monorepo, and is developed alongside the Storyteller platform.

To get started with developing in the Storyteller monorepo, check out the development guides in the docs.