/**
* Copyright 2014 Google Inc. All rights reserved. * Author: Denny Vrandecic <vrandecic@google.com> * * 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 + '> <http://www.w3.org/2000/01/rdf-schema#label> ?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( 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);