Skip to content

Commit

Permalink
line-text (#48)
Browse files Browse the repository at this point in the history
* placeholder for line-text

* bring methdos into the class

* added examples for programmatically setting teh content

* norepeat

* code samples inserted
  • Loading branch information
cubap authored Dec 12, 2024
1 parent 19978e3 commit e895c53
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 50 deletions.
52 changes: 52 additions & 0 deletions components/line-text/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,74 @@ <h1>Line Text Module Import Example</h1>
<h4>A Line like Newberry TPEN with <code>line.resource["cnt:chars"]</code></h4>
<blockquote>
<tpen-line-text tpen-line-id="https://store.rerum.io/v1/id/6685af395b48727bae3fc269"></tpen-line-text>
<br><code>&lt;tpen-line-text tpen-line-id=&quot;https://store.rerum.io/v1/id/6685af395b48727bae3fc269&quot;&gt;&lt;/tpen-line-text&gt;</code>
</blockquote>

<h4>A Line from IIIF fixture <code>line.body.value</code> (A TextualBody example)</h4>
<blockquote>
<tpen-line-text iiif-content="JTdCJTIyaWQlMjIlM0ElMjJodHRwcyUzQSUyRiUyRmlpaWYuaW8lMkZhcGklMkZjb29rYm9vayUyRnJlY2lwZSUyRjAyNjYtZnVsbC1jYW52YXMtYW5ub3RhdGlvbiUyRmNhbnZhcy0xJTJGYW5ub3BhZ2UtMiUyRmFubm8tMSUyMiUyQyUyMnR5cGUlMjIlM0ElMjJBbm5vdGF0aW9uJTIyJTJDJTIybW90aXZhdGlvbiUyMiUzQSUyMmNvbW1lbnRpbmclMjIlMkMlMjJib2R5JTIyJTNBJTdCJTIydHlwZSUyMiUzQSUyMlRleHR1YWxCb2R5JTIyJTJDJTIybGFuZ3VhZ2UlMjIlM0ElMjJkZSUyMiUyQyUyMmZvcm1hdCUyMiUzQSUyMnRleHQlMkZwbGFpbiUyMiUyQyUyMnZhbHVlJTIyJTNBJTIyRyVDMyVCNnR0aW5nZXIlMjBNYXJrdHBsYXR6JTIwbWl0JTIwRyVDMyVBNG5zZWxpZXNlbCUyMEJydW5uZW4lMjIlN0QlMkMlMjJ0YXJnZXQlMjIlM0ElMjJodHRwcyUzQSUyRiUyRmlpaWYuaW8lMkZhcGklMkZjb29rYm9vayUyRnJlY2lwZSUyRjAyNjYtZnVsbC1jYW52YXMtYW5ub3RhdGlvbiUyRmNhbnZhcy0xJTIyJTdE"></tpen-line-text>
<br><code>&lt;tpen-line-text iiif-content=&quot;JTdCJTIyaWQlMjIlM0ElMjJodHRwcyUzQSUyRiUyRmlpaWYuaW8lMkZhcGklMkZjb29rYm9vayUyRnJlY2lwZSUyRjAyNjYtZnVsbC1jYW52YXMtYW5ub3RhdGlvbiUyRmNhbnZhcy0xJTJGYW5ub3BhZ2UtMiUyRmFubm8tMSUyMiUyQyUyMnR5cGUlMjIlM0ElMjJBbm5vdGF0aW9uJTIyJTJDJTIybW90aXZhdGlvbiUyMiUzQSUyMmNvbW1lbnRpbmclMjIlMkMlMjJib2R5JTIyJTNBJTdCJTIydHlwZSUyMiUzQSUyMlRleHR1YWxCb2R5JTIyJTJDJTIybGFuZ3VhZ2UlMjIlM0ElMjJkZSUyMiUyQyUyMmZvcm1hdCUyMiUzQSUyMnRleHQlMkZwbGFpbiUyMiUyQyUyMnZhbHVlJTIyJTNBJTIyRyVDMyVCNnR0aW5nZXIlMjBNYXJrdHBsYXR6JTIwbWl0JTIwRyVDMyVBNG5zZWxpZXNlbCUyMEJydW5uZW4lMjIlN0QlMkMlMjJ0YXJnZXQlMjIlM0ElMjJodHRwcyUzQSUyRiUyRmlpaWYuaW8lMkZhcGklMkZjb29rYm9vayUyRnJlY2lwZSUyRjAyNjYtZnVsbC1jYW52YXMtYW5ub3RhdGlvbiUyRmNhbnZhcy0xJTIyJTdE&quot;&gt;&lt;/tpen-line-text&gt;</code>
</blockquote>

<h4>A Line with a String Literal <code>line.body</code> </h4>
<blockquote>
<tpen-line-text iiif-content="JTdCJTIyaWQlMjIlM0ElMjJidWglMjIlMkMlMjJ0eXBlJTIyJTNBJTIyQW5ub3RhdGlvbiUyMiUyQyUyMmJvZHklMjIlM0ElMjJBbGlxdWlkJTIwU3RyaW5nJTIyJTJDJTIydGFyZ2V0JTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZpaWlmLmlvJTJGYXBpJTJGY29va2Jvb2slMkZyZWNpcGUlMkYwMjY2LWZ1bGwtY2FudmFzLWFubm90YXRpb24lMkZjYW52YXMtMSUyMiU3RA=="></tpen-line-text>
<br><code>&lt;tpen-line-text iiif-content=&quot;JTdCJTIyaWQlMjIlM0ElMjJidWglMjIlMkMlMjJ0eXBlJTIyJTNBJTIyQW5ub3RhdGlvbiUyMiUyQyUyMmJvZHklMjIlM0ElMjJBbGlxdWlkJTIwU3RyaW5nJTIyJTJDJTIydGFyZ2V0JTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZpaWlmLmlvJTJGYXBpJTJGY29va2Jvb2slMkZyZWNpcGUlMkYwMjY2LWZ1bGwtY2FudmFzLWFubm90YXRpb24lMkZjYW52YXMtMSUyMiU3RA==&quot;&gt;&lt;/tpen-line-text&gt;</code>
</blockquote>

<h4>A Line with missing content </h4>
<blockquote>
<tpen-line-text></tpen-line-text>
<br><code>&lt;tpen-line-text&gt;&lt;/tpen-line-text&gt;</code>
</blockquote>

<hr>

<p>The component can also be told directly about a resource by calling <code>elem.drawLineText(text)</code>
with a string literal, an Annotation object, a text-containing body, or an array of these.
</p>
<script>
const randomObjects = [
{
"id": "https://store.rerum.io/v1/id/6685af395b48727bae3fc269",
"type": "Annotation",
"body": {
"type": "TextualBody",
"value": "This is text to include."
}
},
{
"@id" : "https://t-pen.org/TPEN/line/104692230",
"_tpen_line_id" : "line/104692230",
"@type" : "oa:Annotation",
"motivation" : "oad:transcribing",
"resource" : {
"@type" : "cnt:ContentAsText",
"cnt:chars" : "doceret et ecclesiam super quam sedet, predicans preceptum domini "
},
"on" : "https://t-pen.org/TPEN/canvas/13248868#xywh=125,58,229,10"
}, "Just make it a string literal."
]

function assignSomeWords() {
let lineText = document.getElementById("lineText")
let text = lineText.innerText
let currentIndex = randIndex = lineText.dataset.index
do {
randIndex = Math.floor(Math.random() * randomObjects.length)
} while (currentIndex == randIndex)
text = randomObjects[randIndex]
textObj.innerText = JSON.stringify(text, null, 2)
lineText.dataset.index = randIndex
lineText.drawLineText(text)
}
</script>
<button role="button" onclick="assignSomeWords()">♻ the content.</button>
<blockquote>
<tpen-line-text id="lineText"></tpen-line-text>
</blockquote>
<pre id="textObj"></pre>

</body>

</html>
136 changes: 86 additions & 50 deletions components/line-text/index.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,114 @@
import {decodeContentState} from '../iiif-tools/index.mjs'

const LINE_TEXT_HTML = `<span></span>`
const LINE_TEXT_HTML = `<span>
<span style="border-radius: 1em; background-color: lightgrey; width: 100%; min-width:14em;min-height: 1em; display: inline-block;">
</span>
</span>`

class TpenLineText extends HTMLElement {
static get observedAttributes() {
return ['tpen-line-id', 'iiif-content']
}

attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
if (name === 'tpen-line-id') {
this.loadText(newValue);
} else if (name === 'iiif-content') {
this.loadContent(newValue)
}
}
}

#id = () => this.closest('[tpen-line-id]')?.getAttribute('tpen-line-id')
#content = () => this.closest('[iiif-content]')?.getAttribute('iiif-content')

constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = LINE_TEXT_HTML
}

connectedCallback() {
this.shadowRoot.innerHTML = LINE_TEXT_HTML
const SPAN = this.shadowRoot.querySelector('span')

if (!this.#id() && !this.#content()) {
const ERR = new Event('tpen-error', { detail: 'Line ID is required' })
validateContent(null,SPAN,"Line ID is required")
this.validateContent(null,SPAN,"Line ID is required")
}

this.#content() ? loadContent(this.#content(),SPAN) : loadText(this.#id(),SPAN)
this.#content() ? this.loadContent(this.#content(),SPAN) : this.loadText(this.#id(),SPAN)
}
}

customElements.define('tpen-line-text', TpenLineText)

export default {
TpenLineText
}

async function loadText(lineId,elem){
try {
new URL(lineId)
const TEXT_CONTENT = await loadAnnotation(lineId)
elem.innerText = validateContent(TEXT_CONTENT,elem)
} catch (error) {
console.error(error)
return validateContent(null,elem,"Fetching Error")
drawLineText(textObj) {
const SPAN = this.shadowRoot.querySelector('span')
const innerText = this.validateContent(this.getText(textObj))
SPAN.innerText = innerText
}
}

function loadContent(b64,elem){
try {
const TEXT_CONTENT = getText(JSON.parse(decodeContentState(b64)))
elem.innerText = validateContent(TEXT_CONTENT,elem)
} catch (error) {
console.error(error)
return validateContent(null,elem,"Decoding Error")
async loadText(lineId){
if(!lineId) return
try {
new URL(lineId)
const TEXT_CONTENT = await this.#loadAnnotation(lineId)
this.drawLineText(TEXT_CONTENT)
} catch (error) {
console.error(error)
return this.validateContent(null,elem,"Fetching Error")
}
}
}

function loadAnnotation(url){
return fetch(url)
.then(response => {

loadContent(b64,elem){
try {
const TEXT_CONTENT = JSON.parse(decodeContentState(b64))
this.drawLineText(TEXT_CONTENT)
} catch (error) {
console.error(error)
return this.validateContent(null,elem,"Decoding Error")
}
}

async #loadAnnotation(url){
try {
const response = await fetch(url)
if(!response.ok) throw new Error("failed to fetch")
return response.json()
})
.then(anno => getText(anno))
.catch(error => console.error(error))
return await response.json()
} catch (error) {
console.error(error)
}
}
/**
* Extract the text only content from an Annotation or body
* @param {any} textBody String, Array, or Object with text content
* @returns String with text content, flattened from nested objects
*/
getText(textBody){
if(typeof textBody === "string") {
return textBody
}
if(Array.isArray(textBody)){
return textBody.map(t=>this.getText(t)).join(' ')
}
let meaningfulProp = textBody.value ?? textBody.body ?? textBody["cnt:chars"] ?? textBody.bodyValue
?? textBody.chars ?? textBody.text ?? textBody.resource
// possible language mapping
?? textBody[navigator.language] ?? textBody[navigator.language.split('-')[0]] ?? textBody.none
if(meaningfulProp) return this.getText(meaningfulProp)
// maybe this is a nested object, specific language, etc.
console.warn("Unrecognized text body",textBody)
return "<>" // this will always look broken
}

validateContent(content,msg='Invalid content') {
if(content==null){
this.setAttribute('aria-invalid',true)
this.setAttribute('title', msg)
}
return content
}
}

function getText(annotation){
// TODO: currently this is a fragile mess
let textContent = annotation.body?.value
if(annotation.resource) textContent = annotation.resource["cnt:chars"]
if(typeof annotation.body === "string") textContent = annotation.body
return textContent ?? "weird value"
}
customElements.define('tpen-line-text', TpenLineText)

function validateContent(content,elem,msg) {
if(content==null){
elem.setAttribute('aria-invalid',true)
elem.setAttribute('title',msg ?? 'Invalid content')
}
return content
export default {
TpenLineText
}

0 comments on commit e895c53

Please sign in to comment.