refactoring van de cmap structuren bijna compleet
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
/**
|
||||
* Shared DOM and state support for the low-level diagram renderer.
|
||||
*
|
||||
* Derived from ionstage/cmap 0.1.3, (c) 2015 iOnStage, MIT License.
|
||||
*/
|
||||
const CONTENT_TYPE_TEXT = 'text';
|
||||
const CONTENT_TYPE_HTML = 'html';
|
||||
|
||||
class ItemList {
|
||||
constructor() {
|
||||
this.data = [];
|
||||
}
|
||||
|
||||
add(item) {
|
||||
if (!this.contains(item)) this.data.push(item);
|
||||
}
|
||||
|
||||
remove(item) {
|
||||
for (let index = this.data.length - 1; index >= 0; index -= 1) {
|
||||
if (this.equal(this.data[index], item)) {
|
||||
this.data.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contains(item) {
|
||||
return this.data.some((candidate) => this.equal(candidate, item));
|
||||
}
|
||||
|
||||
equal(first, second) {
|
||||
return first === second;
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return this.data.slice();
|
||||
}
|
||||
}
|
||||
|
||||
const helper = {
|
||||
CONTENT_TYPE_HTML,
|
||||
CONTENT_TYPE_TEXT,
|
||||
List: ItemList,
|
||||
|
||||
toNumber(value, defaultValue) {
|
||||
return !isNaN(value) ? Number(value) : defaultValue;
|
||||
},
|
||||
|
||||
toString(value, defaultValue) {
|
||||
return value !== undefined ? String(value) : defaultValue;
|
||||
},
|
||||
|
||||
toBoolean(value, defaultValue) {
|
||||
return value !== undefined ? Boolean(value) : defaultValue;
|
||||
},
|
||||
|
||||
toContentType(value, defaultValue) {
|
||||
if (value === CONTENT_TYPE_TEXT || value === CONTENT_TYPE_HTML) return value;
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
eachInstance(values, constructor, callback) {
|
||||
values.filter((value) => value instanceof constructor).forEach(callback);
|
||||
},
|
||||
|
||||
firstInstance(values, constructor) {
|
||||
return values.find((value) => value instanceof constructor);
|
||||
},
|
||||
|
||||
diffObj(newObject, oldObject) {
|
||||
const difference = {};
|
||||
for (const key in newObject) {
|
||||
if (!oldObject || newObject[key] !== oldObject[key]) {
|
||||
difference[key] = newObject[key];
|
||||
}
|
||||
}
|
||||
return difference;
|
||||
},
|
||||
|
||||
identity(value) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
var dom = {};
|
||||
|
||||
dom.disabled = function() {
|
||||
return (typeof document === 'undefined');
|
||||
};
|
||||
|
||||
dom.el = function(selector) {
|
||||
if (selector.charAt(0) === '<') {
|
||||
selector = selector.match(/<(.+)>/)[1];
|
||||
return document.createElement(selector);
|
||||
}
|
||||
};
|
||||
|
||||
dom.body = function() {
|
||||
return document.body;
|
||||
};
|
||||
|
||||
dom.attr = function(el, props) {
|
||||
for (var key in props) {
|
||||
el.setAttribute(key, props[key]);
|
||||
}
|
||||
};
|
||||
|
||||
dom.css = function(el, props) {
|
||||
var style = el.style;
|
||||
|
||||
for (var key in props) {
|
||||
style[key] = props[key];
|
||||
}
|
||||
};
|
||||
|
||||
dom.rect = function(el) {
|
||||
return el.getBoundingClientRect();
|
||||
};
|
||||
|
||||
dom.clientWidth = function(el) {
|
||||
return el.clientWidth;
|
||||
};
|
||||
|
||||
dom.clientHeight = function(el) {
|
||||
return el.clientHeight;
|
||||
};
|
||||
|
||||
dom.scrollLeft = function(el) {
|
||||
return el.scrollLeft;
|
||||
};
|
||||
|
||||
dom.scrollTop = function(el) {
|
||||
return el.scrollTop;
|
||||
};
|
||||
|
||||
dom.scrollWidth = function(el) {
|
||||
return el.scrollWidth;
|
||||
};
|
||||
|
||||
dom.scrollHeight = function(el) {
|
||||
return el.scrollHeight;
|
||||
};
|
||||
|
||||
dom.text = function(el, s) {
|
||||
el.textContent = s;
|
||||
};
|
||||
|
||||
dom.html = function(el, s) {
|
||||
el.innerHTML = s;
|
||||
};
|
||||
|
||||
dom.append = function(parent, el) {
|
||||
parent.appendChild(el);
|
||||
};
|
||||
|
||||
dom.remove = function(el) {
|
||||
el.parentNode.removeChild(el);
|
||||
};
|
||||
|
||||
dom.child = function(el, index) {
|
||||
return el.childNodes[index];
|
||||
};
|
||||
|
||||
dom.animate = function(callback) {
|
||||
return window.requestAnimationFrame(callback);
|
||||
};
|
||||
|
||||
dom.supportsTouch = function() {
|
||||
return ('ontouchstart' in window || (typeof DocumentTouch !== 'undefined' && document instanceof DocumentTouch));
|
||||
};
|
||||
|
||||
dom.on = function(el, type, listener) {
|
||||
el.addEventListener(type, listener);
|
||||
};
|
||||
|
||||
dom.off = function(el, type, listener) {
|
||||
el.removeEventListener(type, listener);
|
||||
};
|
||||
|
||||
dom.pagePoint = function(event, offset) {
|
||||
if (dom.supportsTouch())
|
||||
event = event.changedTouches[0];
|
||||
|
||||
return {
|
||||
x: event.pageX - (offset ? offset.x : 0),
|
||||
y: event.pageY - (offset ? offset.y : 0)
|
||||
};
|
||||
};
|
||||
|
||||
dom.clientPoint = function(event, offset) {
|
||||
if (dom.supportsTouch())
|
||||
event = event.changedTouches[0];
|
||||
|
||||
return {
|
||||
x: event.clientX - (offset ? offset.x : 0),
|
||||
y: event.clientY - (offset ? offset.y : 0)
|
||||
};
|
||||
};
|
||||
|
||||
dom.cancel = function(event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
class Draggable {
|
||||
constructor(element, onStart, onMove, onEnd) {
|
||||
this.element = element;
|
||||
this.onStart = onStart;
|
||||
this.onMove = onMove;
|
||||
this.onEnd = onEnd;
|
||||
this.start = this.start.bind(this);
|
||||
this.move = this.move.bind(this);
|
||||
this.end = this.end.bind(this);
|
||||
this.locked = false;
|
||||
this.startingPoint = null;
|
||||
this.startEvent = dom.supportsTouch() ? 'touchstart' : 'mousedown';
|
||||
this.moveEvent = dom.supportsTouch() ? 'touchmove' : 'mousemove';
|
||||
this.endEvent = dom.supportsTouch() ? 'touchend' : 'mouseup';
|
||||
|
||||
dom.on(this.element, this.startEvent, this.start);
|
||||
}
|
||||
|
||||
start(event) {
|
||||
if (this.locked)
|
||||
return;
|
||||
|
||||
this.locked = true;
|
||||
this.startingPoint = dom.pagePoint(event);
|
||||
const rectangle = dom.rect(this.element);
|
||||
const point = dom.clientPoint(event, {
|
||||
x: rectangle.left - dom.scrollLeft(this.element),
|
||||
y: rectangle.top - dom.scrollTop(this.element)
|
||||
});
|
||||
|
||||
if (typeof this.onStart === 'function') this.onStart(point.x, point.y, event);
|
||||
|
||||
dom.on(document, this.moveEvent, this.move);
|
||||
dom.on(document, this.endEvent, this.end);
|
||||
}
|
||||
|
||||
move(event) {
|
||||
const distance = dom.pagePoint(event, this.startingPoint);
|
||||
if (typeof this.onMove === 'function') this.onMove(distance.x, distance.y, event);
|
||||
}
|
||||
|
||||
end(event) {
|
||||
dom.off(document, this.moveEvent, this.move);
|
||||
dom.off(document, this.endEvent, this.end);
|
||||
|
||||
const distance = dom.pagePoint(event, this.startingPoint);
|
||||
if (typeof this.onEnd === 'function') this.onEnd(distance.x, distance.y, event);
|
||||
|
||||
this.locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
dom.draggable = function(element, onStart, onMove, onEnd) {
|
||||
if (dom.disabled()) return null;
|
||||
return new Draggable(element, onStart, onMove, onEnd);
|
||||
};
|
||||
|
||||
const dirtyComponents = [];
|
||||
let renderRequestId = null;
|
||||
|
||||
class Component {
|
||||
constructor() {
|
||||
this.disposed = false;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
prop(initialValue, defaultValue, converter) {
|
||||
const convert = typeof converter === 'function' ? converter : helper.identity;
|
||||
let cache = convert(initialValue, defaultValue);
|
||||
|
||||
return (value) => {
|
||||
if (typeof value === 'undefined')
|
||||
return cache;
|
||||
|
||||
if (value === cache)
|
||||
return;
|
||||
|
||||
cache = convert(value, cache);
|
||||
this.markDirty();
|
||||
};
|
||||
}
|
||||
|
||||
relations() {
|
||||
return [];
|
||||
}
|
||||
|
||||
redraw() {}
|
||||
|
||||
notifyRendered() {
|
||||
if (typeof this.renderedHandler === 'function' && this.element())
|
||||
this.renderedHandler(this.element());
|
||||
}
|
||||
|
||||
markDirty() {
|
||||
if (dom.disabled() || this.disposed)
|
||||
return;
|
||||
|
||||
if (!dirtyComponents.includes(this))
|
||||
dirtyComponents.push(this);
|
||||
|
||||
if (renderRequestId !== null)
|
||||
return;
|
||||
|
||||
renderRequestId = dom.animate(redrawDirtyComponents);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDirtyRelations(index) {
|
||||
const initialLength = dirtyComponents.length;
|
||||
for (let position = index; position < initialLength; position += 1) {
|
||||
const component = dirtyComponents[position];
|
||||
if (component.disposed)
|
||||
continue;
|
||||
component.relations().forEach((relation) => {
|
||||
if (!relation.disposed)
|
||||
relation.update(component);
|
||||
});
|
||||
}
|
||||
|
||||
if (dirtyComponents.length > initialLength)
|
||||
updateDirtyRelations(initialLength);
|
||||
}
|
||||
|
||||
function redrawDirtyComponents() {
|
||||
updateDirtyRelations(0);
|
||||
dirtyComponents.forEach((component) => {
|
||||
if (!component.disposed)
|
||||
component.redraw();
|
||||
});
|
||||
dirtyComponents.length = 0;
|
||||
renderRequestId = null;
|
||||
}
|
||||
|
||||
export { Component, dom, helper };
|
||||
Reference in New Issue
Block a user