diff --git a/README.md b/README.md
index 1beb0b3..c8d4901 100644
--- a/README.md
+++ b/README.md
@@ -17,3 +17,9 @@ Only the third step is specific to enabling external vocabulary support. The oth
If you create your own :CVocConf setting value (i.e. to manage other fields), you can use the /examples/config/CVocConf.schema.json file to validate your syntax.
+### Packages
+
+The directory `packages` include complete working sets of metadatablock.tsv / cvoc config and / js files.
+
+- local_contexts
+Is pulling and displaying project-data from https://localcontextshub.org/
\ No newline at end of file
diff --git a/packages/local_contexts/cvocLocalContexts.tsv b/packages/local_contexts/cvocLocalContexts.tsv
new file mode 100644
index 0000000..18e8496
--- /dev/null
+++ b/packages/local_contexts/cvocLocalContexts.tsv
@@ -0,0 +1,5 @@
+#metadataBlock name dataverseAlias displayName blockURI
+ LocalContextsCVoc Local Contexts
+#datasetField name title description watermark fieldType displayOrder displayFormat advancedSearchField allowControlledVocabulary allowmultiples facetable displayoncreate required parent metadatablock_id termURI
+ LCProjectUrl Local Contexts Project Local Contexts project url Project url url 1 "#VALUE" FALSE FALSE FALSE FALSE TRUE FALSE LocalContextsCVoc
+#controlledVocabulary DatasetField Value identifier displayOrder
diff --git a/packages/local_contexts/lc-cvoc-conf.json b/packages/local_contexts/lc-cvoc-conf.json
new file mode 100644
index 0000000..415d079
--- /dev/null
+++ b/packages/local_contexts/lc-cvoc-conf.json
@@ -0,0 +1,25 @@
+[
+ {
+ "field-name": "LCProjectUrl",
+ "term-uri-field": "LCProjectUrl",
+ "js-url": "https://gdcc.github.io/dataverse-external-vocab-support/packages/local_contexts/local_contexts.js",
+ "protocol": "localcontexts",
+ "retrieval-uri": "https://localcontextshub.org/api/v1/projects/{0}",
+ "allow-free-text": false,
+ "prefix": "https://localcontextshub.org/projects/",
+ "managed-fields": {},
+ "languages": "",
+ "vocabs": {},
+ "retrieval-filtering": {
+ "@context": {
+ "scheme": "http://www.w3.org/2004/02/skos/core#inScheme"
+ },
+ "@id": {
+ "pattern": "{0}",
+ "params": [
+ "@id"
+ ]
+ }
+ }
+ }
+]
diff --git a/packages/local_contexts/local_contexts.js b/packages/local_contexts/local_contexts.js
new file mode 100644
index 0000000..81ddc51
--- /dev/null
+++ b/packages/local_contexts/local_contexts.js
@@ -0,0 +1,148 @@
+var cvoc_lc_projectSelector = "span[data-cvoc-protocol='localcontexts']"
+var cvoc_lc_projectInputSelector = "input[data-cvoc-protocol='localcontexts']"
+
+var cvoc_lc_lcBaseUrl = "https://localcontextshub.org"
+
+var cvoc_lc_seach_minimumInputLength = 4
+var cvoc_lc_search_delay = 500
+
+$(document).ready(() => {
+ // console.log("doc ready")
+ cvoc_lc_viewProject()
+ cvoc_lc_editProject()
+})
+
+function cvoc_lc_buildLCProjectPopup(project) {
+
+ const createItemImage = (notice_label) => {
+ return `
+
+

+
${notice_label.name}
+
+
`
+ }
+
+ // console.log(project)
+ const notices = project.notice || []
+ const labels = (project.tk_labels || []).concat(project.bc_labels || [])
+ const lcWrapper = `
+
+ ${notices.map(createItemImage).join("")}
+ ${labels.map(createItemImage).join("")}
+
+
+
`
+
+ const e = document.createElement("div")
+ e.innerHTML = lcWrapper
+ return e
+}
+
+async function cvoc_lc_LoadOrFetch(fullUrl) {
+ const lc_project_url_base = `${cvoc_lc_lcBaseUrl}/projects/`
+ let lc_uuid = fullUrl
+ if (lc_uuid.startsWith(lc_project_url_base)) {
+ lc_uuid = lc_uuid.substring(lc_project_url_base.length)
+ }
+ const inStorage = sessionStorage.getItem(lc_uuid)
+ if (inStorage) {
+ return Promise.resolve(JSON.parse(inStorage))
+ }
+ const response = await fetch(`${cvoc_lc_lcBaseUrl}/api/v1/projects/${lc_uuid}`)
+ const project = await response.json()
+ sessionStorage.setItem(lc_uuid, JSON.stringify(project))
+ return Promise.resolve(project)
+}
+
+async function cvoc_lc_viewProject() {
+ // console.log("cvoc_lc_viewProject")
+ const jqSelect = $(cvoc_lc_projectSelector)
+ if (jqSelect.length === 0)
+ return
+ const projectField = jqSelect[0]
+ if (projectField.getAttribute("expanded") === "true") {
+ return
+ }
+ projectField.setAttribute("expanded", "true")
+ const fullUrl = projectField.textContent
+ const project = await cvoc_lc_LoadOrFetch(fullUrl)
+ let lcContainerElement = cvoc_lc_buildLCProjectPopup(project)
+ projectField.after(lcContainerElement)
+ projectField.innerHTML = `${fullUrl}`
+}
+
+async function cvoc_lc_editProject() {
+ var projectInput = $(cvoc_lc_projectInputSelector)
+ if (projectInput.length === 0)
+ return
+ projectInput.hide()
+ let select_ = document.createElement("select")
+ select_.id = "localcontextsProjectInputSelector"
+ select_.classList = "form-control add-resource select2"
+ select_.setAttribute("aria-hidden", true)
+ select_.setAttribute("tabindex", -1)
+ projectInput.after(select_)
+ let placeholder = ""
+ if (projectInput[0].value !== "") {
+ const project = await cvoc_lc_LoadOrFetch(projectInput[0].value)
+ // console.log(project)
+ placeholder = project.title
+ } else {
+ placeholder = "Search for a project by name or paste the exact project ID"
+ }
+ // todo we have: projectInput.value
+ $(select_).select2({
+ placeholder: placeholder,
+ minimumInputLength: cvoc_lc_seach_minimumInputLength,
+ ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
+ url: (params) => {
+ // check if the user posted a uuid (lc project id) and
+ const uuid_regex = new RegExp("([a-f 0-9]{8})-([a-f 0-9]{4})-([a-f 0-9]{4})-([a-f 0-9]{4})-([a-f 0-9]{12})")
+ if (uuid_regex.test(params.term)) {
+ //await get_or_fetch(params.term)
+ return `${cvoc_lc_lcBaseUrl}/api/v1/projects/${params.term}`
+ } else {
+ return `${cvoc_lc_lcBaseUrl}/api/v1/projects/?search=${params.term}`
+ }
+ },
+ data: {},
+ dataType: 'json',
+ delay: cvoc_lc_search_delay,
+ processResults: function (data, page) { // parse the results into the format expected by Select2.
+ // console.log("processResults", data)
+ // check if we did the search by uuid
+ if (data.results === undefined && data.unique_id !== undefined) {
+ return {
+ results: [{id: data.unique_id, text: data.title}]
+ }
+ }
+ // normal search results
+ return {
+ results: data.results.map(e => ({
+ id: e.unique_id, text: e.title
+ }))
+ }
+ },
+ cache: true
+ }
+ })
+
+ $(select_).on('select2:select', function (e) {
+ let data = e.params.data
+ // console.log(data)
+ projectInput.val(`${cvoc_lc_lcBaseUrl}/projects/${data.id}`)
+ cvoc_lc_LoadOrFetch(data.id)
+ })
+}
+