-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from togostanza/fix-multiple-stanzas
Fix an error when there are multiple stanzas on the same page
- Loading branch information
Showing
3 changed files
with
167 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,189 +1,191 @@ | ||
import Handlebars from 'handlebars/dist/handlebars'; | ||
|
||
export default function Stanza(execute) { | ||
const development = descriptor.development; | ||
|
||
function template(name) { | ||
const t = descriptor.templates[name]; | ||
if (!t) { | ||
throw new Error(`template "${name}" is not found`); | ||
export default function initialize(descriptor) { | ||
return function Stanza(execute) { | ||
const development = descriptor.development; | ||
|
||
function template(name) { | ||
const t = descriptor.templates[name]; | ||
if (!t) { | ||
throw new Error(`template "${name}" is not found`); | ||
} | ||
return t; | ||
} | ||
return t; | ||
} | ||
|
||
function createStanzaHelper(element) { | ||
const handlebars = Handlebars.create(); | ||
function createStanzaHelper(element) { | ||
const handlebars = Handlebars.create(); | ||
|
||
return { | ||
root: element.shadowRoot, | ||
handlebars, | ||
|
||
query(params) { | ||
if (development) { | ||
console.log("query: called", params); | ||
} | ||
const t = template(params.template); | ||
const queryTemplate = handlebars.compile(t, {noEscape: true}); | ||
const query = queryTemplate(params.parameters); | ||
const data = new URLSearchParams(); | ||
data.set("query", query); | ||
|
||
if (development) { | ||
console.log("query: query built:\n" + query); | ||
console.log("query: sending to", params.endpoint); | ||
} | ||
return { | ||
root: element.shadowRoot, | ||
handlebars, | ||
|
||
// NOTE specifying Content-Type explicitly because some browsers sends `application/x-www-form-urlencoded;charset=UTF-8` without this, and some endpoints may not support this form. | ||
return fetch(params.endpoint, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
"Accept": "application/sparql-results+json" | ||
}, | ||
body: data, | ||
}).then((response) => { | ||
query(params) { | ||
if (development) { | ||
console.log("query:", response.statusText, response); | ||
console.log("query: called", params); | ||
} | ||
const t = template(params.template); | ||
const queryTemplate = handlebars.compile(t, {noEscape: true}); | ||
const query = queryTemplate(params.parameters); | ||
const data = new URLSearchParams(); | ||
data.set("query", query); | ||
|
||
return response.json(); | ||
}); | ||
}, | ||
|
||
render(params) { | ||
if (development) { | ||
console.log("render: called", params) | ||
} | ||
|
||
const t = template(params.template); | ||
const htmlTemplate = handlebars.compile(t); | ||
const htmlFragment = htmlTemplate(params.parameters); | ||
|
||
if (development) { | ||
console.log("render: built:\n", htmlFragment) | ||
} | ||
|
||
const selector = params.selector || "main"; | ||
element.shadowRoot.querySelector(selector).innerHTML = htmlFragment; | ||
|
||
if (development) { | ||
console.log("render: wrote to \"" + selector + "\"") | ||
} | ||
}, | ||
|
||
select(selector) { | ||
return this.root.querySelector(selector); | ||
}, | ||
|
||
selectAll(selector) { | ||
return this.root.querySelectorAll(selector); | ||
}, | ||
|
||
grouping(rows, ...keys) { | ||
const normalizedKeys = keys.reduce((acc, key) => { | ||
if (key instanceof Array) { | ||
return acc.concat({key: key, alias: key.join('_')}); | ||
} else if (key instanceof Object) { | ||
return acc.concat(key); | ||
} else { | ||
return acc.concat({key: key, alias: key}); | ||
if (development) { | ||
console.log("query: query built:\n" + query); | ||
console.log("query: sending to", params.endpoint); | ||
} | ||
}, []); | ||
|
||
return (function _grouping(rows, keys) { | ||
const [currentKey, ...remainKeys] = keys; | ||
// NOTE specifying Content-Type explicitly because some browsers sends `application/x-www-form-urlencoded;charset=UTF-8` without this, and some endpoints may not support this form. | ||
return fetch(params.endpoint, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
"Accept": "application/sparql-results+json" | ||
}, | ||
body: data, | ||
}).then((response) => { | ||
if (development) { | ||
console.log("query:", response.statusText, response); | ||
} | ||
|
||
return response.json(); | ||
}); | ||
}, | ||
|
||
function fetch(row, key) { | ||
return key instanceof Array ? key.map((k) => row[k]) : row[currentKey.key]; | ||
render(params) { | ||
if (development) { | ||
console.log("render: called", params) | ||
} | ||
|
||
if (keys.length === 1) { | ||
return rows.map((row) => fetch(row, currentKey.key)); | ||
const t = template(params.template); | ||
const htmlTemplate = handlebars.compile(t); | ||
const htmlFragment = htmlTemplate(params.parameters); | ||
|
||
if (development) { | ||
console.log("render: built:\n", htmlFragment) | ||
} | ||
|
||
return this.groupBy(rows, (row) => { | ||
return fetch(row, currentKey.key); | ||
}).map(([currentValue, remainValues]) => { | ||
const nextKey = remainKeys[0]; | ||
const selector = params.selector || "main"; | ||
element.shadowRoot.querySelector(selector).innerHTML = htmlFragment; | ||
|
||
return { | ||
[currentKey.alias]: currentValue, | ||
[nextKey.alias]: _grouping(remainValues, remainKeys) | ||
}; | ||
if (development) { | ||
console.log("render: wrote to \"" + selector + "\"") | ||
} | ||
}, | ||
|
||
select(selector) { | ||
return this.root.querySelector(selector); | ||
}, | ||
|
||
selectAll(selector) { | ||
return this.root.querySelectorAll(selector); | ||
}, | ||
|
||
grouping(rows, ...keys) { | ||
const normalizedKeys = keys.reduce((acc, key) => { | ||
if (key instanceof Array) { | ||
return acc.concat({key: key, alias: key.join('_')}); | ||
} else if (key instanceof Object) { | ||
return acc.concat(key); | ||
} else { | ||
return acc.concat({key: key, alias: key}); | ||
} | ||
}, []); | ||
|
||
return (function _grouping(rows, keys) { | ||
const [currentKey, ...remainKeys] = keys; | ||
|
||
function fetch(row, key) { | ||
return key instanceof Array ? key.map((k) => row[k]) : row[currentKey.key]; | ||
} | ||
|
||
if (keys.length === 1) { | ||
return rows.map((row) => fetch(row, currentKey.key)); | ||
} | ||
|
||
return this.groupBy(rows, (row) => { | ||
return fetch(row, currentKey.key); | ||
}).map(([currentValue, remainValues]) => { | ||
const nextKey = remainKeys[0]; | ||
|
||
return { | ||
[currentKey.alias]: currentValue, | ||
[nextKey.alias]: _grouping(remainValues, remainKeys) | ||
}; | ||
}); | ||
})(rows, normalizedKeys); | ||
}, | ||
|
||
groupBy(array, func) { | ||
const ret = []; | ||
|
||
array.forEach((item) => { | ||
const key = func(item); | ||
const entry = ret.filter((e) => e[0] === key)[0]; | ||
|
||
if (entry) { | ||
entry[1].push(item); | ||
} else { | ||
ret.push([key, [item]]); | ||
} | ||
}); | ||
})(rows, normalizedKeys); | ||
}, | ||
|
||
groupBy(array, func) { | ||
const ret = []; | ||
|
||
array.forEach((item) => { | ||
const key = func(item); | ||
const entry = ret.filter((e) => e[0] === key)[0]; | ||
|
||
if (entry) { | ||
entry[1].push(item); | ||
} else { | ||
ret.push([key, [item]]); | ||
} | ||
}); | ||
return ret; | ||
}, | ||
|
||
return ret; | ||
}, | ||
unwrapValueFromBinding(queryResult) { | ||
const bindings = queryResult.results.bindings; | ||
|
||
unwrapValueFromBinding(queryResult) { | ||
const bindings = queryResult.results.bindings; | ||
return bindings.map((binding) => { | ||
const ret = {}; | ||
|
||
return bindings.map((binding) => { | ||
const ret = {}; | ||
Object.keys(binding).forEach((key) => { | ||
ret[key] = binding[key].value; | ||
}); | ||
|
||
Object.keys(binding).forEach((key) => { | ||
ret[key] = binding[key].value; | ||
return ret; | ||
}); | ||
} | ||
}; | ||
} | ||
|
||
return ret; | ||
}); | ||
} | ||
}; | ||
} | ||
|
||
function update(element) { | ||
const params = {}; | ||
descriptor.parameters.forEach((key) => { | ||
params[key] = element.getAttribute(key); | ||
}); | ||
execute(createStanzaHelper(element), params); | ||
} | ||
function update(element) { | ||
const params = {}; | ||
descriptor.parameters.forEach((key) => { | ||
params[key] = element.getAttribute(key); | ||
}); | ||
execute(createStanzaHelper(element), params); | ||
} | ||
|
||
class StanzaElement extends HTMLElement { | ||
constructor() { | ||
super(); | ||
class StanzaElement extends HTMLElement { | ||
constructor() { | ||
super(); | ||
|
||
const shadow = this.attachShadow({mode: "open"}); | ||
const main = document.createElement("main"); | ||
shadow.appendChild(main); | ||
const shadow = this.attachShadow({mode: "open"}); | ||
const main = document.createElement("main"); | ||
shadow.appendChild(main); | ||
|
||
update(this); | ||
} | ||
update(this); | ||
} | ||
|
||
static get observedAttributes() { | ||
return descriptor.parameters; | ||
} | ||
static get observedAttributes() { | ||
return descriptor.parameters; | ||
} | ||
|
||
attributeChangedCallback(attrName, oldVal, newVal) { | ||
let found = false; | ||
descriptor.parameters.forEach(function(key) { | ||
if (attrName == key) { | ||
found = true; | ||
attributeChangedCallback(attrName, oldVal, newVal) { | ||
let found = false; | ||
descriptor.parameters.forEach(function(key) { | ||
if (attrName == key) { | ||
found = true; | ||
} | ||
}); | ||
if (found) { | ||
update(this); | ||
} | ||
}); | ||
if (found) { | ||
update(this); | ||
} | ||
} | ||
} | ||
|
||
if ('customElements' in window && !window.customElements.get(descriptor.elementName)) { | ||
window.customElements.define(descriptor.elementName, StanzaElement); | ||
if ('customElements' in window && !window.customElements.get(descriptor.elementName)) { | ||
window.customElements.define(descriptor.elementName, StanzaElement); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters