{
	"translatorID": "e7243cef-a709-4a46-ba46-1b1318051bec",
	"label": "Citavi 5 XML",
	"creator": "Philipp Zumstein",
	"target": "xml",
	"minVersion": "3.0",
	"maxVersion": "",
	"priority": 100,
	"configOptions": {
		"dataMode": "xml/dom"
	},
	"inRepository": true,
	"translatorType": 1,
	"browserSupport": "gcsi",
	"lastUpdated": "2016-12-29 13:57:06"
}

/*
	***** BEGIN LICENSE BLOCK *****
	
	Copyright © 2016 Philipp Zumstein
	
	This file is part of Zotero.
	
	Zotero is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	Zotero is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU Affero General Public License for more details.
	
	You should have received a copy of the GNU Affero General Public License
	along with Zotero. If not, see <http://www.gnu.org/licenses/>.
	
	***** END LICENSE BLOCK *****
*/


/*
TEST DATA can be found here:
 - Single reference (162 KB) text: https://gist.github.com/zuphilip/02d6478ace4636e4e090e348443c551e
 - Larger project (1221 KB): https://gist.github.com/zuphilip/76ce89ebbdac0386507b36cff3fd499a
*/


function detectImport() {
	var text = Zotero.read(1000);
	return text.indexOf("<CitaviExchangeData") != -1;
}

//This maps the Citavi types to the Zotero types.
//https://www.citavi.com/sub/manual5/en/referencetypeselectiondialog.html
var typeMapping = {
	"ArchiveMaterial" : "manuscript", //Archivgut
	"AudioBook" : "book", //Hörbuch
	"AudioOrVideoDocument" : "document", //Ton- oder Filmdokument
	"Book" : "book", //Buch (Monographie)
	"BookEdited" : "book", //Buch (Sammelwerk)
	"Broadcast" : "tvBroadcast", //Radio- oder Fernsehsendung
	"CollectedWorks" : "book", //Schriften eines Autors
	"ComputerProgram" : "computerProgram", //Software
	"ConferenceProceedings" : "book", //Tagungsband
	"Contribution" : "bookSection", //Beitrag in ...
	"ContributionInLegalCommentary" : "bookSection", //Beitrag in Gesetzeskommentar
	"CourtDecision" : "case", //Gerichtsentscheid
	"File" : "manuscript", //Akte
	"InternetDocument" : "webpage", //Internetdokument
	"InterviewMaterial" : "interview", //Interviewmaterial
	"JournalArticle" : "journalArticle", //Zeitschriftenaufsatz
	"Lecture" : "presentation", //Vortrag
	"LegalCommentary" : "book", //Gesetzeskommentar
	"Manuscript" : "manuscript", //Manuskript
	"Map" : "map", //Geographische Karte
	"Movie" : "videoRecording", //Spielfilm
	"MusicTrack" : "audioRecording", //Musiktitel in ...
	"MusicAlbum" : "audioRecording", //Musikwerk / Musikalbum
	"NewsAgencyReport" : "report", //Agenturmeldung
	"NewspaperArticle" : "newspaperArticle", //Zeitungsartikel
	"Patent" : "patent", //Patentschrift
	"PersonalCommunication" : "email", //Persönliche Mitteilung
	"PressRelease" : "report", //Pressemitteilung
	"RadioPlay" : "podcast", //Hörspiel
	"SpecialIssue" : "book", //Sonderheft, Beiheft
	"Standard" : "report", //Norm
	"StatuteOrRegulation" : "statute", //Gesetz / Verordnung
	"Thesis" : "thesis", //Hochschulschrift
	"Unknown" : "document", //Unklarer Dokumententyp
	"UnpublishedWork" : "report" //Graue Literatur / Bericht / Report
};


function doImport() {
	var doc = Zotero.getXML();
	
	//Groups will also be mapped to tags which can be assigned to
	//items or notes.
	var groups = ZU.xpath(doc, '//Groups/Group');
	var rememberTags = {};
	for (var i=0; i<groups.length; i++) {
		var id = ZU.xpathText(groups[i], './@id');
		var name = ZU.xpathText(groups[i], './Name');
		var referenceGroups = ZU.xpath(doc, '//ReferenceGroups/OnetoN[contains(text(), "'+id+'")]|//KnowledgeItemGroups/OnetoN[contains(text(), "'+id+'")]');
		for (var j=0; j<referenceGroups.length; j++) {
			var refid = referenceGroups[j].textContent.split(';')[0];
			if (rememberTags[refid]) {
				rememberTags[refid].push(name);
			} else {
				rememberTags[refid] = [name];
			}
		}
	}
	
	//Main information for each reference.
	var item;
	var references = ZU.xpath(doc, '//References/Reference');
	var unfinishedReferences = [];
	var itemIdList = {};
	for (var i=0, n=references.length; i<n; i++) {
		var type = ZU.xpathText(references[i], 'ReferenceType');
		if (type && typeMapping[type]) {
			item = new Zotero.Item(typeMapping[type]);
		} else {
			Z.debug("Not yet supported type: " + type);
			Z.debug("Therefore use default type 'journalArticle'");
			item = new Zotero.Item("journalArticle");
		}
		item.itemID = ZU.xpathText(references[i], './@id');
		item.type = type;
		//Z.debug(item.itemID);
		
		item.title = ZU.xpathText(references[i], './Title');
		var subtitle = ZU.xpathText(references[i], './Subtitle');
		if (subtitle) {
			item.title += ": " + subtitle;
		}
		item.abstractNote = ZU.xpathText(references[i], './Abstract');
		item.url = ZU.xpathText(references[i], './OnlineAddress');
		item.volume = ZU.xpathText(references[i], './Volume');
		item.issue = ZU.xpathText(references[i], './Number');
		item.DOI = ZU.xpathText(references[i], './DOI');
		item.ISBN = ZU.xpathText(references[i], './ISBN');
		item.edition = ZU.xpathText(references[i], './Edition');
		item.place = ZU.xpathText(references[i], './PlaceOfPublication');
		item.numberOfVolumes = ZU.xpathText(references[i], './NumberOfVolumes');
		
		addExtraLine(item, "PMID", ZU.xpathText(references[i], './PubMedID'));
		
		item.pages = extractPages(ZU.xpathText(references[i], './PageRange'));
		item.numPages = extractPages(ZU.xpathText(references[i], './PageCount'));
		
		item.date = ZU.xpathText(references[i], './DateForSorting') ||
			ZU.xpathText(references[i], './Date') ||
			ZU.xpathText(references[i], './Year');
		item.accessDate = ZU.xpathText(references[i], './AccessDate');
		
		for (var field of ['Notes', 'TableOfContents', 'Evaluation']) {
			var note = ZU.xpathText(references[i], './'+field);
			if (note) {
				item.notes.push({ note : note , tags : ["#" + field] });
			}
		}
		
		var seriesID = ZU.xpathText(references[i], './SeriesTitleID');
		if (seriesID) {
			item.series = ZU.xpathText(doc.getElementById(seriesID), './Name');
		}
		
		var periodicalID = ZU.xpathText(references[i], './PeriodicalID');
		if (periodicalID) {
			var periodical = doc.getElementById(periodicalID);
			item.publicationTitle = ZU.xpathText(periodical, './Name');
			item.ISSN = ZU.xpathText(periodical, './ISSN');
			item.journalAbbreviation = ZU.xpathText(periodical, './StandardAbbreviation') ||
				ZU.xpathText(periodical, './UserAbbreviation1') ||
				ZU.xpathText(periodical, './UserAbbreviation2');
		}
		
		var authors = ZU.xpathText(doc, '//ReferenceAuthors/OnetoN[starts-with(text(), "'+item.itemID+'")]');
		attachPersons(doc, item, authors, "author");
		var editors = ZU.xpathText(doc, '//ReferenceEditors/OnetoN[starts-with(text(), "'+item.itemID+'")]');
		attachPersons(doc, item, editors, "editor");
		var collaborators = ZU.xpathText(doc, '//ReferenceCollaborators/OnetoN[starts-with(text(), "'+item.itemID+'")]');
		attachPersons(doc, item, collaborators, "contributor");
		var organizations = ZU.xpathText(doc, '//ReferenceOrganizations/OnetoN[starts-with(text(), "'+item.itemID+'")]');
		attachPersons(doc, item, organizations, "contributor");
		
		var publishers = ZU.xpathText(doc, '//ReferencePublishers/OnetoN[starts-with(text(), "'+item.itemID+'")]');
		if (publishers && publishers.length>0) {
			item.publisher = attachName(doc, publishers).join('; ');
		}
		
		var keywords = ZU.xpathText(doc, '//ReferenceKeywords/OnetoN[starts-with(text(), "'+item.itemID+'")]');
		if (keywords && keywords.length>0) {
			item.tags = attachName(doc, keywords);
		}
		if (rememberTags[item.itemID]) {
			for (var j=0; j<rememberTags[item.itemID].length; j++) {
				item.tags.push(rememberTags[item.itemID][j]);
			}
		}
		
		//For all corresponding knowledge items attach a note containing
		//the information of it.
		var citations = ZU.xpath(doc, '//KnowledgeItem[ReferenceID="'+item.itemID+'"]');
		for (var j=0; j<citations.length; j++) {
			var noteObject = {};
			noteObject.id = ZU.xpathText(citations[j], '@id');
			var title = ZU.xpathText(citations[j], 'CoreStatement');
			var text = ZU.xpathText(citations[j], 'Text');
			var pages = extractPages(ZU.xpathText(citations[j], 'PageRange'));
			noteObject.note = '';
			if (title) {
				noteObject.note += '<h1>' + title + "</h1>\n";
			}
			if (text) { 
				noteObject.note += "<p>" + ZU.xpathText(citations[j], 'Text') + "</p>\n";
			}
			if (pages) {
				noteObject.note += "<i>" + pages + "</i>";
			}
			if (rememberTags[noteObject.id]) {
				noteObject.tags = rememberTags[noteObject.id];
			}
			if (noteObject.note != "") {
				item.notes.push(noteObject);
			}
		}
		
		//Locations will be saved as URIs in attachments, DOI, extra etc.
		var locations = ZU.xpath(doc, '//Locations/Location[ReferenceID="'+item.itemID+'"]');
		//If we only have partial information about the callnumber or
		//library location, then we save this info in these two arrays
		//which will then processed after the for loop if no other info
		//was found.
		var onlyLibraryInfo = [];
		var onlyCallNumber = [];
		for (var j=0; j<locations.length; j++) {
			var address = ZU.xpathText(locations[j], 'Address');
			var addressType = ZU.xpathText(locations[j], 'MirrorsReferencePropertyId');
			if (address) {
				if (addressType == "Doi" && !item.DOI) {
					item.DOI = address;
				} else if (addressType == "PubMedId" && item.extra.indexOf("PMID") == -1) {
					addExtraLine(item, "PMID", address);
				} else {
					item.attachments.push({
						url: address,
						title:"Location"
					});
				}
			}
			var callNumber = ZU.xpathText(locations[j], 'CallNumber');
			var libraryId = ZU.xpathText(locations[j], 'LibraryID');
			if (callNumber && libraryId) {
				item.callNumber = callNumber;
				item.libraryCatalog = ZU.xpathText(doc.getElementById(libraryId), "Name");
			} else if (callNumber) {
				onlyCallNumber.push(callNumber);
			} else if (libraryId) {
				onlyLibraryInfo.push(ZU.xpathText(doc.getElementById(libraryId), "Name"));
			}
		}
		if (!item.callNumber) {
			if (onlyCallNumber.length>0) {
				item.callNumber = onlyCallNumber[0];
			} else if (onlyLibraryInfo.length>0) {
				item.libraryCatalog = onlyLibraryInfo[0];
			}
		}
		
		//Only for journalArticle and conferencePaper the DOI field is
		//currently established and therefore we need to add the info for
		//all other itemTypes in the extra field.
		if (item.DOI && item.itemType != "journalArticle" && item.itemType != "conferencePaper") {
			addExtraLine(item, "DOI", item.DOI);
		}
		
		//The items of type contribution need more data from their container
		//element and are therefore not yet finished. The other items can
		//be completed here.
		itemIdList[item.itemID] = item;
		if (type == "Contribution") {
			unfinishedReferences.push(item);
		} else {
			item.complete();
		}
	}
	
	
	//For unfinished references we add additional data from the
	//container item and save the relation between them as well.
	for (var i=0; i< unfinishedReferences.length; i++) {
		var item = unfinishedReferences[i];
		var containerString = ZU.xpathText(doc, '//ReferenceReferences/OnetoN[contains(text(), "'+item.itemID+'")]');
		if (containerString) {
			var containerId = containerString.split(';')[0];
			var containerItem = itemIdList[containerId];
			if (containerItem.type == "ConferenceProceedings") {
				item.itemType = "conferencePaper";
			}
			item.publicationTitle = containerItem.title;
			item.place = containerItem.place;
			item.publisher = containerItem.publisher;
			item.ISBN = containerItem.ISBN;
			item.volume = containerItem.volume;
			item.edition = containerItem.edition;
			item.series = containerItem.series;

			for (var j=0; j<containerItem.creators.length; j++) {
				var creatorObject = containerItem.creators[j];
				var role = creatorObject.creatorType;
				if (role == "author") {
					creatorObject.creatorType = "bookAuthor";
				}
				item.creators.push(creatorObject);
			}
			
			item.seeAlso.push(containerItem.itemID);
			
		}
		item.complete();
	}
	
	//Categories will be mapped to collections where the name contains
	//also the hierarchy number which we first calculate from the
	//CategoryCategories list.
	var categories = ZU.xpath(doc, '//Categories/Category');
	var hierarchy = ZU.xpath(doc, '//CategoryCatgories/OnetoN');
	var numbering = { "00000000-0000-0000-0000-000000000000" : "$" };
	//we will have fixed prefix "$." for all collections (see below),
	//such that only the substring starting form index 2 is relevant.
	for (var i=0, n=hierarchy.length; i<n; i++) {
		var categoryLists = hierarchy[i].textContent.split(";");
		var referencePoint = categoryLists[0];
		if (!numbering[referencePoint]) {
			Z.debug("Warning: Reference point for categorization hierarchy not yet found");
			Z.debug(categoryLists);
			continue;
		}
		for (var j=1; j<categoryLists.length; j++) {
			numbering[categoryLists[j]] = numbering[referencePoint] + "." + j;
		}
	}
	var collectionList = [];
	for (var i=0, n=categories.length; i<n; i++) {
		var collection = new Zotero.Collection();
		collection.id = ZU.xpathText(categories[i], './@id');
		collection.name = numbering[collection.id].substr(2) + ' ' + ZU.xpathText(categories[i], './Name');
		collection.type = 'collection';
		collection.children = [];
		var referenceCategories = ZU.xpath(doc, '//ReferenceCategories/OnetoN[contains(text(), "'+collection.id+'")]');
		for (var j=0; j<referenceCategories.length; j++) {
			var refid = referenceCategories[j].textContent.split(';')[0];
			collection.children.push({ type: 'item', id: refid });
		}
		collectionList.push(collection);
		collection.complete();
	}
	
	
	//Task items will be mapped to new standalone note
	var tasks = ZU.xpath(doc, '//TaskItems/TaskItem');
	for (var i=0, n=tasks.length; i<n; i++) {
		item = new Zotero.Item("note");
		
		var dueDate = ZU.xpathText(tasks[i], './DueDate');
		if (dueDate) {
			item.note = "<h1>" + ZU.xpathText(tasks[i], './Name') + " until " + dueDate + "</h1>";
		} else {
			item.note = "<h1>" + ZU.xpathText(tasks[i], './Name') + "</h1>";
		}
		var noteText = ZU.xpathText(tasks[i], './Notes');
		if (noteText) {
			item.note += "\n" + noteText;
		}
		
		item.seeAlso.push(ZU.xpathText(tasks[i], './ReferenceID'));
		
		item.tags.push("#todo");
		item.complete();
	}
	
	
}

function attachName(doc, ids) {
	if (!ids || !ids.length || ids.length<=0) {
		return ;
	}
	var valueList = [];
	var idList = ids.split(';');
	//skip the first element which is the id of reference
	for (var j=1; j<idList.length; j++) {
		var author = doc.getElementById(idList[j]);
		valueList.push(ZU.xpathText(author, 'Name'));
	}
	return valueList;
}

//For each id in the list of ids, find the 
//corresponding node in the document and 
//attach the data to the creators array.
function attachPersons(doc, item, ids, type) {
	if (!ids || !ids.length || ids.length<=0) {
		return ;
	}
	var authorIds = ids.split(';');
	//skip the first element which is the id of reference
	for (var j=1; j<authorIds.length; j++) {
		var author = doc.getElementById(authorIds[j]);
		var lastName = ZU.xpathText(author, 'LastName');
		var firstName = ZU.xpathText(author, 'FirstName');
		var middleName = ZU.xpathText(author, 'MiddleName');
		if(firstName && lastName) {
			if (middleName) {
				firstName += ' ' + middleName;
			}
			item.creators.push({ lastName : lastName, firstName : firstName, creatorType : type });
		}
		if(!firstName && lastName) {
			item.creators.push({ lastName : lastName, creatorType : type , fieldMode : true});
		}
	}
	
}

function addExtraLine(item, prefix, text) {
	if (text) {
		if (!item.extra) {
			item.extra = '';
		}
		item.extra += prefix + ': ' + text + "\n"; 
	}
}

function extractPages(multilineText) {
	if (multilineText) {
		var parts = multilineText.split("\n");
		return parts[parts.length-1].replace(/[^0-9\-–]/g, '');
	}
}/** BEGIN TEST CASES **/
var testCases = []
/** END TEST CASES **/