mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-20 22:39:50 -05:00
Implement new DomElement micro lib, supporting creation of elements, getting and setting of style, content, and events, in an elegant fashion.
This commit is contained in:
parent
e6c59672ac
commit
a7320cf2e2
3 changed files with 157 additions and 64 deletions
|
@ -19,67 +19,160 @@
|
||||||
* @namespace
|
* @namespace
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
var DomElement = {
|
var DomElement = new function() {
|
||||||
getBounds: function(el, viewport) {
|
|
||||||
var rect = el.getBoundingClientRect(),
|
// We use a mix of Bootstrap.js legacy and Bonzo.js magic, ported over and
|
||||||
doc = el.ownerDocument,
|
// furhter simplified to a subset actually required by Paper.js
|
||||||
body = doc.body,
|
|
||||||
docEl = doc.documentElement,
|
var special = /^(checked|value|selected|disabled)$/i,
|
||||||
x = rect.left - (docEl.clientLeft || body.clientLeft || 0),
|
translated = { text: 'textContent', html: 'innerHTML' },
|
||||||
y = rect.top - (docEl.clientTop || body.clientTop || 0);
|
unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1 };
|
||||||
if (!viewport) {
|
|
||||||
var win = DomElement.getViewport(doc);
|
function create(nodes, parent) {
|
||||||
x += win.pageXOffset || docEl.scrollLeft || body.scrollLeft;
|
var res = [];
|
||||||
y += win.pageYOffset || docEl.scrollTop || body.scrollTop;
|
for (var i = 0, l = nodes && nodes.length; i < l;) {
|
||||||
|
var tag = nodes[i++];
|
||||||
|
if (typeof tag === 'string') {
|
||||||
|
var el = document.createElement(tag);
|
||||||
|
// Do we have attributes?
|
||||||
|
if (typeof nodes[i] === 'object' && !Array.isArray(nodes[i]))
|
||||||
|
DomElement.set(el, nodes[i++]);
|
||||||
|
// Do we have children?
|
||||||
|
if (Array.isArray(nodes[i]))
|
||||||
|
create(nodes[i++], el);
|
||||||
|
// Are we adding to a parent?
|
||||||
|
if (parent)
|
||||||
|
parent.appendChild(el);
|
||||||
|
res.push(el);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new Rectangle(x, y, rect.width, rect.height);
|
return res;
|
||||||
},
|
|
||||||
|
|
||||||
getOffset: function(el, viewport) {
|
|
||||||
return this.getBounds(el, viewport).getPoint();
|
|
||||||
},
|
|
||||||
|
|
||||||
getSize: function(el) {
|
|
||||||
return this.getBounds(el, true).getSize();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if element is invisibile (display: none, ...)
|
|
||||||
*/
|
|
||||||
isInvisible: function(el) {
|
|
||||||
return this.getSize(el).equals([0, 0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if element is visibile in current viewport
|
|
||||||
*/
|
|
||||||
isVisible: function(el) {
|
|
||||||
// See if the viewport bounds intersect with the windows rectangle
|
|
||||||
// which always starts at 0, 0
|
|
||||||
return !this.isInvisible(el) && this.getViewportBounds(el).intersects(
|
|
||||||
this.getBounds(el, true));
|
|
||||||
},
|
|
||||||
|
|
||||||
getViewport: function(doc) {
|
|
||||||
return doc.defaultView || doc.parentWindow;
|
|
||||||
},
|
|
||||||
|
|
||||||
getViewportBounds: function(el) {
|
|
||||||
var doc = el.ownerDocument,
|
|
||||||
view = this.getViewport(doc),
|
|
||||||
body = doc.getElementsByTagName(
|
|
||||||
doc.compatMode === 'CSS1Compat' ? 'html' : 'body')[0];
|
|
||||||
return Rectangle.create(0, 0,
|
|
||||||
view.innerWidth || body.clientWidth,
|
|
||||||
view.innerHeight || body.clientHeight
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getComputedStyle: function(el, name) {
|
|
||||||
if (el.currentStyle)
|
|
||||||
return el.currentStyle[Base.camelize(name)];
|
|
||||||
var style = this.getViewport(el.ownerDocument)
|
|
||||||
.getComputedStyle(el, null);
|
|
||||||
return style ? style.getPropertyValue(Base.hyphenate(name)) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
create: function(tag, attributes, children) {
|
||||||
|
var res = create(arguments);
|
||||||
|
return res.length == 1 ? res[0] : res;
|
||||||
|
},
|
||||||
|
|
||||||
|
find: function(selector) {
|
||||||
|
return document.querySelector(selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
findAll: function(selector) {
|
||||||
|
return document.querySelectorAll(selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function(el, key) {
|
||||||
|
return el
|
||||||
|
? special.test(key)
|
||||||
|
? key === 'value'
|
||||||
|
? el[key]
|
||||||
|
: typeof el[key] === 'string'
|
||||||
|
: key in translated
|
||||||
|
? el[translated[key]]
|
||||||
|
: el.getAttribute(key)
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function(el, key, value) {
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
for (var name in key)
|
||||||
|
if (key.hasOwnProperty(name))
|
||||||
|
this.set(el, name, key[name]);
|
||||||
|
} else if (special.test(key)) {
|
||||||
|
el[key] = value;
|
||||||
|
} else if (key in translated) {
|
||||||
|
el[translated[key]] = value;
|
||||||
|
} else if (key === 'style') {
|
||||||
|
this.setStyle(el, value);
|
||||||
|
} else if (key === 'events') {
|
||||||
|
DomEvent.add(el, value);
|
||||||
|
} else {
|
||||||
|
el.setAttribute(key, value);
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
},
|
||||||
|
|
||||||
|
getStyle: function(el, key) {
|
||||||
|
var style = el.ownerDocument.defaultView.getComputedStyle(el, '');
|
||||||
|
return el.style[key] || style && style[key] || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
setStyle: function(el, key, value) {
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
for (var name in key)
|
||||||
|
if (key.hasOwnProperty(name))
|
||||||
|
this.setStyle(el, name, key[name]);
|
||||||
|
} else {
|
||||||
|
if (/^-?[\d\.]+$/.test(value) && !(key in unitless))
|
||||||
|
value += 'px';
|
||||||
|
el.style[key] = value;
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
},
|
||||||
|
|
||||||
|
hasClass: function(el, cls) {
|
||||||
|
return new RegExp('\\s*' + cls + '\\s*').test(el.className);
|
||||||
|
},
|
||||||
|
|
||||||
|
addClass: function(el, cls) {
|
||||||
|
el.className = (el.className + ' ' + cls).trim();
|
||||||
|
},
|
||||||
|
|
||||||
|
removeClass: function(el, cls) {
|
||||||
|
el.className = el.className.replace(
|
||||||
|
new RegExp('\\s*' + cls + '\\s*'), ' ').trim();
|
||||||
|
},
|
||||||
|
|
||||||
|
getBounds: function(el, viewport) {
|
||||||
|
var rect = el.getBoundingClientRect(),
|
||||||
|
doc = el.ownerDocument,
|
||||||
|
body = doc.body,
|
||||||
|
html = doc.documentElement,
|
||||||
|
x = rect.left - (html.clientLeft || body.clientLeft || 0),
|
||||||
|
y = rect.top - (html.clientTop || body.clientTop || 0);
|
||||||
|
if (!viewport) {
|
||||||
|
var view = doc.defaultView;
|
||||||
|
x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
|
||||||
|
y += view.pageYOffset || html.scrollTop || body.scrollTop;
|
||||||
|
}
|
||||||
|
return new Rectangle(x, y, rect.width, rect.height);
|
||||||
|
},
|
||||||
|
|
||||||
|
getViewportBounds: function(el) {
|
||||||
|
var doc = el.ownerDocument,
|
||||||
|
view = doc.defaultView,
|
||||||
|
html = doc.documentElement;
|
||||||
|
return Rectangle.create(0, 0,
|
||||||
|
view.innerWidth || html.clientWidth,
|
||||||
|
view.innerHeight || html.clientHeight
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getOffset: function(el, viewport) {
|
||||||
|
return this.getBounds(el, viewport).getPoint();
|
||||||
|
},
|
||||||
|
|
||||||
|
getSize: function(el) {
|
||||||
|
return this.getBounds(el, true).getSize();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if element is invisibile (display: none, ...)
|
||||||
|
*/
|
||||||
|
isInvisible: function(el) {
|
||||||
|
return this.getSize(el).equals([0, 0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if element is visibile in current viewport
|
||||||
|
*/
|
||||||
|
isInView: function(el) {
|
||||||
|
// See if the viewport bounds intersect with the windows rectangle
|
||||||
|
// which always starts at 0, 0
|
||||||
|
return !this.isInvisible(el) && this.getViewportBounds(el).intersects(
|
||||||
|
this.getBounds(el, true));
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,7 +102,7 @@ DomEvent.requestAnimationFrame = new function() {
|
||||||
// is defined in callbacks, and if not, clear request again so we won't
|
// is defined in callbacks, and if not, clear request again so we won't
|
||||||
// use the faulty method.
|
// use the faulty method.
|
||||||
request(function(time) {
|
request(function(time) {
|
||||||
if (time == undefined)
|
if (time == null)
|
||||||
request = null;
|
request = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ DomEvent.requestAnimationFrame = new function() {
|
||||||
func = entry[0],
|
func = entry[0],
|
||||||
el = entry[1];
|
el = entry[1];
|
||||||
if (!el || (PaperScript.getAttribute(el, 'keepalive') == 'true'
|
if (!el || (PaperScript.getAttribute(el, 'keepalive') == 'true'
|
||||||
|| focused) && DomElement.isVisible(el)) {
|
|| focused) && DomElement.isInView(el)) {
|
||||||
// Handle callback and remove it from callbacks list.
|
// Handle callback and remove it from callbacks list.
|
||||||
callbacks.splice(i, 1);
|
callbacks.splice(i, 1);
|
||||||
func(Date.now());
|
func(Date.now());
|
||||||
|
|
|
@ -306,7 +306,7 @@ var View = this.View = Base.extend(Callback, /** @lends View# */{
|
||||||
* @return {Boolean} Whether the view is visible.
|
* @return {Boolean} Whether the view is visible.
|
||||||
*/
|
*/
|
||||||
isVisible: function() {
|
isVisible: function() {
|
||||||
return DomElement.isVisible(this._element);
|
return DomElement.isInView(this._element);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue