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.
 
 
 
 

316 lines
8.7 KiB

var Class = require('../../core/class');
var Transform = require('../../core/transform');
var Color = require('../../core/color');
var Node = require('./node');
var DOM = require('./dom');
var precision = 100;
var defaultBox = { left: 0, top: 0, width: 500, height: 500 };
module.exports = Class(Node, {
element_initialize: Node.prototype.initialize,
initialize: function(tag){
this.element_initialize(tag);
var element = this.element;
var skew = this.skewElement = DOM.createElement('skew');
skew.on = true;
element.appendChild(skew);
var fill = this.fillElement = DOM.createElement('fill');
fill.on = false;
element.appendChild(fill);
var stroke = this.strokeElement = DOM.createElement('stroke');
stroke.on = false;
element.appendChild(stroke);
},
/* transform */
_transform: function(){
var container = this.parentNode;
// Active Transformation Matrix
var m = container ? new Transform(container._activeTransform).transform(this) : this;
// Box in shape user space
var box = this._boxCoords || this._size || defaultBox;
var originX = box.left || 0,
originY = box.top || 0,
width = box.width || 1,
height = box.height || 1;
// Flipped
var flip = m.yx / m.xx > m.yy / m.xy;
if (m.xx < 0 ? m.xy >= 0 : m.xy < 0) flip = !flip;
flip = flip ? -1 : 1;
m = new Transform().scale(flip, 1).transform(m);
// Rotation is approximated based on the transform
var rotation = Math.atan2(-m.xy, m.yy) * 180 / Math.PI;
// Reverse the rotation, leaving the final transform in box space
var rad = rotation * Math.PI / 180, sin = Math.sin(rad), cos = Math.cos(rad);
var transform = new Transform(
(m.xx * cos - m.xy * sin),
(m.yx * cos - m.yy * sin) * flip,
(m.xy * cos + m.xx * sin) * flip,
(m.yy * cos + m.yx * sin)
);
var rotationTransform = new Transform().rotate(rotation, 0, 0);
var shapeToBox = new Transform().rotate(-rotation, 0, 0).transform(m).moveTo(0,0);
// Scale box after reversing rotation
width *= Math.abs(shapeToBox.xx);
height *= Math.abs(shapeToBox.yy);
// Place box
var left = m.x, top = m.y;
// Compensate for offset by center origin rotation
var vx = -width / 2, vy = -height / 2;
var point = rotationTransform.point(vx, vy);
left -= point.x - vx;
top -= point.y - vy;
// Adjust box position based on offset
var rsm = new Transform(m).moveTo(0,0);
point = rsm.point(originX, originY);
left += point.x;
top += point.y;
if (flip < 0) left = -left - width;
// Place transformation origin
var point0 = rsm.point(-originX, -originY);
var point1 = rotationTransform.point(width, height);
var point2 = rotationTransform.point(width, 0);
var point3 = rotationTransform.point(0, height);
var minX = Math.min(0, point1.x, point2.x, point3.x),
maxX = Math.max(0, point1.x, point2.x, point3.x),
minY = Math.min(0, point1.y, point2.y, point3.y),
maxY = Math.max(0, point1.y, point2.y, point3.y);
var transformOriginX = (point0.x - point1.x / 2) / (maxX - minX) * flip,
transformOriginY = (point0.y - point1.y / 2) / (maxY - minY);
// Adjust the origin
point = shapeToBox.point(originX, originY);
originX = point.x;
originY = point.y;
// Scale stroke
var strokeWidth = this._strokeWidth;
if (strokeWidth){
// Scale is the hypothenus between the two vectors
// TODO: Use area calculation instead
var vx = m.xx + m.xy, vy = m.yy + m.yx;
strokeWidth *= Math.sqrt(vx * vx + vy * vy) / Math.sqrt(2);
}
// convert to multiplied precision space
originX *= precision;
originY *= precision;
left *= precision;
top *= precision;
width *= precision;
height *= precision;
// Set box
var element = this.element;
element.coordorigin = originX + ',' + originY;
element.coordsize = width + ',' + height;
element.style.left = left + 'px';
element.style.top = top + 'px';
element.style.width = width;
element.style.height = height;
element.style.rotation = rotation.toFixed(8);
element.style.flip = flip < 0 ? 'x' : '';
// Set transform
var skew = this.skewElement;
skew.matrix = [transform.xx.toFixed(4), transform.xy.toFixed(4), transform.yx.toFixed(4), transform.yy.toFixed(4), 0, 0];
skew.origin = transformOriginX + ',' + transformOriginY;
// Set stroke
this.strokeElement.weight = strokeWidth + 'px';
},
/* styles */
_createGradient: function(style, stops){
var fill = this.fillElement;
// Temporarily eject the fill from the DOM
this.element.removeChild(fill);
fill.type = style;
fill.method = 'none';
fill.rotate = true;
var colors = [], color1, color2;
var addColor = function(offset, color){
color = Color.detach(color);
if (color1 == null) color1 = color2 = color;
else color2 = color;
colors.push(offset + ' ' + color[0]);
};
// Enumerate stops, assumes offsets are enumerated in order
if ('length' in stops) for (var i = 0, l = stops.length - 1; i <= l; i++) addColor(i / l, stops[i]);
else for (var offset in stops) addColor(offset, stops[offset]);
fill.color = color1[0];
fill.color2 = color2[0];
//if (fill.colors) fill.colors.value = colors; else
fill.colors = colors;
// Opacity order gets flipped when color stops are specified
fill.opacity = color2[1];
fill['ao:opacity2'] = color1[1];
fill.on = true;
this.element.appendChild(fill);
return fill;
},
_setColor: function(type, color){
var element = type == 'fill' ? this.fillElement : this.strokeElement;
if (color == null){
element.on = false;
} else {
color = Color.detach(color);
element.color = color[0];
element.opacity = color[1];
element.on = true;
}
},
fill: function(color){
if (arguments.length > 1){
this.fillLinear(arguments);
} else {
this._boxCoords = defaultBox;
var fill = this.fillElement;
fill.type = 'solid';
fill.color2 = '';
fill['ao:opacity2'] = '';
if (fill.colors) fill.colors.value = '';
this._setColor('fill', color);
}
return this;
},
fillRadial: function(stops, focusX, focusY, radiusX, radiusY, centerX, centerY){
var fill = this._createGradient('gradientradial', stops);
if (focusX == null) focusX = this.left + this.width * 0.5;
if (focusY == null) focusY = this.top + this.height * 0.5;
if (radiusY == null) radiusY = radiusX || (this.height * 0.5);
if (radiusX == null) radiusX = this.width * 0.5;
if (centerX == null) centerX = focusX;
if (centerY == null) centerY = focusY;
centerX += centerX - focusX;
centerY += centerY - focusY;
var box = this._boxCoords = {
left: centerX - radiusX * 2,
top: centerY - radiusY * 2,
width: radiusX * 4,
height: radiusY * 4
};
focusX -= box.left;
focusY -= box.top;
focusX /= box.width;
focusY /= box.height;
fill.focussize = '0 0';
fill.focusposition = focusX + ',' + focusY;
fill.focus = '50%';
this._transform();
return this;
},
fillLinear: function(stops, x1, y1, x2, y2){
var fill = this._createGradient('gradient', stops);
fill.focus = '100%';
if (arguments.length == 5){
var w = Math.abs(x2 - x1), h = Math.abs(y2 - y1);
this._boxCoords = {
left: Math.min(x1, x2),
top: Math.min(y1, y2),
width: w < 1 ? h : w,
height: h < 1 ? w : h
};
fill.angle = (360 + Math.atan2((x2 - x1) / h, (y2 - y1) / w) * 180 / Math.PI) % 360;
} else {
this._boxCoords = null;
fill.angle = (x1 == null) ? 0 : (90 + x1) % 360;
}
this._transform();
return this;
},
fillImage: function(url, width, height, left, top, color1, color2){
var fill = this.fillElement;
if (color1 != null){
color1 = Color.detach(color1);
if (color2 != null) color2 = Color.detach(color2);
fill.type = 'pattern';
fill.color = color1[0];
fill.color2 = color2 == null ? color1[0] : color2[0];
fill.opacity = color2 == null ? 0 : color2[1];
fill['ao:opacity2'] = color1[1];
} else {
fill.type = 'tile';
fill.color = '';
fill.color2 = '';
fill.opacity = 1;
fill['ao:opacity2'] = 1;
}
if (fill.colors) fill.colors.value = '';
fill.rotate = true;
fill.src = url;
fill.size = '1,1';
fill.position = '0,0';
fill.origin = '0,0';
fill.aspect = 'ignore'; // ignore, atleast, atmost
fill.on = true;
if (!left) left = 0;
if (!top) top = 0;
this._boxCoords = width ? { left: left + 0.5, top: top + 0.5, width: width, height: height } : null;
this._transform();
return this;
},
/* stroke */
stroke: function(color, width, cap, join){
var stroke = this.strokeElement;
this._strokeWidth = (width != null) ? width : 1;
stroke.weight = (width != null) ? width + 'px' : 1;
stroke.endcap = (cap != null) ? ((cap == 'butt') ? 'flat' : cap) : 'round';
stroke.joinstyle = (join != null) ? join : 'round';
this._setColor('stroke', color);
return this;
}
});