=======
Reusable Chart component for D3 - Using factories
# Compiled source # | |
################### | |
*.com | |
*.class | |
*.dll | |
*.exe | |
*.o | |
*.so | |
# Packages # | |
############ | |
# it's better to unpack these files and commit the raw source | |
# git has its own built in compression methods | |
*.7z | |
*.dmg | |
*.gz | |
*.iso | |
*.jar | |
*.rar | |
*.tar | |
*.zip | |
# Logs and databases # | |
###################### | |
*.log | |
*.sql | |
*.sqlite | |
# OS generated files # | |
###################### | |
.DS_Store | |
.DS_Store? | |
._* | |
.Spotlight-V100 | |
.Trashes | |
ehthumbs.db | |
Thumbs.db |
function avatar() { | |
} | |
avatar.draw = function() { | |
var args = Array.prototype.slice.call(arguments) | |
var fontWeight = 'bold', textColour = '#ffffff'; | |
// TODO: CHECK RETURNS FALSE, WHY? | |
// if (args[args.length - 1] instanceof SVGElement) | |
var target = args.pop() | |
var mark = args.pop() | |
var radius = args.pop() | |
var id = args.pop() | |
var charCode = args.pop() | |
var secColour = args.pop() | |
var primColour = args.pop() | |
var text = document.createElementNS("http://www.w3.org/2000/svg", "text"); | |
text.setAttribute('class', 'avatar--text__main'); | |
text.setAttributeNS(null, "fill", textColour); | |
text.setAttributeNS(null, "pointer-events", "auto"); | |
text.setAttributeNS(null, "id", id); | |
text.setAttributeNS(null, "dy", '0.35em'); | |
text.setAttributeNS(null, "text-anchor", "middle"); | |
text.setAttributeNS(null, "font-weight", fontWeight); | |
text.setAttributeNS(null, "font-size", radius - 2); | |
text.innerHTML = charCode; | |
var circle = document.createElementNS("http://www.w3.org/2000/svg", | |
"circle"); | |
var inner; | |
if (mark) { | |
circle.setAttribute('class', 'avatar--circle__mark-stroke'); | |
circle.setAttributeNS(null, "stroke", secColour); | |
circle.setAttributeNS(null, "r", radius - 1); | |
circle.setAttributeNS(null, "stroke-width", 2); | |
circle.setAttributeNS(null, "fill", "none"); | |
inner = document.createElementNS("http://www.w3.org/2000/svg", | |
"circle"); | |
inner.setAttribute('class', 'avatar--circle__mark-main'); | |
inner.setAttributeNS(null, "r", radius - 3); | |
inner.setAttributeNS(null, "stroke", "none"); | |
inner.setAttributeNS(null, "fill", primColour); | |
inner.setAttributeNS(null, "id", id); | |
if(target != null) target.appendChild(inner); | |
} else { | |
circle.setAttribute('class', 'avatar--circle__mark-main'); | |
circle.setAttributeNS(null, "r", radius); | |
circle.setAttributeNS(null, "stroke", "none"); | |
circle.setAttributeNS(null, "fill", primColour); | |
} | |
circle.setAttributeNS(null, "id", id); | |
if(target != null) { | |
target.appendChild(circle); | |
target.appendChild(text); | |
return; | |
} | |
var a = new Array(circle, text); | |
if(inner != null) a.unshift(inner); | |
return a; | |
}; |
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-dispatch'), require('d3-drag'), require('d3-interpolate'), require('d3-selection'), require('d3-transition')) : | |
typeof define === 'function' && define.amd ? define(['exports', 'd3-dispatch', 'd3-drag', 'd3-interpolate', 'd3-selection', 'd3-transition'], factory) : | |
(factory((global.d3 = global.d3 || {}),global.d3,global.d3,global.d3,global.d3,global.d3)); | |
}(this, (function (exports,d3Dispatch,d3Drag,d3Interpolate,d3Selection,d3Transition) { 'use strict'; | |
var constant = function(x) { | |
return function() { | |
return x; | |
}; | |
}; | |
var BrushEvent = function(target, type, selection) { | |
this.target = target; | |
this.type = type; | |
this.selection = selection; | |
}; | |
function nopropagation() { | |
d3Selection.event.stopImmediatePropagation(); | |
} | |
var noevent = function() { | |
d3Selection.event.preventDefault(); | |
d3Selection.event.stopImmediatePropagation(); | |
}; | |
var MODE_DRAG = {name: "drag"}; | |
var MODE_SPACE = {name: "space"}; | |
var MODE_HANDLE = {name: "handle"}; | |
var MODE_CENTER = {name: "center"}; | |
var X = { | |
name: "x", | |
handles: ["e", "w"].map(type), | |
input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; }, | |
output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } | |
}; | |
var Y = { | |
name: "y", | |
handles: ["n", "s"].map(type), | |
input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; }, | |
output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } | |
}; | |
var XY = { | |
name: "xy", | |
handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), | |
input: function(xy) { return xy; }, | |
output: function(xy) { return xy; } | |
}; | |
var cursors = { | |
overlay: "crosshair", | |
selection: "move", | |
n: "ns-resize", | |
e: "ew-resize", | |
s: "ns-resize", | |
w: "ew-resize", | |
nw: "nwse-resize", | |
ne: "nesw-resize", | |
se: "nwse-resize", | |
sw: "nesw-resize" | |
}; | |
var flipX = { | |
e: "w", | |
w: "e", | |
nw: "ne", | |
ne: "nw", | |
se: "sw", | |
sw: "se" | |
}; | |
var flipY = { | |
n: "s", | |
s: "n", | |
nw: "sw", | |
ne: "se", | |
se: "ne", | |
sw: "nw" | |
}; | |
var signsX = { | |
overlay: +1, | |
selection: +1, | |
n: null, | |
e: +1, | |
s: null, | |
w: -1, | |
nw: -1, | |
ne: +1, | |
se: +1, | |
sw: -1 | |
}; | |
var signsY = { | |
overlay: +1, | |
selection: +1, | |
n: -1, | |
e: null, | |
s: +1, | |
w: null, | |
nw: -1, | |
ne: -1, | |
se: +1, | |
sw: +1 | |
}; | |
function type(t) { | |
return {type: t}; | |
} | |
// Ignore right-click, since that should open the context menu. | |
function defaultFilter() { | |
return !d3Selection.event.button; | |
} | |
function defaultExtent() { | |
var svg = this.ownerSVGElement || this; | |
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; | |
} | |
// Like d3.local, but with the name “__brush” rather than auto-generated. | |
function local(node) { | |
while (!node.__brush) if (!(node = node.parentNode)) return; | |
return node.__brush; | |
} | |
function empty(extent) { | |
return extent[0][0] === extent[1][0] | |
|| extent[0][1] === extent[1][1]; | |
} | |
function brushSelection(node) { | |
var state = node.__brush; | |
return state ? state.dim.output(state.selection) : null; | |
} | |
function brushX() { | |
return brush$1(X); | |
} | |
function brushY() { | |
return brush$1(Y); | |
} | |
var brush = function() { | |
return brush$1(XY); | |
}; | |
function brush$1(dim) { | |
var extent = defaultExtent, | |
filter = defaultFilter, | |
listeners = d3Dispatch.dispatch(brush, "start", "brush", "end"), | |
handleSize = 6, | |
touchending; | |
function brush(group) { | |
var overlay = group | |
.property("__brush", initialize) | |
.selectAll(".overlay") | |
.data([type("overlay")]); | |
overlay.enter().append("rect") | |
.attr("class", "overlay") | |
.attr("pointer-events", "all") | |
.attr("cursor", cursors.overlay) | |
.merge(overlay) | |
.each(function() { | |
var extent = local(this).extent; | |
d3Selection.select(this) | |
.attr("x", extent[0][0]) | |
.attr("y", extent[0][1]) | |
.attr("width", extent[1][0] - extent[0][0]) | |
.attr("height", extent[1][1] - extent[0][1]); | |
}); | |
group.selectAll(".selection") | |
.data([type("selection")]) | |
.enter().append("rect") | |
.attr("class", "selection") | |
.attr("cursor", cursors.selection) | |
.attr("fill", "#777") | |
.attr("fill-opacity", 0.3) | |
.attr("stroke", "#fff") | |
.attr("shape-rendering", "crispEdges"); | |
var handle = group.selectAll(".handle") | |
.data(dim.handles, function(d) { return d.type; }); | |
handle.exit().remove(); | |
handle.enter().append("rect") | |
.attr("class", function(d) { return "handle handle--" + d.type; }) | |
.attr("cursor", function(d) { return cursors[d.type]; }); | |
group | |
.each(redraw) | |
.attr("fill", "none") | |
.attr("pointer-events", "all") | |
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") | |
.on("mousedown.brush touchstart.brush", started); | |
} | |
brush.move = function(group, selection) { | |
if (group.selection) { | |
group | |
.on("start.brush", function() { emitter(this, arguments).beforestart().start(); }) | |
.on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); }) | |
.tween("brush", function() { | |
var that = this, | |
state = that.__brush, | |
emit = emitter(that, arguments), | |
selection0 = state.selection, | |
selection1 = dim.input(typeof selection === "function" ? selection.apply(this, arguments) : selection, state.extent), | |
i = d3Interpolate.interpolate(selection0, selection1); | |
function tween(t) { | |
state.selection = t === 1 && empty(selection1) ? null : i(t); | |
redraw.call(that); | |
emit.brush(); | |
} | |
return selection0 && selection1 ? tween : tween(1); | |
}); | |
} else { | |
group | |
.each(function() { | |
var that = this, | |
args = arguments, | |
state = that.__brush, | |
selection1 = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent), | |
emit = emitter(that, args).beforestart(); | |
d3Transition.interrupt(that); | |
state.selection = selection1 == null || empty(selection1) ? null : selection1; | |
redraw.call(that); | |
emit.start().brush().end(); | |
}); | |
} | |
}; | |
function redraw() { | |
var group = d3Selection.select(this), | |
selection = local(this).selection; | |
if (selection) { | |
group.selectAll(".selection") | |
.style("display", null) | |
.attr("x", selection[0][0]) | |
.attr("y", selection[0][1]) | |
.attr("width", selection[1][0] - selection[0][0]) | |
.attr("height", selection[1][1] - selection[0][1]); | |
group.selectAll(".handle") | |
.style("display", null) | |
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; }) | |
.attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; }) | |
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; }) | |
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; }); | |
} | |
else { | |
group.selectAll(".selection,.handle") | |
.style("display", "none") | |
.attr("x", null) | |
.attr("y", null) | |
.attr("width", null) | |
.attr("height", null); | |
} | |
} | |
function emitter(that, args) { | |
return that.__brush.emitter || new Emitter(that, args); | |
} | |
function Emitter(that, args) { | |
this.that = that; | |
this.args = args; | |
this.state = that.__brush; | |
this.active = 0; | |
} | |
Emitter.prototype = { | |
beforestart: function() { | |
if (++this.active === 1) this.state.emitter = this, this.starting = true; | |
return this; | |
}, | |
start: function() { | |
if (this.starting) this.starting = false, this.emit("start"); | |
return this; | |
}, | |
brush: function() { | |
this.emit("brush"); | |
return this; | |
}, | |
end: function() { | |
if (--this.active === 0) delete this.state.emitter, this.emit("end"); | |
return this; | |
}, | |
emit: function(type) { | |
d3Selection.customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]); | |
} | |
}; | |
function started() { | |
if (d3Selection.event.touches) { if (d3Selection.event.changedTouches.length < d3Selection.event.touches.length) return noevent(); } | |
else if (touchending) return; | |
if (!filter.apply(this, arguments)) return; | |
var that = this, | |
type = d3Selection.event.target.__data__.type, | |
mode = (d3Selection.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (d3Selection.event.altKey ? MODE_CENTER : MODE_HANDLE), | |
signX = dim === Y ? null : signsX[type], | |
signY = dim === X ? null : signsY[type], | |
state = local(that), | |
extent = state.extent, | |
selection = state.selection, | |
W = extent[0][0], w0, w1, | |
N = extent[0][1], n0, n1, | |
E = extent[1][0], e0, e1, | |
S = extent[1][1], s0, s1, | |
dx, | |
dy, | |
moving, | |
lockX, | |
lockY, | |
point0 = d3Selection.mouse(that), | |
point = point0, | |
emit = emitter(that, arguments).beforestart(); | |
if (type === "overlay") { | |
state.selection = selection = [ | |
[w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], | |
[e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] | |
]; | |
} else { | |
w0 = selection[0][0]; | |
n0 = selection[0][1]; | |
e0 = selection[1][0]; | |
s0 = selection[1][1]; | |
} | |
w1 = w0; | |
n1 = n0; | |
e1 = e0; | |
s1 = s0; | |
var group = d3Selection.select(that) | |
.attr("pointer-events", "none"); | |
var overlay = group.selectAll(".overlay") | |
.attr("cursor", cursors[type]); | |
if (d3Selection.event.touches) { | |
group | |
.on("touchmove.brush", moved, true) | |
.on("touchend.brush touchcancel.brush", ended, true); | |
} else { | |
var view = d3Selection.select(d3Selection.event.view) | |
.on("keydown.brush", keydowned, true) | |
.on("keyup.brush", keyupped, true) | |
.on("mousemove.brush", moved, true) | |
.on("mouseup.brush", ended, true); | |
d3Drag.dragDisable(d3Selection.event.view); | |
} | |
nopropagation(); | |
d3Transition.interrupt(that); | |
redraw.call(that); | |
emit.start(); | |
function moved() { | |
var point1 = d3Selection.mouse(that); | |
point = point1; | |
moving = true; | |
noevent(); | |
move(); | |
} | |
function move() { | |
var t; | |
dx = point[0] - point0[0]; | |
dy = point[1] - point0[1]; | |
switch (mode) { | |
case MODE_SPACE: | |
case MODE_DRAG: { | |
if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; | |
if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; | |
break; | |
} | |
case MODE_HANDLE: { | |
if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; | |
else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; | |
if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0; | |
else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy; | |
break; | |
} | |
case MODE_CENTER: { | |
if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX)); | |
if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY)); | |
break; | |
} | |
} | |
if (e1 < w1) { | |
signX *= -1; | |
t = w0, w0 = e0, e0 = t; | |
t = w1, w1 = e1, e1 = t; | |
if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); | |
} | |
if (s1 < n1) { | |
signY *= -1; | |
t = n0, n0 = s0, s0 = t; | |
t = n1, n1 = s1, s1 = t; | |
if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); | |
} | |
if (state.selection) selection = state.selection; // May be set by brush.move! | |
if (lockX) w1 = selection[0][0], e1 = selection[1][0]; | |
if (lockY) n1 = selection[0][1], s1 = selection[1][1]; | |
if (selection[0][0] !== w1 | |
|| selection[0][1] !== n1 | |
|| selection[1][0] !== e1 | |
|| selection[1][1] !== s1) { | |
state.selection = [[w1, n1], [e1, s1]]; | |
redraw.call(that); | |
emit.brush(); | |
} | |
} | |
function ended() { | |
nopropagation(); | |
if (d3Selection.event.touches) { | |
if (d3Selection.event.touches.length) return; | |
if (touchending) clearTimeout(touchending); | |
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! | |
group.on("touchmove.brush touchend.brush touchcancel.brush", null); | |
} else { | |
d3Drag.dragEnable(d3Selection.event.view, moving); | |
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); | |
} | |
group.attr("pointer-events", "all"); | |
overlay.attr("cursor", cursors.overlay); | |
if (state.selection) selection = state.selection; // May be set by brush.move (on start)! | |
if (empty(selection)) state.selection = null, redraw.call(that); | |
emit.end(); | |
} | |
function keydowned() { | |
switch (d3Selection.event.keyCode) { | |
case 18: { // ALT | |
if (mode === MODE_HANDLE) { | |
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; | |
if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; | |
mode = MODE_CENTER; | |
move(); | |
} | |
break; | |
} | |
case 32: { // SPACE; takes priority over ALT | |
if (mode === MODE_HANDLE || mode === MODE_CENTER) { | |
if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; | |
if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; | |
mode = MODE_SPACE; | |
overlay.attr("cursor", cursors.selection); | |
move(); | |
} | |
break; | |
} | |
default: return; | |
} | |
noevent(); | |
} | |
function keyupped() { | |
switch (d3Selection.event.keyCode) { | |
case 18: { // ALT | |
if (mode === MODE_CENTER) { | |
if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; | |
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; | |
mode = MODE_HANDLE; | |
move(); | |
} | |
break; | |
} | |
case 32: { // SPACE | |
if (mode === MODE_SPACE) { | |
if (d3Selection.event.altKey) { | |
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; | |
if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; | |
mode = MODE_CENTER; | |
} else { | |
if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; | |
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; | |
mode = MODE_HANDLE; | |
} | |
overlay.attr("cursor", cursors[type]); | |
move(); | |
} | |
break; | |
} | |
default: return; | |
} | |
noevent(); | |
} | |
} | |
function initialize() { | |
var state = this.__brush || {selection: null}; | |
state.extent = extent.apply(this, arguments); | |
state.dim = dim; | |
return state; | |
} | |
brush.extent = function(_) { | |
return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent; | |
}; | |
brush.filter = function(_) { | |
return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), brush) : filter; | |
}; | |
brush.handleSize = function(_) { | |
return arguments.length ? (handleSize = +_, brush) : handleSize; | |
}; | |
brush.on = function() { | |
var value = listeners.on.apply(listeners, arguments); | |
return value === listeners ? brush : value; | |
}; | |
return brush; | |
} | |
exports.brush = brush; | |
exports.brushX = brushX; | |
exports.brushY = brushY; | |
exports.brushSelection = brushSelection; | |
Object.defineProperty(exports, '__esModule', { value: true }); | |
}))); |
{ | |
"defs": { | |
"nominal": { | |
"color1": "#8DA0CB", | |
"color2": "#FC8D62", | |
"label": "N" | |
}, | |
"crime": { | |
"color1": "#66C2A5", | |
"color2": "#FC8D62", | |
"label": "CR" | |
}, | |
"location": { | |
"color1": "#D870AD", | |
"color2": "#D870AD", | |
"label": "L" | |
} | |
}, | |
"nodes": [ | |
{ | |
"id": "http://ns.valcri.org/data/crimes#crime138640301", | |
"label": "138640301", | |
"type": "crime" | |
}, | |
{ | |
"hasTown": "CARSINGTON", | |
"hasStreet": "PEARN ROAD", | |
"id": "http://ns.valcri.org/data/locations#-1_8130219_52_46728", | |
"label": "http://ns.valcri.org/data/locations#-1_8130219_52_46728", | |
"type": "location", | |
"long": 52.467281341552734, | |
"lat": -1.8130218982696533 | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/crimes#crime162033325", | |
"hasLocation": "http://ns.valcri.org/data/locations#-1_8130219_52_46728", | |
"label": "162033325", | |
"type": "crime" | |
}, | |
{ | |
"victimOf": "http://ns.valcri.org/data/crimes#crime162033325", | |
"id": "http://ns.valcri.org/data/nominals#nominal9084944P", | |
"label": "9084944P", | |
"hasHome": "http://ns.valcri.org/data/locations#-1_8130219_52_46728", | |
"type": "nominal" | |
} | |
], | |
"links": [ | |
{ | |
"sourceType": "crime", | |
"sourceLabel": "162033325", | |
"targetLabel": "http://ns.valcri.org/data/locations#-1_8130219_52_46728", | |
"targetType": "location", | |
"id": "link0", | |
"source": "http://ns.valcri.org/data/crimes#crime162033325", | |
"target": "http://ns.valcri.org/data/locations#-1_8130219_52_46728" | |
}, | |
{ | |
"sourceType": "nominal", | |
"sourceLabel": "9084944P", | |
"targetLabel": "138640301", | |
"targetType": "crime", | |
"id": "link1", | |
"source": "http://ns.valcri.org/data/nominals#nominal9084944P", | |
"target": "http://ns.valcri.org/data/crimes#crime138640301" | |
}, | |
{ | |
"sourceType": "nominal", | |
"sourceLabel": "9084944P", | |
"targetLabel": "162033325", | |
"targetType": "crime", | |
"id": "link2", | |
"source": "http://ns.valcri.org/data/nominals#nominal9084944P", | |
"target": "http://ns.valcri.org/data/crimes#crime162033325" | |
}, | |
{ | |
"sourceType": "nominal", | |
"sourceLabel": "9084944P", | |
"targetLabel": "http://ns.valcri.org/data/locations#-1_8130219_52_46728", | |
"targetType": "location", | |
"id": "link3", | |
"source": "http://ns.valcri.org/data/nominals#nominal9084944P", | |
"target": "http://ns.valcri.org/data/locations#-1_8130219_52_46728" | |
} | |
] | |
} |
{ | |
"nodes": [ | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime126467988", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal7860282G", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime124047883", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal82376756A", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime127780722", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal8456992G", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime144160853", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal87105490C", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime156633059", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal93487729Z", | |
"type": "nominal" | |
} | |
], | |
"links": [ | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime162033325", | |
"target": "http://ns.valcri.org/data/crimes#crime126467988" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime126467988", | |
"target": "http://ns.valcri.org/data/nominals#nominal7860282G" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime162033325", | |
"target": "http://ns.valcri.org/data/crimes#crime124047883" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime124047883", | |
"target": "http://ns.valcri.org/data/nominals#nominal82376756A" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime162033325", | |
"target": "http://ns.valcri.org/data/crimes#crime127780722" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime127780722", | |
"target": "http://ns.valcri.org/data/nominals#nominal8456992G" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime162033325", | |
"target": "http://ns.valcri.org/data/crimes#crime144160853" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime144160853", | |
"target": "http://ns.valcri.org/data/nominals#nominal87105490C" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime162033325", | |
"target": "http://ns.valcri.org/data/crimes#crime156633059" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime156633059", | |
"target": "http://ns.valcri.org/data/nominals#nominal93487729Z" | |
} | |
] | |
} |
{ | |
"nodes": [ | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime164040649", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal51121931R", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime164042156", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal86695964J", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime164043663", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal5023564K", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime164056952", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal621260409Y", | |
"type": "nominal" | |
}, | |
{ | |
"isSolved": true, | |
"id": "http://ns.valcri.org/data/crimes#crime164061610", | |
"type": "crime" | |
}, | |
{ | |
"id": "http://ns.valcri.org/data/nominals#nominal22538418L", | |
"type": "nominal" | |
} | |
], | |
"links": [ | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime138640301", | |
"target": "http://ns.valcri.org/data/crimes#crime164040649" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime164040649", | |
"target": "http://ns.valcri.org/data/nominals#nominal51121931R" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime138640301", | |
"target": "http://ns.valcri.org/data/crimes#crime164042156" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime164042156", | |
"target": "http://ns.valcri.org/data/nominals#nominal86695964J" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime138640301", | |
"target": "http://ns.valcri.org/data/crimes#crime164043663" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime164043663", | |
"target": "http://ns.valcri.org/data/nominals#nominal5023564K" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime138640301", | |
"target": "http://ns.valcri.org/data/crimes#crime164056952" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime164056952", | |
"target": "http://ns.valcri.org/data/nominals#nominal621260409Y" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "crime", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime138640301", | |
"target": "http://ns.valcri.org/data/crimes#crime164061610" | |
}, | |
{ | |
"sourceType": "crime", | |
"link": "similarity", | |
"targetType": "nominal", | |
"id": "link", | |
"source": "http://ns.valcri.org/data/crimes#crime164061610", | |
"target": "http://ns.valcri.org/data/nominals#nominal22538418L" | |
} | |
] | |
} |
#vis { | |
position: absolute; | |
top: 0; | |
left: 0; | |
bottom: 0; | |
right: 0; | |
overflow-x: hidden; | |
overflow-y: hidden; | |
} | |
.node .selected { | |
stroke: black; | |
} |
(function() { | |
// *************************************************** | |
// Globals | |
// *************************************************** | |
var gBrushHolder; | |
var gBrush = null; | |
var gDraw = null; | |
var brushMode = false; | |
var brushing = false; | |
var shiftKey; | |
var node, link, n, l; | |
var brush = null; | |
var d3; | |
// *************************************************** | |
// Defaults | |
// *************************************************** | |
/** | |
* Define default variables here. Those are combined within | |
* the incoming user options which would overwrite the | |
* defaults. | |
**/ | |
var defaults = { | |
data: {}, | |
datalev2: {}, | |
custnodedraw: null, | |
custlinkdraw: null | |
}; | |
/** | |
* Static initialisation | |
**/ | |
window.EntityGraph = { | |
instanceOf : function (d3version, options) { | |
// Publish instance | |
return new EntityGraph(d3version, options); | |
} | |
}; | |
// *************************************************** | |
// Define Chart class and methods | |
// *************************************************** | |
function EntityGraph(d3version, options) { | |
d3 = d3version | |
this.options = extend({}, defaults, options != null ? options : {}); | |
makeAccessor.call(this, this.options) | |
this.render = drawSelection.bind(this); | |
} | |
/** | |
* Define the D3 callback to render graph within the specified | |
* selection DOMs | |
*/ | |
function drawSelection(selection) { | |
brush = d3.brush() | |
.on("start", brushstarted) | |
.on("brush", brushed) | |
.on("end", brushended); | |
d3.select('body').on('keydown', keydown); | |
d3.select('body').on('keyup', keyup); | |
_this = this; | |
selection.each(function() { | |
//TODO allow overwrite of width height | |
// Width/Height are sized to the selection's node size | |
var width = parseInt(d3.select(this).node().getBoundingClientRect().width, 10); | |
var height = parseInt(d3.select(this).node().getBoundingClientRect().height, 10); | |
// Retrieve local variables from class variables | |
var data = _this.data(); | |
// Create base svg and add the data | |
var svg = d3.select(this).selectAll("svg").data([ data ]); | |
var gEnter = svg.enter().append('div').classed( | |
'svg-container', true).append('svg') | |
.attr("width", width) | |
.attr("height", height) | |
// Remove any previous graphs | |
gEnter.selectAll('.g-main').remove(); | |
// Add our full zoomable graph group | |
var gMain = gEnter.append('g') | |
.classed('g-main', true); | |
var zoom = d3.zoom().on('zoom', zoomed) | |
gMain.call(zoom); | |
// Background, catches clicks to reset node selection | |
var rect = gMain.append('rect') | |
.attr('width', width) | |
.attr('height', height) | |
.style('fill', 'white') | |
.on('click', () => { | |
node.selectAll('.node-el').each(function(d) { | |
this.selected = false; | |
this.previouslySelected = false; | |
}); | |
node.selectAll('.node-el').classed("selected", false); | |
}); | |
// Container to draw our graph in | |
gDraw = gMain.append('g').attr("class", "drawContainer"); | |
// Brush container | |
// The brush needs to go before the nodes so that it doesn't | |
// get called when the mouse is over a node | |
gBrushHolder = gDraw.append('g').attr("class", "brushHolder"); | |
gBrush = null; | |
// Simulation forces | |
simulation = d3.forceSimulation() | |
.force("link", d3.forceLink() | |
.id(function(d) { return d.id; }) | |
.distance(function(d) { | |
return 10; | |
}) | |
) | |
.force("charge", d3.forceManyBody().strength(-120)) | |
.force("center", d3.forceCenter(width / 2, height / 2)) | |
.force("x", d3.forceX(width/2)) | |
.force("y", d3.forceY(height/2)); | |
_this.update(this); | |
// *************************************************** | |
// Update methods | |
// Reacting automatically once the corresponding variable is set | |
// *************************************************** | |
_this.updateData = function() { | |
update(); | |
}; | |
_this.updateDatalev2 = function() { | |
_this.updateDataLevel2(this); | |
}; | |
}); | |
} | |
EntityGraph.prototype.updateDataLevel2 = function(selection) | |
{ | |
_this = this; | |
var dataLev2 = _this.datalev2(); | |
var data = _this.data(); | |
// data.nodes = data.nodes.filter(function(n) { | |
// return n.level !== "level2" | |
// }); | |
// data.links = data.links.filter(function(l) { | |
// return l.level !== "level2" | |
// }); | |
var i; | |
for (i = 0; i < dataLev2.nodes.length; i++) { | |
data.nodes.push(dataLev2.nodes[i]); | |
} | |
for (i = 0; i < dataLev2.links.length; i++) { | |
data.links.push(dataLev2.links[i]); | |
} | |
_this.update(selection); | |
simulation.alpha(1).restart(); | |
} | |
/** | |
* Draw the initial and update the existing graph with the full | |
* current data set | |
*/ | |
EntityGraph.prototype.update = function(selection) | |
{ | |
_this = this; | |
// Retrieve local variables from class variables | |
var data = _this.data(); | |
var custnodedraw = _this.custnodedraw(); | |
var custlinkdraw = _this.custlinkdraw(); | |
// Update data | |
l = gDraw.selectAll(".link") | |
.data(data.links, function(d) {return d.source + "," + d.target}); | |
n = gDraw.selectAll(".node").data(data.nodes, function(d) {return d.id}) | |
// Apply general update pattern | |
exitLinks(l); | |
enterLinks(l, custlinkdraw); | |
exitNodes(n); | |
enterNodes(n, custnodedraw); | |
// Refernce to node and link selections | |
link = gDraw.selectAll(".link"); | |
node = gDraw.selectAll(".node"); | |
// Feed simulation with current data | |
simulation | |
.nodes(data.nodes) | |
.on("tick", ticked); | |
simulation.force("link") | |
.links(data.links); | |
} | |
/** | |
* Defines how new nodes are drawn | |
*/ | |
function enterNodes(n, drawCallback) { | |
n = n.enter().append("g") | |
.attr("class", function(d){return "node"}) | |
.call(d3.drag() | |
.on("start", dragstarted) | |
.on("drag", dragged) | |
.on("end", dragended)); | |
n.each(function (d) { | |
if(drawCallback) { | |
drawCallback(this, d); | |
} | |
else { | |
d3.select(this).append("circle") | |
.attr("cx", 0) | |
.attr("cy", 0) | |
.attr("r", function(d) {return 10}) | |
.classed("node-el", true); | |
} | |
}); | |
n.selectAll('.avatar--circle__mark-main').classed("node-el", true); | |
} | |
/** | |
* Defines how new links are drawn | |
*/ | |
function enterLinks(l, drawCallback) { | |
l = l.enter() | |
l.each(function(d) { | |
if(drawCallback) { | |
drawCallback(this, d); | |
} | |
else { | |
d3.select(this).insert("line", ".node") | |
.attr("class", "link") | |
.attr("stroke", function(d) { return d.color || '#999'; }) | |
.attr("stroke-width", function(d) { return Math.sqrt(d.value); }); | |
} | |
}); | |
} | |
/** | |
* Removes nodes that have been removed using update id | |
*/ | |
function exitNodes(n) { | |
n.exit().remove(); | |
} | |
/** | |
* Removes links that have been removed using update id | |
*/ | |
function exitLinks(l) { | |
l.exit().remove(); | |
} | |
/** | |
* On zoom | |
*/ | |
function zoomed() { | |
gDraw.attr("transform", d3.event.transform); | |
} | |
function brushstarted() { | |
// keep track of whether we're actively brushing so that we | |
// don't remove the brush on keyup in the middle of a selection | |
brushing = true; | |
node.selectAll('.node-el').each(function(d) { | |
this.previouslySelected = shiftKey && this.selected; | |
}); | |
} | |
function brushed() { | |
if (!d3.event.sourceEvent) return; | |
if (!d3.event.selection) return; | |
var extent = d3.event.selection; | |
node.each(function(d){ | |
d3.select(this).select('.node-el').classed("selected", function(){ | |
return this.selected = this.previouslySelected ^ | |
(extent[0][0] <= d.x && d.x < extent[1][0] | |
&& extent[0][1] <= d.y && d.y < extent[1][1]); | |
}); | |
}); | |
} | |
function brushended() { | |
if (!d3.event.sourceEvent) return; | |
if (!d3.event.selection) return; | |
if (!gBrush) return; | |
gBrush.call(brush.move, null); | |
if (!brushMode) { | |
// the shift key has been release before we ended our brushing | |
gBrush.remove(); | |
gBrush = null; | |
} | |
brushing = false; | |
} | |
function keydown() { | |
shiftKey = d3.event.shiftKey; | |
if (shiftKey) { | |
// if we already have a brush, don't do anything | |
if (gBrush) | |
return; | |
brushMode = true; | |
if (!gBrush) { | |
gBrush = gBrushHolder.append('g'); | |
gBrush.call(brush); | |
} | |
} | |
} | |
function ticked() { | |
// update node and line positions at every step of | |
// the force simulation | |
link.attr("x1", function(d) { return d.source.x; }) | |
.attr("y1", function(d) { return d.source.y; }) | |
.attr("x2", function(d) { return d.target.x; }) | |
.attr("y2", function(d) { return d.target.y; }); | |
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); | |
} | |
function keyup() { | |
shiftKey = false; | |
brushMode = false; | |
if (!gBrush) | |
return; | |
if (!brushing) { | |
// only remove the brush if we're not actively brushing | |
// otherwise it'll be removed when the brushing ends | |
gBrush.remove(); | |
gBrush = null; | |
} | |
} | |
function dragstarted(d) { | |
if (!d3.event.active) simulation.alphaTarget(0.9).restart(); | |
if (!d.selected && !shiftKey) { | |
// if this node isn't selected, then we have to unselect every other node | |
node.selectAll('.node-el').classed("selected", function(p) { return this.selected = this.previouslySelected = false; }); | |
} | |
d3.select(this).select('.node-el').classed("selected", function(p) { this.previouslySelected = this.selected; return d.selected = true; }); | |
node.filter(function(d) { return d3.select(this).select('.node-el').classed('selected'); }) | |
.each(function(d) { | |
d.fx = d.x; | |
d.fy = d.y; | |
}) | |
} | |
function dragged(d) { | |
node.filter(function(d) { return d3.select(this).select('.node-el').classed('selected'); }) | |
.each(function(d) { | |
d.fx += d3.event.dx; | |
d.fy += d3.event.dy; | |
}) | |
} | |
function dragended(d) { | |
if (!d3.event.active) simulation.alphaTarget(0); | |
d.fx = null; | |
d.fy = null; | |
node.filter(function(d) { return d3.select(this).select('.node-el').classed('selected'); }) | |
.each(function(d) { | |
d.fx = null; | |
d.fy = null; | |
}) | |
} | |
// *************************************************** | |
// Utility methods | |
// *************************************************** | |
function makeAccessor(properties) { | |
var _this = this; | |
for (var i in properties) { | |
(function (i) { | |
_this[i] = function (value) { | |
if(value === null || value === undefined) | |
{ | |
return properties[i]; | |
} | |
properties[i] = value; | |
updateFunc = ("update" + i.toCamelCase()); | |
if (typeof _this[updateFunc] === 'function') _this[updateFunc](); | |
return _this; | |
} | |
})(i); | |
} | |
} | |
String.prototype.toCamelCase = function() { | |
return this.charAt(0).toUpperCase() + this.slice(1); | |
} | |
function extend() { | |
for (var i = 1; i < arguments.length; i++) { | |
for (var prop in arguments[i]) { | |
if (arguments[i].hasOwnProperty(prop)) { | |
arguments[0][prop] = arguments[i][prop]; | |
} | |
} | |
} | |
return arguments[0]; | |
} | |
})(); |
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Entity Graph</title> | |
<meta name="description" content="Entity Graph"> | |
<meta name="author" content="Patrick Seidler"> | |
<meta name="viewport" content="width=device-width,initial-scale=1"> | |
<link rel="stylesheet" href="reset.css"> | |
<link rel="stylesheet" href="graph.css"> | |
<script type="text/javascript"> | |
window.onload = function() { | |
var chart; | |
var defs; | |
var avatars = {}; | |
var id = "162033325"; | |
var custNodeDrawCallback = function(selection, d) | |
{ | |
var def = defs[d.type] | |
if(d.label == id) { | |
var mark = d.type == 'crime' && !d.isSolved | |
avatar.draw(def.color1, def.color2, def.label, d.id, 20, mark, selection); | |
return; | |
} | |
else if(!avatars.hasOwnProperty(d.type + d.isSolved)) | |
{ | |
var mark = d.type == 'crime' && !d.isSolved | |
avatars[d.type + d.isSolved] = avatar.draw(def.color1, def.color2, def.label, d.id, 10, mark, null); | |
} | |
var a = avatars[d.type + d.isSolved] | |
var i; | |
for(i = 0; i < a.length; i++) { | |
var savedAvatar = a[i].cloneNode(true) | |
savedAvatar.setAttributeNS(null, "id", d.id); | |
selection.appendChild(savedAvatar); | |
} | |
} | |
var custLinkDrawCallback = function(selection, d) | |
{ | |
var line = d3.select(selection).insert("line", ".node"); | |
line.attr("class", "link") | |
.attr("stroke", function(d) { return d.color || '#999'; }) | |
.attr("stroke-width", function(d) { return Math.sqrt(d.value); }); | |
if(d.link == 'similarity') | |
line.attr("stroke-dasharray", "5 5"); | |
} | |
function display(data) { | |
defs = data.defs; | |
chart = EntityGraph | |
.instanceOf(d3) | |
.data(data) | |
.custnodedraw(custNodeDrawCallback) | |
.custlinkdraw(custLinkDrawCallback); | |
//Or like this | |
//var graph = Chart.instanceOf( { width : 500 } ); | |
var svg = d3.select("#vis") | |
.call(chart.render); | |
} | |
d3.timeout(function() { | |
d3.json('data_add1.json', chart.datalev2); | |
d3.json('data_add2.json', chart.datalev2); | |
}, 3000); | |
d3.json('data.json', display); | |
}; | |
</script> | |
</head> | |
<body> | |
<div class="container"> | |
<div id="vis"></div> | |
<div class="footer"> | |
</div> | |
</div> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="d3v4-brush-lite.js"></script> | |
<script src="avatar.js"></script> | |
<script src="graph.js"></script> | |
</body> | |
</html> |
/* HTML5 ✰ Boilerplate | |
* ==|== normalize ========================================================== | |
*/ | |
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } | |
audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } | |
audio:not([controls]) { display: none; } | |
[hidden] { display: none; } | |
html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } | |
body { margin: 0; font-size: 13px; line-height: 1.231; } | |
body, button, input, select, textarea { font-family: sans-serif; color: #222; } | |
::-moz-selection { background: #fe57a1; color: #fff; text-shadow: none; } | |
::selection { background: #fe57a1; color: #fff; text-shadow: none; } | |
a { color: #00e; } | |
a:visited { color: #551a8b; } | |
a:hover { color: #06e; } | |
a:focus { outline: thin dotted; } | |
a:hover, a:active { outline: 0; } | |
abbr[title] { border-bottom: 1px dotted; } | |
b, strong { font-weight: bold; } | |
blockquote { margin: 1em 40px; } | |
dfn { font-style: italic; } | |
hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } | |
ins { background: #ff9; color: #000; text-decoration: none; } | |
mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } | |
pre, code, kbd, samp { font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 1em; } | |
pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } | |
q { quotes: none; } | |
q:before, q:after { content: ""; content: none; } | |
small { font-size: 85%; } | |
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } | |
sup { top: -0.5em; } | |
sub { bottom: -0.25em; } | |
ul, ol { margin: 1em 0; padding: 0 0 0 40px; } | |
dd { margin: 0 0 0 40px; } | |
nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; } | |
img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } | |
svg:not(:root) { overflow: hidden; } | |
figure { margin: 0; } | |
form { margin: 0; } | |
fieldset { border: 0; margin: 0; padding: 0; } | |
label { cursor: pointer; } | |
legend { border: 0; *margin-left: -7px; padding: 0; } | |
button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } | |
button, input { line-height: normal; *overflow: visible; } | |
table button, table input { *overflow: auto; } | |
button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } | |
input[type="checkbox"], input[type="radio"] { box-sizing: border-box; } | |
input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } | |
input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } | |
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } | |
textarea { overflow: auto; vertical-align: top; resize: vertical; } | |
input:valid, textarea:valid { } | |
input:invalid, textarea:invalid { background-color: #f0dddd; } | |
table { border-collapse: collapse; border-spacing: 0; } | |
td { vertical-align: top; } | |
/* ==|== primary styles ===================================================== | |
Author: | |
========================================================================== */ | |
/* ==|== non-semantic helper classes ======================================== */ | |
.ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; } | |
.ir br { display: none; } | |
.hidden { display: none !important; visibility: hidden; } | |
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } | |
.visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } | |
.invisible { visibility: hidden; } | |
.clearfix:before, .clearfix:after { content: ""; display: table; } | |
.clearfix:after { clear: both; } | |
.clearfix { zoom: 1; } | |
/* ==|== media queries ====================================================== */ | |
@media only screen and (min-width: 480px) { | |
} | |
@media only screen and (min-width: 768px) { | |
} | |
/* ==|== print styles ======================================================= */ | |
@media print { | |
* { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } | |
a, a:visited { text-decoration: underline; } | |
a[href]:after { content: " (" attr(href) ")"; } | |
abbr[title]:after { content: " (" attr(title) ")"; } | |
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } | |
pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } | |
thead { display: table-header-group; } | |
tr, img { page-break-inside: avoid; } | |
img { max-width: 100% !important; } | |
@page { margin: 0.5cm; } | |
p, h2, h3 { orphans: 3; widows: 3; } | |
h2, h3 { page-break-after: avoid; } | |
} |