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.
 
 
 
 

680 lines
19 KiB

"use strict";
var ruler = function (options) {
this.api = this.builder();
this.api.constructRulers.call(this, options);
};
ruler.prototype.builder = function () {
var VERTICAL = 1,
HORIZONTAL = 2,
CUR_DELTA_X = 0,
CUR_DELTA_Y = 0,
CUR_SCALE = 1;
var options,
rulerz = {},
guides = [],
theRulerDOM = document.createElement('div'),
corners = [],
defaultOptions = {
rulerHeight: 15,
fontFamily: 'arial',
fontSize: '8px',
strokeStyle: 'gray',
sides: ['top', 'left'],
cornerSides: ['TL'],
lineWidth: 1,
enableMouseTracking: true,
enableToolTip: true
};
var rotateRuler = function (curRuler, angle) {
var rotation = 'rotate(' + angle + 'deg)';
var origin = ruler.prototype.utils.pixelize(Math.abs(parseInt(curRuler.canvas.style.left))) + ' 100%';
curRuler.canvas.style.webkitTransform = rotation;
curRuler.canvas.style.MozTransform = rotation;
curRuler.canvas.style.OTransform = rotation;
curRuler.canvas.style.msTransform = rotation;
curRuler.canvas.style.transform = rotation;
curRuler.canvas.style.webkitTransformOrigin = origin;
curRuler.canvas.style.MozTransformOrigin = origin;
curRuler.canvas.style.OTransformOrigin = origin;
curRuler.canvas.style.msTransformOrigin = origin;
curRuler.canvas.style.transformOrigin = origin;
};
var positionRuler = function (curRuler, alignment) {
curRuler.canvas.style.left = ruler.prototype.utils.pixelize(-((curRuler.canvas.width / 2) - curRuler.canvas.height));
switch (alignment) {
case 'top':
curRuler.orgPos = parseInt(curRuler.canvas.style.left);
break;
case 'left':
curRuler.canvas.style.top = ruler.prototype.utils.pixelize(-curRuler.canvas.height - 1);
curRuler.orgPos = parseInt(curRuler.canvas.style.top);
rotateRuler(curRuler, 90);
break;
}
};
var attachListeners = function (container, curRul) {
var mousedown = function (e) {
constructGuide(curRul.dimension, e.clientX, e.clientY, e);
};
curRul.canvas.addEventListener('mousedown', mousedown);
curRul.clearListeners = function () {
curRul.canvas.removeEventListener('mousedown', mousedown);
}
};
var constructGuide = function (dimension, x, y, e, isSet) {
var guideIndex;
var moveCB = function (line, x, y) {
var coor = line.dimension === VERTICAL ? x : y;
if (!line.assigned()) {
if (coor > options.rulerHeight) {
line.assigned(true);
}
return;
}
if (coor < options.rulerHeight) {
guides.some(function (guideLine, index) {
if (guideLine.line === line) {
guideIndex = index;
return true;
}
});
line.destroy();
guides.splice(guideIndex, 1);
}
};
var guide = document.createElement('div'),
guideStyle = dimension === VERTICAL ? 'rul_lineVer' : 'rul_lineHor',
curDelta = dimension === VERTICAL ? CUR_DELTA_X : CUR_DELTA_Y;
guide.title = 'Double click to delete';
ruler.prototype.utils.addClasss(guide, ['rul_line', guideStyle]);
guide = theRulerDOM.appendChild(guide);
if (dimension === VERTICAL) {
guide.style.left = ruler.prototype.utils.pixelize(x - options.container.getBoundingClientRect().left);
if (isSet) guide.style.left = ruler.prototype.utils.pixelize(Math.round(x / CUR_SCALE) + options.rulerHeight);
}
else {
guide.style.top = ruler.prototype.utils.pixelize(y - options.container.getBoundingClientRect().top);
if (isSet) guide.style.top = ruler.prototype.utils.pixelize(Math.round(y / CUR_SCALE) + options.rulerHeight);
}
guides.push({
dimension: dimension,
line: ruler.prototype.guideLine(guide, options.container.querySelector('.rul_wrapper'), dimension, options, curDelta, moveCB, e)
});
};
var constructRuler = function (container, alignment) {
var canvas,
dimension = alignment === 'left' || alignment === 'right' ? VERTICAL : HORIZONTAL,
rulerStyle = dimension === VERTICAL ? 'rul_ruler_Vertical' : 'rul_ruler_Horizontal',
element = document.createElement('canvas');
ruler.prototype.utils.addClasss(element, ['rul_ruler', rulerStyle, 'rul_align_' + alignment]);
canvas = container.appendChild(element);
rulerz[alignment] = ruler.prototype.rulerConstructor(canvas, options, dimension);
rulerz[alignment].drawRuler(container.offsetWidth, options.rulerHeight);
positionRuler(rulerz[alignment], alignment);
attachListeners(container, rulerz[alignment]);
};
var constructCorner = (function () {
function cornerDraw(container, side) {
var corner = document.createElement('div'),
cornerStyle = 'rul_corner' + side.toUpperCase();
corner.title = 'Clear Guide lines';
ruler.prototype.utils.addClasss(corner, ['rul_corner', cornerStyle]);
corner.style.width = ruler.prototype.utils.pixelize(options.rulerHeight + 1);
corner.style.height = ruler.prototype.utils.pixelize(options.rulerHeight);
return container.appendChild(corner);
}
function mousedown(e) {
e.stopPropagation();
clearGuides();
}
return function (container, cornerSides) {
cornerSides.forEach(function (side) {
var corner = cornerDraw(container, side);
corner.addEventListener('mousedown', mousedown);
corner.destroy = function () {
corner.removeEventListener('mousedown', mousedown);
corner.parentNode.removeChild(corner);
};
corners.push(corner);
})
}
})();
var mouseup = function (e) {
guides.forEach(function (guide) {
guide.line.stopDrag();
})
};
var constructRulers = function (curOptions) {
theRulerDOM = ruler.prototype.utils.addClasss(theRulerDOM, 'rul_wrapper');
options = ruler.prototype.utils.extend(defaultOptions, curOptions);
theRulerDOM = options.container.appendChild(theRulerDOM);
options.sides.forEach(function (side) {
constructRuler(theRulerDOM, side);
});
constructCorner(theRulerDOM, options.cornerSides);
options.container.addEventListener('mouseup', mouseup);
};
var forEachRuler = function (cb) {
var index = 0;
for (var rul in rulerz) {
if (rulerz.hasOwnProperty(rul)) {
cb(rulerz[rul], index++);
}
}
};
var setPos = function (values) {
var orgX = 0,
orgY,
deltaX = 0,
deltaY = 0;
forEachRuler(function (curRul) {
if (curRul.dimension === VERTICAL) {
orgY = curRul.canvas.style.top;
curRul.canvas.style.top = ruler.prototype.utils.pixelize(curRul.orgPos + (parseInt(values.y)));
deltaY = parseInt(orgY) - parseInt(curRul.canvas.style.top);
}
else {
orgX = curRul.canvas.style.left;
curRul.canvas.style.left = ruler.prototype.utils.pixelize(curRul.orgPos + (parseInt(values.x)));
deltaX = parseInt(orgX) - parseInt(curRul.canvas.style.left);
}
});
guides.forEach(function (guide) {
if (guide.dimension === HORIZONTAL) {
guide.line.guideLine.style.top = ruler.prototype.utils.pixelize(parseInt(guide.line.guideLine.style.top) - deltaY);
guide.line.curPosDelta(parseInt(values.y));
}
else {
guide.line.guideLine.style.left = ruler.prototype.utils.pixelize(parseInt(guide.line.guideLine.style.left) - deltaX);
guide.line.curPosDelta(parseInt(values.x));
}
});
CUR_DELTA_X = parseInt(values.x);
CUR_DELTA_Y = parseInt(values.y);
};
var setScale = function (newScale) {
var curPos, orgDelta, curScaleFac;
forEachRuler(function (rul) {
rul.context.clearRect(0, 0, rul.canvas.width, rul.canvas.height);
rul.context.beginPath();
rul.setScale(newScale);
rul.context.stroke();
CUR_SCALE = newScale;
});
guides.forEach(function (guide) {
if (guide.dimension === HORIZONTAL) {
curPos = parseInt(guide.line.guideLine.style.top);
orgDelta = options.rulerHeight + 1;
curScaleFac = (parseFloat(newScale) / guide.line.curScale());
guide.line.guideLine.style.top = ruler.prototype.utils.pixelize(((curPos - orgDelta - CUR_DELTA_Y) / curScaleFac) + orgDelta + CUR_DELTA_Y);
guide.line.curScale(newScale);
}
else {
curPos = parseInt(guide.line.guideLine.style.left);
orgDelta = options.rulerHeight + 1;
curScaleFac = (parseFloat(newScale) / guide.line.curScale());
guide.line.guideLine.style.left = ruler.prototype.utils.pixelize(((curPos - orgDelta - CUR_DELTA_X) / curScaleFac) + orgDelta + CUR_DELTA_X);
guide.line.curScale(newScale);
}
});
};
var clearGuides = function () {
guides.forEach(function (guide) {
guide.line.destroy();
});
guides = [];
};
var toggleGuideVisibility = function (val) {
var func = val ? 'show' : 'hide';
guides.forEach(function (guide) {
guide.line[func]();
});
};
var toggleRulerVisibility = function (val) {
var state = val ? 'block' : 'none';
theRulerDOM.style.display = state;
var trackers = options.container.querySelectorAll('.rul_tracker');
if (trackers.length > 0) {
trackers[0].style.display = state;
trackers[1].style.display = state;
}
};
var getGuides = function () {
return guides.map(function (guide) {
return {
posX: Math.round((parseInt(guide.line.guideLine.style.left) - CUR_DELTA_X - options.rulerHeight) * CUR_SCALE),
posY: Math.round((parseInt(guide.line.guideLine.style.top) - CUR_DELTA_Y - options.rulerHeight) * CUR_SCALE),
dimension: guide.dimension
}
});
};
var setGuides = function (_guides) {
if (!_guides) { return }
_guides.forEach(function (guide) {
constructGuide(guide.dimension, guide.posX, guide.posY, null, true)
})
};
var destroy = function () {
clearGuides();
forEachRuler(function (ruler) {
ruler.destroy();
});
corners.forEach(function (corner) {
corner.destroy();
});
options.container.removeEventListener('mouseup', mouseup);
theRulerDOM.parentNode.removeChild(theRulerDOM);
};
return {
VERTICAL: VERTICAL,
HORIZONTAL: HORIZONTAL,
setPos: setPos,
setScale: setScale,
clearGuides: clearGuides,
getGuides: getGuides,
setGuides: setGuides,
constructRulers: constructRulers,
toggleRulerVisibility: toggleRulerVisibility,
toggleGuideVisibility: toggleGuideVisibility,
destroy: destroy
}
};
ruler.prototype.rulerConstructor = function (_canvas, options, rulDimension) {
var canvas = _canvas,
context = canvas.getContext('2d'),
rulThickness = 0,
rulLength = 0,
rulScale = 1,
dimension = rulDimension || 2,
orgPos = 0,
tracker = document.createElement('div');
var getLength = function () {
return rulLength;
};
var getThickness = function () {
return rulThickness;
};
var getScale = function () {
return rulScale;
};
var setScale = function (newScale) {
rulScale = parseFloat(newScale);
drawPoints();
return rulScale;
};
var drawRuler = function (_rulerLength, _rulerThickness, _rulerScale) {
rulLength = canvas.width = _rulerLength * 4;
rulThickness = canvas.height = _rulerThickness;
rulScale = _rulerScale || rulScale;
context.strokeStyle = options.strokeStyle;
context.font = options.fontSize + ' ' + options.fontFamily;
context.lineWidth = options.lineWidth;
context.beginPath();
drawPoints();
context.stroke();
};
var drawPoints = function () {
var pointLength = 0,
label = '',
delta = 0,
draw = false,
lineLengthMax = 0,
lineLengthMed = rulThickness / 2,
lineLengthMin = rulThickness / 2;
for (var pos = 0; pos <= rulLength; pos += 1) {
delta = ((rulLength / 2) - pos);
draw = false;
label = '';
if (delta % 50 === 0) {
pointLength = lineLengthMax;
if ((Math.abs(delta) * rulScale) > 10 || (Math.abs(delta) * rulScale) == 0) {
label = Math.round(Math.abs(delta) * rulScale);
} else {
label = parseFloat(Math.abs(delta) * rulScale).toFixed(1);
}
// label = Math.round(Math.abs(delta) * rulScale);
draw = true;
}
else if (delta % 25 === 0) {
pointLength = lineLengthMed;
draw = true;
}
else if (delta % 5 === 0) {
pointLength = lineLengthMin;
draw = true;
}
if (draw) {
context.moveTo(pos + 0.5, rulThickness + 0.5);
context.lineTo(pos + 0.5, pointLength + 0.5);
context.fillText(label, pos + 1.5, (rulThickness / 2) + 1);
}
}
};
var mousemove = function (e) {
var posX = e.clientX;
var posY = e.clientY;
if (dimension === 2) {
tracker.style.left = ruler.prototype.utils.pixelize(posX - parseInt(options.container.getBoundingClientRect().left));
}
else {
tracker.style.top = ruler.prototype.utils.pixelize(posY - parseInt(options.container.getBoundingClientRect().top));
}
};
var destroy = function () {
options.container.removeEventListener('mousemove', mousemove);
tracker.parentNode.removeChild(tracker);
this.clearListeners && this.clearListeners();
};
var initTracker = function () {
tracker = options.container.appendChild(tracker);
ruler.prototype.utils.addClasss(tracker, 'rul_tracker');
var height = ruler.prototype.utils.pixelize(options.rulerHeight);
if (dimension === 2) {
tracker.style.height = height;
}
else {
tracker.style.width = height;
}
options.container.addEventListener('mousemove', mousemove);
};
if (options.enableMouseTracking) {
initTracker();
}
return {
getLength: getLength,
getThickness: getThickness,
getScale: getScale,
setScale: setScale,
dimension: dimension,
orgPos: orgPos,
canvas: canvas,
context: context,
drawRuler: drawRuler,
drawPoints: drawPoints,
destroy: destroy
}
};
/**
* Created by maor.frankel on 5/23/15.
*/
ruler.prototype.guideLine = function (line, _dragContainer, lineDimension, options, curDelta, moveCB, event) {
var self,
guideLine = line,
_curScale = 1,
_assigned = false,
_curPosDelta = curDelta || 0,
dragContainer = _dragContainer,
dimension = lineDimension || 2,
moveCB = moveCB || function () {
};
var curPosDelta = function (val) {
if (typeof val === 'undefined') {
return _curPosDelta;
}
return (_curPosDelta = val);
};
var assigned = function (val) {
if (typeof val === 'undefined') {
return _assigned;
}
return (_assigned = val);
};
var curScale = function (val) {
if (typeof val === 'undefined') {
return _curScale;
}
return (_curScale = val);
};
var draggable = (function () {
return {
move: function (xpos, ypos) {
guideLine.style.left = ruler.prototype.utils.pixelize(xpos);
guideLine.style.top = ruler.prototype.utils.pixelize(ypos);
updateToolTip(xpos, ypos);
moveCB(self, xpos, ypos);
},
startMoving: function (evt) {
ruler.prototype.utils.addClasss(guideLine, ['rul_line_dragged']);
evt = evt || window.event;
var posX = evt ? evt.clientX : 0,
posY = evt ? evt.clientY : 0,
divTop = parseInt(guideLine.style.top || 0),
divLeft = parseInt(guideLine.style.left || 0),
eWi = parseInt(guideLine.offsetWidth),
eHe = parseInt(guideLine.offsetHeight),
cWi = parseInt(dragContainer.offsetWidth),
cHe = parseInt(dragContainer.offsetHeight),
cursor = dimension === 2 ? 'ns-resize' : 'ew-resize';
options.container.style.cursor = cursor;
guideLine.style.cursor = cursor;
var diffX = posX - divLeft,
diffY = posY - divTop;
document.onmousemove = function moving(evt) {
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
aX = posX - diffX,
aY = posY - diffY;
if (aX < 0) {
aX = 0;
}
if (aY < 0) {
aY = 0;
}
if (aX + eWi > cWi) {
aX = cWi - eWi;
}
if (aY + eHe > cHe) {
aY = cHe - eHe;
}
draggable.move(aX, aY);
};
showToolTip();
},
stopMoving: function () {
options.container.style.cursor = null;
guideLine.style.cursor = null;
document.onmousemove = function () {
};
hideToolTip();
ruler.prototype.utils.removeClasss(guideLine, ['rul_line_dragged']);
}
}
})();
var showToolTip = function (e) {
if (!options.enableToolTip) {
return;
}
ruler.prototype.utils.addClasss(guideLine, 'rul_tooltip');
};
var updateToolTip = function (x, y) {
if (y) {
guideLine.dataset.tip = 'Y: ' + Math.round((y - options.rulerHeight - 1 - _curPosDelta) * _curScale) + ' px';
}
else {
guideLine.dataset.tip = 'X: ' + Math.round((x - options.rulerHeight - 1 - _curPosDelta) * _curScale) + ' px';
}
};
var hideToolTip = function (e) {
ruler.prototype.utils.removeClasss(guideLine, 'rul_tooltip');
};
var destroy = function () {
draggable.stopMoving();
moveCB = null;
guideLine.removeEventListener('mousedown', mousedown);
guideLine.removeEventListener('mouseup', mouseup);
guideLine.removeEventListener('dblclick', dblclick);
guideLine.parentNode && guideLine.parentNode.removeChild(guideLine);
};
var hide = function () {
guideLine.style.display = 'none';
};
var show = function () {
guideLine.style.display = 'block';
};
var mousedown = function (e) {
e.stopPropagation();
draggable.startMoving();
};
var mouseup = function (e) {
draggable.stopMoving();
};
var dblclick = function (e) {
e.stopPropagation();
destroy();
};
guideLine.addEventListener('mousedown', mousedown);
guideLine.addEventListener('mouseup', mouseup);
guideLine.addEventListener('dblclick', dblclick);
if (event) draggable.startMoving(event);
self = {
setAsDraggable: draggable,
startDrag: draggable.startMoving,
stopDrag: draggable.stopMoving,
destroy: destroy,
curScale: curScale,
assigned: assigned,
curPosDelta: curPosDelta,
guideLine: guideLine,
dimension: dimension,
hide: hide,
show: show
};
return self;
};
/**
* Created by maor.frankel on 5/25/15.
*/
ruler.prototype.utils = {
extend: function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
},
pixelize: function (val) {
return val + 'px';
},
prependChild: function (container, element) {
return container.insertBefore(element, container.firstChild);
},
addClasss: function (element, classNames) {
if (!(classNames instanceof Array)) {
classNames = [classNames];
}
classNames.forEach(function (name) {
element.className += ' ' + name;
});
return element;
},
removeClasss: function (element, classNames) {
var curCalsss = element.className;
if (!(classNames instanceof Array)) {
classNames = [classNames];
}
classNames.forEach(function (name) {
curCalsss = curCalsss.replace(name, '');
});
element.className = curCalsss;
return element;
}
};