{"action":"created","comment":{"url":"https://api.github.com/repos/zotero/translators/pulls/comments/43718033","id":43718033,"diff_hunk":"@@ -0,0 +1,577 @@\n+{\n+\t\"translatorID\": \"f715a5a1-c362-47cf-b736-2cb2c882b852\",\n+\t\"label\": \"CONTENTdm\",\n+\t\"creator\": \"Emma Reisz\",\n+\t\"target\": \"/cdm/|/cdm4/\",\n+\t\"minVersion\": \"3.0.9\",\n+\t\"maxVersion\": \"\",\n+\t\"priority\": 100,\n+\t\"inRepository\": true,\n+\t\"translatorType\": 4,\n+\t\"browserSupport\": \"gcsbv\",\n+\t\"lastUpdated\": \"2015-11-02 10:55:08\"\n+}\n+\n+/**\n+\t***** BEGIN LICENSE BLOCK *****\n+\n+\tCONTENTdm translator; Copyright © 2015 Emma Reisz\n+\tThis file is part of Zotero.\n+\n+\tZotero is free software: you can redistribute it and/or modify\n+\tit under the terms of the GNU Affero General Public License as published by\n+\tthe Free Software Foundation, either version 3 of the License, or\n+\t(at your option) any later version.\n+\n+\tZotero is distributed in the hope that it will be useful,\n+\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n+\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+\tGNU Affero General Public License for more details.\n+\n+\tYou should have received a copy of the GNU Affero General Public License\n+\talong with Zotero.  If not, see <http://www.gnu.org/licenses/>.\n+\n+\t***** END LICENSE BLOCK *****\n+*/\n+\n+/*\n+  ContentDM is an OCLC product used by several hundred libraries, archives and museums.\n+  CDM may be hosted by OCLC or by the customer.\n+  CDM includes an API which calls metadata as XML or JSON, but customers may disable it.\n+  By preference, this translator scrapes XML from an API query (slow)\n+  and from a webpage as a fallback (quick but vulnerable to a redesign).\n+  XML is preferred to JSON because it allows code to be shared with the webscraper.\n+  The translator will also scrape some data from legacy CDM webpages (version < 6).\n+  Older CDM versions had no standardised field names so comprehensive scraping is unrealistic.\n+*/\n+\n+function detectWeb( doc, url ) {\n+  if ( !url.match( \"/cdm/\" ) && !url.match( \"/cdm4/\" ) ) return;\n+  if ( url.match( \"/cdm/search/\" ) ) return \"multiple\";\n+  else if ( url.match( \"/cdm/landingpage\" ) || !url.substring( url.indexOf( '/cdm/' ) + 5 ) ) return \"webpage\";\n+  else if ( url.match( \"/cdm/fullbrowser\" ) ) { // Pages without item metadata\n+\toriginatingItem = ZU.xpath( doc, '//link[@rel=\"canonical\"]' );\n+\tif ( originatingItem && originatingItem[ 0 ].href ) return \"document\"; // Slow to scrape again for item type\n+\telse return \"\";\n+  }\n+  return ( function() {\n+\tvar itemType = fetchXPathText( doc, '//*[@id=\"metadata_type\"]' );\n+\tif ( !itemType ) itemType = fetchXPathText( doc, '//*[@id=\"metadata_object_type\"]' );\n+\tswitch ( itemType ) {\n+\t  case \"Image\":\n+\t  case \"Photograph\":\n+\t  case \"Engraved portrait\":\n+\t  case \"Negative\":\n+\t\treturn 'artwork';\n+\t  case \"Text\":\n+\t\treturn 'manuscript';\n+\t  default:\n+\t\treturn 'document';\n+\t}\n+  }() );\n+}\n+\n+function fetchXPathText( doc, xpath ) { // Removes excess whitespace & breaks, respaces items overtrimmed by trimInternal.\n+  if ( ZU.xpathText( doc, xpath ) ) {\n+\treturn ZU.trimInternal( ZU.xpathText( doc, xpath ).replace( /^\\s+|\\s+$|\\;$|\\,$/g, '' ).replace( /([a-z])([A-Z])/g, '$1\\. $2' ) );\n+  } else return;\n+}\n+\n+function fetchXPathValue( doc, xpath ) {\n+  if ( ZU.xpath( doc, xpath )[ 0 ] ) return ZU.xpath( doc, xpath )[ 0 ].value;\n+  else return;\n+}\n+\n+function fetchXmlUrl( doc, url ) { // Fetches xml urls if using OCLC server (as constructor)\n+  var serverID = new RegExp( \"/collection\\/p([A-Z0-9]+)coll\", \"i\" );\n+  var serverID2 = new RegExp( \"//cdm([0-9]+).contentdm.oclc.org\" );\n+  var collectionID = new RegExp( \"/collection\\/([A-Z0-9]+)\\/\", \"i\" );\n+  var objectID = new RegExp( \"/id\\/([A-Z0-9]+)\", \"i\" );\n+  var itemID = new RegExp( \"/show\\/([A-Z0-9]+)\", \"i\" );\n+  var objectUrlHttp = fetchXPathValue( doc, '//input[@id=\"cdm_objectRefUrl\"]' );\n+  var itemUrlHttp = fetchXPathValue( doc, '//input[@id=\"cdm_newRefUrl\"]' );\n+  var server;\n+  var xmlQueryUrl;\n+  if ( objectUrlHttp ) url = objectUrlHttp; // Use object page as primary even if item page is present\n+  else if ( itemUrlHttp ) {\n+\turl = itemUrlHttp;\n+\titemUrlHttp = \"\";\n+  } // Use item page as primary if no object page is present\n+  if ( url.match( serverID ) ) server = url.match( serverID )[ 1 ];\n+  else if ( url.match( serverID2 ) ) server = url.match( serverID2 )[ 1 ];\n+  if ( server && url.match( collectionID ) && url.match( objectID ) ) {\n+\txmlQueryUrl = \"https://server\" + server + \".contentdm.oclc.org/dmwebservices/index.php?q=dmGetItemInfo/\" + url.match( collectionID )[ 1 ] + \"/\";\n+\tthis.object = xmlQueryUrl + url.match( objectID )[ 1 ] + \"/show/xml\";\n+  } else this.object = \"\";\n+  if ( itemUrlHttp ) this.item = xmlQueryUrl + itemUrlHttp.match( objectID )[ 1 ] + \"/show/xml\";\n+  else if ( this.object && url.match( itemID ) ) this.item = xmlQueryUrl + url.match( itemID )[ 1 ] + \"/show/xml\";\n+  else this.item = \"\";\n+}\n+\n+function scrape( doc, url ) {\n+  var item = new Zotero.Item( detectWeb( doc, url ) );\n+  url = doc.documentURI;\n+  var xmlUrl = new fetchXmlUrl( doc, url );\n+\n+  /* Mappings for XML and Web (CDM>5)\n+   * The scraper only scrapes fields specified in the mapping table.\n+   * Enter mappings as [zoteroField, CDMfield];\n+   * note CDM allows customers to configure fields\n+   * so these are inconsistently implemented.\n+   */\n+  var mapping = [\n+\t\t\t[ 'title', 'title' ],\n+\t\t\t[ 'creator', 'creato' ],\n+\t\t\t[ 'creator2', 'creata' ],\n+\t\t\t[ 'contributor', 'contri' ],\n+\t\t\t[ 'abstractNote', 'descri' ],\n+\t\t\t[ 'language', 'langua' ],\n+\t\t\t[ 'language2', 'languag' ],\n+\t\t\t[ 'archiveLocation', 'identi' ], // On CDM this is 'Identifier'\n+\t\t\t[ 'archiveLocation2', 'locala' ],\n+\t\t\t[ 'archive', 'source' ],\n+\t\t\t[ 'collection', 'collec' ],\n+\t\t\t[ 'collection2', 'relati' ],\n+\t\t\t[ 'publisher', 'publis' ], // Omitted by manuscript item type\n+\t\t\t[ 'rights', 'rights' ],\n+\t\t\t[ 'subject', 'subject' ], // Scraped into tags\n+\t\t\t[ 'subject2', 'subjea' ],\n+\t\t\t[ 'subject3', 'subjeb' ],\n+\t\t\t[ 'subject4', 'subjec' ],\n+\t\t\t[ 'person', 'person' ],\n+\t\t\t[ 'medium', 'type' ],\n+\t\t\t[ 'format', 'format' ],\n+\t\t\t[ 'date', 'date' ],\n+\t\t\t[ 'date2', 'dated' ],\n+\t\t\t[ 'place', 'covera' ],\n+\t\t\t[ 'place2', 'coverab' ],\n+\t\t\t[ 'period', 'period' ],\n+\t\t  ];\n+\n+  /* Mappings for Web (CDM<6)\n+   * CDM < 6 does not provide standard field names.\n+   * The scraper scrapes the whole display table,\n+   * and converts displayed fieldnames to camelCase\n+   * to maximise the chance of a Zotero match.\n+   * Hardcode additional matches here as [zoteroField, displayedField];\n+   */\n+\n+  var legacyMapping = [ // Enter mappings as [zoteroField,camelizedCDMField]\n+\t\t\t[ \"place\", \"town\" ],\n+\t\t\t[ \"abstractNote\", \"description\" ],\n+\t\t\t[ \"subject2\", \"keywords\" ],\n+\t\t\t[ \"subject3\", \"subjectKeywords\" ],\n+\t\t\t[ \"date2\", \"workDate\" ],\n+\t\t\t[ \"creator\", \"bookCreator\" ],\n+\t\t\t[ \"rights\", \"rights--UsageStatement\" ],\n+\t\t\t[ \"contributor2\", \"photographer\" ],\n+\t\t\t[ \"title2\", \"bookTitle\" ],\n+\t\t  ];\n+\n+  var docTitle = ( function() {\n+\tif ( doc.title == \"ContentDM\" ) return url.substring( url.indexOf( '://' ) + 3, url.indexOf( '/cdm/' ) ).replace( /^\\s+|\\s+$/g, '' );\n+\telse return doc.title.replace( /^\\s+|\\s+$/g, '' );\n+  }() );\n+\n+  item.libraryCatalog = ( function() {\n+\tvar catalog = fetchXPathText( doc, '//*[@id=\"breadcrumb_top_content\"]/a[2]' );\n+\tif ( catalog ) return catalog;\n+\telse if ( docTitle.match( '::' ) ) return docTitle.substring( doc.title.indexOf( '::' ) + 3 ); // Fallback: use second part of webpage title\n+\telse return docTitle;\n+  }() );\n+\n+  item.websiteType = \"ContentDM\";\n+  item.attachments.push( {\n+\ttitle: \"Snapshot of CONTENTdm record\",\n+\tmimeType: \"text/html\",\n+\tdocument: doc\n+  } );\n+  item.url = url;\n+  //  item.attachments.push({url:url, title: \"Link to Record\", mimeType: \"text/html\", snapshot: false}); // optional; url set instead\n+  attachPDF( doc, item );\n+\n+  ZU.doGet( xmlUrl.object, function( text ) {\n+\t// Z.debug(text);                                           // View primary XML doc\n+\tvar scrapeMode;\n+\tif ( !xmlUrl.object || !text || text.match( \"The web server is not configured\" ) ) scrapeMode = \"Web\";\n+\telse { // Scrape from API XML\n+\t  var docxml = ( new DOMParser() ).parseFromString( text, \"text/xml\" );\n+\t  if ( xmlUrl.item ) { // Scraping from two XML docs\n+\t\tZU.doGet( xmlUrl.item, function( text ) {\n+\t\t  var doc2xml = ( new DOMParser() ).parseFromString( text, \"text/xml\" );\n+\t\t  // Z.debug(text);                                   // View secondary XML doc\n+\t\t  scrapeToItem( docxml, item, url, docTitle, mapping, \"xml\", doc2xml );\n+\t\t  item.complete();\n+\t\t} );\n+\t  } else { // Scraping from a single XML doc\n+\t\tscrapeToItem( docxml, item, url, docTitle, mapping, \"xml\" );\n+\t\titem.complete();\n+\t  }\n+\t  return; // Return from the ZU.doGet callback after XML scrape\n+\t}\n+\tif ( scrapeMode == \"Web\" ) {\n+\t  var headerXPath;\n+\t  var contentXPath;\n+\t  var offset;\n+\t  var xhttp = new XMLHttpRequest();\n+\t  xhttp.open( \"GET\", url, false );\n+\t  xhttp.send();\n+\t  fullSource = xhttp.responseText;\n+\n+\t  if ( doc.documentElement.innerHTML.match( \"CONTENTdm Version\" ) ) { // Web scrape for CDM version 6+;\n+\t\tscrapeToItem( doc, item, url, docTitle, mapping, \"web\" );\n+\t  }\n+\n+\t  else if ( fullSource.match( \"CONTENTdm Version 5\" ) ) { // Web scrape for CDM Version 5\n+\t\theaderXPath = '//table/tbody/tr/td/table/tbody/tr/td/table[2]/tbody/tr[2]/td/table/tbody/tr[2]/td/table/tbody/tr/td[1]';\n+\t\tcontentXPath = '//table/tbody/tr/td/table/tbody/tr/td/table[2]/tbody/tr[2]/td/table/tbody/tr[2]/td/table/tbody/tr/td[2]';\n+\t\toffset = 1;\n+\t\tlegacyScrapeToItem( doc, item, headerXPath, contentXPath, offset, legacyMapping, docTitle, url );\n+\t  }\n+\n+\t  else if ( fullSource.match( \"CONTENTdm\" ) ) { // Web scrape for CDM Version 4 or below\n+\t\theaderXPath = '//div/div[3]/table[2]/tbody/tr/td[1]';\n+\t\tcontentXPath = '//div/div[3]/table[2]/tbody/tr/td[2]';\n+\t\toffset = 0;\n+\t\tlegacyScrapeToItem( doc, item, headerXPath, contentXPath, offset, legacyMapping, docTitle, url );\n+\t  }\n+\n+\t  item.complete();\n+\n+\t  return; // Return from the ZU.doGet callback after Web scrape\n+\t}\n+  } );\n+  return '';\n+}\n+\n+function legacyScrapeToItem( doc, item, headerXPath, contentXPath, offset, mapping, docTitle, url ) {\n+  var headers = ZU.xpath( doc, headerXPath ); // Scrapes the whole metadata table and looks for matches after.\n+  var contents = ZU.xpath( doc, contentXPath );\n+  for ( i = 0; i < contents.length; i++ ) {\n+\tvar header;\n+\tvar content;\n+\tj = i + offset;\n+\tif ( headers[ j ].textContent ) {\n+\t  header = headers[ j ].textContent;\n+\t  header = camelize( header );\n+\t}\n+\tif ( contents[ i ].textContent ) content = contents[ i ].textContent;\n+\titem[ header ] = content;\n+  }\n+  for ( var i in mapping ) item[ mapping[ i ][ 0 ] ] = item[ mapping[ i ][ 1 ] ];\n+  tidyItem( item );\n+  if ( !item.title ) item.title = getTitle( doc, docTitle, url );\n+\n+  function camelize( str ) {\n+\treturn str.replace( /(?:^\\w|[A-Z]|\\b\\w)/g, function( letter, index ) {\n+\t  return index === 0 ? letter.toLowerCase() : letter.toUpperCase();\n+\t} ).replace( /\\s+/g, '' );\n+  }\n+}\n+\n+function scrapeToItem( doc, item, url, docTitle, mapping, prefixes, doc2 ) {\n+  var info = []; // Scrapes only fields named in the mapping array.\n+  var xpath;\n+  var itemText;\n+  for ( var i in mapping ) {\n+\tvar thisField = [];\n+\tvar field = mapping[ i ][ 1 ];\n+\tif ( prefixes == \"web\" ) xpath = {\n+\t  object: \"//*[@id='metadata_object_\" + field + \"']\",\n+\t  item: \"//*[@id='metadata_\" + field + \"']\"\n+\t};\n+\telse if ( prefixes == \"xml\" ) xpath = {\n+\t  object: \"//\" + field,\n+\t  item: \"\"\n+\t};\n+\tthisField = [ mapping[ i ][ 0 ], xpath.object, xpath.item ];\n+\tinfo.push( thisField );\n+  }\n+  for ( var j in info ) {\n+\tvar text = fetchXPathText( doc, info[ j ][ 1 ] );\n+\tif ( !doc2 ) { // Scrape from single document\n+\t  if ( info[ j ][ 0 ] == \"title\" && !text ) text = getTitle( doc, docTitle, url );\n+\t  if ( info[ j ][ 2 ] ) itemText = fetchXPathText( doc, info[ j ][ 2 ] );\n+\t} else { // Scrape from two documents\n+\t  itemText = fetchXPathText( doc2, info[ j ][ 1 ] );\n+\t  if ( info[ j ][ 0 ] == \"title\" && !itemText ) itemText = getTitle( doc2, docTitle, url );\n+\t}\n+\tif ( text != itemText ) text = [ itemText, text ].filter( function( val ) {\n+\t  return val;\n+\t} ).join( '. ' );\n+\titem[ info[ j ][ 0 ] ] = text;\n+  }\n+  tidyItem( item );\n+  return item;\n+}\n+\n+function tidyItem( item ) {\n+  mergeFields( item );\n+  replaceFields( item );\n+  cleanAuthor( item );\n+  item.tags = []; //Tags must be in an array\n+  item.tags = item.subject.replace( /\\([^)]*\\)/g, '' ).split( /[.;]/ );\n+  for ( var k in item.tags ) item.tags[ k ] = ZU.trimInternal( item.tags[ k ].replace( /^\\s+|\\s+$/g, '' ).replace( /\\s+\\,/g, ',' ) );\n+  return item;\n+}\n+\n+function mergeFields( item ) {\n+  var merges = [\n+\t\t  [ 'subject', 'subject2', 'subject3', 'subject4', 'person' ], // List all the sets of fields to be combined, Zotero field first\n+\t\t  [ 'archive', 'collection', 'collection2' ],\n+\t\t  [ 'place', 'place2' ],\n+\t\t  [ 'creator', 'creator2' ],\n+\t\t  [ 'archiveLocation', 'archiveLocation2' ],\n+\t\t  [ 'medium', 'format' ],\n+\t\t  [ 'contributor', 'contributor2' ],\n+\t\t];\n+  for ( var i in merges ) {\n+\tvar mergeArray = [];\n+\tfor ( var j in merges[ i ] ) {\n+\t  mergeArray.push( item[ merges[ i ][ j ] ] ); // Arrays are easier to filter\n+\t  if ( j > 0 ) item[ merges[ i ][ j ] ] = \"\";\n+\t}\n+\titem[ merges[ i ][ 0 ] ] = mergeArray.filter( function( val ) {\n+\t  return val;\n+\t} ).join( '; ' );\n+  }\n+}\n+\n+function replaceFields( item ) {\n+  var replace = [\n+\t\t  [ 'date', 'date2', 'period' ], // List all the sets of fields to be checked in order of preference, Zotero field first\n+\t\t  [ 'language', 'language2' ],\n+\t\t  [ 'title', 'title2' ],\n+\t\t];\n+  for ( var i in replace ) {\n+\tfor ( var j in replace[ i ] ) {\n+\t  if ( item[ replace[ i ][ 0 ] ] ) break;\n+\t  else {\n+\t\titem[ replace[ i ][ 0 ] ] = item[ replace[ i ][ j ] ];\n+\t\tif ( j > 0 ) item[ replace[ i ][ j ] ] = \"\";\n+\t  }\n+\t}\n+  }\n+}\n+\n+function getTitle( doc, docTitle, url ) {\n+  var title;\n+  if ( docTitle.match( \"::\" ) ) docTitle = docTitle.substring( 0, docTitle.indexOf( \"::\" ) );\n+  if ( url.match( \"/cdm/landingpage\" ) ) title = docTitle + \": About this collection\";\n+  else title = docTitle; // Fallback: first part of webpage title\n+  return title.replace( /^\\s+|\\s+$/g, '' );\n+}\n+\n+function cleanAuthor( item ) {\n+  var authorType = [ \"creator\", \"contributor\" ]; // Author field names in the mapping table must be Zotero author types\n+  for ( var i in authorType ) {\n+\tif ( item[ authorType[ i ] ] ) {\n+\t  fetchedAuthor = item[ authorType[ i ] ].replace( /\\([^)]*\\)|\\[[^\\]]*\\]|\\;$/g, '' ).replace( /, Sir |, Dr /g, ', ' ); // Fetch and remove parentheses and honorifics\n+\t  var nameArray = [];\n+\t  item[ authorType[ i ] ] = \"\";\n+\t  nameArray = fetchedAuthor.split( \";\" ); // Split authors at semicolon and push into an array\n+\t  for ( var j in nameArray ) item.creators.push( ZU.cleanAuthor( nameArray[ j ], authorType[ i ], 1 ) );\n+\t}\n+  }\n+  for ( var k in item.creators ) {\n+\tif ( !item.creators[ k ].firstName ) item.creators[ k ].fieldMode = 1;\n+  }\n+}\n+\n+function attachPDF( doc, item ) {\n+  var pdfPath = '//embed';\n+  var pdfScrape = ZU.xpath( doc, pdfPath );\n+  var pdfObject = pdfScrape[ 0 ]; // Could iterate looking for multiple attachments\n+  var pdfTitle;\n+  if ( pdfObject && pdfObject.src ) { // Can't simply test for .src\n+\tvar itemTitle = fetchXPathText( doc, '//*[@id=\"metadata_title\"]' );\n+\tif ( itemTitle ) pdfTitle = \"Attachment: \" + itemTitle;\n+\telse pdfTitle = \"Attachment\";\n+\titem.attachments.push( {\n+\t  url: pdfObject.src,\n+\t  title: pdfTitle,\n+\t  mimeType: \"application/pdf\"\n+\t} );\n+  }\n+}\n+\n+function doWeb( doc, url ) {\n+  if ( detectWeb( doc, url ) == \"multiple\" ) { // If on a multiple result page, call Zotero.selectItems\n+\tvar titlePath = '//div/ul/li/ul/li[2]/div/a';\n+\tvar items = {};\n+\tvar articles = [];\n+\tvar titles = ZU.xpath( doc, titlePath );\n+\tif ( titles.length < 1 ) return false; // Null search results will be ignored\n+\tfor ( var i in titles ) {\n+\t  items[ titles[ i ].href ] = ZU.trimInternal( titles[ i ].textContent );\n+\t}\n+\tZotero.selectItems( items, function( items ) {\n+\t  if ( !items ) return true;\n+\t  for ( var i in items ) {\n+\t\tarticles.push( i );\n+\t  }\n+\t  ZU.processDocuments( articles, scrape );\n+\t} );\n+\n+  } else if ( url.match( \"/cdm/fullbrowser\" ) ) {\n+\tvar originatingItem = ZU.xpath( doc, '//link[@rel=\"canonical\"]' ); // Look for link to originating item\n+\tif ( originatingItem[ 0 ] && originatingItem[ 0 ].href ) {\n+\t  url = originatingItem[ 0 ].href;\n+\t  ZU.processDocuments( url, scrape );\n+\t} else return false;\n+\n+  } else if ( ZU.xpath( doc, '//input[@id=\"cdm_newRefUrl\"]' ) ) {\n+\tvar realItem = ZU.xpath( doc, '//input[@id=\"cdm_newRefUrl\"]' ); // CDM pages can display info for other items; look for real url of item\n+\tif ( realItem[ 0 ] && realItem[ 0 ].value ) {\n+\t  url = realItem[ 0 ].value;\n+\t  ZU.processDocuments( url, scrape );\n+\t} else scrape( doc, url );\n+\n+  } else {\n+\tscrape( doc, url );\n+  }\n+}\n+\n+\n+/** BEGIN TEST CASES **/\n+var testCases = [\n+\t{\n+\t\t\"type\": \"web\",\n+\t\t\"url\": \"http://cdm15979.contentdm.oclc.org/cdm/compoundobject/collection/p15979coll3/id/2419\",\n+\t\t\"items\": [\n+\t\t\t{\n+\t\t\t\t\"itemType\": \"manuscript\",\n+\t\t\t\t\"title\": \"MS.15.1.2.000. Sir Robert Hart Diary: Volume 02: February 1855-July 1855\",","path":"CONTENTdm.js","position":444,"original_position":444,"commit_id":"846d3bea99fd320aa9ea82897c8378137e3109f9","original_commit_id":"846d3bea99fd320aa9ea82897c8378137e3109f9","user":{"login":"adam3smith","id":128986,"avatar_url":"https://avatars.githubusercontent.com/u/128986?v=3","gravatar_id":"","url":"https://api.github.com/users/adam3smith","html_url":"https://github.com/adam3smith","followers_url":"https://api.github.com/users/adam3smith/followers","following_url":"https://api.github.com/users/adam3smith/following{/other_user}","gists_url":"https://api.github.com/users/adam3smith/gists{/gist_id}","starred_url":"https://api.github.com/users/adam3smith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adam3smith/subscriptions","organizations_url":"https://api.github.com/users/adam3smith/orgs","repos_url":"https://api.github.com/users/adam3smith/repos","events_url":"https://api.github.com/users/adam3smith/events{/privacy}","received_events_url":"https://api.github.com/users/adam3smith/received_events","type":"User","site_admin":false},"body":"this looks like the archive location is prepended to the title. Could we clean that up better?","created_at":"2015-11-03T05:48:34Z","updated_at":"2015-11-03T05:48:34Z","html_url":"https://github.com/zotero/translators/pull/968#discussion_r43718033","pull_request_url":"https://api.github.com/repos/zotero/translators/pulls/968","_links":{"self":{"href":"https://api.github.com/repos/zotero/translators/pulls/comments/43718033"},"html":{"href":"https://github.com/zotero/translators/pull/968#discussion_r43718033"},"pull_request":{"href":"https://api.github.com/repos/zotero/translators/pulls/968"}}},"pull_request":{"url":"https://api.github.com/repos/zotero/translators/pulls/968","id":49418754,"html_url":"https://github.com/zotero/translators/pull/968","diff_url":"https://github.com/zotero/translators/pull/968.diff","patch_url":"https://github.com/zotero/translators/pull/968.patch","issue_url":"https://api.github.com/repos/zotero/translators/issues/968","number":968,"state":"open","locked":false,"title":"Translator for CONTENTdm archive and library database software","user":{"login":"emmareisz","id":8190209,"avatar_url":"https://avatars.githubusercontent.com/u/8190209?v=3","gravatar_id":"","url":"https://api.github.com/users/emmareisz","html_url":"https://github.com/emmareisz","followers_url":"https://api.github.com/users/emmareisz/followers","following_url":"https://api.github.com/users/emmareisz/following{/other_user}","gists_url":"https://api.github.com/users/emmareisz/gists{/gist_id}","starred_url":"https://api.github.com/users/emmareisz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emmareisz/subscriptions","organizations_url":"https://api.github.com/users/emmareisz/orgs","repos_url":"https://api.github.com/users/emmareisz/repos","events_url":"https://api.github.com/users/emmareisz/events{/privacy}","received_events_url":"https://api.github.com/users/emmareisz/received_events","type":"User","site_admin":false},"body":"Translator for CONTENTdm archive and library database software, v6 with limited support for v4 and v5.","created_at":"2015-11-02T11:04:09Z","updated_at":"2015-11-03T05:48:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"556be05c88b33862764a7457c0d638324e09c9d6","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/zotero/translators/pulls/968/commits","review_comments_url":"https://api.github.com/repos/zotero/translators/pulls/968/comments","review_comment_url":"https://api.github.com/repos/zotero/translators/pulls/comments{/number}","comments_url":"https://api.github.com/repos/zotero/translators/issues/968/comments","statuses_url":"https://api.github.com/repos/zotero/translators/statuses/846d3bea99fd320aa9ea82897c8378137e3109f9","head":{"label":"emmareisz:master","ref":"master","sha":"846d3bea99fd320aa9ea82897c8378137e3109f9","user":{"login":"emmareisz","id":8190209,"avatar_url":"https://avatars.githubusercontent.com/u/8190209?v=3","gravatar_id":"","url":"https://api.github.com/users/emmareisz","html_url":"https://github.com/emmareisz","followers_url":"https://api.github.com/users/emmareisz/followers","following_url":"https://api.github.com/users/emmareisz/following{/other_user}","gists_url":"https://api.github.com/users/emmareisz/gists{/gist_id}","starred_url":"https://api.github.com/users/emmareisz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emmareisz/subscriptions","organizations_url":"https://api.github.com/users/emmareisz/orgs","repos_url":"https://api.github.com/users/emmareisz/repos","events_url":"https://api.github.com/users/emmareisz/events{/privacy}","received_events_url":"https://api.github.com/users/emmareisz/received_events","type":"User","site_admin":false},"repo":{"id":45389143,"name":"translators","full_name":"emmareisz/translators","owner":{"login":"emmareisz","id":8190209,"avatar_url":"https://avatars.githubusercontent.com/u/8190209?v=3","gravatar_id":"","url":"https://api.github.com/users/emmareisz","html_url":"https://github.com/emmareisz","followers_url":"https://api.github.com/users/emmareisz/followers","following_url":"https://api.github.com/users/emmareisz/following{/other_user}","gists_url":"https://api.github.com/users/emmareisz/gists{/gist_id}","starred_url":"https://api.github.com/users/emmareisz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emmareisz/subscriptions","organizations_url":"https://api.github.com/users/emmareisz/orgs","repos_url":"https://api.github.com/users/emmareisz/repos","events_url":"https://api.github.com/users/emmareisz/events{/privacy}","received_events_url":"https://api.github.com/users/emmareisz/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/emmareisz/translators","description":"Zotero Translators","fork":true,"url":"https://api.github.com/repos/emmareisz/translators","forks_url":"https://api.github.com/repos/emmareisz/translators/forks","keys_url":"https://api.github.com/repos/emmareisz/translators/keys{/key_id}","collaborators_url":"https://api.github.com/repos/emmareisz/translators/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/emmareisz/translators/teams","hooks_url":"https://api.github.com/repos/emmareisz/translators/hooks","issue_events_url":"https://api.github.com/repos/emmareisz/translators/issues/events{/number}","events_url":"https://api.github.com/repos/emmareisz/translators/events","assignees_url":"https://api.github.com/repos/emmareisz/translators/assignees{/user}","branches_url":"https://api.github.com/repos/emmareisz/translators/branches{/branch}","tags_url":"https://api.github.com/repos/emmareisz/translators/tags","blobs_url":"https://api.github.com/repos/emmareisz/translators/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/emmareisz/translators/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/emmareisz/translators/git/refs{/sha}","trees_url":"https://api.github.com/repos/emmareisz/translators/git/trees{/sha}","statuses_url":"https://api.github.com/repos/emmareisz/translators/statuses/{sha}","languages_url":"https://api.github.com/repos/emmareisz/translators/languages","stargazers_url":"https://api.github.com/repos/emmareisz/translators/stargazers","contributors_url":"https://api.github.com/repos/emmareisz/translators/contributors","subscribers_url":"https://api.github.com/repos/emmareisz/translators/subscribers","subscription_url":"https://api.github.com/repos/emmareisz/translators/subscription","commits_url":"https://api.github.com/repos/emmareisz/translators/commits{/sha}","git_commits_url":"https://api.github.com/repos/emmareisz/translators/git/commits{/sha}","comments_url":"https://api.github.com/repos/emmareisz/translators/comments{/number}","issue_comment_url":"https://api.github.com/repos/emmareisz/translators/issues/comments{/number}","contents_url":"https://api.github.com/repos/emmareisz/translators/contents/{+path}","compare_url":"https://api.github.com/repos/emmareisz/translators/compare/{base}...{head}","merges_url":"https://api.github.com/repos/emmareisz/translators/merges","archive_url":"https://api.github.com/repos/emmareisz/translators/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/emmareisz/translators/downloads","issues_url":"https://api.github.com/repos/emmareisz/translators/issues{/number}","pulls_url":"https://api.github.com/repos/emmareisz/translators/pulls{/number}","milestones_url":"https://api.github.com/repos/emmareisz/translators/milestones{/number}","notifications_url":"https://api.github.com/repos/emmareisz/translators/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/emmareisz/translators/labels{/name}","releases_url":"https://api.github.com/repos/emmareisz/translators/releases{/id}","created_at":"2015-11-02T10:58:11Z","updated_at":"2015-11-02T10:58:14Z","pushed_at":"2015-11-02T11:02:39Z","git_url":"git://github.com/emmareisz/translators.git","ssh_url":"git@github.com:emmareisz/translators.git","clone_url":"https://github.com/emmareisz/translators.git","svn_url":"https://github.com/emmareisz/translators","homepage":"http://www.zotero.org/support/dev/translators","size":17479,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"zotero:master","ref":"master","sha":"38a41f3ac2001657e240ad7642c3009006b809c7","user":{"login":"zotero","id":891917,"avatar_url":"https://avatars.githubusercontent.com/u/891917?v=3","gravatar_id":"","url":"https://api.github.com/users/zotero","html_url":"https://github.com/zotero","followers_url":"https://api.github.com/users/zotero/followers","following_url":"https://api.github.com/users/zotero/following{/other_user}","gists_url":"https://api.github.com/users/zotero/gists{/gist_id}","starred_url":"https://api.github.com/users/zotero/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zotero/subscriptions","organizations_url":"https://api.github.com/users/zotero/orgs","repos_url":"https://api.github.com/users/zotero/repos","events_url":"https://api.github.com/users/zotero/events{/privacy}","received_events_url":"https://api.github.com/users/zotero/received_events","type":"Organization","site_admin":false},"repo":{"id":1991863,"name":"translators","full_name":"zotero/translators","owner":{"login":"zotero","id":891917,"avatar_url":"https://avatars.githubusercontent.com/u/891917?v=3","gravatar_id":"","url":"https://api.github.com/users/zotero","html_url":"https://github.com/zotero","followers_url":"https://api.github.com/users/zotero/followers","following_url":"https://api.github.com/users/zotero/following{/other_user}","gists_url":"https://api.github.com/users/zotero/gists{/gist_id}","starred_url":"https://api.github.com/users/zotero/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zotero/subscriptions","organizations_url":"https://api.github.com/users/zotero/orgs","repos_url":"https://api.github.com/users/zotero/repos","events_url":"https://api.github.com/users/zotero/events{/privacy}","received_events_url":"https://api.github.com/users/zotero/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zotero/translators","description":"Zotero Translators","fork":false,"url":"https://api.github.com/repos/zotero/translators","forks_url":"https://api.github.com/repos/zotero/translators/forks","keys_url":"https://api.github.com/repos/zotero/translators/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zotero/translators/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zotero/translators/teams","hooks_url":"https://api.github.com/repos/zotero/translators/hooks","issue_events_url":"https://api.github.com/repos/zotero/translators/issues/events{/number}","events_url":"https://api.github.com/repos/zotero/translators/events","assignees_url":"https://api.github.com/repos/zotero/translators/assignees{/user}","branches_url":"https://api.github.com/repos/zotero/translators/branches{/branch}","tags_url":"https://api.github.com/repos/zotero/translators/tags","blobs_url":"https://api.github.com/repos/zotero/translators/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zotero/translators/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zotero/translators/git/refs{/sha}","trees_url":"https://api.github.com/repos/zotero/translators/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zotero/translators/statuses/{sha}","languages_url":"https://api.github.com/repos/zotero/translators/languages","stargazers_url":"https://api.github.com/repos/zotero/translators/stargazers","contributors_url":"https://api.github.com/repos/zotero/translators/contributors","subscribers_url":"https://api.github.com/repos/zotero/translators/subscribers","subscription_url":"https://api.github.com/repos/zotero/translators/subscription","commits_url":"https://api.github.com/repos/zotero/translators/commits{/sha}","git_commits_url":"https://api.github.com/repos/zotero/translators/git/commits{/sha}","comments_url":"https://api.github.com/repos/zotero/translators/comments{/number}","issue_comment_url":"https://api.github.com/repos/zotero/translators/issues/comments{/number}","contents_url":"https://api.github.com/repos/zotero/translators/contents/{+path}","compare_url":"https://api.github.com/repos/zotero/translators/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zotero/translators/merges","archive_url":"https://api.github.com/repos/zotero/translators/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zotero/translators/downloads","issues_url":"https://api.github.com/repos/zotero/translators/issues{/number}","pulls_url":"https://api.github.com/repos/zotero/translators/pulls{/number}","milestones_url":"https://api.github.com/repos/zotero/translators/milestones{/number}","notifications_url":"https://api.github.com/repos/zotero/translators/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zotero/translators/labels{/name}","releases_url":"https://api.github.com/repos/zotero/translators/releases{/id}","created_at":"2011-07-03T17:40:38Z","updated_at":"2015-10-27T10:12:59Z","pushed_at":"2015-11-02T11:04:10Z","git_url":"git://github.com/zotero/translators.git","ssh_url":"git@github.com:zotero/translators.git","clone_url":"https://github.com/zotero/translators.git","svn_url":"https://github.com/zotero/translators","homepage":"http://www.zotero.org/support/dev/translators","size":17479,"stargazers_count":155,"watchers_count":155,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":140,"mirror_url":null,"open_issues_count":133,"forks":140,"open_issues":133,"watchers":155,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/zotero/translators/pulls/968"},"html":{"href":"https://github.com/zotero/translators/pull/968"},"issue":{"href":"https://api.github.com/repos/zotero/translators/issues/968"},"comments":{"href":"https://api.github.com/repos/zotero/translators/issues/968/comments"},"review_comments":{"href":"https://api.github.com/repos/zotero/translators/pulls/968/comments"},"review_comment":{"href":"https://api.github.com/repos/zotero/translators/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/zotero/translators/pulls/968/commits"},"statuses":{"href":"https://api.github.com/repos/zotero/translators/statuses/846d3bea99fd320aa9ea82897c8378137e3109f9"}}}}