/** * Copyright 2014 Google Inc. All rights reserved. * Author: Denny Vrandecic * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ ;(function ($, window, document, undefined) { var language = null; var labels = {}; var loaders = {}; loaders.count = 0; // private functions var checkString = function(str) { if (!(typeof str == 'string' || str instanceof String)) { throw "parameter is not a string"; } }; var checkLanguage = function(lang) { checkString(lang); if (!(/^[a-z\-]+$/i.test(lang))) { throw "lang parameter is not a lang code"; } }; var setLanguage = function(lang) { checkLanguage(lang); language = lang; }; var display = function() { $('.qlabel').each(function(index, element) { var id = getURI(element); if (id !== undefined) { var label = getLabel(getURI(element), getLanguage()); if (label !== undefined) $(element).text(label); } }); }; var getURI = function(element) { var $this = $(element); var uri = undefined; $.each(['data-qlabel', 'its-ta-ident-ref', 'resource', 'about', 'href'], function(index, attribute) { if ($this.attr(attribute) !== undefined) { uri = $this.attr(attribute); return false; } }); return uri; }; var gatherURIs = function() { return $('.qlabel').map(function() { return getURI(this); }).get(); }; var checkURI = checkString; // TODO, could be made a bit tighter var normalizeURI = function(URI) { for (var i=loaders.count-1; i>=0; i--) { var result = loaders[i].tester(URI); if (result !== undefined) { return result; } } return URI; }; var checkLabel = checkString; var checkURIs = function(URIs) { if (!$.isArray(URIs)) { throw "URIs parameter is wrong" } $(URIs).each(function(index, URI) { checkURI(URI); }); }; var checkTester = $.isFunction; var checkLoader = $.isFunction; // loaders // Wikidata loader var wikidataTester = function(URI) { var pattern1 = /^Q\d+$/i; if (pattern1.test(URI)) return URI; var pattern2 = /^https?:\/\/(?:www\.)?wikidata\.org\/(?:wiki|entity)\/(Q\d+)$/i; var m = URI.match(pattern2); if (m === null) return undefined; return m[1]; }; var wikidataLoader = function(URIs) { // TODO deal with more than 50 entities return $.getJSON('https://www.wikidata.org/w/api.php?callback=?', { action: 'wbgetentities' , ids: URIs.join('|'), props: 'labels' , format: 'json' }, function(data) { // TODO handle errors and exceptions $.each(data.entities, function(id, entity) { $.each(entity.labels, function(lang, label) { setLabel(id, lang, label.value); }); }); }); }; // Freebase loader var freebaseTester = function(URI) { var pattern1 = /^\/(m|g)\/[0-9a-z_]+$/i; if (pattern1.test(URI)) { return URI; } var pattern2 = /^https?:\/\/(?:www\.)?freebase\.com(\/(m|g)\/[0-9a-z_]+)$/i; var m = URI.match(pattern2); if (m !== null) { return m[1]; } var pattern3 = /^https?:\/\/ns\.freebase\.com(\/(m|g)\.[0-9a-z_]+)$/i; m = URI.match(pattern3); if (m === null) { return undefined; } return m[1].slice(0,2) + '/' + m[1].slice(3); }; var freebaseLoader = function(URIs) { var param = { 'filter': '(any id:' + URIs.join(' id:') + ')', 'output': '(/type/object/name)', // TODO only for these languages 'lang': 'en,es,fr,de,it,pt,zh,ja,ko,ru,sv,fi,da,nl,el,ro,tr,hu,th,pl,cs,id,bg,uk,ca,eu,no,sl,sk,hr,sr,ar,hi,vi,fa,ga,iw,lv,lt,fil', }; if (getFreebaseKey() !== '') { param.key = getFreebaseKey(); } return $.getJSON('https://www.googleapis.com/freebase/v1/search?callback=?', param, function(response) { // TODO handle errors and exceptions $.each(response.result, function(index, res) { $.each(res.output['/type/object/name']['/type/object/name'], function(index, name) { setLabel(res.mid, name.lang, name.value); }); }); }); }; var freebaseKey = ''; var setFreebaseKey = function(key) { checkString(key); freebaseKey = key; }; var getFreebaseKey = function() { return freebaseKey; }; // semweb loader var semwebTester = function(URI) { return URI; }; var semwebLoader = function(URIs) { // check if rdfquery library is available if (!('rdf' in $)) { throw 'semwebLoader: rdfquery library is missing'; } var promises = $.map(URIs, function(URI) { // TODO using any23.org in order to avoid CORS problems return $.get('http://any23.org/rdfxml/' + URI, function(data) { // TODO handle errors and exceptions var graph = $.rdf().load(data); var labelset = graph.where('<' + URI + '> ?label'); $.each(labelset, function(index, label) { setLabel(URI, label.label.lang, label.label.value.slice(1,-1)); // TODO why do I have to use the slice? It seems that rdfquery is adding extra quotes }); }); }); return $.when.apply($, promises).promise(); }; // public functions var switchLanguage = function(lang) { setLanguage(lang); return loadLabels(gatherURIs()).then(display).promise(); }; var getLanguage = function() { return language; }; var setLabel = function(URI, lang, label) { checkURI(URI); checkLanguage(lang); checkLabel(label); URI = normalizeURI(URI); if (!(URI in labels)) { labels[URI] = {}; } labels[URI][lang] = label; return getLabel(URI, lang); }; var getLabel = function(URI, lang) { checkURI(URI); checkLanguage(lang); URI = normalizeURI(URI); if (URI in labels) { if (lang in labels[URI]) { return labels[URI][lang]; } } return undefined; }; var getLabels = function(URI) { checkURI(URI); URI = normalizeURI(URI); if (URI in labels) { return $.extend({}, labels[URI]); } return undefined; }; // TODO this function can be cleaned up var loadLabels = function(URIs) { if (URIs === [] || URIs === undefined || URIs === null) { URIs = gatherURIs(); } checkURIs(URIs); // do not load already loaded URIs: URIs = URIs - knownuris URIs = $.map(URIs, function(uri, index) { var normalURI = normalizeURI(uri); if ($.inArray(normalURI, getKnownURIs()) !== -1) { return undefined; } return normalURI; }); var promises = []; for (var i=loaders.count-1; i>=0; i--) { var rest = []; var loadableURIs = $.map(URIs, function(uri, index) { var normalURI = loaders[i].tester(uri); if (normalURI === undefined) rest.push(uri); return normalURI; }); if (loadableURIs.length > 0) { promises.push(loaders[i].loader(loadableURIs)); } URIs = rest; } return $.when.apply($, promises).promise(); }; var getKnownURIs = function() { return $.map(labels, function(value, key) { return key; }); }; var setLoader = function(tester, loader) { checkTester(tester); checkLoader(loader); loaders[loaders.count] = { tester: tester, loader: loader }; return ++loaders.count; }; $.qLabel = { switchLanguage: switchLanguage, getLanguage: getLanguage, setLabel: setLabel, getLabel: getLabel, getLabels: getLabels, loadLabels: loadLabels, getKnownURIs: getKnownURIs, setLoader: setLoader, setFreebaseKey: setFreebaseKey, getFreebaseKey: getFreebaseKey }; setLoader(semwebTester, semwebLoader); setLoader(freebaseTester, freebaseLoader); setLoader(wikidataTester, wikidataLoader); // public interface documentation // switchLanguage(lang) - set the language and switch the display // getLanguage() - get the currently set language // getLabel(URI, lang) - return the label of the URI in the given language // getLabels(URI) - return all labels for a URI // setLabel(URI, lang, label) - set a specific label in the given language // loadLabels([callback, [URIs]]) - load the URIs, then execute callback. If no // URIs are given, load all URIs mentioned on the page in qlabel elements. // callback takes no parameters. // getKnownURIs() - return all loaded URIs // setLoader(tester, loader) - set a loader for URIs that will be run when the // tester function hits. signature of the callback functions: // string tester(string URI) - takes a URI and returns a posibly normalized // URI to load, or undefined otherwise // promise loader([string] URIs) - takes an array of URIs and returns a // promise. The promise returns URIs to languages to labels. // setFreebaseKey, getFreebaseKey - set and get the Freebase API key })(jQuery, window, document);