This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.8 KiB
150 lines
4.8 KiB
var SVGParser = require('./core'); |
|
|
|
SVGParser.implement({ |
|
|
|
findLinkedAttributes: function(element, callback){ |
|
var self = this; |
|
|
|
var cb = function(result){ |
|
var attributes = element.attributes; |
|
for (var i = 0, l = attributes.length; i < l; i++){ |
|
var attribute = attributes[i]; |
|
result[attribute.nodeName] = attribute.nodeValue; |
|
} |
|
if (element.childNodes.length > 0) result.container = element; |
|
callback.call(self, result); |
|
}; |
|
|
|
var href = element.getAttribute('xlink:href') || element.getAttribute('href'); |
|
if (!href){ cb.call(this, {}); return; } |
|
|
|
this.findByURL(element.ownerDocument, href, function(parent){ |
|
if (!parent) cb.call(this, {}); |
|
else this.findLinkedAttributes(parent, function(parentResult){ |
|
cb.call(this, parentResult); |
|
}); |
|
}); |
|
}, |
|
|
|
getBBox: function(graphic){ |
|
var path = graphic.getPath && graphic.getPath(); |
|
return path ? path.measure() : graphic; |
|
}, |
|
|
|
parseBBLength: function(value, bbox, dimension){ |
|
value = /%/.test(value) ? parseFloat(value) / 100 : parseFloat(value); |
|
if (dimension == 'x') return (bbox.left || 0) + (bbox.width || 0) * value; |
|
if (dimension == 'y') return (bbox.top || 0) + (bbox.height || 0) * value; |
|
}, |
|
|
|
getGradientStops: function(element, styles){ |
|
var stops = null, node = element.firstChild; |
|
while (node){ |
|
if (node.nodeName == 'stop'){ |
|
var stopStyles = this.parseStyles(node, styles); |
|
var color = this.parseColor(stopStyles['stop-color'] || 'black', stopStyles['stop-opacity'], stopStyles), |
|
offset = node.getAttribute('offset'); |
|
if (color && offset){ |
|
offset = /%/.test(offset) ? parseFloat(offset) / 100 : parseFloat(offset); |
|
if (offset < 0) offset = 0; |
|
if (offset > 1) offset = 1; |
|
if (!stops) stops = {}; |
|
stops[offset.toFixed(4)] = color; |
|
} |
|
} |
|
node = node.nextSibling; |
|
} |
|
return stops; |
|
}, |
|
|
|
radialGradientFill: function(element, styles, target, x, y){ |
|
this.findLinkedAttributes(element, function(attrs){ |
|
if (!attrs.container) return; |
|
|
|
var stops = this.getGradientStops(attrs.container, styles); |
|
if (!stops) return; |
|
|
|
// TODO: Transform |
|
|
|
var cx = attrs.cx || '50%', |
|
cy = attrs.cy || '50%', |
|
rx = attrs.r || '50%', ry, |
|
fx = attrs.fx || cx, |
|
fy = attrs.fy || cy; |
|
|
|
if (attrs['gradientUnits'] == 'userSpaceOnUse'){ |
|
cx = this.parseLength(cx, styles, 'x') - x; |
|
cy = this.parseLength(cy, styles, 'y') - y; |
|
rx = ry = this.parseLength(rx, styles); |
|
fx = this.parseLength(fx, styles, 'x') - x; |
|
fy = this.parseLength(fy, styles, 'y') - y; |
|
} else { |
|
var bb = this.getBBox(target); |
|
cx = this.parseBBLength(cx, bb, 'x'); |
|
cy = this.parseBBLength(cy, bb, 'y'); |
|
rx = this.parseBBLength(rx, bb, 'x'); |
|
ry = rx * (bb.height / bb.width); |
|
fx = this.parseBBLength(fx, bb, 'x'); |
|
fy = this.parseBBLength(fy, bb, 'y'); |
|
} |
|
|
|
target.fillRadial(stops, fx, fy, rx, ry, cx, cy); |
|
|
|
}); |
|
}, |
|
|
|
linearGradientFill: function(element, styles, target, x, y){ |
|
this.findLinkedAttributes(element, function(attrs){ |
|
if (!attrs.container) return; |
|
|
|
var stops = this.getGradientStops(attrs.container, styles); |
|
if (!stops) return; |
|
|
|
var x1 = attrs.x1 || 0, |
|
y1 = attrs.y1 || 0, |
|
x2 = attrs.x2 || '100%', |
|
y2 = attrs.y2 || 0; |
|
|
|
// TODO: Transform |
|
|
|
if (attrs['gradientUnits'] == 'userSpaceOnUse'){ |
|
x1 = this.parseLength(x1, styles, 'x') - x; |
|
y1 = this.parseLength(y1, styles, 'y') - y; |
|
x2 = this.parseLength(x2, styles, 'x') - x; |
|
y2 = this.parseLength(y2, styles, 'y') - y; |
|
} else { |
|
x1 = /%/.test(x1) ? parseFloat(x1) / 100 : parseFloat(x1); |
|
y1 = /%/.test(y1) ? parseFloat(y1) / 100 : parseFloat(y1); |
|
x2 = /%/.test(x2) ? parseFloat(x2) / 100 : parseFloat(x2); |
|
y2 = /%/.test(y2) ? parseFloat(y2) / 100 : parseFloat(y2); |
|
|
|
// If both points are close to the opposite edges, use rotation angle instead |
|
var closeToEdge = |
|
(x1 > -0.01 && x1 < 0.01 && x2 > 0.99 && x2 < 1.01) || |
|
(x1 > 0.99 && x1 < 1.01 && x2 > -0.01 && x2 < 0.01) || |
|
(y1 > -0.01 && y1 < 0.01 && y2 > 0.99 && y2 < 1.01) || |
|
(y1 > 0.99 && y1 < 1.01 && y2 > -0.01 && y2 < 0.01); |
|
|
|
if (closeToEdge){ |
|
var angle = Math.atan2(y1 - y2, x2 - x1) * 180 / Math.PI; |
|
target.fillLinear(stops, angle); |
|
return; |
|
} |
|
|
|
// TODO: Always use rotation angle, but offset stops instead to adjust |
|
// TODO2: Never use rotation angle because measuring is deprecated |
|
var bb = this.getBBox(target); |
|
x1 = this.parseBBLength(x1, bb, 'x'); |
|
y1 = this.parseBBLength(y1, bb, 'y'); |
|
x2 = this.parseBBLength(x2, bb, 'x'); |
|
y2 = this.parseBBLength(y2, bb, 'y'); |
|
} |
|
target.fillLinear(stops, x1, y1, x2, y2); |
|
}); |
|
}, |
|
|
|
patternFill: function(element, styles, target, x, y){ |
|
// TODO: If the pattern is an image, fillImage |
|
} |
|
|
|
});
|
|
|