{
	"translatorID": "14763d24-8ba0-45df-8f52-b8d1108e7ac9",
	"translatorType": 2,
	"label": "Zotero RDF",
	"creator": "Simon Kornblith",
	"target": "rdf",
	"minVersion": "1.0.0b4.r1",
	"maxVersion": "",
	"priority": 25,
	"configOptions": {
		"getCollections": "true",
		"dataMode": "rdf/xml"
	},
	"displayOptions": {
		"exportNotes": true,
		"exportFileData": false
	},
	"inRepository": true,
	"lastUpdated": "2021-01-25 06:49:57"
}

var addedCollections = new Set();
var item;
var rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

var n = {
	bib:"http://purl.org/net/biblio#",
	dc:"http://purl.org/dc/elements/1.1/",
	dcterms:"http://purl.org/dc/terms/",
	prism:"http://prismstandard.org/namespaces/1.2/basic/",
	foaf:"http://xmlns.com/foaf/0.1/",
	vcard:"http://nwalsh.com/rdf/vCard#",
	vcard2:"http://www.w3.org/2006/vcard/ns#",	// currently used only for NSF, but is probably
												// very similar to the nwalsh vcard ontology in a
												// different namespace
	link:"http://purl.org/rss/1.0/modules/link/",
	z:"http://www.zotero.org/namespaces/export#"
};

function generateRelations(resource, relations) {
	for (let predicate in relations) {
		if (predicate == 'dc:relation') {
			for (let uri of relations[predicate]) {
				if (itemResources[uri]) {
					Zotero.RDF.addStatement(resource, n.dc + "relation", itemResources[uri], false);
				}
			}
		}
	}
}

function generateTags(resource, tags) {
	Zotero.debug("processing tags");
	for (var i=0; i<tags.length; i++) {
		var tag = tags[i];
		if (tag.type == 1) {
			var tagResource = Zotero.RDF.newResource();
			// set tag type and value
			Zotero.RDF.addStatement(tagResource, rdf+"type", n.z+"AutomaticTag", false);
			Zotero.RDF.addStatement(tagResource, rdf+"value", tag.tag, true);
			// add relationship to resource
			Zotero.RDF.addStatement(resource, n.dc+"subject", tagResource, false);
		} else {
			Zotero.RDF.addStatement(resource, n.dc+"subject", tag.tag, true);
		}
	}
}

function generateCollection(collection) {
	var collectionResource = "#collection_"+collection.id;
	Zotero.RDF.addStatement(collectionResource, rdf+"type", n.z+"Collection", false);
	Zotero.RDF.addStatement(collectionResource, n.dc+"title", collection.name, true);
	
	var children = collection.children ? collection.children : collection.descendents;
	if (!children) return;
	for (var i=0; i<children.length; i++) {
		var child = children[i];
		// add child list items
		if (child.type == "collection") {
			Zotero.RDF.addStatement(collectionResource, n.dcterms+"hasPart", "#collection_"+child.id, false);
			addedCollections.add(child.id);
			// do recursive processing of collections
			generateCollection(child);
		} else if (itemResources[child.id]) {
			Zotero.RDF.addStatement(collectionResource, n.dcterms+"hasPart", itemResources[child.id], false);
		}
	}
}

/**
 * Get display title
 * Analogous to getDisplayTitle in item.js, but returns null if no display title distinct from
 * title property
 */
function getDisplayTitle(item) {
	if (!item.title && (item.itemType == "interview" || item.itemType == "letter")) {
		var participants = [];
		for (var i=0; i<item.creators.length; i++) {
			var creator = item.creators[i];
			if (item.itemType == "letter" && creator.creatorType == "recipient" ||
					item.itemType == "interview" && creator.creatorType == "interviewer") {
			   participants.push(creator);
			}
		}
		
		var displayTitle = "["+(item.itemType == "letter" ? "Letter" : "Interview");
		if (participants.length) {
			//var names = [creator.firstName ? creator.firstName+" "+creator.lastName : creator.lastName
			var names = [];
			for (var i=0; i<participants.length; i++) {
				names.push(participants[i].lastName);
			}
			
			displayTitle += (item.itemType == "letter" ? " to " : " of ")+names[0];
			
			if (participants.length == 2) {
				displayTitle += " and "+names[1];
			} else if (participants.length == 3) {
				displayTitle += ", "+names[1]+", and "+names[2];
			} else if (participants.length > 3) {
				displayTitle += " et al.";
			}
		}
		
		return displayTitle+"]";
	} if (item.itemType == "case" && item.title && item.reporter) { // 'case' itemTypeID
		return item.title+' (' + item.reporter + ')';
	}
	return null;
}

function generateItem(item, zoteroType, resource) {
	var container = null;
	var containerElement = null;
	
	/** CORE FIELDS **/
	
	// type
	var type = null;
	if (zoteroType == "book") {
		type = n.bib+"Book";
	} else if (zoteroType == "bookSection") {
		type = n.bib+"BookSection";
		container = n.bib+"Book";
	} else if (zoteroType == "journalArticle") {
		type = n.bib+"Article";
		container = n.bib+"Journal";
	} else if (zoteroType == "magazineArticle") {
		type = n.bib+"Article";
		container = n.bib+"Periodical";
	} else if (zoteroType == "newspaperArticle") {
		type = n.bib+"Article";
		container = n.bib+"Newspaper";
	} else if (zoteroType == "thesis") {
		type = n.bib+"Thesis";
	} else if (zoteroType == "letter") {
		type = n.bib+"Letter";
	} else if (zoteroType == "manuscript") {
		type = n.bib+"Manuscript";
	} else if (zoteroType == "interview") {
		type = n.bib+"Interview";
	} else if (zoteroType == "film") {
		type = n.bib+"MotionPicture";
	} else if (zoteroType == "artwork") {
		type = n.bib+"Illustration";
	} else if (zoteroType == "webpage") {
		type = n.bib+"Document";
		container = n.z+"Website";
	} else if (zoteroType == "note") {
		type = n.bib+"Memo";
		if (!Zotero.getOption("exportNotes")) {
			return;
		}
	} else if (zoteroType == "attachment") {
		type = n.z+"Attachment";
	} else if (zoteroType == "report") {
		type = n.bib+"Report";
	} else if (zoteroType == "bill") {
		type = n.bib+"Legislation";
	} else if (zoteroType == "case") {
		type = n.bib+"Document";	// ??
		container = n.bib+"CourtReporter";
	} else if (zoteroType == "hearing") {
		type = n.bib+"Report";
	} else if (zoteroType == "patent") {
		type = n.bib+"Patent";
	} else if (zoteroType == "statute") {
		type = n.bib+"Legislation";
	} else if (zoteroType == "email") {
		type = n.bib+"Letter";
	} else if (zoteroType == "map") {
		type = n.bib+"Image";
	} else if (zoteroType == "blogPost") {
		type = n.bib+"Document";
		container = n.z+"Blog";
	} else if (zoteroType == "instantMessage") {
		type = n.bib+"Letter";
	} else if (zoteroType == "forumPost") {
		type = n.bib+"Document";
		container = n.z+"Forum";
	} else if (zoteroType == "audioRecording") {
		type = n.bib+"Recording";
	} else if (zoteroType == "presentation") {
		type = n.bib+"ConferenceProceedings";
	} else if (zoteroType == "videoRecording") {
		type = n.bib+"Recording";
	} else if (zoteroType == "tvBroadcast") {
		type = n.bib+"Recording";
	} else if (zoteroType == "radioBroadcast") {
		type = n.bib+"Recording";
	} else if (zoteroType == "podcast") {
		type = n.bib+"Recording";
	} else if (zoteroType == "computerProgram") {
		type = n.bib+"Data";
	} else if (zoteroType == "encyclopediaArticle"
		|| zoteroType == "dictionaryEntry") {
		container = n.bib+"Book";
	} else if (zoteroType == "conferencePaper") {
		container = n.bib+"Journal";
	}
	
	if (type) {
		Zotero.RDF.addStatement(resource, rdf+"type", type, false);
	}
	Zotero.RDF.addStatement(resource, n.z+"itemType", zoteroType, true);
	
	// generate section
	if (item.section) {
		var section = Zotero.RDF.newResource();
		// set section type
		Zotero.RDF.addStatement(section, rdf+"type", n.bib+"Part", false);
		// set section title
		Zotero.RDF.addStatement(section, n.dc+"title", item.section, true);
		// add relationship to resource
		Zotero.RDF.addStatement(resource, n.dcterms+"isPartOf", section, false);
	}
	
	// generate container
	if (container) {
		var testISSN = "urn:issn:"+encodeURI(item.ISSN);
		if (item.ISSN && !Zotero.RDF.getArcsIn(testISSN)) {
			// use ISSN as container URI if no other item is
			containerElement = testISSN;
		} else {
			containerElement = Zotero.RDF.newResource();
		}
		// attach container to section (if exists) or resource
		Zotero.RDF.addStatement((section ? section : resource), n.dcterms+"isPartOf", containerElement, false);
		// add container type
		Zotero.RDF.addStatement(containerElement, rdf+"type", container, false);
	}
	
	// generate series
	if (item.series || item.seriesTitle || item.seriesText || item.seriesNumber) {
		var series = Zotero.RDF.newResource();
		// set series type
		Zotero.RDF.addStatement(series, rdf+"type", n.bib+"Series", false);
		// add relationship to resource
		Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.dcterms+"isPartOf", series, false);
	}
	
	// generate publisher
	// BEGIN NSF
	if (zoteroType == "nsfReviewer") {
		var organization = Zotero.RDF.newResource();
		Zotero.RDF.addStatement(organization, rdf+"type", n.vcard2+"Organization", false);
		Zotero.RDF.addStatement(resource, n.vcard2+"org", organization, false);
	} else {
	// END NSF
		if (item.publisher || item.distributor || item.label || item.company || item.institution || item.place) {
			var organization = Zotero.RDF.newResource();
			// set organization type
			Zotero.RDF.addStatement(organization, rdf+"type", n.foaf+"Organization", false);
			// add relationship to resource
			Zotero.RDF.addStatement(resource, n.dc+"publisher", organization, false);
		}
	}
	
	var typeProperties = ["reportType", "videoRecordingType", "letterType",
							"manuscriptType", "mapType", "thesisType", "websiteType",
							"audioRecordingType", "presentationType", "postType",
							"audioFileType"];
	var ignoreProperties = ["itemID", "itemType", "firstCreator", "dateAdded",
							"dateModified", "section", "sourceItemID"];
	
	// creators
	if (item.creators) {			// authors/editors/contributors
		var creatorContainers = new Object();
		
		// not yet in biblio
		var biblioCreatorTypes = ["author", "editor", "contributor"];
		
		for (var j in item.creators) {
			var creator = Zotero.RDF.newResource();
			Zotero.RDF.addStatement(creator, rdf+"type", n.foaf+"Person", false);
			// gee. an entire vocabulary for describing people, and these aren't even
			// standardized in it. oh well. using them anyway.
			Zotero.RDF.addStatement(creator, n.foaf+"surname", item.creators[j].lastName, true);
			if (item.creators[j].firstName) {
				Zotero.RDF.addStatement(creator, n.foaf+"givenName", item.creators[j].firstName, true);
			}
			
			if (biblioCreatorTypes.indexOf(item.creators[j].creatorType) != -1) {
				var cTag = n.bib+item.creators[j].creatorType+"s";
			} else {
				var cTag = n.z+item.creators[j].creatorType+"s";
			}
			
			if (!creatorContainers[cTag]) {
				var creatorResource = Zotero.RDF.newResource();
				// create new seq for author type
				creatorContainers[cTag] = Zotero.RDF.newContainer("seq", creatorResource);
				// attach container to resource
				Zotero.RDF.addStatement(resource, cTag, creatorResource, false);
			}
			Zotero.RDF.addContainerElement(creatorContainers[cTag], creator, false);
		}
	}
	
	// notes
	if (item.notes && Zotero.getOption("exportNotes")) {
		for (let note of item.notes) {
			let noteResource = itemResources[note.itemID];
			
			// add note tag
			Zotero.RDF.addStatement(noteResource, rdf+"type", n.bib+"Memo", false);
			// add note item.notes
			Zotero.RDF.addStatement(noteResource, rdf + "value", note.note, true);
			// add relationship between resource and note
			Zotero.RDF.addStatement(resource, n.dcterms+"isReferencedBy", noteResource, false);
			
			// Add note relations to RDF
			if (note.relations) generateRelations(noteResource, note.relations);
			generateTags(noteResource, note.tags);
		}
	}
	
	// child attachments
	if (item.attachments) {
		for (var i=0; i<item.attachments.length; i++) {
			var attachment = item.attachments[i];
			var attachmentResource = itemResources[attachment.itemID];
			Zotero.RDF.addStatement(resource, n.link+"link", attachmentResource, false);
			generateItem(attachment, "attachment", attachmentResource);
		}
	}
	
	// relative file path for attachment items
	if (item.defaultPath) {	// For Zotero 3.0
		item.saveFile(item.defaultPath, true);
		Zotero.RDF.addStatement(resource, rdf+"resource", item.defaultPath, false);
	} else if (item.path) {	// For Zotero 2.1
		Zotero.RDF.addStatement(resource, rdf+"resource", item.path, false);
	}
    
	// Related items and tags
	if (item.relations) generateRelations(resource, item.relations);
	if (item.tags) generateTags(resource, item.tags);
	
	for (var property in item.uniqueFields) {
		var value = item[property];
		if (!value) continue;
		
		if (property == "title") {					// title
			// BEGIN NSF
			if (zoteroType == "nsfReviewer") {
				Zotero.RDF.addStatement(resource, n.vcard2+"fn", value, true);
			} else {
			// END NSF
				Zotero.RDF.addStatement(resource, n.dc+"title", value, true);
			}
		} else if (property == "source") {			// authors/editors/contributors
			Zotero.RDF.addStatement(resource, n.dc+"source", value, true);
		} else if (property == "url") {				// url
			// BEGIN NSF
			if (item.homepage) {
				Zotero.RDF.addStatement(resource, n.vcard2+"url", value, false);
			} else {
			// END NSF
				// add url as identifier
				var term = Zotero.RDF.newResource();
				// set term type
				Zotero.RDF.addStatement(term, rdf+"type", n.dcterms+"URI", false);
				// set url value
				Zotero.RDF.addStatement(term, rdf+"value", value, true);
				// add relationship to resource
				Zotero.RDF.addStatement(resource, n.dc+"identifier", term, false);
			}
		} else if (property == "accessionNumber") {	// accessionNumber as generic ID
			Zotero.RDF.addStatement(resource, n.dc+"identifier", value, true);
		} else if (property == "rights") {			// rights
			Zotero.RDF.addStatement(resource, n.dc+"rights", value, true);
		} else if (property == "edition" ||			// edition
		          property == "version") {			// version
			Zotero.RDF.addStatement(resource, n.prism+"edition", value, true);
		} else if (property == "date") {				// date
			if (item.dateSent) {
				Zotero.RDF.addStatement(resource, n.dcterms+"dateSubmitted", value, true);
			} else {
				Zotero.RDF.addStatement(resource, n.dc+"date", value, true);
			}
		} else if (property == "accessDate") {		// accessDate
			Zotero.RDF.addStatement(resource, n.dcterms+"dateSubmitted", value, true);
		} else if (property == "issueDate") {		// issueDate
			Zotero.RDF.addStatement(resource, n.dcterms+"issued", value, true);
		} else if (property == "pages") {			// pages
			// not yet part of biblio, but should be soon
			Zotero.RDF.addStatement(resource, n.bib+"pages", value, true);
		} else if (property == "extra") {			// extra
			Zotero.RDF.addStatement(resource, n.dc+"description", value, true);
		} else if (property == "mimeType") {			// mimeType
			Zotero.RDF.addStatement(resource, n.link+"type", value, true);
		} else if (property == "charset") {			// charset
			Zotero.RDF.addStatement(resource, n.link+"charset", value, true);
		// THE FOLLOWING ARE ALL PART OF THE CONTAINER
		} else if (property == "ISSN") {				// ISSN
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.dc+"identifier", "ISSN "+value, true);
		} else if (property == "ISBN") {				// ISBN
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.dc+"identifier", "ISBN "+value, true);
		} else if (property == "DOI") {				// DOI
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.dc+"identifier", "DOI "+value, true);
		} else if (property == "publicationTitle" ||	// publicationTitle
		          property == "reporter") {			// reporter
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.dc+"title", value, true);
		} else if (property == "journalAbbreviation") {	// journalAbbreviation
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.dcterms+"alternative", value, true);
		} else if (property == "volume") {			// volume
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.prism+"volume", value, true);
		} else if (property == "issue" ||			// issue
				  property == "number" ||			// number
				  property == "patentNumber") {		// patentNumber
			Zotero.RDF.addStatement((containerElement ? containerElement : resource), n.prism+"number", value, true);
		} else if (property == "callNumber") {
			var term = Zotero.RDF.newResource();
			// set term type
			Zotero.RDF.addStatement(term, rdf+"type", n.dcterms+"LCC", false);
			// set callNumber value
			Zotero.RDF.addStatement(term, rdf+"value", value, true);
			// add relationship to resource
			Zotero.RDF.addStatement(resource, n.dc+"subject", term, false);
		} else if (property == "abstractNote") {
			Zotero.RDF.addStatement(resource, n.dcterms+"abstract", value, true);
		// THE FOLLOWING ARE ALL PART OF THE SERIES
		} else if (property == "series") {			// series
			Zotero.RDF.addStatement(series, n.dc+"title", value, true);
		} else if (property == "seriesTitle") {		// seriesTitle
			Zotero.RDF.addStatement(series, n.dcterms+"alternative", value, true);
		} else if (property == "seriesText") {		// seriesText
			Zotero.RDF.addStatement(series, n.dc+"description", value, true);
		} else if (property == "seriesNumber") {		// seriesNumber
			Zotero.RDF.addStatement(series, n.dc+"identifier", value, true);
		// THE FOLLOWING ARE ALL PART OF THE PUBLISHER
		} else if (property == "publisher" ||		// publisher
		          property == "distributor" ||		// distributor (film)
		          property == "label" ||			// label (audioRecording)
		          property == "company" ||			// company (computerProgram)
		          property == "institution") {		// institution (report)
		    // BEGIN NSF
		    if (zoteroType == "nsfReviewer") {
		    	Zotero.RDF.addStatement(organization, n.vcard2+"organization-name", value, true);
		    } else {
		    // END NSF
				Zotero.RDF.addStatement(organization, n.foaf+"name", value, true);
			}
		} else if (property == "place") {			// place
			var address = Zotero.RDF.newResource();
			// set address type
			Zotero.RDF.addStatement(address, rdf+"type", n.vcard+"Address", false);
			// set address locality
			Zotero.RDF.addStatement(address, n.vcard+"locality", value, true);
			// add relationship to organization
			Zotero.RDF.addStatement(organization, n.vcard+"adr", address, false);
		} else if (property == "archiveLocation") {	// archiveLocation
			Zotero.RDF.addStatement(resource, n.dc+"coverage", value, true);
		} else if (property == "interviewMedium" ||
		          property == "artworkMedium") {	// medium
			Zotero.RDF.addStatement(resource, n.dcterms+"medium", value, true);
		} else if (property == "conferenceName") {
			var conference = Zotero.RDF.newResource();
			// set conference type
			Zotero.RDF.addStatement(conference, rdf+"type", n.bib+"Conference", false);
			// set conference title
			Zotero.RDF.addStatement(conference, n.dc+"title", value, true);
			// add relationship to conference
			Zotero.RDF.addStatement(resource, n.bib+"presentedAt", conference, false);
		} else if (typeProperties.indexOf(property) != -1) {
			Zotero.RDF.addStatement(resource, n.dc+"type", value, true);
		// THE FOLLOWING RELATE TO NOTES
		} else if (property == "note") {
			if (Zotero.getOption("exportNotes")) {
				if (item.itemType == "attachment") {
					Zotero.RDF.addStatement(resource, n.dc+"description", value, true);
				} else if (item.itemType == "note") {
					Zotero.RDF.addStatement(resource, rdf+"value", value, true);
				}
			}
		// BEGIN NSF
		} else if (property == "address") {
			var address = Zotero.RDF.newResource();
			Zotero.RDF.addStatement(address, rdf+"type", n.vcard2+"Address", false);
			Zotero.RDF.addStatement(address, n.vcard2+"label", value, true);
			Zotero.RDF.addStatement(resource, n.vcard2+"adr", address, false);
		} else if (property == "telephone") {
			Zotero.RDF.addStatement(resource, n.vcard2+"tel", value, true);
		} else if (property == "email") {
			Zotero.RDF.addStatement(resource, n.vcard2+"email", value, true);
		} else if (property == "accepted") {
			Zotero.RDF.addStatement(resource, n.dcterms+"dateAccepted", value, true);
		// END NSF
		// THIS CATCHES ALL REMAINING PROPERTIES
		} else if (ignoreProperties.indexOf(property) == -1) {
			Zotero.debug("Zotero RDF: using Zotero namespace for property "+property);
			Zotero.RDF.addStatement(resource, n.z+property, value, true);
		}
	}
	
	var displayTitle = getDisplayTitle(item);
	if (displayTitle) Zotero.RDF.addStatement(resource, n.z+"displayTitle", displayTitle, true);
}

function doExport() {
	// add namespaces
	for (var i in n) {
		Zotero.RDF.addNamespace(i, n[i]);
	}
	
	// leave as global
	itemResources = new Array();
	
	// keep track of resources already assigned (in case two book items have the
	// same ISBN, or something like that)
	var usedResources = new Array();
	
	var items = new Array();
	
	// first, map each ID to a resource
	while (item = Zotero.nextItem()) {
		items.push(item);
		Zotero.debug(item);
		
		var testISBN = "urn:isbn:"+encodeURI(item.ISBN);
		if (item.ISBN && !usedResources[testISBN]) {
			itemResources[item.itemID] = itemResources[item.uri] = testISBN;
			usedResources[itemResources[item.itemID]] = true;
		} else if (item.itemType != "attachment" && item.url && !usedResources[item.url]) {
			itemResources[item.itemID] = itemResources[item.uri] = item.url;
			usedResources[itemResources[item.itemID]] = true;
		} else {
			// just specify a node ID
			itemResources[item.itemID] = itemResources[item.uri] = "#item_" + item.itemID;
		}
		
		if (item.notes) {
			for (var j in item.notes) {
				itemResources[item.notes[j].itemID] = itemResources[item.notes[j].uri] = "#item_" + item.notes[j].itemID;
			}
		}
		
		if (item.attachments) {
			for (var i=0; i<item.attachments.length; i++) {
				var attachment = item.attachments[i];
				// just specify a node ID
				itemResources[attachment.itemID] = itemResources[attachment.uri] = "#item_" + attachment.itemID;
			}
		}
	}
	
	for (var i=0; i<items.length; i++) {
		var item = items[i];
		// these items are global
		generateItem(item, item.itemType, itemResources[item.itemID]);
	}
	
	/** RDF COLLECTION STRUCTURE **/
	var collection;
	while (collection = Zotero.nextCollection()) {
		// Skip collections already added via recursion in generateCollection()
		// TODO: Remove after everyone has 5.0.96, which fixes this with b3220e83b
		if (addedCollections.has(collection.id)) continue;
		
		generateCollection(collection);
	}
}
