Child pages
  • Zotero-Scaffold Asset Actions Translator

This space has moved to IU's Confluence.
It is located at https://uisapp2.iu.edu/confluence-prd/display/iulDLFAquifer/

Skip to end of metadata
Go to start of metadata

An experimental Zotero translator for use with the DLF-Aquifer ASHO portal

Detailed below are instructions for implementing an experimental translator for Aquifer portal. This translator is a modified version of an original translator written by Esha Datta of NYU Library. Modifications by Tim Cole of the UIUC Library.

Pre-requisites:

  • Current version of Mozilla
  • Current version of Zotero
  • Current version of Scaffold IDE for creating Zotero translators

If you don't have Scaffold installed, get if from:  http://www.zotero.org/support/dev/scaffold You will need to restart Firefox after installing Scaffold. Once Scaffold has been successfully installed you should have "Scaffold" as an additional menu option on your Firefox tools menu.

IMPORTANT: If you do already have Scaffold installed and have previously had a translator with same target as mentioned below in step 1, you'll probably just want to modify the Code section (step 3 below) of your existing translator, leaving Label and TranslatorID for the translator as they currently are, rather than trying to create a new translator under a different name. Backup your current code, of course. I've found it very difficult to delete translators and Zotero gets very flaky when there's more than one translator for the same target URL pattern. I don't know as it's allowed to have multiple translators with the same target.

Implementing the translator (as of 25 Jan 2009):

1.  Select "Scaffold" from the Firefox Tools menu. Edit the metadata tab, inserting the following values (typical):

  • Label: Aquifer Asset Actions
  • Creator: Esha Datta; modified by Tim Cole
  • Target:
    http://www.dlfaquifer.org/search.*
    
  • Priority: 50

       Make sure the Web box is checked under "Translator Type"

2. Now edit the Detect Code tab, inserting the following text:

function detectWeb(doc, url) {     
                return "multiple";            
}

3. Now edit the Code tab, inserting the following text:

function doWeb(doc, url) {
    
    var nsResolver = doc.createNSResolver(doc.documentElement);
    //getting the unAPI url
    var link= doc.evaluate('//link[@rel="unapi-server"]',doc,nsResolver,XPathResult.ANY_TYPE, null).iterateNext();
    var unAPIhref = link.getAttribute("href");
    //unAPI id
    var abbr= doc.evaluate('//abbr[@class="unapi-id"]',doc,nsResolver,XPathResult.ANY_TYPE, null).iterateNext();
    var unAPIRecordId=abbr.getAttribute("title");
        
    var unAPItext="";
    //unAPI url with format hardcoded
    var unAPIUrl = unAPIhref+"?id="+unAPIRecordId+"&format=ore";
    //asynchronous call to url
    Zotero.Utilities.HTTP.doGet(unAPIUrl,function(text){
        
        //returns asset actions
        unAPItext = genXML(text);
        
        //selectItems method presents dialog box populated with associative array
        var items = Zotero.selectItems(unAPItext);
        if(!items) {
            return true;
        }
        //processes items selected by user
        processAssetActions(text,items);
        
        Zotero.done();
    })
    
    Zotero.wait();

}

function genXML(text){
    text = text.replace(/<\?xml[^>]*\?>/, "");
    
    var ore = new Namespace("http://www.w3.org/2005/Atom");
    var dc= new Namespace("http://purl.org/dc/elements/1.1/");
    var rdf= new Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
    //creates xml instance of text
    var xml = new XML(text);

    
    var assetLink;
    var assetLabel;
    var assetAction=new Object();
    var count=0;
    var actionInfo;
    //hard coding acceptable image asset actions that can be used
    var action =  ["getThumbnail","getScreensize","getMaxSize","getDynamicView"]
    //going through each entry segment in ore feed
    for each(var aa in xml..ore::entry){
        
            //grabbing asset action in rdf type element
            var rdfAA= aa.rdf::type;
            
            for(var i=0; i < rdfAA.length(); i++){
                var rdfType = rdfAA[i].toString();
                //removing info uri before asset action
                var cleanUp = /info.*?(get.*)/;
                rdfType = rdfType.replace(cleanUp,"$1");
                //checking value against action array which contains list of acceptable asset actions
                for(var v in action){
                    if(rdfType == action[v]){    
                        assetLink = aa.ore::link.@href.toString();
                        assetAction[assetLink] = rdfType;
                    }
                }
            
            }
    }
    //returns associative array with asset action and corresponding resource link
    return assetAction;
    
}
//function sends text again to process mods and once mods returns item object, adds asset action links as attachments
function processAssetActions(oreResponse,items){
    
    newItem=processMods(oreResponse);    
        
    for(var i in items){
        Zotero.debug(i + " " + items[i]);
        
        newItem.attachments.push({title:items[i], snapshot:true, mimeType:"text/html", url:i});

        newItem.notes.push({note:"_" + items[i] + ": " + i});
        
    }
    newItem.complete();
    
    
}


//copied from mods translator. This returns the object. The translator doesn't return the object
function processMods(text) {
    var marcGenres = {
        "book":"book",
        "periodical":"journalArticle",
        "newspaper":"newspaperArticle",
        "theses":"thesis",
        "letter":"letter",
        "map":"map",
        "graphic":"artwork",
        "motion picture":"film",
        "art original":"artwork",
        "still photo":"artwork",
        "still image":"artwork",
        "notated music":"artwork",
        "web site":"webpage"
    };
    

    var text = text.replace(/<\?xml[^>]*\?>/, "");
    
    
    var m = new Namespace("http://www.loc.gov/mods/v3");
    
    var atm = new Namespace("http://www.w3.org/2005/Atom");
    
    //again xmlizes the text
    var xml = new XML(text);
    
    if(xml..m::mods.length()) {
        var modsElements = xml..m::mods;
    } else {
        var modsElements = [xml];
    }
    var newItem = new Zotero.Item();
    var partialItemTypes = ["bookSection", "journalArticle", "magazineArticle", "newspaperArticle"];

    for each(var mods in modsElements) {
        //this was a correction on my part. It's hokey because the real mods translator populates the alternative title as the item's title. In the collections in ASHO, this appears to be some sort of collection title
        //also mapping alternative title to a real title is wrong. I couldn't figure out how to do not attribute in E4X, the xml processing language used here
        for each(var titleInfo in mods.m::titleInfo) {
            //basically don't do anything if attribute -- modified by twc to cover all @type values and populated title in case no non-attributed value present
            //if(titleInfo.@type == "abbreviated" || titleInfo.@type == "alternative" || titleInfo.@type == "uniform" ) {
            if(titleInfo.@type.length()) {
                if(!newItem.title.length()) {
                    newItem.title = titleInfo.m::title;
                }
            }
            else{
                newItem.title = titleInfo.m::title;
            }
            
        }
        // try to get genre from local genre
        for each(var genre in mods.m::genre) {
            if(genre.@authority == "local" && Zotero.Utilities.itemTypeExists(genre)) {
                newItem.itemType = genre.text().toString();
            } else if(!newItem.itemType && (genre.@authority == "marcgt" || genre.@authority == "marc")) {
                // otherwise, look at the marc genre
                newItem.itemType = marcGenres[genre.text().toString()];
            }
        }
        
        if(!newItem.itemType) {
            // try to get genre data from host
            for each(var relatedItem in mods.m::relatedItem) {
                if(relatedItem.@type == "host") {
                    for each(var genre in relatedItem.m::genre) {
                        if(genre.@authority == "marcgt" || genre.@authority == "marc") {
                            newItem.itemType = marcGenres[genre.text().toString()];
                            break;
                        }
                    }
                }
            }
        }
            
        // check if this is an electronic resource
        if(!newItem.itemType) {
            for each(var form in mods.m::physicalDescription.m::form) {
                if(form.@authority == "marcform" || form.@authority == "marc") {
                    if(form.text().toString() == "electronic") {
                        newItem.itemType = "webpage";
                        break;
                    }
                }
            }
        }
                
        if(!newItem.itemType) {
            if(mods.m::typeOfResource.length()) {
                newItem.itemType = marcGenres[mods.m::typeOfResource[0].text().toString()];
            }
        }

        if(!newItem.itemType) newItem.itemType = "book";
        
        var isPartialItem = Zotero.Utilities.inArray(newItem.itemType, partialItemTypes);
        
        // TODO: thesisType, type
        
        for each(var name in mods.m::name) {
            // TODO: institutional authors
            var creator = new Array();
            for each(var namePart in name.m::namePart) {
                if(namePart.@type == "given") {
                    creator.firstName = namePart.text().toString();
                } else if(namePart.@type == "family") {
                    creator.lastName = namePart.text().toString();
                } else {
                    var backupName = namePart.text().toString();
                }
            }
            
            if(backupName && !creator.firstName && !creator.lastName) {
                creator = Zotero.Utilities.cleanAuthor(backupName, "author", true);
            }
            
            // look for roles
            for(var role in name.m::role.m::roleTerm) {
                if(role.@type == "code" && role.@authority == "marcrelator") {
                    if(role == "edt") {
                        creator.creatorType = "editor";
                    } else if(role == "ctb") {
                        creator.creatorType = "contributor";
                    } else if(role == "trl") {
                        creator.creatorType = "translator";
                    }
                }
            }
            if(!creator.creatorType) creator.creatorType = "author";
            
            newItem.creators.push(creator);
        }
        
        // source
        newItem.source = mods.m::recordInfo.m::recordContentSource.text().toString();
        // accessionNumber
        newItem.accessionNumber = mods.m::recordInfo.m::recordIdentifier.text().toString();

        // rights
        if (mods.m::accessCondition.length()) {
            newItem.rights = mods.m::accessCondition.text().toString();
        } else if (xml..atm::feed.atm::rights.length()) {
            newItem.rights =xml..atm::feed.atm::rights.text().toString();
        }
        
        // type
        newItem.type = mods.m::typeOfResource.text().toString();
        // scale and artwork size
        newItem.scale = mods.m::physicalDescription.m::extent.text().toString();
        newItem.artworkSize = mods.m::physicalDescription.m::extent.text().toString();
        
        // repository
        newItem.repository = "";
        for each(var relatedItem in mods.m::relatedItem) {
            if(relatedItem.@type == "host") {
                newItem.repository = relatedItem.m::titleInfo.m::title.text().toString();
            }
        }
        
        // language
        for each(var languageTerm in mods.m::language.m::languageTerm) {
            if(languageTerm.@type == "text" || languageTerm.@type == "code") {
                newItem.language = languageTerm.text().toString();
            }
        }
                
        /** SUPPLEMENTAL FIELDS **/
        
        var part = false, originInfo = false;
        
        // series
        for each(var relatedItem in mods.m::relatedItem) {
            if(relatedItem.@type == "host") {
                for each(var titleInfo in relatedItem.m::titleInfo) {
                    if(titleInfo.@type == "abbreviated") {
                        newItem.journalAbbreviation = titleInfo.m::title.text().toString();
                        if(!newItem.publicationTitle) newItem.publicationTitle = newItem.journalAbbreviation;
                    } else {
                        newItem.publicationTitle = titleInfo.m::title.text().toString();
                    }
                }
                part = relatedItem.m::part;
                originInfo = relatedItem.m::originInfo;
                processIdentifiers(newItem, relatedItem.m::identifier);
            } else if(relatedItem.@type == "series") {
                newItem.series = relatedItem.m::titleInfo.m::title.text().toString();
                newItem.seriesTitle = relatedItem.m::titleInfo.m::partTitle.text().toString();
                newItem.seriesText = relatedItem.m::titleInfo.m::subTitle.text().toString();
                newItem.seriesNumber = relatedItem.m::titleInfo.m::partNumber.text().toString();
            }
        }
        
        // get part
        if(!part) {
            part = mods.m::part;
            originInfo = mods.m::originInfo;
        }
        
        if(part) {
            for each(var detail in part.m::detail) {
                // volume
                if(detail.@type == "volume") {
                    newItem.volume = detail.m::number.text().toString();
                    if(!newItem.volume) {
                        newItem.volume = detail.m::text.text().toString();
                    }
                }
                
                // number
                if(detail.@type == "issue") {
                    newItem.issue = detail.m::number.text().toString();
                    if(!newItem.issue) {
                        newItem.issue = detail.m::text.text().toString();
                    }
                }
                
                // section
                if(detail.@type == "section") {
                    newItem.section = detail.m::number.text().toString();
                    if(!newItem.section) {
                        newItem.section = detail.m::text.text().toString();
                    }
                }
            }
            
            // pages
            for each(var extent in part.m::extent) {
                if(extent.@unit == "pages" || extent.@unit == "page") {
                    var pagesStart = extent.m::start.text().toString();
                    var pagesEnd = extent.m::end.text().toString();
                    if(pagesStart || pagesEnd) {
                        if(pagesStart == pagesEnd) {
                            newItem.pages = pagesStart;
                        } else if(pagesStart && pagesEnd) {
                            newItem.pages = pagesStart+"-"+pagesEnd;
                        } else {
                            newItem.pages = pagesStart+pagesEnd;
                        }
                    }
                }
            }
        }

        // identifier
        processIdentifiers(newItem, mods.m::identifier);
        // edition
        newItem.edition = originInfo.m::edition.text().toString();
        // place
        for each(var placeTerm in originInfo.m::place.m::placeTerm) {
            if(placeTerm.@type == "text") {
                newItem.place = placeTerm.text().toString();
            }
        }
        // publisher/distributor
        if(originInfo.m::publisher.length()) {
            if(newItem.itemType == "webpage" || newItem.itemType == "website") {
                newItem.publicationTitle = originInfo.m::publisher[0].text().toString();
            } else {
                newItem.publisher = originInfo.m::publisher[0].text().toString();
            }
        }
        
        // date
        if(originInfo.m::copyrightDate.length()) {
            newItem.date = originInfo.m::copyrightDate[0].text().toString();
        } else if(originInfo.m::dateIssued.length()) {
            newItem.date = originInfo.m::dateIssued[0].text().toString();    
            for each(var dateIssued in originInfo.m::dateIssued) {
                if(dateIssued.@encoding == "marc") {
                    newItem.date = dateIssued.text().toString();
                }
            }
        } else if(originInfo.m::dateCreated.length()) {
            newItem.date = originInfo.m::dateCreated[0].text().toString();
        } else if(mods.m::originInfo.m::copyrightDate.length()) {
            newItem.date = originInfo.m::copyrightDate[0].text().toString();
        } else if(mods.m::originInfo.m::dateIssued.length()) {
            newItem.date = mods.m::originInfo.m::dateIssued[0].text().toString();    
            for each(var dateIssued in mods.m::originInfo.m::dateIssued) {
                if(dateIssued.@encoding == "marc") {
                    newItem.date = dateIssued.text().toString();
                }
            }
        } else if(mods.m::originInfo.m::dateCreated.length()) {
            newItem.date = originInfo.m::dateCreated[0].text().toString();
        }
        
        // lastModified
        newItem.lastModified = originInfo.m::dateModified.text().toString();
        // accessDate
        newItem.accessDate = originInfo.m::dateCaptured.text().toString();
        
        // call number
        newItem.callNumber = mods.m::classification.text().toString();
        // archiveLocation
        newItem.archiveLocation = mods.m::location.m::physicalLocation.text().toString();

        // url
        //newItem.url = mods.m::location.m::url.text().toString();
        for each(var location in mods.m::location) {
            for each(url in location.m::url) {
                if(!newItem.url) {                
                    newItem.url = url.text().toString();
                }
                if(url.@usage=="primary display") {
                    newItem.url = url.text().toString();
                }
            }    
        }

        // abstract
        newItem.abstractNote = mods.m::abstract.text().toString();
        
        /** NOTES **/
        
        for each(var note in mods.m::note) {
            newItem.notes.push({note:note.text().toString()});
        }
        
        /** TAGS **/
        for each(var subject in mods.m::subject.m::topic) {
            newItem.tags.push(subject.text().toString());
        }
        for each(var subject in mods.m::subject.m::geographic) {
            newItem.tags.push(subject.text().toString());
        }
        for each(var subject in mods.m::subject.m::temporal) {
            newItem.tags.push(subject.text().toString());
        }
        for each(var subject in mods.m::subject.m::name) {
            newItem.tags.push(subject.text().toString());
        }
        
    }
    
        //returns item back to processItems. The regular mods translator ties everything up with the complete() method used in the processItems function.
        return newItem;
        

}

function processIdentifiers(newItem, identifier) {
    for each(var myIdentifier in identifier) {
        if(myIdentifier.@type == "isbn") {
            newItem.ISBN = myIdentifier.text().toString()
        } else if(myIdentifier.@type == "issn") {
            newItem.ISSN = myIdentifier.text().toString()
        } else if(myIdentifier.@type == "doi") {
            newItem.DOI = myIdentifier.text().toString()
        }
    }
}

4. Finally (to avoid having to recreate this translator every time you open Firefox), click on the "Save to Database" button (above the tabs on the Scaffold display, second from left -- hover and you should get a tooltip).

Next time you go to the DLF-Aquifer portal using Firefox and try to collect an image item enabled for asset actions into Zotero, you should get a menu of additional views of the image that you can collect into Zotero. Try it for LoC images or Cushman images since they tend to have the best quality Max Size images.

Note, if you want to do further development on this translator, the Scaffold IDE does offer an immediate debug window. Navigate your browser to an appropriate full record display in the Aquifer portal, then open Scaffold, load in the Aquifer Asset Action translator, go to the Code tab, and click the lightening rod. Use the Zotero.debug method to write out diagnostics while debugging this way.

  • No labels