goog.provide('lime.Renderer.DOM');
goog.require('goog.dom');
goog.require('lime.Renderer');
goog.require('lime.style');
/**
* DOM renderer. This renders as div dom elements and
* styles are added with CSS
* @const
* @type {lime.Renderer}
*/
lime.Renderer.DOM = new lime.Renderer();
/**
* Update the DOM tree relations of the element.
* @this {lime.Node}
*/
lime.Renderer.DOM.updateLayout = function() {
var j = 0,
el;
for (var i = 0, child; child = this.children_[i]; i++) {
el = child instanceof lime.Node ? child.rootElement : child;
if (el == this.domElement.childNodes[j]) {
j++;
continue;
} else {
if (goog.dom.contains(this.containerElement, el)) {
goog.dom.removeNode(el);
}
lime.Renderer.DOM.appendAt_(this.containerElement, el, j++);
continue;
}
}
/* var lastIndex = this.containerElement.childNodes.length - 1;
while (lastIndex >= j) {
goog.dom.removeNode(this.containerElement.childNodes[lastIndex]);
lastIndex--;
}*/
};
/**
* Calculate the size and position of the element and draw it
* @this {lime.Node}
*/
lime.Renderer.DOM.drawSizePosition = function() {
var size = this.getSize(),
quality = this.getQuality(),
position = this.getPosition(),
rquality = this.relativeQuality_ || 1,
enable3D = this.getCSS3DTransformsAllowed();
if (this.transitionsActive_[lime.Transition.POSITION]) {
position = this.transitionsActive_[lime.Transition.POSITION];
}
var width = Math.round(size.width * rquality);
var height = Math.round(size.height * rquality);
var realScale = this.getScale().clone();
if (this.transitionsActive_[lime.Transition.SCALE]) {
realScale = this.transitionsActive_[lime.Transition.SCALE].clone();
}
if (width != 0) realScale.scale(size.width / (width * quality / rquality));
else realScale.scale(1 / quality);
lime.style.setSize(this.domElement, width, height);
lime.style.setTransformOrigin(this.domElement,
this.anchorPoint_.x * 100, this.anchorPoint_.y * 100, true);
var ax = this.anchorPoint_.x * size.width * rquality;
var ay = this.anchorPoint_.y * size.height * rquality;
var px = position.x * rquality / quality - ax,
py = position.y * rquality / quality - ay;
var so = this.stroke_ ? this.stroke_.width_ : 0;
if (((ax - so) != 0 || (ay - so) != 0) && this.domElement == this.containerElement &&
this.children_.length) {
lime.Renderer.DOM.makeContainer.call(this);
}
if (this.domElement != this.containerElement) {
if (!this.transitionsActiveSet_[lime.Transition.POSITION] && !this.transitionsActiveSet_[lime.Transition.SCALE] && !this.transitionsActiveSet_[lime.Transition.ROTATION])
lime.style.setTransform(this.containerElement,
new lime.style.Transform()
.set3DAllowed(enable3D)
.translate(ax - so, ay - so));
}
if (this.mask_ != this.activeMask_) {
if (this.activeMask_) {
lime.Renderer.DOM.removeMask.call(this);
}
if (this.mask_) {
lime.Renderer.DOM.addMask.call(this);
}
}
var transform = new lime.style.Transform()
.setPrecision(0.1)
.set3DAllowed(enable3D);
if (this.mask_) {
lime.Renderer.DOM.calculateMaskPosition.call(this.mask_);
transform.setPrecision(0.1)
.translate(-this.mask_.mX - ax, -this.mask_.mY - ay)
.rotate(this.mask_.mRot, 'rad').translate(ax, ay).setPrecision(1);
}
var rotation = -this.getRotation();
if (goog.isDef(this.transitionsActive_[lime.Transition.ROTATION])) {
rotation = -this.transitionsActive_[lime.Transition.ROTATION];
}
transform.translate(px, py).scale(realScale.x, realScale.y).
rotate(rotation);
if (!this.transitionsActiveSet_[lime.Transition.POSITION] && !this.transitionsActiveSet_[lime.Transition.SCALE] && !this.transitionsActiveSet_[lime.Transition.ROTATION]) {
// console.log('transform',this.transition_position_set_,this.transition_position_);
lime.style.setTransform(this.domElement, transform);
}
};
/**
* Update nodes dirty values and call draw after
* @this {lime.Node}
*/
lime.Renderer.DOM.update = function() {
if (!this.domElement) return;
lime.Renderer.DOM.drawSizePosition.call(this);
if (!this.transitionsActiveSet_[lime.Transition.OPACITY]) {
var opacity = this.opacity_;
if (goog.isDef(this.transitionsActive_[lime.Transition.OPACITY])) {
opacity = this.transitionsActive_[lime.Transition.OPACITY];
}
if (this.getDirty() & lime.Dirty.ALPHA) {
goog.style.setOpacity(this.domElement, opacity);
}
}
if (this.getDirty() & lime.Dirty.VISIBILITY) {
this.domElement.style['display'] = this.hidden_ ? 'none' : 'block';
}
if (!this.maskTarget_)
this.renderer.draw.call(this, this.domElement);
};
/**
* Update the mask changes to reflect on the element
* @this {lime.Node}
*/
lime.Renderer.DOM.calculateMaskPosition = function() {
if (!goog.isDef(this.targetNode) || !this.targetNode.inTree_) return;
var target = this.targetNode;
//todo: replace with bounds method
var bb = this.getFrame();
var tl = new goog.math.Coordinate(bb.left, bb.top);
var tr = new goog.math.Coordinate(bb.right, bb.top);
var br = new goog.math.Coordinate(bb.right, bb.bottom);
var targetParent = target.getParent();
tl = this.localToNode(tl, targetParent);
tr = this.localToNode(tr, targetParent);
br = this.localToNode(br, targetParent);
/*
console.log('tl: '+tl.x+' '+tl.y);
console.log('tr: '+tr.x+' '+tr.y);
console.log('br: '+br.x+' '+br.y);
*/
var rot = Math.atan2(tl.y - tr.y, tr.x - tl.x);
var x1 = tr.x - tl.x;
var y1 = tl.y - tr.y;
var x2 = br.x - tr.x;
var y2 = br.y - tr.y;
var cos = Math.cos(rot);
var sin = Math.sin(rot);
this.mWidth = Math.round(Math.sqrt(x1 * x1 + y1 * y1));
this.mHeight = Math.round(Math.sqrt(x2 * x2 + y2 * y2));
if (target.renderer.getType() == lime.Renderer.DOM) {
var el = target.rootElement;
//todo: can be optimized
goog.style.setSize(el, this.mWidth, this.mHeight);
lime.style.setTransform(el, new lime.style.Transform()
.setPrecision(0.1)
.set3DAllowed(this.getCSS3DTransformsAllowed())
.translate(tl.x, tl.y).rotate(-rot, 'rad'));
}
if (this.renderer.getType() == lime.Renderer.DOM) {
this.domElement.style['display'] = 'none';
}
this.mPos = target.parentToLocal(tl.clone());
this.mSet = true;
this.mX = cos * tl.x - sin * tl.y;
this.mY = cos * tl.y + sin * tl.x;
this.mRot = rot;
/*
target.setDirty(lime.Dirty.POSITION);
target.update();
*/
};
/**
* Helper function to add DOM node to the specific position
* as a child.
* @param {Object} p Parent DOM element.
* @param {Object} c Child DOM element.
* @param {number} opt_pos Position of the child.
* @private
*/
lime.Renderer.DOM.appendAt_ = function(p, c, opt_pos) {
if (opt_pos == undefined || p.childNodes.length <= opt_pos)
p.appendChild(c);
else
p.insertBefore(c, p.childNodes[opt_pos]);
};
/**
* Make separate container element for the childnodes
* @this {lime.Node}
*/
lime.Renderer.DOM.makeContainer = function() {
this.containerElement = goog.dom.createDom('div');
var fragment = document.createDocumentFragment(),
child;
while ((child = this.domElement.firstChild)) {
this.domElement.removeChild(child);
fragment.appendChild(child);
}
this.containerElement.appendChild(fragment);
this.domElement.appendChild(this.containerElement);
};
/**
* Remove mask element relations
* @thisĀ {lime.Node}
*/
lime.Renderer.DOM.removeMask = function() {
if (this.domElement == this.rootElement) return;
if (this.renderer.getType() == lime.Renderer.DOM) {
goog.dom.removeNode(this.domElement);
goog.dom.replaceNode(this.domElement, this.rootElement);
this.rootElement = this.domElement;
}
this.activeMask_.isMask = 0;
this.activeMask_ = null;
};
/**
* Add mask element and make its relations
* @this {lime.Node}
*/
lime.Renderer.DOM.addMask = function() {
if (this.renderer.getType() == lime.Renderer.DOM) {
this.rootElement = goog.dom.createDom('div');
this.rootElement.style.cssText = 'position:absolute;overflow:hidden;';
//todo: combine into css class
lime.style.setTransformOrigin(this.rootElement, 0, 0);
goog.dom.replaceNode(this.rootElement, this.domElement);
this.rootElement.appendChild(this.domElement);
}
this.mask_.isMask = 1;
this.mask_.targetNode = this;
this.activeMask_ = this.mask_;
this.mask_.setDirty(lime.Dirty.POSITION);
};