var _core = require("./core");
var createElement = _core.createElement;
var dataUtil = require("../utils/data_structure_util");
var Path = require("../graphic/Path");
var QImage = require("../graphic/Image");
var QText = require("../graphic/Text");
var arrayDiff = require("../utils/array_diff2");
var GradientManager = require("./GradientManager");
var ClippathManager = require("./ClippathManager");
var ShadowManager = require("./ShadowManager");
var _graphic = require("./graphic");
var svgPath = _graphic.path;
var svgImage = _graphic.image;
var svgText = _graphic.text;
/* eslint-disable no-unused-vars */
/**
* @class qrenderer.svg.SVGPainter
*
* SVG 画笔。
*
* @docauthor 大漠穷秋 damoqiongqiu@126.com
*/
/**
* @private
* @method getSvgProxy
*
* QImage 映射成 svgImage,ZText 映射成 svgText,其它所有都映射成 svgPath。
*
* @param {Element} el
*/
function getSvgProxy(el) {
if (el instanceof Path) {
return svgPath;
} else if (el instanceof QImage) {
return svgImage;
} else if (el instanceof QText) {
return svgText;
}
return svgPath;
}
function checkParentAvailable(parent, child) {
return child && parent && child.parentNode !== parent;
}
function insertAfter(parent, child, prevSibling) {
if (checkParentAvailable(parent, child) && prevSibling) {
var nextSibling = prevSibling.nextSibling;
nextSibling ? parent.insertBefore(child, nextSibling) : parent.appendChild(child);
}
}
function prepend(parent, child) {
if (checkParentAvailable(parent, child)) {
var firstChild = parent.firstChild;
firstChild ? parent.insertBefore(child, firstChild) : parent.appendChild(child);
}
}
function remove(parent, child) {
if (child && parent && child.parentNode === parent) {
parent.removeChild(child);
}
}
function getTextSvgElement(displayable) {
return displayable.__textSvgEl;
}
function getSvgElement(displayable) {
return displayable.__svgEl;
}
/**
* @method constructor SVGPainter
* @param {HTMLElement} host
* @param {qrenderer.core.Storage} storage
* @param {Object} opts
*/
var SVGPainter = function SVGPainter(host, storage) {
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var qrId = arguments.length > 3 ? arguments[3] : undefined;
opts = dataUtil.extend({}, opts);
this._opts = opts;
this.host = host;
this.storage = storage;
this._visibleList = [];
var svgRoot = createElement('svg');
svgRoot.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svgRoot.setAttribute('version', '1.1');
svgRoot.setAttribute('baseProfile', 'full');
svgRoot.style.cssText = 'user-select:none;position:absolute;left:0;top:0;';
this.gradientManager = new GradientManager(qrId, svgRoot);
this.clipPathManager = new ClippathManager(qrId, svgRoot);
this.shadowManager = new ShadowManager(qrId, svgRoot);
this._svgRoot = svgRoot;
var div = document.createElement('div');
div.style.cssText = 'overflow:hidden;position:relative';
this._host = div;
this.host.appendChild(div);
this._host.appendChild(svgRoot);
this.resize(opts.width, opts.height);
};
SVGPainter.prototype = {
constructor: SVGPainter,
/**
* @method getType
*/
getType: function getType() {
return 'svg';
},
/**
* @method getHost
*/
getHost: function getHost() {
return this._host;
},
/**
* @method getViewportRootOffset
*/
getViewportRootOffset: function getViewportRootOffset() {
var viewportRoot = this.getViewportRoot();
if (viewportRoot) {
return {
offsetLeft: viewportRoot.offsetLeft || 0,
offsetTop: viewportRoot.offsetTop || 0
};
}
},
/**
* @method refresh
*/
refresh: function refresh() {
var list = this.storage.getDisplayList(true);
this._paintList(list);
},
/**
* @method setBackgroundColor
*/
setBackgroundColor: function setBackgroundColor(backgroundColor) {
// TODO gradient
this._host.style.background = backgroundColor;
},
/**
* @private
* @method _paintList
*/
_paintList: function _paintList(list) {
this.gradientManager.markAllUnused();
this.clipPathManager.markAllUnused();
this.shadowManager.markAllUnused();
var svgRoot = this._svgRoot;
var visibleList = this._visibleList;
var listLen = list.length;
var newVisibleList = [];
var i;
var svgElement;
var textSvgElement;
for (i = 0; i < listLen; i++) {
var displayable = list[i];
var svgProxy = getSvgProxy(displayable);
svgElement = getSvgElement(displayable) || getTextSvgElement(displayable);
if (!displayable.invisible) {
if (displayable.__dirty) {
svgProxy && svgProxy.render(displayable); // Update clipPath
this.clipPathManager.update(displayable); // Update gradient and shadow
if (displayable.style.fill && displayable.style.stroke) {
this.gradientManager.update(displayable.style.fill);
this.gradientManager.update(displayable.style.stroke);
}
this.shadowManager.update(svgElement, displayable);
displayable.__dirty = false;
}
newVisibleList.push(displayable);
}
}
var diff = arrayDiff(visibleList, newVisibleList);
var prevSvgElement; // First do remove, in case element moved to the head and do remove
// after add
for (i = 0; i < diff.length; i++) {
var item = diff[i];
if (item.removed) {
for (var k = 0; k < item.count; k++) {
var _displayable = visibleList[item.indices[k]];
svgElement = getSvgElement(_displayable);
textSvgElement = getTextSvgElement(_displayable);
remove(svgRoot, svgElement);
remove(svgRoot, textSvgElement);
}
}
}
for (i = 0; i < diff.length; i++) {
var _item = diff[i];
if (_item.added) {
for (var _k = 0; _k < _item.count; _k++) {
var _displayable2 = newVisibleList[_item.indices[_k]];
svgElement = getSvgElement(_displayable2);
textSvgElement = getTextSvgElement(_displayable2);
prevSvgElement ? insertAfter(svgRoot, svgElement, prevSvgElement) : prepend(svgRoot, svgElement);
if (svgElement) {
insertAfter(svgRoot, textSvgElement, svgElement);
} else if (prevSvgElement) {
insertAfter(svgRoot, textSvgElement, prevSvgElement);
} else {
prepend(svgRoot, textSvgElement);
} // Insert text
insertAfter(svgRoot, textSvgElement, svgElement);
prevSvgElement = textSvgElement || svgElement || prevSvgElement; // qrenderer.Text only create textSvgElement.
this.gradientManager.addWithoutUpdate(svgElement || textSvgElement, _displayable2);
this.shadowManager.addWithoutUpdate(svgElement || textSvgElement, _displayable2);
this.clipPathManager.markUsed(_displayable2);
}
} else if (!_item.removed) {
for (var _k2 = 0; _k2 < _item.count; _k2++) {
var _displayable3 = newVisibleList[_item.indices[_k2]];
svgElement = getSvgElement(_displayable3);
textSvgElement = getTextSvgElement(_displayable3);
svgElement = getSvgElement(_displayable3);
textSvgElement = getTextSvgElement(_displayable3);
this.gradientManager.markUsed(_displayable3);
this.gradientManager.addWithoutUpdate(svgElement || textSvgElement, _displayable3);
this.shadowManager.markUsed(_displayable3);
this.shadowManager.addWithoutUpdate(svgElement || textSvgElement, _displayable3);
this.clipPathManager.markUsed(_displayable3);
if (textSvgElement) {
// Insert text.
insertAfter(svgRoot, textSvgElement, svgElement);
}
prevSvgElement = svgElement || textSvgElement || prevSvgElement;
}
}
}
this.gradientManager.removeUnused();
this.clipPathManager.removeUnused();
this.shadowManager.removeUnused();
this._visibleList = newVisibleList;
},
/**
* @private
* @method _paintList
*/
_getDefs: function _getDefs(isForceCreating) {
var svgRoot = this._svgRoot;
var defs = this._svgRoot.getElementsByTagName('defs');
if (defs.length !== 0) {
return defs[0];
} // Not exist
if (!isForceCreating) {
return null;
}
defs = svgRoot.insertBefore(createElement('defs'), // Create new tag
svgRoot.firstChild // Insert in the front of svg
);
if (!defs.contains) {
// IE doesn't support contains method
defs.contains = function (el) {
var children = defs.children;
if (!children) {
return false;
}
for (var i = children.length - 1; i >= 0; --i) {
if (children[i] === el) {
return true;
}
}
return false;
};
}
return defs;
},
/**
* @method resize
*/
resize: function resize(width, height) {
var viewport = this._host; // FIXME Why ?
viewport.style.display = 'none'; // Save input w/h
var opts = this._opts;
width != null && (opts.width = width);
height != null && (opts.height = height);
width = this._getSize(0);
height = this._getSize(1);
viewport.style.display = '';
if (this._width !== width || this._height !== height) {
this._width = width;
this._height = height;
var viewportStyle = viewport.style;
viewportStyle.width = width + 'px';
viewportStyle.height = height + 'px';
var svgRoot = this._svgRoot; // Set width by 'svgRoot.width = width' is invalid
svgRoot.setAttribute('width', width);
svgRoot.setAttribute('height', height);
}
},
/**
* @method getWidth
* 获取绘图区域宽度
*/
getWidth: function getWidth() {
return this._width;
},
/**
* @method getHeight
* 获取绘图区域高度
*/
getHeight: function getHeight() {
return this._height;
},
/**
* @private
* @method _getSize
*/
_getSize: function _getSize(whIdx) {
var opts = this._opts;
var wh = ['width', 'height'][whIdx];
var cwh = ['clientWidth', 'clientHeight'][whIdx];
var plt = ['paddingLeft', 'paddingTop'][whIdx];
var prb = ['paddingRight', 'paddingBottom'][whIdx];
if (opts[wh] != null && opts[wh] !== 'auto') {
return parseFloat(opts[wh]);
}
var host = this.host; // IE8 does not support getComputedStyle, but it use VML.
var stl = document.defaultView.getComputedStyle(host);
return (host[cwh] || dataUtil.parseInt10(stl[wh]) || dataUtil.parseInt10(host.style[wh])) - (dataUtil.parseInt10(stl[plt]) || 0) - (dataUtil.parseInt10(stl[prb]) || 0) | 0;
},
/**
* @method dispose
*/
dispose: function dispose() {
this.host.innerHTML = '';
this._svgRoot = this._host = this.storage = null;
},
/**
* @method clear
*/
clear: function clear() {
if (this._host) {
this.host.removeChild(this._host);
}
},
/**
* @method pathToDataUrl
*/
pathToDataUrl: function pathToDataUrl() {
this.refresh();
var html = this._svgRoot.outerHTML;
return 'data:image/svg+xml;charset=UTF-8,' + html;
}
}; // Not supported methods
function createMethodNotSupport(method) {
return function () {
console.log('In SVG mode painter not support method "' + method + '"');
};
} // Unsuppoted methods
['getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'].forEach(function (name, index) {
SVGPainter.prototype[name] = createMethodNotSupport(name);
});
var _default = SVGPainter;
module.exports = _default;