Skip to content

Commit 0cf7aad

Browse files
committed
Merge pull request #57 from UNC-Libraries/vocabularies
Vocabularies
2 parents 6ddaf0c + 81b00fc commit 0cf7aad

File tree

6 files changed

+274
-12
lines changed

6 files changed

+274
-12
lines changed

demo/examples/marc_relators.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[ "Abridger", "Art copyist", "Actor", "Art director", "Adapter", "Author of afterword, colophon, etc.", "Analyst", "Animator", "Annotator", "Bibliographic antecedent", "Appellee", "Appellant", "Applicant", "Author in quotations or text abstracts", "Architect", "Artistic director", "Arranger", "Artist", "Assignee", "Associated name", "Autographer", "Attributed name", "Auctioneer", "Author of dialog", "Author of introduction, etc.", "Screenwriter", "Author", "Binding designer", "Bookjacket designer", "Book designer", "Book producer", "Blurb writer", "Binder", "Bookplate designer", "Broadcaster", "Braille embosser", "Bookseller", "Caster", "Conceptor", "Choreographer", "Client", "Calligrapher", "Colorist", "Collotyper", "Commentator", "Composer", "Compositor", "Conductor", "Cinematographer", "Censor", "Contestant-appellee", "Collector", "Compiler", "Conservator", "Collection registrar", "Contestant", "Contestant-appellant", "Court governed", "Cover designer", "Copyright claimant", "Complainant-appellee", "Copyright holder", "Complainant", "Complainant-appellant", "Creator", "Correspondent", "Corrector", "Court reporter", "Consultant", "Consultant to a project", "Costume designer", "Contributor", "Contestee-appellee", "Cartographer", "Contractor", "Contestee", "Contestee-appellant", "Curator", "Commentator for written text", "Distribution place", "Defendant", "Defendant-appellee", "Defendant-appellant", "Degree granting institution", "Degree supervisor", "Dissertant", "Delineator", "Dancer", "Donor", "Depicted", "Depositor", "Draftsman", "Director", "Designer", "Distributor", "Data contributor", "Dedicatee", "Data manager", "Dedicator", "Dubious author", "Editor of compilation", "Editor of moving image work", "Editor", "Engraver", "Electrician", "Electrotyper", "Engineer", "Enacting jurisdiction", "Etcher", "Event place", "Expert", "Facsimilist", "Film distributor", "Field director", "Film editor", "Film director", "Filmmaker", "Former owner", "Film producer", "Funder", "First party", "Forger", "Geographic information specialist", "Host institution", "Honoree", "Host", "Illustrator", "Illuminator", "Inscriber", "Inventor", "Issuing body", "Instrumentalist", "Interviewee", "Interviewer", "Judge", "Jurisdiction governed", "Laboratory", "Librettist", "Laboratory director", "Lead", "Libelee-appellee", "Libelee", "Lender", "Libelee-appellant", "Lighting designer", "Libelant-appellee", "Libelant", "Libelant-appellant", "Landscape architect", "Licensee", "Licensor", "Lithographer", "Lyricist", "Music copyist", "Metadata contact", "Medium", "Manufacture place", "Manufacturer", "Moderator", "Monitor", "Marbler", "Markup editor", "Musical director", "Metal-engraver", "Minute taker", "Musician", "Narrator", "Opponent", "Originator", "Organizer", "Onscreen presenter", "Other", "Owner", "Panelist", "Patron", "Publishing director", "Publisher", "Project director", "Proofreader", "Photographer", "Platemaker", "Permitting agency", "Production manager", "Printer of plates", "Papermaker", "Puppeteer", "Praeses", "Process contact", "Production personnel", "Presenter", "Performer", "Programmer", "Printmaker", "Production company", "Producer", "Production place", "Production designer", "Printer", "Provider", "Patent applicant", "Plaintiff-appellee", "Plaintiff", "Patent holder", "Plaintiff-appellant", "Publication place", "Rubricator", "Recordist", "Recording engineer", "Addressee", "Radio director", "Redaktor", "Renderer", "Researcher", "Reviewer", "Radio producer", "Repository", "Reporter", "Responsible party", "Respondent-appellee", "Restager", "Respondent", "Restorationist", "Respondent-appellant", "Research team head", "Research team member", "Scientific advisor", "Scenarist", "Sculptor", "Scribe", "Sound designer", "Secretary", "Stage director", "Signer", "Supporting host", "Seller", "Singer", "Speaker", "Sponsor", "Second party", "Surveyor", "Set designer", "Setting", "Storyteller", "Stage manager", "Standards body", "Stereotyper", "Technical director", "Teacher", "Thesis advisor", "Television director", "Television producer", "Transcriber", "Translator", "Type designer", "Typographer", "University place", "Voice actor", "Videographer", "Writer of added commentary", "Writer of added lyrics", "Writer of accompanying material", "Writer of added text", "Woodcutter", "Wood engraver", "Writer of introduction", "Witness", "Writer of preface", "Writer of supplementary textual content" ]

demo/relator.html

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
5+
<link rel="stylesheet" href="stylesheets/reset.css" type="text/css" />
6+
<link rel="stylesheet" href="stylesheets/demo.css" type="text/css" />
7+
<link rel="stylesheet" href="../css/jquery.xmleditor.css" type="text/css" />
8+
<script src="../lib/ace/src-min/ace.js"></script>
9+
<script src="../lib/jquery.min.js"></script>
10+
<script src="../lib/jquery-ui.min.js"></script>
11+
<script src="../lib/json2.js"></script>
12+
<script src="../lib/cycle.js"></script>
13+
<script src="../lib/jquery.autosize-min.js"></script>
14+
<script src="../xsd/xsd2json.js"></script>
15+
<script src="../jquery.xmleditor.js"></script>
16+
<style>
17+
.ui-autocomplete {
18+
width: 200px;
19+
line-height: 24px;
20+
}
21+
</style>
22+
</head>
23+
<body>
24+
25+
<div id="xml_editor">
26+
&lt;mods xmlns=&quot;http://www.loc.gov/mods/v3&quot;&gt;
27+
&lt;name&gt;
28+
&lt;role&gt;
29+
&lt;roleTerm /&gt;
30+
&lt;/role&gt;
31+
&lt;/name&gt;
32+
&lt;/mods&gt;
33+
</div>
34+
<script>
35+
$(function() {
36+
var availableTags = "examples/marc_relators.json";
37+
var extractor = new Xsd2Json("mods-3-4.xsd", {
38+
"schemaURI" : "examples/mods-3-4/"
39+
});
40+
$("#xml_editor").xmlEditor(
41+
{
42+
schema : extractor.getSchema(),
43+
vocabularyConfigs : {
44+
vocabularies : {
45+
"marcRelators" : {
46+
"url" : "examples/marc_relators.json"
47+
},
48+
"departments" : {
49+
"values" : ["blah", "Department of Sonoeland Studies"]
50+
}/*,
51+
"fast" : {
52+
url : "example.com",
53+
type : "query",
54+
keypress : function(input) {
55+
56+
}
57+
}*/
58+
},
59+
xpathSelectors: {
60+
"mods:mods/mods:name//mods:roleTerm" : "marcRelators"
61+
},
62+
xpathNamespaces: {
63+
"mods" : "http://www.loc.gov/mods/v3"
64+
}
65+
/*cssSelectors: {
66+
"roleTerm": availableTags
67+
}*/
68+
}
69+
});
70+
});
71+
</script>
72+
</body>
73+
</html>

jquery.xmleditor.js

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ $.widget( "xml.xmlEditor", {
317317
});
318318
}
319319

320+
// Load any external vocabularies
321+
this.loadVocabularies(this.options.vocabularyConfigs);
322+
320323
// Start loading the document for editing
321324
this.loadDocument(this.options.ajaxOptions, localXMLContent);
322325
},
@@ -416,6 +419,37 @@ $.widget( "xml.xmlEditor", {
416419
}
417420
},
418421

422+
loadVocabularies : function(vocabularyConfigs) {
423+
var self = this;
424+
this.loadingVocabs = 0;
425+
426+
if (!this.options.vocabularyConfigs || !this.options.vocabularyConfigs.vocabularies) {
427+
return;
428+
}
429+
430+
$.each(this.options.vocabularyConfigs.vocabularies, function(vocabName, vocabInfo) {
431+
if ("url" in vocabInfo) {
432+
self.loadingVocabs++;
433+
}
434+
});
435+
436+
$.each(this.options.vocabularyConfigs.vocabularies, function(vocabName, vocabInfo) {
437+
if ("url" in vocabInfo) {
438+
$.ajax({
439+
url : vocabInfo["url"],
440+
type : "GET",
441+
dataType : "json"
442+
}).done(function(data){
443+
vocabInfo.values = data;
444+
self.loadingVocabs--;
445+
if (self.loadingVocabs == 0) {
446+
self._everythingReady();
447+
}
448+
});
449+
}
450+
});
451+
},
452+
419453
_templating : function() {
420454
var dialog;
421455
var self = this;
@@ -434,7 +468,7 @@ $.widget( "xml.xmlEditor", {
434468
this.undoHistory.setStateChangeEvent(function() {
435469
self.refreshDisplay();
436470
});
437-
this._documentAndSchemaReady();
471+
this._everythingReady();
438472
},
439473

440474
// Schema object loaded event
@@ -444,14 +478,14 @@ $.widget( "xml.xmlEditor", {
444478
}
445479
this.schemaTree = new SchemaTree(this.schema);
446480
this.schemaTree.build();
447-
this._documentAndSchemaReady();
481+
this._everythingReady();
448482
},
449483

450484
// Performs initialization of editor after rejoining document and schema loading workflows
451485
// to support asychronous/multithreaded loading
452-
_documentAndSchemaReady : function() {
486+
_everythingReady : function() {
453487
// Join back up asynchronous loading of document and schema
454-
if (!this.schemaTree || !this.xmlState)
488+
if (!this.schemaTree || !this.xmlState || this.loadingVocabs != 0)
455489
return;
456490

457491
this.targetPrefix = this.xmlState.namespaces.getNamespacePrefix(this.options.targetNS);
@@ -1165,6 +1199,58 @@ $.widget( "xml.xmlEditor", {
11651199
$("#" + xmlMenuHeaderPrefix + this.toString()).removeClass("disabled").data("menuItemData").enabled = true;
11661200
else $("#" + xmlMenuHeaderPrefix + this.toString()).addClass("disabled").data("menuItemData").enabled = false;
11671201
});
1202+
},
1203+
1204+
// Finds the associated vocabulary for an xml element
1205+
getVocabulary: function(xmlElement) {
1206+
if (!this.options.vocabularyConfigs) {
1207+
return null;
1208+
}
1209+
var self = this;
1210+
var matchingVocab = null;
1211+
var xmlDocument = this.xmlState.xml;
1212+
if (this.options.vocabularyConfigs.cssSelectors) {
1213+
$.each(this.options.vocabularyConfigs.cssSelectors, function(selector, vocabulary){
1214+
// find elements in xml document that match this vocabulary's selector
1215+
var matches = $(selector, xmlDocument);
1216+
1217+
// Check to see if our xmlElement was in the matching list
1218+
for (var i = 0; i < matches.length; i++) {
1219+
if (xmlElement.xmlNode[0] === matches[i]) {
1220+
matchingVocab = vocabulary;
1221+
return false;
1222+
}
1223+
}
1224+
});
1225+
}
1226+
1227+
var nsResolver = function nsResolver(prefix) {
1228+
return self.options.vocabularyConfigs.xpathNamespaces[prefix] || null;
1229+
};
1230+
nsResolver.lookupNamespaceURI = nsResolver;
1231+
1232+
if (this.options.vocabularyConfigs.xpathSelectors) {
1233+
$.each(this.options.vocabularyConfigs.xpathSelectors, function(selector, vocabulary){
1234+
// find elements in xml document that match this vocabulary's selector
1235+
var matchesIterate = xmlDocument[0].evaluate(selector, xmlDocument[0], nsResolver, null, null);
1236+
1237+
// Check to see if our xmlElement was in the matching 0list
1238+
var match = matchesIterate.iterateNext()
1239+
while (match) {
1240+
if (xmlElement.xmlNode[0] === match) {
1241+
matchingVocab = vocabulary;
1242+
return false;
1243+
}
1244+
match = matchesIterate.iterateNext();
1245+
}
1246+
});
1247+
}
1248+
1249+
if (!matchingVocab || !(matchingVocab in this.options.vocabularyConfigs.vocabularies)) {
1250+
return null;
1251+
}
1252+
1253+
return this.options.vocabularyConfigs.vocabularies[matchingVocab];
11681254
}
11691255
});
11701256
function AbstractXMLObject(objectType, editor) {
@@ -4397,7 +4483,9 @@ XMLElement.prototype.renderChild = function(childNode, recursive) {
43974483
};
43984484

43994485
XMLElement.prototype.renderText = function(childNode, prepend) {
4400-
var textNode = new XMLTextNode(childNode, this.objectType.type, this.editor);
4486+
var vocabulary = this.editor.getVocabulary(this);
4487+
4488+
var textNode = new XMLTextNode(childNode, this.objectType.type, this.editor, vocabulary);
44014489
textNode.render(this, prepend);
44024490

44034491
this.nodeCount++;
@@ -5064,14 +5152,15 @@ XMLTemplates.prototype.loadEvents = function(dialog) {
50645152
self.processForm();
50655153
});
50665154
};
5067-
function XMLTextNode(textNode, dataType, editor) {
5155+
function XMLTextNode(textNode, dataType, editor, vocabulary) {
50685156
var textType = {
50695157
text : true,
50705158
type : dataType
50715159
};
50725160

50735161
this.textNode = textNode;
50745162
this.xmlNode = $(textNode);
5163+
this.vocabulary = vocabulary;
50755164

50765165
AbstractXMLObject.call(this, textType, editor);
50775166

@@ -5133,6 +5222,11 @@ XMLTextNode.prototype.render = function(parentElement, prepend) {
51335222
this.textInput = AbstractXMLObject.prototype.createElementInput.call(this,
51345223
this.domNodeID + "_text", textValue, inputColumn);
51355224
this.textInput.addClass('element_text');
5225+
if (this.vocabulary && this.vocabulary.values) {
5226+
this.textInput.autocomplete({
5227+
source : this.vocabulary.values
5228+
});
5229+
}
51365230

51375231
this.deleteButton = document.createElement('div');
51385232
this.deleteButton.className = 'xml_delete';

src/jquery.xmleditor.js

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ $.widget( "xml.xmlEditor", {
317317
});
318318
}
319319

320+
// Load any external vocabularies
321+
this.loadVocabularies(this.options.vocabularyConfigs);
322+
320323
// Start loading the document for editing
321324
this.loadDocument(this.options.ajaxOptions, localXMLContent);
322325
},
@@ -416,6 +419,37 @@ $.widget( "xml.xmlEditor", {
416419
}
417420
},
418421

422+
loadVocabularies : function(vocabularyConfigs) {
423+
var self = this;
424+
this.loadingVocabs = 0;
425+
426+
if (!this.options.vocabularyConfigs || !this.options.vocabularyConfigs.vocabularies) {
427+
return;
428+
}
429+
430+
$.each(this.options.vocabularyConfigs.vocabularies, function(vocabName, vocabInfo) {
431+
if ("url" in vocabInfo) {
432+
self.loadingVocabs++;
433+
}
434+
});
435+
436+
$.each(this.options.vocabularyConfigs.vocabularies, function(vocabName, vocabInfo) {
437+
if ("url" in vocabInfo) {
438+
$.ajax({
439+
url : vocabInfo["url"],
440+
type : "GET",
441+
dataType : "json"
442+
}).done(function(data){
443+
vocabInfo.values = data;
444+
self.loadingVocabs--;
445+
if (self.loadingVocabs == 0) {
446+
self._everythingReady();
447+
}
448+
});
449+
}
450+
});
451+
},
452+
419453
_templating : function() {
420454
var dialog;
421455
var self = this;
@@ -434,7 +468,7 @@ $.widget( "xml.xmlEditor", {
434468
this.undoHistory.setStateChangeEvent(function() {
435469
self.refreshDisplay();
436470
});
437-
this._documentAndSchemaReady();
471+
this._everythingReady();
438472
},
439473

440474
// Schema object loaded event
@@ -444,14 +478,14 @@ $.widget( "xml.xmlEditor", {
444478
}
445479
this.schemaTree = new SchemaTree(this.schema);
446480
this.schemaTree.build();
447-
this._documentAndSchemaReady();
481+
this._everythingReady();
448482
},
449483

450484
// Performs initialization of editor after rejoining document and schema loading workflows
451485
// to support asychronous/multithreaded loading
452-
_documentAndSchemaReady : function() {
486+
_everythingReady : function() {
453487
// Join back up asynchronous loading of document and schema
454-
if (!this.schemaTree || !this.xmlState)
488+
if (!this.schemaTree || !this.xmlState || this.loadingVocabs != 0)
455489
return;
456490

457491
this.targetPrefix = this.xmlState.namespaces.getNamespacePrefix(this.options.targetNS);
@@ -1165,5 +1199,57 @@ $.widget( "xml.xmlEditor", {
11651199
$("#" + xmlMenuHeaderPrefix + this.toString()).removeClass("disabled").data("menuItemData").enabled = true;
11661200
else $("#" + xmlMenuHeaderPrefix + this.toString()).addClass("disabled").data("menuItemData").enabled = false;
11671201
});
1202+
},
1203+
1204+
// Finds the associated vocabulary for an xml element
1205+
getVocabulary: function(xmlElement) {
1206+
if (!this.options.vocabularyConfigs) {
1207+
return null;
1208+
}
1209+
var self = this;
1210+
var matchingVocab = null;
1211+
var xmlDocument = this.xmlState.xml;
1212+
if (this.options.vocabularyConfigs.cssSelectors) {
1213+
$.each(this.options.vocabularyConfigs.cssSelectors, function(selector, vocabulary){
1214+
// find elements in xml document that match this vocabulary's selector
1215+
var matches = $(selector, xmlDocument);
1216+
1217+
// Check to see if our xmlElement was in the matching list
1218+
for (var i = 0; i < matches.length; i++) {
1219+
if (xmlElement.xmlNode[0] === matches[i]) {
1220+
matchingVocab = vocabulary;
1221+
return false;
1222+
}
1223+
}
1224+
});
1225+
}
1226+
1227+
var nsResolver = function nsResolver(prefix) {
1228+
return self.options.vocabularyConfigs.xpathNamespaces[prefix] || null;
1229+
};
1230+
nsResolver.lookupNamespaceURI = nsResolver;
1231+
1232+
if (this.options.vocabularyConfigs.xpathSelectors) {
1233+
$.each(this.options.vocabularyConfigs.xpathSelectors, function(selector, vocabulary){
1234+
// find elements in xml document that match this vocabulary's selector
1235+
var matchesIterate = xmlDocument[0].evaluate(selector, xmlDocument[0], nsResolver, null, null);
1236+
1237+
// Check to see if our xmlElement was in the matching 0list
1238+
var match = matchesIterate.iterateNext()
1239+
while (match) {
1240+
if (xmlElement.xmlNode[0] === match) {
1241+
matchingVocab = vocabulary;
1242+
return false;
1243+
}
1244+
match = matchesIterate.iterateNext();
1245+
}
1246+
});
1247+
}
1248+
1249+
if (!matchingVocab || !(matchingVocab in this.options.vocabularyConfigs.vocabularies)) {
1250+
return null;
1251+
}
1252+
1253+
return this.options.vocabularyConfigs.vocabularies[matchingVocab];
11681254
}
11691255
});

src/xml_element.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,9 @@ XMLElement.prototype.renderChild = function(childNode, recursive) {
382382
};
383383

384384
XMLElement.prototype.renderText = function(childNode, prepend) {
385-
var textNode = new XMLTextNode(childNode, this.objectType.type, this.editor);
385+
var vocabulary = this.editor.getVocabulary(this);
386+
387+
var textNode = new XMLTextNode(childNode, this.objectType.type, this.editor, vocabulary);
386388
textNode.render(this, prepend);
387389

388390
this.nodeCount++;

0 commit comments

Comments
 (0)