mirror of
https://github.com/gnosygnu/xowa.git
synced 2026-03-02 03:49:30 +00:00
Res: Add resources from xowa_app_windows_64_v4.5.26.1810
This commit is contained in:
3
res/bin/any/xowa/xtns/Graph/lib/d3-global.js
vendored
Normal file
3
res/bin/any/xowa/xtns/Graph/lib/d3-global.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Back-compat: Export module as global
|
||||
// XOWA: unused b/c module is not available
|
||||
window.d3 = module.exports;
|
||||
9554
res/bin/any/xowa/xtns/Graph/lib/d3.js
vendored
Normal file
9554
res/bin/any/xowa/xtns/Graph/lib/d3.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
505
res/bin/any/xowa/xtns/Graph/lib/d3.layout.cloud.js
Normal file
505
res/bin/any/xowa/xtns/Graph/lib/d3.layout.cloud.js
Normal file
@@ -0,0 +1,505 @@
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g=(g.d3||(g.d3 = {}));g=(g.layout||(g.layout = {}));g.cloud = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
// Word cloud layout by Jason Davies, https://www.jasondavies.com/wordcloud/
|
||||
// Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf
|
||||
|
||||
var dispatch = require("d3-dispatch").dispatch;
|
||||
|
||||
var cloudRadians = Math.PI / 180,
|
||||
cw = 1 << 11 >> 5,
|
||||
ch = 1 << 11;
|
||||
|
||||
module.exports = function() {
|
||||
var size = [256, 256],
|
||||
text = cloudText,
|
||||
font = cloudFont,
|
||||
fontSize = cloudFontSize,
|
||||
fontStyle = cloudFontNormal,
|
||||
fontWeight = cloudFontNormal,
|
||||
rotate = cloudRotate,
|
||||
padding = cloudPadding,
|
||||
spiral = archimedeanSpiral,
|
||||
words = [],
|
||||
timeInterval = Infinity,
|
||||
event = dispatch("word", "end"),
|
||||
timer = null,
|
||||
random = Math.random,
|
||||
cloud = {},
|
||||
canvas = cloudCanvas;
|
||||
|
||||
cloud.canvas = function(_) {
|
||||
return arguments.length ? (canvas = functor(_), cloud) : canvas;
|
||||
};
|
||||
|
||||
cloud.start = function() {
|
||||
var contextAndRatio = getContext(canvas()),
|
||||
board = zeroArray((size[0] >> 5) * size[1]),
|
||||
bounds = null,
|
||||
n = words.length,
|
||||
i = -1,
|
||||
tags = [],
|
||||
data = words.map(function(d, i) {
|
||||
d.text = text.call(this, d, i);
|
||||
d.font = font.call(this, d, i);
|
||||
d.style = fontStyle.call(this, d, i);
|
||||
d.weight = fontWeight.call(this, d, i);
|
||||
d.rotate = rotate.call(this, d, i);
|
||||
d.size = ~~fontSize.call(this, d, i);
|
||||
d.padding = padding.call(this, d, i);
|
||||
return d;
|
||||
}).sort(function(a, b) { return b.size - a.size; });
|
||||
|
||||
if (timer) clearInterval(timer);
|
||||
timer = setInterval(step, 0);
|
||||
step();
|
||||
|
||||
return cloud;
|
||||
|
||||
function step() {
|
||||
var start = Date.now();
|
||||
while (Date.now() - start < timeInterval && ++i < n && timer) {
|
||||
var d = data[i];
|
||||
d.x = (size[0] * (random() + .5)) >> 1;
|
||||
d.y = (size[1] * (random() + .5)) >> 1;
|
||||
cloudSprite(contextAndRatio, d, data, i);
|
||||
if (d.hasText && place(board, d, bounds)) {
|
||||
tags.push(d);
|
||||
event.word(d);
|
||||
if (bounds) cloudBounds(bounds, d);
|
||||
else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}];
|
||||
// Temporary hack
|
||||
d.x -= size[0] >> 1;
|
||||
d.y -= size[1] >> 1;
|
||||
}
|
||||
}
|
||||
if (i >= n) {
|
||||
cloud.stop();
|
||||
event.end(tags, bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloud.stop = function() {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
return cloud;
|
||||
};
|
||||
|
||||
function getContext(canvas) {
|
||||
canvas.width = canvas.height = 1;
|
||||
var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
|
||||
canvas.width = (cw << 5) / ratio;
|
||||
canvas.height = ch / ratio;
|
||||
|
||||
var context = canvas.getContext("2d");
|
||||
context.fillStyle = context.strokeStyle = "red";
|
||||
context.textAlign = "center";
|
||||
|
||||
return {context: context, ratio: ratio};
|
||||
}
|
||||
|
||||
function place(board, tag, bounds) {
|
||||
var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}],
|
||||
startX = tag.x,
|
||||
startY = tag.y,
|
||||
maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]),
|
||||
s = spiral(size),
|
||||
dt = random() < .5 ? 1 : -1,
|
||||
t = -dt,
|
||||
dxdy,
|
||||
dx,
|
||||
dy;
|
||||
|
||||
while (dxdy = s(t += dt)) {
|
||||
dx = ~~dxdy[0];
|
||||
dy = ~~dxdy[1];
|
||||
|
||||
if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break;
|
||||
|
||||
tag.x = startX + dx;
|
||||
tag.y = startY + dy;
|
||||
|
||||
if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 ||
|
||||
tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue;
|
||||
// TODO only check for collisions within current bounds.
|
||||
if (!bounds || !cloudCollide(tag, board, size[0])) {
|
||||
if (!bounds || collideRects(tag, bounds)) {
|
||||
var sprite = tag.sprite,
|
||||
w = tag.width >> 5,
|
||||
sw = size[0] >> 5,
|
||||
lx = tag.x - (w << 4),
|
||||
sx = lx & 0x7f,
|
||||
msx = 32 - sx,
|
||||
h = tag.y1 - tag.y0,
|
||||
x = (tag.y + tag.y0) * sw + (lx >> 5),
|
||||
last;
|
||||
for (var j = 0; j < h; j++) {
|
||||
last = 0;
|
||||
for (var i = 0; i <= w; i++) {
|
||||
board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
|
||||
}
|
||||
x += sw;
|
||||
}
|
||||
delete tag.sprite;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
cloud.timeInterval = function(_) {
|
||||
return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval;
|
||||
};
|
||||
|
||||
cloud.words = function(_) {
|
||||
return arguments.length ? (words = _, cloud) : words;
|
||||
};
|
||||
|
||||
cloud.size = function(_) {
|
||||
return arguments.length ? (size = [+_[0], +_[1]], cloud) : size;
|
||||
};
|
||||
|
||||
cloud.font = function(_) {
|
||||
return arguments.length ? (font = functor(_), cloud) : font;
|
||||
};
|
||||
|
||||
cloud.fontStyle = function(_) {
|
||||
return arguments.length ? (fontStyle = functor(_), cloud) : fontStyle;
|
||||
};
|
||||
|
||||
cloud.fontWeight = function(_) {
|
||||
return arguments.length ? (fontWeight = functor(_), cloud) : fontWeight;
|
||||
};
|
||||
|
||||
cloud.rotate = function(_) {
|
||||
return arguments.length ? (rotate = functor(_), cloud) : rotate;
|
||||
};
|
||||
|
||||
cloud.text = function(_) {
|
||||
return arguments.length ? (text = functor(_), cloud) : text;
|
||||
};
|
||||
|
||||
cloud.spiral = function(_) {
|
||||
return arguments.length ? (spiral = spirals[_] || _, cloud) : spiral;
|
||||
};
|
||||
|
||||
cloud.fontSize = function(_) {
|
||||
return arguments.length ? (fontSize = functor(_), cloud) : fontSize;
|
||||
};
|
||||
|
||||
cloud.padding = function(_) {
|
||||
return arguments.length ? (padding = functor(_), cloud) : padding;
|
||||
};
|
||||
|
||||
cloud.random = function(_) {
|
||||
return arguments.length ? (random = _, cloud) : random;
|
||||
};
|
||||
|
||||
cloud.on = function() {
|
||||
var value = event.on.apply(event, arguments);
|
||||
return value === event ? cloud : value;
|
||||
};
|
||||
|
||||
return cloud;
|
||||
};
|
||||
|
||||
function cloudText(d) {
|
||||
return d.text;
|
||||
}
|
||||
|
||||
function cloudFont() {
|
||||
return "serif";
|
||||
}
|
||||
|
||||
function cloudFontNormal() {
|
||||
return "normal";
|
||||
}
|
||||
|
||||
function cloudFontSize(d) {
|
||||
return Math.sqrt(d.value);
|
||||
}
|
||||
|
||||
function cloudRotate() {
|
||||
return (~~(Math.random() * 6) - 3) * 30;
|
||||
}
|
||||
|
||||
function cloudPadding() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Fetches a monochrome sprite bitmap for the specified text.
|
||||
// Load in batches for speed.
|
||||
function cloudSprite(contextAndRatio, d, data, di) {
|
||||
if (d.sprite) return;
|
||||
var c = contextAndRatio.context,
|
||||
ratio = contextAndRatio.ratio;
|
||||
|
||||
c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
|
||||
var x = 0,
|
||||
y = 0,
|
||||
maxh = 0,
|
||||
n = data.length;
|
||||
--di;
|
||||
while (++di < n) {
|
||||
d = data[di];
|
||||
c.save();
|
||||
c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font;
|
||||
var w = c.measureText(d.text + "m").width * ratio,
|
||||
h = d.size << 1;
|
||||
if (d.rotate) {
|
||||
var sr = Math.sin(d.rotate * cloudRadians),
|
||||
cr = Math.cos(d.rotate * cloudRadians),
|
||||
wcr = w * cr,
|
||||
wsr = w * sr,
|
||||
hcr = h * cr,
|
||||
hsr = h * sr;
|
||||
w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5;
|
||||
h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
|
||||
} else {
|
||||
w = (w + 0x1f) >> 5 << 5;
|
||||
}
|
||||
if (h > maxh) maxh = h;
|
||||
if (x + w >= (cw << 5)) {
|
||||
x = 0;
|
||||
y += maxh;
|
||||
maxh = 0;
|
||||
}
|
||||
if (y + h >= ch) break;
|
||||
c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio);
|
||||
if (d.rotate) c.rotate(d.rotate * cloudRadians);
|
||||
c.fillText(d.text, 0, 0);
|
||||
if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0);
|
||||
c.restore();
|
||||
d.width = w;
|
||||
d.height = h;
|
||||
d.xoff = x;
|
||||
d.yoff = y;
|
||||
d.x1 = w >> 1;
|
||||
d.y1 = h >> 1;
|
||||
d.x0 = -d.x1;
|
||||
d.y0 = -d.y1;
|
||||
d.hasText = true;
|
||||
x += w;
|
||||
}
|
||||
var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data,
|
||||
sprite = [];
|
||||
while (--di >= 0) {
|
||||
d = data[di];
|
||||
if (!d.hasText) continue;
|
||||
var w = d.width,
|
||||
w32 = w >> 5,
|
||||
h = d.y1 - d.y0;
|
||||
// Zero the buffer
|
||||
for (var i = 0; i < h * w32; i++) sprite[i] = 0;
|
||||
x = d.xoff;
|
||||
if (x == null) return;
|
||||
y = d.yoff;
|
||||
var seen = 0,
|
||||
seenRow = -1;
|
||||
for (var j = 0; j < h; j++) {
|
||||
for (var i = 0; i < w; i++) {
|
||||
var k = w32 * j + (i >> 5),
|
||||
m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0;
|
||||
sprite[k] |= m;
|
||||
seen |= m;
|
||||
}
|
||||
if (seen) seenRow = j;
|
||||
else {
|
||||
d.y0++;
|
||||
h--;
|
||||
j--;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
d.y1 = d.y0 + seenRow;
|
||||
d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
|
||||
}
|
||||
}
|
||||
|
||||
// Use mask-based collision detection.
|
||||
function cloudCollide(tag, board, sw) {
|
||||
sw >>= 5;
|
||||
var sprite = tag.sprite,
|
||||
w = tag.width >> 5,
|
||||
lx = tag.x - (w << 4),
|
||||
sx = lx & 0x7f,
|
||||
msx = 32 - sx,
|
||||
h = tag.y1 - tag.y0,
|
||||
x = (tag.y + tag.y0) * sw + (lx >> 5),
|
||||
last;
|
||||
for (var j = 0; j < h; j++) {
|
||||
last = 0;
|
||||
for (var i = 0; i <= w; i++) {
|
||||
if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0))
|
||||
& board[x + i]) return true;
|
||||
}
|
||||
x += sw;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function cloudBounds(bounds, d) {
|
||||
var b0 = bounds[0],
|
||||
b1 = bounds[1];
|
||||
if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0;
|
||||
if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0;
|
||||
if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1;
|
||||
if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1;
|
||||
}
|
||||
|
||||
function collideRects(a, b) {
|
||||
return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y;
|
||||
}
|
||||
|
||||
function archimedeanSpiral(size) {
|
||||
var e = size[0] / size[1];
|
||||
return function(t) {
|
||||
return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)];
|
||||
};
|
||||
}
|
||||
|
||||
function rectangularSpiral(size) {
|
||||
var dy = 4,
|
||||
dx = dy * size[0] / size[1],
|
||||
x = 0,
|
||||
y = 0;
|
||||
return function(t) {
|
||||
var sign = t < 0 ? -1 : 1;
|
||||
// See triangular numbers: T_n = n * (n + 1) / 2.
|
||||
switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) {
|
||||
case 0: x += dx; break;
|
||||
case 1: y += dy; break;
|
||||
case 2: x -= dx; break;
|
||||
default: y -= dy; break;
|
||||
}
|
||||
return [x, y];
|
||||
};
|
||||
}
|
||||
|
||||
// TODO reuse arrays?
|
||||
function zeroArray(n) {
|
||||
var a = [],
|
||||
i = -1;
|
||||
while (++i < n) a[i] = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
function cloudCanvas() {
|
||||
return document.createElement("canvas");
|
||||
}
|
||||
|
||||
function functor(d) {
|
||||
return typeof d === "function" ? d : function() { return d; };
|
||||
}
|
||||
|
||||
var spirals = {
|
||||
archimedean: archimedeanSpiral,
|
||||
rectangular: rectangularSpiral
|
||||
};
|
||||
|
||||
},{"d3-dispatch":2}],2:[function(require,module,exports){
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
factory((global.dispatch = {}));
|
||||
}(this, function (exports) { 'use strict';
|
||||
|
||||
function Dispatch(types) {
|
||||
var i = -1,
|
||||
n = types.length,
|
||||
callbacksByType = {},
|
||||
callbackByName = {},
|
||||
type,
|
||||
that = this;
|
||||
|
||||
that.on = function(type, callback) {
|
||||
type = parseType(type);
|
||||
|
||||
// Return the current callback, if any.
|
||||
if (arguments.length < 2) {
|
||||
return (callback = callbackByName[type.name]) && callback.value;
|
||||
}
|
||||
|
||||
// If a type was specified…
|
||||
if (type.type) {
|
||||
var callbacks = callbacksByType[type.type],
|
||||
callback0 = callbackByName[type.name],
|
||||
i;
|
||||
|
||||
// Remove the current callback, if any, using copy-on-remove.
|
||||
if (callback0) {
|
||||
callback0.value = null;
|
||||
i = callbacks.indexOf(callback0);
|
||||
callbacksByType[type.type] = callbacks = callbacks.slice(0, i).concat(callbacks.slice(i + 1));
|
||||
delete callbackByName[type.name];
|
||||
}
|
||||
|
||||
// Add the new callback, if any.
|
||||
if (callback) {
|
||||
callback = {value: callback};
|
||||
callbackByName[type.name] = callback;
|
||||
callbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, if a null callback was specified, remove all callbacks with the given name.
|
||||
else if (callback == null) {
|
||||
for (var otherType in callbacksByType) {
|
||||
if (callback = callbackByName[otherType + type.name]) {
|
||||
callback.value = null;
|
||||
var callbacks = callbacksByType[otherType], i = callbacks.indexOf(callback);
|
||||
callbacksByType[otherType] = callbacks.slice(0, i).concat(callbacks.slice(i + 1));
|
||||
delete callbackByName[callback.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
while (++i < n) {
|
||||
type = types[i] + "";
|
||||
if (!type || (type in that)) throw new Error("illegal or duplicate type: " + type);
|
||||
callbacksByType[type] = [];
|
||||
that[type] = applier(type);
|
||||
}
|
||||
|
||||
function parseType(type) {
|
||||
var i = (type += "").indexOf("."), name = type;
|
||||
if (i >= 0) type = type.slice(0, i); else name += ".";
|
||||
if (type && !callbacksByType.hasOwnProperty(type)) throw new Error("unknown type: " + type);
|
||||
return {type: type, name: name};
|
||||
}
|
||||
|
||||
function applier(type) {
|
||||
return function() {
|
||||
var callbacks = callbacksByType[type], // Defensive reference; copy-on-remove.
|
||||
callback,
|
||||
callbackValue,
|
||||
i = -1,
|
||||
n = callbacks.length;
|
||||
|
||||
while (++i < n) {
|
||||
if (callbackValue = (callback = callbacks[i]).value) {
|
||||
callbackValue.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
return that;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function dispatch() {
|
||||
return new Dispatch(arguments);
|
||||
}
|
||||
|
||||
dispatch.prototype = Dispatch.prototype; // allow instanceof
|
||||
|
||||
exports.dispatch = dispatch;
|
||||
|
||||
}));
|
||||
},{}]},{},[1])(1)
|
||||
});
|
||||
697
res/bin/any/xowa/xtns/Graph/lib/graph2.compiled.js
Normal file
697
res/bin/any/xowa/xtns/Graph/lib/graph2.compiled.js
Normal file
@@ -0,0 +1,697 @@
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
( function ( $, mw, vg ) {
|
||||
|
||||
'use strict';
|
||||
/* global require */
|
||||
|
||||
var VegaWrapper = require( 'mw-graph-shared' );
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new VegaWrapper( {
|
||||
datalib: vg.util,
|
||||
useXhr: true,
|
||||
isTrusted: true, // mw.config.get( 'wgGraphIsTrusted' ),
|
||||
domains: '', // mw.config.get( 'wgGraphAllowedDomains' ),
|
||||
domainMap: false,
|
||||
logger: function ( warning ) {
|
||||
mw.log.warn( warning );
|
||||
},
|
||||
parseUrl: function ( opt ) {
|
||||
// Parse URL
|
||||
var uri = {}; // new mw.Uri( opt.url );
|
||||
|
||||
// reduce confusion, only keep expected values
|
||||
if ( uri.port ) {
|
||||
uri.host += ':' + uri.port;
|
||||
delete uri.port;
|
||||
}
|
||||
// If url begins with protocol:///... mark it as having relative host
|
||||
if ( /^[a-z]+:\/\/\//.test( opt.url ) ) {
|
||||
uri.isRelativeHost = true;
|
||||
}
|
||||
if ( uri.protocol ) {
|
||||
// All other libs use trailing colon in the protocol field
|
||||
uri.protocol += ':';
|
||||
}
|
||||
// Node's path includes the query, whereas pathname is without the query
|
||||
// Standardizing on pathname
|
||||
uri.pathname = uri.path;
|
||||
|
||||
delete uri.path;
|
||||
return uri;
|
||||
},
|
||||
formatUrl: function ( uri, opt ) {
|
||||
// Format URL back into a string
|
||||
// Revert path into pathname
|
||||
uri.path = uri.pathname;
|
||||
delete uri.pathname;
|
||||
|
||||
if ( location.host.toLowerCase() === uri.host.toLowerCase() ) {
|
||||
if ( !mw.config.get( 'wgGraphIsTrusted' ) ) {
|
||||
// Only send this header when hostname is the same.
|
||||
// This is broader than the same-origin policy,
|
||||
// but playing on the safer side.
|
||||
opt.headers = { 'Treat-as-Untrusted': 1 };
|
||||
}
|
||||
} else if ( opt.addCorsOrigin ) {
|
||||
// All CORS api calls require origin parameter.
|
||||
// It would be better to use location.origin,
|
||||
// but apparently it's not universal yet.
|
||||
uri.query.origin = location.protocol + '//' + location.host;
|
||||
}
|
||||
|
||||
uri.protocol = VegaWrapper.removeColon( uri.protocol );
|
||||
|
||||
return uri.toString();
|
||||
},
|
||||
languageCode: 'en' // mw.config.get( 'wgUserLanguage' )
|
||||
} );
|
||||
|
||||
/**
|
||||
* Set up drawing canvas inside the given element and draw graph data
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {Object|string} data graph spec
|
||||
* @param {Function} [callback] function(error) called when drawing is done
|
||||
*/
|
||||
window.drawVegaGraph = function ( element, data, callback ) {
|
||||
vg.parse.spec( data, function ( error, chart ) {
|
||||
if ( !error ) {
|
||||
chart( { el: element } ).update();
|
||||
}
|
||||
if ( callback ) {
|
||||
callback( error );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
// mw.hook( 'wikipage.content' ).add( function ( $content ) {
|
||||
var $content = $('#content');
|
||||
|
||||
var specs = {}; // mw.config.get( 'wgGraphSpecs' );
|
||||
if ( !specs ) {
|
||||
return;
|
||||
}
|
||||
$content.find( '.mw-graph.mw-graph-always' ).each( function () {
|
||||
var graphId = $( this ).data( 'graph-id' );
|
||||
if ( !specs.hasOwnProperty( graphId ) ) {
|
||||
// mw.log.warn( graphId );
|
||||
} else {
|
||||
window.drawVegaGraph( this, specs[ graphId ], function ( error ) {
|
||||
if ( error ) {
|
||||
// mw.log.warn( error );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
// } );
|
||||
|
||||
}( jQuery, null, vg ) );
|
||||
|
||||
},{"mw-graph-shared":3}],2:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Convert a list of domains into an object with a test method.
|
||||
* equivalent regex: (any-subdomain)\.(wikipedia\.org|wikivoyage\.org|...)
|
||||
*
|
||||
* @param domains array of string domains
|
||||
* @param allowSubdomains if true, allows any sub and sub-sub-* domains
|
||||
* @returns {*}
|
||||
*/
|
||||
module.exports = function makeValidator(domains, allowSubdomains) {
|
||||
if (!domains || domains.length === 0) return {
|
||||
// Optimization - always return false
|
||||
test: function () {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return new RegExp(
|
||||
(allowSubdomains ? '^([^@/:]*\\.)?(' : '^(') +
|
||||
domains
|
||||
.map(function (s) {
|
||||
return s.replace('.', '\\.');
|
||||
})
|
||||
.join('|') + ')$', 'i');
|
||||
};
|
||||
|
||||
},{}],3:[function(require,module,exports){
|
||||
'use strict';
|
||||
/* global module */
|
||||
|
||||
var makeValidator = require('domain-validator'),
|
||||
parseWikidataValue = require('wd-type-parser');
|
||||
|
||||
module.exports = VegaWrapper;
|
||||
module.exports.removeColon = removeColon;
|
||||
|
||||
/**
|
||||
* Utility function to remove trailing colon from a protocol
|
||||
* @param {string} protocol
|
||||
* @return {string}
|
||||
*/
|
||||
function removeColon(protocol) {
|
||||
return protocol && protocol.length && protocol[protocol.length - 1] === ':'
|
||||
? protocol.substring(0, protocol.length - 1) : protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared library to wrap around vega code
|
||||
* @param {Object} wrapperOpts Configuration options
|
||||
* @param {Object} wrapperOpts.datalib Vega's datalib object
|
||||
* @param {Object} wrapperOpts.datalib.load Vega's data loader
|
||||
* @param {Function} wrapperOpts.datalib.load.loader Vega's data loader function
|
||||
* @param {Function} wrapperOpts.datalib.extend similar to jquery's extend()
|
||||
* @param {boolean} wrapperOpts.useXhr true if we should use XHR, false for node.js http loading
|
||||
* @param {boolean} wrapperOpts.isTrusted true if the graph spec can be trusted
|
||||
* @param {Object} wrapperOpts.domains allowed protocols and a list of their domains
|
||||
* @param {Object} wrapperOpts.domainMap domain remapping
|
||||
* @param {Function} wrapperOpts.logger
|
||||
* @param {Function} wrapperOpts.parseUrl
|
||||
* @param {Function} wrapperOpts.formatUrl
|
||||
* @param {string} [wrapperOpts.languageCode]
|
||||
* @constructor
|
||||
*/
|
||||
function VegaWrapper(wrapperOpts) {
|
||||
var self = this;
|
||||
// Copy all options into this object
|
||||
self.objExtender = wrapperOpts.datalib.extend;
|
||||
self.objExtender(self, wrapperOpts);
|
||||
self.validators = {};
|
||||
|
||||
self.datalib.load.loader = function (opt, callback) {
|
||||
var error = callback || function (e) { throw e; }, url;
|
||||
|
||||
try {
|
||||
url = self.sanitizeUrl(opt); // enable override
|
||||
} catch (err) {
|
||||
error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process data response
|
||||
var cb = function (error, data) {
|
||||
return self.dataParser(error, data, opt, callback);
|
||||
};
|
||||
|
||||
/*
|
||||
if (self.useXhr) {
|
||||
return self.datalib.load.xhr(url, opt, cb);
|
||||
} else {
|
||||
return self.datalib.load.http(url, opt, cb);
|
||||
}
|
||||
*/
|
||||
return xo.xtns.graph.load_xowa(url, opt, cb);
|
||||
};
|
||||
|
||||
self.datalib.load.sanitizeUrl = self.sanitizeUrl.bind(self);
|
||||
|
||||
// Prevent accidental use
|
||||
self.datalib.load.file = alwaysFail;
|
||||
if (self.useXhr) {
|
||||
self.datalib.load.http = alwaysFail;
|
||||
} else {
|
||||
self.datalib.load.xhr = alwaysFail;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if host was listed in the allowed domains, normalize it, and get correct protocol
|
||||
* @param {string} host
|
||||
* @returns {Object}
|
||||
*/
|
||||
VegaWrapper.prototype.sanitizeHost = function sanitizeHost(host) {
|
||||
// First, map the host
|
||||
host = (this.domainMap && this.domainMap[host]) || host;
|
||||
|
||||
if (this.testHost('https:', host)) {
|
||||
return {host: host, protocol: 'https:'};
|
||||
} else if (this.testHost('http:', host)) {
|
||||
return {host: host, protocol: 'http:'};
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test host against the list of allowed domains based on the protocol
|
||||
* @param {string} protocol
|
||||
* @param {string} host
|
||||
* @returns {boolean}
|
||||
*/
|
||||
VegaWrapper.prototype.testHost = function testHost(protocol, host) {
|
||||
if (!this.validators[protocol]) {
|
||||
var domains = this._getProtocolDomains(protocol);
|
||||
if (domains) {
|
||||
this.validators[protocol] = makeValidator(domains, protocol === 'https:' || protocol === 'http:');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.validators[protocol].test(host);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets allowed domains for a given protocol. Assumes protocol ends with a ':'.
|
||||
* Handles if this.domains's keys do not end in the ':'.
|
||||
* @param {string} protocol
|
||||
* @return {[]|false}
|
||||
* @private
|
||||
*/
|
||||
VegaWrapper.prototype._getProtocolDomains = function _getProtocolDomains(protocol) {
|
||||
return this.domains[protocol] || this.domains[removeColon(protocol)];
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate and update urlObj to be safe for client-side and server-side usage
|
||||
* @param {Object} opt passed by the vega loader, and will add 'graphProtocol' param
|
||||
* @returns {boolean} true on success
|
||||
*/
|
||||
VegaWrapper.prototype.sanitizeUrl = function sanitizeUrl(opt) {
|
||||
// In some cases we may receive a badly formed URL in a form customprotocol:https://...
|
||||
opt.url = opt.url.replace(/^([a-z]+:)https?:\/\//, '$1//');
|
||||
|
||||
// XOWA: comment out URL parsing code as it relies on various unavailable MediaWiki functions; DATE:2018-03-13
|
||||
// note that URL parsing will be done directly in Xograph.js
|
||||
/*
|
||||
var decodedPathname,
|
||||
isRelativeProtocol = /^\/\//.test(opt.url),
|
||||
urlParts = this.parseUrl(opt),
|
||||
sanitizedHost = this.sanitizeHost(urlParts.host);
|
||||
|
||||
// if (!sanitizedHost) {
|
||||
// throw new Error('URL hostname is not whitelisted: ' + opt.url);
|
||||
// }
|
||||
urlParts.host = sanitizedHost.host;
|
||||
if (!urlParts.protocol) {
|
||||
// node.js mode only - browser's url parser will always set protocol to current one
|
||||
// Update protocol-relative URLs
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
isRelativeProtocol = true;
|
||||
}
|
||||
|
||||
// Save original protocol to post-process the data
|
||||
opt.graphProtocol = urlParts.protocol;
|
||||
|
||||
if (opt.type === 'open') {
|
||||
|
||||
// Trim the value here because mediawiki will do it anyway, so we might as well save on redirect
|
||||
decodedPathname = decodeURIComponent(urlParts.pathname).trim();
|
||||
|
||||
switch (urlParts.protocol) {
|
||||
case 'http:':
|
||||
case 'https:':
|
||||
// The default protocol for the open action is wikititle, so if isRelativeProtocol is set,
|
||||
// we treat the whole pathname as title (without the '/' prefix).
|
||||
if (!isRelativeProtocol) {
|
||||
// If we get http:// and https:// protocol hardcoded, remove the '/wiki/' prefix instead
|
||||
if (!/^\/wiki\/.+$/.test(decodedPathname)) {
|
||||
throw new Error('wikititle: http(s) links must begin with /wiki/ prefix');
|
||||
}
|
||||
decodedPathname = decodedPathname.substring('/wiki'.length);
|
||||
}
|
||||
opt.graphProtocol = 'wikititle';
|
||||
// fall-through
|
||||
|
||||
case 'wikititle:':
|
||||
// wikititle:///My_page or wikititle://en.wikipedia.org/My_page
|
||||
// open() at this point may only be used to link to a Wiki page, as it may be invoked
|
||||
// without a click, thus potentially causing a privacy issue.
|
||||
if (Object.keys(urlParts.query).length !== 0) {
|
||||
throw new Error('wikititle: query parameters are not allowed');
|
||||
}
|
||||
if (!/^\/[^|]+$/.test(decodedPathname)) {
|
||||
throw new Error('wikititle: invalid title');
|
||||
}
|
||||
urlParts.pathname = '/wiki/' + encodeURIComponent(decodedPathname.substring(1).replace(' ', '_'));
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('"open()" action only allows links with wikititle protocol, e.g. wikititle:///My_page');
|
||||
}
|
||||
} else {
|
||||
|
||||
switch (urlParts.protocol) {
|
||||
case 'http:':
|
||||
case 'https:':
|
||||
if (!this.isTrusted) {
|
||||
throw new Error('HTTP and HTTPS protocols are not supported for untrusted graphs.\n' +
|
||||
'Use wikiraw:, wikiapi:, wikirest:, wikirawupload:, and other protocols.\n' +
|
||||
'See https://www.mediawiki.org/wiki/Extension:Graph#External_data');
|
||||
}
|
||||
// keep the original URL
|
||||
break;
|
||||
|
||||
case 'wikiapi:':
|
||||
// wikiapi:///?action=query&list=allpages
|
||||
// Call to api.php - ignores the path parameter, and only uses the query
|
||||
urlParts.query = this.objExtender(urlParts.query, {format: 'json', formatversion: '2'});
|
||||
urlParts.pathname = '/w/api.php';
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
opt.addCorsOrigin = true;
|
||||
break;
|
||||
|
||||
case 'wikirest:':
|
||||
// wikirest:///api/rest_v1/page/...
|
||||
// Call to RESTbase api - requires the path to start with "/api/"
|
||||
// The /api/... path is safe for GET requests
|
||||
if (!/^\/api\//.test(urlParts.pathname)) {
|
||||
throw new Error('wikirest: protocol must begin with the /api/ prefix');
|
||||
}
|
||||
// keep urlParts.query
|
||||
// keep urlParts.pathname
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
break;
|
||||
|
||||
case 'wikiraw:':
|
||||
case 'tabular:':
|
||||
case 'map:':
|
||||
// wikiraw:///MyPage/data
|
||||
// Get content of a wiki page, where the path is the title
|
||||
// of the page with an additional leading '/' which gets removed.
|
||||
// Uses mediawiki api, and extract the content after the request
|
||||
// Query value must be a valid MediaWiki title string, but we only ensure
|
||||
// there is no pipe symbol, the rest is handled by the api.
|
||||
decodedPathname = decodeURIComponent(urlParts.pathname);
|
||||
if (!/^\/[^|]+$/.test(decodedPathname)) {
|
||||
throw new Error(urlParts.protocol + ' invalid title');
|
||||
}
|
||||
if (urlParts.protocol === 'wikiraw:') {
|
||||
urlParts.query = {
|
||||
format: 'json',
|
||||
formatversion: '2',
|
||||
action: 'query',
|
||||
prop: 'revisions',
|
||||
rvprop: 'content',
|
||||
titles: decodedPathname.substring(1)
|
||||
};
|
||||
} else {
|
||||
urlParts.query = {
|
||||
format: 'json',
|
||||
formatversion: '2',
|
||||
action: 'jsondata',
|
||||
title: decodedPathname.substring(1)
|
||||
};
|
||||
if (urlParts.siteLanguage || this.languageCode) {
|
||||
urlParts.query.uselang = urlParts.siteLanguage || this.languageCode;
|
||||
}
|
||||
}
|
||||
|
||||
urlParts.pathname = '/w/api.php';
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
opt.addCorsOrigin = true;
|
||||
break;
|
||||
|
||||
case 'wikifile:':
|
||||
// wikifile:///Einstein_1921.jpg
|
||||
// Get an image for the graph, e.g. from commons, by using Special:Redirect
|
||||
urlParts.pathname = '/wiki/Special:Redirect/file' + urlParts.pathname;
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
// keep urlParts.query
|
||||
break;
|
||||
|
||||
case 'wikirawupload:':
|
||||
// wikirawupload://upload.wikimedia.org/wikipedia/commons/3/3e/Einstein_1921.jpg
|
||||
// Get an image for the graph, e.g. from commons
|
||||
// This tag specifies any content from the uploads.* domain, without query params
|
||||
this._validateExternalService(urlParts, sanitizedHost, opt.url);
|
||||
urlParts.query = {};
|
||||
// keep urlParts.pathname
|
||||
break;
|
||||
|
||||
case 'wikidatasparql:':
|
||||
// wikidatasparql:///?query=<QUERY>
|
||||
// Runs a SPARQL query, converting it to
|
||||
// https://query.wikidata.org/bigdata/namespace/wdq/sparql?format=json&query=...
|
||||
this._validateExternalService(urlParts, sanitizedHost, opt.url);
|
||||
if (!urlParts.query || !urlParts.query.query) {
|
||||
throw new Error('wikidatasparql: missing query parameter in: ' + opt.url);
|
||||
}
|
||||
// Only keep the "query" parameter
|
||||
urlParts.query = {query: urlParts.query.query};
|
||||
urlParts.pathname = '/bigdata/namespace/wdq/sparql';
|
||||
opt.headers = this.objExtender(opt.headers || {}, {'Accept': 'application/sparql-results+json'});
|
||||
break;
|
||||
|
||||
case 'geoshape:':
|
||||
case 'geoline:':
|
||||
// geoshape:///?ids=Q16,Q30 or geoshape:///?query=...
|
||||
// Get geoshapes data from OSM database by supplying Wikidata IDs
|
||||
// https://maps.wikimedia.org/shape?ids=Q16,Q30
|
||||
// 'geoline:' is an identical service, except that it returns lines instead of polygons
|
||||
this._validateExternalService(urlParts, sanitizedHost, opt.url, 'geoshape:');
|
||||
if (!urlParts.query || (!urlParts.query.ids && !urlParts.query.query)) {
|
||||
throw new Error(opt.graphProtocol + ' missing ids or query parameter in: ' + opt.url);
|
||||
}
|
||||
// the query object is not modified
|
||||
urlParts.pathname = '/' + removeColon(opt.graphProtocol);
|
||||
break;
|
||||
|
||||
case 'mapsnapshot:':
|
||||
// mapsnapshot:///?width=__&height=__&zoom=__&lat=__&lon=__ [&style=__]
|
||||
// Converts it into a snapshot image request for Kartotherian:
|
||||
// https://maps.wikimedia.org/img/{style},{zoom},{lat},{lon},{width}x{height}[@{scale}x].{format}
|
||||
// (scale will be set to 2, and format to png)
|
||||
if (!urlParts.query) {
|
||||
throw new Error('mapsnapshot: missing required parameters');
|
||||
}
|
||||
validate(urlParts, 'width', 1, 4096);
|
||||
validate(urlParts, 'height', 1, 4096);
|
||||
validate(urlParts, 'zoom', 0, 22);
|
||||
validate(urlParts, 'lat', -90, 90, true);
|
||||
validate(urlParts, 'lon', -180, 180, true);
|
||||
|
||||
var query = urlParts.query;
|
||||
if (query.style && !/^[-_0-9a-z]+$/.test(query.style)) {
|
||||
throw new Error('mapsnapshot: if style is given, it must be letters/numbers/dash/underscores only');
|
||||
}
|
||||
|
||||
// Uses the same configuration as geoshape service, so reuse settings
|
||||
this._validateExternalService(urlParts, sanitizedHost, opt.url, 'geoshape:');
|
||||
|
||||
urlParts.pathname = '/img/' + (query.style || 'osm-intl') + ',' + query.zoom + ',' +
|
||||
query.lat + ',' + query.lon + ',' + query.width + 'x' + query.height + '@2x.png';
|
||||
urlParts.query = {}; // deleting it would cause errors in mw.Uri()
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Unknown protocol ' + opt.url);
|
||||
}
|
||||
}
|
||||
|
||||
return this.formatUrl(urlParts, opt);
|
||||
*/
|
||||
return opt.url;
|
||||
};
|
||||
|
||||
function validate(urlParts, name, min, max, isFloat) {
|
||||
var value = urlParts.query[name];
|
||||
if (value === undefined) {
|
||||
throw new Error(urlParts.protocol + ' parameter ' + name + ' is not set');
|
||||
}
|
||||
if (!(isFloat ? /^-?[0-9]+\.?[0-9]*$/ : /^-?[0-9]+$/).test(value)) {
|
||||
throw new Error(urlParts.protocol + ' parameter ' + name + ' is not a number');
|
||||
}
|
||||
value = isFloat ? parseFloat(value) : parseInt(value);
|
||||
if (value < min || value > max) {
|
||||
throw new Error(urlParts.protocol + ' parameter ' + name + ' is not valid');
|
||||
}
|
||||
}
|
||||
|
||||
VegaWrapper.prototype._validateExternalService = function _validateExternalService(urlParts, sanitizedHost, url, protocolOverride) {
|
||||
var protocol = protocolOverride || urlParts.protocol,
|
||||
domains = this._getProtocolDomains(protocol);
|
||||
if (!domains) {
|
||||
throw new Error(protocol + ': protocol is disabled: ' + url);
|
||||
}
|
||||
if (urlParts.isRelativeHost) {
|
||||
urlParts.host = domains[0];
|
||||
urlParts.protocol = this.sanitizeHost(urlParts.host).protocol;
|
||||
} else {
|
||||
urlParts.protocol = sanitizedHost.protocol;
|
||||
}
|
||||
if (!this.testHost(protocol, urlParts.host)) {
|
||||
throw new Error(protocol + ': URL must either be relative (' + protocol + '///...), or use one of the allowed hosts: ' + url);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs post-processing of the data requested by the graph's spec
|
||||
*/
|
||||
VegaWrapper.prototype.dataParser = function dataParser(error, data, opt, callback) {
|
||||
if (!error) {
|
||||
try {
|
||||
data = this.parseDataOrThrow(data, opt);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
if (error) data = undefined;
|
||||
callback(error, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the response from MW Api, throwing an error or logging warnings
|
||||
*/
|
||||
VegaWrapper.prototype.parseMWApiResponse = function parseMWApiResponse(data) {
|
||||
data = JSON.parse(data);
|
||||
if (data.error) {
|
||||
throw new Error('API error: ' + JSON.stringify(data.error));
|
||||
}
|
||||
if (data.warnings) {
|
||||
this.logger('API warnings: ' + JSON.stringify(data.warnings));
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs post-processing of the data requested by the graph's spec, and throw on error
|
||||
*/
|
||||
VegaWrapper.prototype.parseDataOrThrow = function parseDataOrThrow(data, opt) {
|
||||
switch (opt.xowa_protocol) { // XOWA: use opt.xowa_protocol
|
||||
case 'wikiapi:':
|
||||
data = this.parseMWApiResponse(data);
|
||||
break;
|
||||
case 'wikiraw:':
|
||||
// XOWA: comment out call to MW API; Xograph will make call to xo_server; DATE:2018-03-13
|
||||
/*
|
||||
data = this.parseMWApiResponse(data);
|
||||
try {
|
||||
data = data.query.pages[0].revisions[0].content;
|
||||
} catch (e) {
|
||||
throw new Error('Page content not available ' + opt.url);
|
||||
}
|
||||
*/
|
||||
break;
|
||||
case null:
|
||||
case 'tabular:':
|
||||
case 'map:':
|
||||
data = this.parseMWApiResponse(data).jsondata;
|
||||
var metadata = [{
|
||||
description: data.description,
|
||||
license_code: data.license.code,
|
||||
license_text: data.license.text,
|
||||
license_url: data.license.url,
|
||||
sources: data.sources
|
||||
}];
|
||||
if (opt.xowa_protocol === 'tabular:') {
|
||||
var fields = data.schema.fields.map(function (v) {
|
||||
return v.name;
|
||||
});
|
||||
data = {
|
||||
meta: metadata,
|
||||
fields: data.schema.fields,
|
||||
data: data.data.map(function (v) {
|
||||
var row = {}, i;
|
||||
for (i = 0; i < fields.length; i++) {
|
||||
// Need to copy nulls too -- Vega has no easy way to test for undefined
|
||||
row[fields[i]] = v[i];
|
||||
}
|
||||
return row;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
metadata[0].zoom = data.zoom;
|
||||
metadata[0].latitude = data.latitude;
|
||||
metadata[0].longitude = data.longitude;
|
||||
data = {
|
||||
meta: metadata,
|
||||
data: data.data
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'wikidatasparql:':
|
||||
data = JSON.parse(data);
|
||||
if (!data.results || !Array.isArray(data.results.bindings)) {
|
||||
throw new Error('SPARQL query result does not have "results.bindings"');
|
||||
}
|
||||
data = data.results.bindings.map(function (row) {
|
||||
var key, result = {};
|
||||
for (key in row) {
|
||||
if (row.hasOwnProperty(key)) {
|
||||
result[key] = parseWikidataValue(row[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Throw an error when called
|
||||
*/
|
||||
function alwaysFail() {
|
||||
throw new Error('Disabled');
|
||||
}
|
||||
|
||||
},{"domain-validator":2,"wd-type-parser":4}],4:[function(require,module,exports){
|
||||
'use strict';
|
||||
/* global module */
|
||||
|
||||
module.exports = parseWikidataValue;
|
||||
|
||||
/**
|
||||
* Given a value object as returned from Wikidata Query Service, returns a simplified value
|
||||
* @param {object} value Original object as sent by the Wikidata query service
|
||||
* @param {string} value.type SPARQL data type (literal, uri)
|
||||
* @param {string} value.datatype XMLSchema data type
|
||||
* @param {*} value.value The actual value sent by the Wikidata query service
|
||||
* @param {boolean=} ignoreUnknown if false, will return value.value even if it cannot be recognized
|
||||
* @return {*}
|
||||
*/
|
||||
function parseWikidataValue(value, ignoreUnknown) {
|
||||
var temp;
|
||||
|
||||
if (!value || !value.type || value.value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (value.type) {
|
||||
case 'literal':
|
||||
switch (value.datatype) {
|
||||
case 'http://www.w3.org/2001/XMLSchema#double':
|
||||
case 'http://www.w3.org/2001/XMLSchema#float':
|
||||
case 'http://www.w3.org/2001/XMLSchema#decimal':
|
||||
case 'http://www.w3.org/2001/XMLSchema#integer':
|
||||
case 'http://www.w3.org/2001/XMLSchema#long':
|
||||
case 'http://www.w3.org/2001/XMLSchema#int':
|
||||
case 'http://www.w3.org/2001/XMLSchema#short':
|
||||
case 'http://www.w3.org/2001/XMLSchema#nonNegativeInteger':
|
||||
case 'http://www.w3.org/2001/XMLSchema#positiveInteger':
|
||||
case 'http://www.w3.org/2001/XMLSchema#unsignedLong':
|
||||
case 'http://www.w3.org/2001/XMLSchema#unsignedInt':
|
||||
case 'http://www.w3.org/2001/XMLSchema#unsignedShort':
|
||||
case 'http://www.w3.org/2001/XMLSchema#nonPositiveInteger':
|
||||
case 'http://www.w3.org/2001/XMLSchema#negativeInteger':
|
||||
temp = parseFloat(value.value);
|
||||
if (temp.toString() === value.value) {
|
||||
// use number only if it is fully round-tripable back to string
|
||||
// TBD: this might be overcautios, and would cause more problems than solve
|
||||
return temp;
|
||||
}
|
||||
break;
|
||||
case 'http://www.opengis.net/ont/geosparql#wktLiteral':
|
||||
// Point(-64.2 -36.62) -- (longitude latitude)
|
||||
temp = /^Point\(([-0-9.]+) ([-0-9.]+)\)$/.exec(value.value);
|
||||
if (temp) {
|
||||
return [parseFloat(temp[1]), parseFloat(temp[2])];
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'uri':
|
||||
// "http://www.wikidata.org/entity/Q12345" -> "Q12345"
|
||||
temp = /^http:\/\/www\.wikidata\.org\/entity\/(Q[1-9][0-9]*)$/.exec(value.value);
|
||||
if (temp) {
|
||||
return temp[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ignoreUnknown ? undefined : value.value;
|
||||
}
|
||||
|
||||
|
||||
},{}]},{},[1]);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*!
|
||||
* StyleSheet for JQuery splitter Plugin
|
||||
* Copyright (C) 2010 Jakub Jankiewicz <http://jcubic.pl>
|
||||
*
|
||||
* Same license as plugin
|
||||
*/
|
||||
.splitter_panel {
|
||||
position: relative;
|
||||
}
|
||||
.splitter_panel .vsplitter {
|
||||
background-color: grey;
|
||||
cursor: col-resize;
|
||||
z-index:900;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.splitter_panel .hsplitter {
|
||||
background-color: #5F5F5F;
|
||||
cursor: row-resize;
|
||||
z-index: 800;
|
||||
height: 7px;
|
||||
}
|
||||
.splitter_panel .vsplitter.splitter-invisible,
|
||||
.splitter_panel .hsplitter.splitter-invisible {
|
||||
background: none;
|
||||
}
|
||||
.splitter_panel .vsplitter, .splitter_panel .left_panel, .splitter_panel .right_panel,
|
||||
.splitter_panel .hsplitter, .splitter_panel .top_panel, .splitter_panel .bottom_panel {
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
}
|
||||
.splitter_panel .vsplitter, .splitter_panel .left_panel, .splitter_panel .right_panel {
|
||||
height: 100%;
|
||||
}
|
||||
.splitter_panel .hsplitter, .splitter_panel .top_panel, .splitter_panel .bottom_panel {
|
||||
width: 100%;
|
||||
}
|
||||
.splitter_panel .top_panel, .splitter_panel .left_panel, .splitter_panel .vsplitter {
|
||||
top: 0;
|
||||
}
|
||||
.splitter_panel .top_panel, .splitter_panel .bottom_panel, .splitter_panel .left_panel, .splitter_panel .hsplitter {
|
||||
left: 0;
|
||||
}
|
||||
.splitter_panel .bottom_panel {
|
||||
bottom: 0;
|
||||
}
|
||||
.splitter_panel .right_panel {
|
||||
right: 0;
|
||||
}
|
||||
.splitterMask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/*!
|
||||
* JQuery Spliter Plugin version 0.20.1
|
||||
* Copyright (C) 2010-2016 Jakub Jankiewicz <http://jcubic.pl>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
(function($, undefined) {
|
||||
var count = 0;
|
||||
var splitter_id = null;
|
||||
var splitters = [];
|
||||
var current_splitter = null;
|
||||
$.fn.split = function(options) {
|
||||
var data = this.data('splitter');
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
var panel_1;
|
||||
var panel_2;
|
||||
var settings = $.extend({
|
||||
limit: 100,
|
||||
orientation: 'horizontal',
|
||||
position: '50%',
|
||||
invisible: false,
|
||||
onDragStart: $.noop,
|
||||
onDragEnd: $.noop,
|
||||
onDrag: $.noop
|
||||
}, options || {});
|
||||
this.settings = settings;
|
||||
var cls;
|
||||
var children = this.children();
|
||||
if (settings.orientation == 'vertical') {
|
||||
panel_1 = children.first().addClass('left_panel');
|
||||
panel_2 = panel_1.next().addClass('right_panel');
|
||||
cls = 'vsplitter';
|
||||
} else if (settings.orientation == 'horizontal') {
|
||||
panel_1 = children.first().addClass('top_panel');
|
||||
panel_2 = panel_1.next().addClass('bottom_panel');
|
||||
cls = 'hsplitter';
|
||||
}
|
||||
if (settings.invisible) {
|
||||
cls += ' splitter-invisible';
|
||||
}
|
||||
var width = this.width();
|
||||
var height = this.height();
|
||||
var id = count++;
|
||||
this.addClass('splitter_panel');
|
||||
var splitter = $('<div/>').addClass(cls).bind('mouseenter touchstart', function() {
|
||||
splitter_id = id;
|
||||
}).bind('mouseleave touchend', function() {
|
||||
splitter_id = null;
|
||||
}).insertAfter(panel_1);
|
||||
var position;
|
||||
|
||||
function get_position(position) {
|
||||
if (typeof position === 'number') {
|
||||
return position;
|
||||
} else if (typeof position === 'string') {
|
||||
var match = position.match(/^([0-9\.]+)(px|%)$/);
|
||||
if (match) {
|
||||
if (match[2] == 'px') {
|
||||
return +match[1];
|
||||
} else {
|
||||
if (settings.orientation == 'vertical') {
|
||||
return (width * +match[1]) / 100;
|
||||
} else if (settings.orientation == 'horizontal') {
|
||||
return (height * +match[1]) / 100;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//throw position + ' is invalid value';
|
||||
}
|
||||
} else {
|
||||
//throw 'position have invalid type';
|
||||
}
|
||||
}
|
||||
|
||||
var self = $.extend(this, {
|
||||
refresh: function() {
|
||||
var new_width = this.width();
|
||||
var new_height = this.height();
|
||||
if (width != new_width || height != new_height) {
|
||||
width = this.width();
|
||||
height = this.height();
|
||||
self.position(position);
|
||||
}
|
||||
},
|
||||
position: (function() {
|
||||
if (settings.orientation == 'vertical') {
|
||||
return function(n, silent) {
|
||||
if (n === undefined) {
|
||||
return position;
|
||||
} else {
|
||||
position = get_position(n);
|
||||
var sw = splitter.width();
|
||||
var sw2 = sw/2, pw;
|
||||
if (settings.invisible) {
|
||||
pw = panel_1.width(position).outerWidth();
|
||||
panel_2.width(self.width()-pw);
|
||||
splitter.css('left', pw-sw2);
|
||||
} else {
|
||||
pw = panel_1.width(position-sw2).outerWidth();
|
||||
panel_2.width(self.width()-pw-sw);
|
||||
splitter.css('left', pw);
|
||||
}
|
||||
}
|
||||
if (!silent) {
|
||||
self.trigger('splitter.resize');
|
||||
self.find('.splitter_panel').trigger('splitter.resize');
|
||||
}
|
||||
return self;
|
||||
};
|
||||
} else if (settings.orientation == 'horizontal') {
|
||||
return function(n, silent) {
|
||||
if (n === undefined) {
|
||||
return position;
|
||||
} else {
|
||||
position = get_position(n);
|
||||
var sw = splitter.height();
|
||||
var sw2 = sw/2, pw;
|
||||
if (settings.invisible) {
|
||||
pw = panel_1.height(position).outerHeight();
|
||||
panel_2.height(self.height()-pw);
|
||||
splitter.css('top', pw-sw2);
|
||||
} else {
|
||||
pw = panel_1.height(position-sw2).outerHeight();
|
||||
panel_2.height(self.height()-pw-sw);
|
||||
splitter.css('top', pw);
|
||||
}
|
||||
}
|
||||
if (!silent) {
|
||||
self.trigger('splitter.resize');
|
||||
self.find('.splitter_panel').trigger('splitter.resize');
|
||||
}
|
||||
return self;
|
||||
};
|
||||
} else {
|
||||
return $.noop;
|
||||
}
|
||||
})(),
|
||||
orientation: settings.orientation,
|
||||
limit: settings.limit,
|
||||
isActive: function() {
|
||||
return splitter_id === id;
|
||||
},
|
||||
destroy: function() {
|
||||
self.removeClass('splitter_panel');
|
||||
splitter.unbind('mouseenter');
|
||||
splitter.unbind('mouseleave');
|
||||
splitter.unbind('touchstart');
|
||||
splitter.unbind('touchmove');
|
||||
splitter.unbind('touchend');
|
||||
splitter.unbind('touchleave');
|
||||
splitter.unbind('touchcancel');
|
||||
if (settings.orientation == 'vertical') {
|
||||
panel_1.removeClass('left_panel');
|
||||
panel_2.removeClass('right_panel');
|
||||
} else if (settings.orientation == 'horizontal') {
|
||||
panel_1.removeClass('top_panel');
|
||||
panel_2.removeClass('bottom_panel');
|
||||
}
|
||||
self.unbind('splitter.resize');
|
||||
self.trigger('splitter.resize');
|
||||
self.find('.splitter_panel').trigger('splitter.resize');
|
||||
splitters[i] = null;
|
||||
count--;
|
||||
splitter.remove();
|
||||
self.removeData('splitter');
|
||||
var not_null = false;
|
||||
for (var i=splitters.length; i--;) {
|
||||
if (splitters[i] !== null) {
|
||||
not_null = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//remove document events when no splitters
|
||||
if (!not_null) {
|
||||
$(document.documentElement).unbind('.splitter');
|
||||
$(window).unbind('resize.splitter');
|
||||
splitters = [];
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
self.bind('splitter.resize', function(e) {
|
||||
var pos = self.position();
|
||||
if (self.orientation == 'vertical' &&
|
||||
pos > self.width()) {
|
||||
pos = self.width() - self.limit-1;
|
||||
} else if (self.orientation == 'horizontal' &&
|
||||
pos > self.height()) {
|
||||
pos = self.height() - self.limit-1;
|
||||
}
|
||||
if (pos < self.limit) {
|
||||
pos = self.limit + 1;
|
||||
}
|
||||
e.stopPropagation();
|
||||
self.position(pos, true);
|
||||
});
|
||||
//inital position of splitter
|
||||
var pos;
|
||||
if (settings.orientation == 'vertical') {
|
||||
if (pos > width-settings.limit) {
|
||||
pos = width-settings.limit;
|
||||
} else {
|
||||
pos = get_position(settings.position);
|
||||
}
|
||||
} else if (settings.orientation == 'horizontal') {
|
||||
//position = height/2;
|
||||
if (pos > height-settings.limit) {
|
||||
pos = height-settings.limit;
|
||||
} else {
|
||||
pos = get_position(settings.position);
|
||||
}
|
||||
}
|
||||
if (pos < settings.limit) {
|
||||
pos = settings.limit;
|
||||
}
|
||||
self.position(pos, true);
|
||||
if (splitters.length === 0) { // first time bind events to document
|
||||
$(window).bind('resize.splitter', function() {
|
||||
$.each(splitters, function(i, splitter) {
|
||||
if (splitter) {
|
||||
splitter.refresh();
|
||||
}
|
||||
});
|
||||
});
|
||||
$(document.documentElement).on('mousedown.splitter touchstart.splitter', function(e) {
|
||||
if (splitter_id !== null) {
|
||||
current_splitter = splitters[splitter_id];
|
||||
setTimeout(function() {
|
||||
$('<div class="splitterMask"></div>').
|
||||
css('cursor', current_splitter.children().eq(1).css('cursor')).
|
||||
insertAfter(current_splitter);
|
||||
});
|
||||
current_splitter.settings.onDragStart(e);
|
||||
}
|
||||
}).bind('mouseup.splitter touchend.splitter touchleave.splitter touchcancel.splitter', function(e) {
|
||||
if (current_splitter) {
|
||||
setTimeout(function() {
|
||||
$('.splitterMask').remove();
|
||||
});
|
||||
current_splitter.settings.onDragEnd(e);
|
||||
current_splitter = null;
|
||||
}
|
||||
}).bind('mousemove.splitter touchmove.splitter', function(e) {
|
||||
if (current_splitter !== null) {
|
||||
var limit = current_splitter.limit;
|
||||
var offset = current_splitter.offset();
|
||||
if (current_splitter.orientation == 'vertical') {
|
||||
var pageX = e.pageX;
|
||||
if(e.originalEvent && e.originalEvent.changedTouches){
|
||||
pageX = e.originalEvent.changedTouches[0].pageX;
|
||||
}
|
||||
var x = pageX - offset.left;
|
||||
if (x <= current_splitter.limit) {
|
||||
x = current_splitter.limit + 1;
|
||||
} else if (x >= current_splitter.width() - limit) {
|
||||
x = current_splitter.width() - limit - 1;
|
||||
}
|
||||
if (x > current_splitter.limit &&
|
||||
x < current_splitter.width()-limit) {
|
||||
current_splitter.position(x, true);
|
||||
current_splitter.trigger('splitter.resize');
|
||||
current_splitter.find('.splitter_panel').
|
||||
trigger('splitter.resize');
|
||||
//e.preventDefault();
|
||||
}
|
||||
} else if (current_splitter.orientation == 'horizontal') {
|
||||
var pageY = e.pageY;
|
||||
if(e.originalEvent && e.originalEvent.changedTouches){
|
||||
pageY = e.originalEvent.changedTouches[0].pageY;
|
||||
}
|
||||
var y = pageY-offset.top;
|
||||
if (y <= current_splitter.limit) {
|
||||
y = current_splitter.limit + 1;
|
||||
} else if (y >= current_splitter.height() - limit) {
|
||||
y = current_splitter.height() - limit - 1;
|
||||
}
|
||||
if (y > current_splitter.limit &&
|
||||
y < current_splitter.height()-limit) {
|
||||
current_splitter.position(y, true);
|
||||
current_splitter.trigger('splitter.resize');
|
||||
current_splitter.find('.splitter_panel').
|
||||
trigger('splitter.resize');
|
||||
//e.preventDefault();
|
||||
}
|
||||
}
|
||||
current_splitter.settings.onDrag(e);
|
||||
}
|
||||
});//*/
|
||||
}
|
||||
splitters[id] = self;
|
||||
self.data('splitter', self);
|
||||
return self;
|
||||
};
|
||||
})(jQuery);
|
||||
11003
res/bin/any/xowa/xtns/Graph/lib/vega1/vega.js
Normal file
11003
res/bin/any/xowa/xtns/Graph/lib/vega1/vega.js
Normal file
File diff suppressed because one or more lines are too long
22092
res/bin/any/xowa/xtns/Graph/lib/vega2/vega.js
Normal file
22092
res/bin/any/xowa/xtns/Graph/lib/vega2/vega.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user