779 lines
18 KiB
JavaScript
779 lines
18 KiB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
|
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
module.exports = factory();
|
|
else if(typeof define === 'function' && define.amd)
|
|
define([], factory);
|
|
else {
|
|
var a = factory();
|
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
|
}
|
|
})(window, function() {
|
|
return /******/ (function(modules) { // webpackBootstrap
|
|
/******/ // The module cache
|
|
/******/ var installedModules = {};
|
|
/******/
|
|
/******/ // The require function
|
|
/******/ function __webpack_require__(moduleId) {
|
|
/******/
|
|
/******/ // Check if module is in cache
|
|
/******/ if(installedModules[moduleId]) {
|
|
/******/ return installedModules[moduleId].exports;
|
|
/******/ }
|
|
/******/ // Create a new module (and put it into the cache)
|
|
/******/ var module = installedModules[moduleId] = {
|
|
/******/ i: moduleId,
|
|
/******/ l: false,
|
|
/******/ exports: {}
|
|
/******/ };
|
|
/******/
|
|
/******/ // Execute the module function
|
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
/******/
|
|
/******/ // Flag the module as loaded
|
|
/******/ module.l = true;
|
|
/******/
|
|
/******/ // Return the exports of the module
|
|
/******/ return module.exports;
|
|
/******/ }
|
|
/******/
|
|
/******/
|
|
/******/ // expose the modules object (__webpack_modules__)
|
|
/******/ __webpack_require__.m = modules;
|
|
/******/
|
|
/******/ // expose the module cache
|
|
/******/ __webpack_require__.c = installedModules;
|
|
/******/
|
|
/******/ // define getter function for harmony exports
|
|
/******/ __webpack_require__.d = function(exports, name, getter) {
|
|
/******/ if(!__webpack_require__.o(exports, name)) {
|
|
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
|
/******/ }
|
|
/******/ };
|
|
/******/
|
|
/******/ // define __esModule on exports
|
|
/******/ __webpack_require__.r = function(exports) {
|
|
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
/******/ }
|
|
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
/******/ };
|
|
/******/
|
|
/******/ // create a fake namespace object
|
|
/******/ // mode & 1: value is a module id, require it
|
|
/******/ // mode & 2: merge all properties of value into the ns
|
|
/******/ // mode & 4: return value when already ns object
|
|
/******/ // mode & 8|1: behave like require
|
|
/******/ __webpack_require__.t = function(value, mode) {
|
|
/******/ if(mode & 1) value = __webpack_require__(value);
|
|
/******/ if(mode & 8) return value;
|
|
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
|
/******/ var ns = Object.create(null);
|
|
/******/ __webpack_require__.r(ns);
|
|
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
|
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
|
/******/ return ns;
|
|
/******/ };
|
|
/******/
|
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
/******/ __webpack_require__.n = function(module) {
|
|
/******/ var getter = module && module.__esModule ?
|
|
/******/ function getDefault() { return module['default']; } :
|
|
/******/ function getModuleExports() { return module; };
|
|
/******/ __webpack_require__.d(getter, 'a', getter);
|
|
/******/ return getter;
|
|
/******/ };
|
|
/******/
|
|
/******/ // Object.prototype.hasOwnProperty.call
|
|
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
|
/******/
|
|
/******/ // __webpack_public_path__
|
|
/******/ __webpack_require__.p = "";
|
|
/******/
|
|
/******/
|
|
/******/ // Load entry module and return exports
|
|
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
|
/******/ })
|
|
/************************************************************************/
|
|
/******/ ([
|
|
/* 0 */
|
|
/***/ (function(module, exports) {
|
|
|
|
const hex = (color) => {
|
|
let result = null
|
|
|
|
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
|
return color
|
|
// eslint-disable-next-line no-cond-assign
|
|
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
|
return '#' + result[2].split(',').map((part, index) => {
|
|
part = part.trim()
|
|
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
|
part = part.toString(16)
|
|
if (part.length === 1) {
|
|
part = '0' + part
|
|
}
|
|
return part
|
|
}).join('')
|
|
} else {
|
|
return '#00000000'
|
|
}
|
|
}
|
|
|
|
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
|
if (index === 0) {
|
|
return part
|
|
}
|
|
return part[0].toUpperCase() + part.slice(1)
|
|
}).join('')
|
|
|
|
const compareVersion = (v1, v2) => {
|
|
v1 = v1.split('.')
|
|
v2 = v2.split('.')
|
|
const len = Math.max(v1.length, v2.length)
|
|
while (v1.length < len) {
|
|
v1.push('0')
|
|
}
|
|
while (v2.length < len) {
|
|
v2.push('0')
|
|
}
|
|
for (let i = 0; i < len; i++) {
|
|
const num1 = parseInt(v1[i], 10)
|
|
const num2 = parseInt(v2[i], 10)
|
|
|
|
if (num1 > num2) {
|
|
return 1
|
|
} else if (num1 < num2) {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
module.exports = {
|
|
hex,
|
|
splitLineToCamelCase,
|
|
compareVersion
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 1 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
|
|
const xmlParse = __webpack_require__(2)
|
|
const {Widget} = __webpack_require__(3)
|
|
const {Draw} = __webpack_require__(5)
|
|
const {compareVersion} = __webpack_require__(0)
|
|
|
|
const canvasId = 'weui-canvas'
|
|
|
|
Component({
|
|
properties: {
|
|
width: {
|
|
type: Number,
|
|
value: 400
|
|
},
|
|
height: {
|
|
type: Number,
|
|
value: 300
|
|
}
|
|
},
|
|
data: {
|
|
use2dCanvas: false, // 2.9.2 后可用canvas 2d 接口
|
|
},
|
|
lifetimes: {
|
|
attached() {
|
|
const {SDKVersion, pixelRatio: dpr} = wx.getSystemInfoSync()
|
|
const use2dCanvas = compareVersion(SDKVersion, '2.9.2') >= 0
|
|
this.dpr = dpr
|
|
this.setData({use2dCanvas}, () => {
|
|
if (use2dCanvas) {
|
|
const query = this.createSelectorQuery()
|
|
query.select(`#${canvasId}`)
|
|
.fields({node: true, size: true})
|
|
.exec(res => {
|
|
const canvas = res[0].node
|
|
const ctx = canvas.getContext('2d')
|
|
canvas.width = res[0].width * dpr
|
|
canvas.height = res[0].height * dpr
|
|
ctx.scale(dpr, dpr)
|
|
this.ctx = ctx
|
|
this.canvas = canvas
|
|
})
|
|
} else {
|
|
this.ctx = wx.createCanvasContext(canvasId, this)
|
|
}
|
|
})
|
|
}
|
|
},
|
|
methods: {
|
|
async renderToCanvas(args) {
|
|
const {wxml, style} = args
|
|
const ctx = this.ctx
|
|
const canvas = this.canvas
|
|
const use2dCanvas = this.data.use2dCanvas
|
|
|
|
if (use2dCanvas && !canvas) {
|
|
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'))
|
|
}
|
|
|
|
ctx.clearRect(0, 0, this.data.width, this.data.height)
|
|
const {root: xom} = xmlParse(wxml)
|
|
|
|
const widget = new Widget(xom, style)
|
|
const container = widget.init()
|
|
this.boundary = {
|
|
top: container.layoutBox.top,
|
|
left: container.layoutBox.left,
|
|
width: container.computedStyle.width,
|
|
height: container.computedStyle.height,
|
|
}
|
|
const draw = new Draw(ctx, canvas, use2dCanvas)
|
|
await draw.drawNode(container)
|
|
|
|
if (!use2dCanvas) {
|
|
await this.canvasDraw(ctx)
|
|
}
|
|
return Promise.resolve(container)
|
|
},
|
|
|
|
canvasDraw(ctx, reserve) {
|
|
return new Promise(resolve => {
|
|
ctx.draw(reserve, () => {
|
|
resolve()
|
|
})
|
|
})
|
|
},
|
|
|
|
canvasToTempFilePath(args = {}) {
|
|
const use2dCanvas = this.data.use2dCanvas
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const {
|
|
top, left, width, height
|
|
} = this.boundary
|
|
|
|
const copyArgs = {
|
|
x: left,
|
|
y: top,
|
|
width,
|
|
height,
|
|
destWidth: width * this.dpr,
|
|
destHeight: height * this.dpr,
|
|
canvasId,
|
|
fileType: args.fileType || 'png',
|
|
quality: args.quality || 1,
|
|
success: resolve,
|
|
fail: reject
|
|
}
|
|
|
|
if (use2dCanvas) {
|
|
delete copyArgs.canvasId
|
|
copyArgs.canvas = this.canvas
|
|
}
|
|
wx.canvasToTempFilePath(copyArgs, this)
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
/***/ }),
|
|
/* 2 */
|
|
/***/ (function(module, exports) {
|
|
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
|
|
/**
|
|
* Expose `parse`.
|
|
*/
|
|
|
|
|
|
/**
|
|
* Parse the given string of `xml`.
|
|
*
|
|
* @param {String} xml
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
function parse(xml) {
|
|
xml = xml.trim()
|
|
|
|
// strip comments
|
|
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
|
|
|
return document()
|
|
|
|
/**
|
|
* XML document.
|
|
*/
|
|
|
|
function document() {
|
|
return {
|
|
declaration: declaration(),
|
|
root: tag()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Declaration.
|
|
*/
|
|
|
|
function declaration() {
|
|
const m = match(/^<\?xml\s*/)
|
|
if (!m) return
|
|
|
|
// tag
|
|
const node = {
|
|
attributes: {}
|
|
}
|
|
|
|
// attributes
|
|
while (!(eos() || is('?>'))) {
|
|
const attr = attribute()
|
|
if (!attr) return node
|
|
node.attributes[attr.name] = attr.value
|
|
}
|
|
|
|
match(/\?>\s*/)
|
|
|
|
return node
|
|
}
|
|
|
|
/**
|
|
* Tag.
|
|
*/
|
|
|
|
function tag() {
|
|
const m = match(/^<([\w-:.]+)\s*/)
|
|
if (!m) return
|
|
|
|
// name
|
|
const node = {
|
|
name: m[1],
|
|
attributes: {},
|
|
children: []
|
|
}
|
|
|
|
// attributes
|
|
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
|
const attr = attribute()
|
|
if (!attr) return node
|
|
node.attributes[attr.name] = attr.value
|
|
}
|
|
|
|
// self closing tag
|
|
if (match(/^\s*\/>\s*/)) {
|
|
return node
|
|
}
|
|
|
|
match(/\??>\s*/)
|
|
|
|
// content
|
|
node.content = content()
|
|
|
|
// children
|
|
let child
|
|
while (child = tag()) {
|
|
node.children.push(child)
|
|
}
|
|
|
|
// closing
|
|
match(/^<\/[\w-:.]+>\s*/)
|
|
|
|
return node
|
|
}
|
|
|
|
/**
|
|
* Text content.
|
|
*/
|
|
|
|
function content() {
|
|
const m = match(/^([^<]*)/)
|
|
if (m) return m[1]
|
|
return ''
|
|
}
|
|
|
|
/**
|
|
* Attribute.
|
|
*/
|
|
|
|
function attribute() {
|
|
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
|
if (!m) return
|
|
return {name: m[1], value: strip(m[2])}
|
|
}
|
|
|
|
/**
|
|
* Strip quotes from `val`.
|
|
*/
|
|
|
|
function strip(val) {
|
|
return val.replace(/^['"]|['"]$/g, '')
|
|
}
|
|
|
|
/**
|
|
* Match `re` and advance the string.
|
|
*/
|
|
|
|
function match(re) {
|
|
const m = xml.match(re)
|
|
if (!m) return
|
|
xml = xml.slice(m[0].length)
|
|
return m
|
|
}
|
|
|
|
/**
|
|
* End-of-source.
|
|
*/
|
|
|
|
function eos() {
|
|
return xml.length == 0
|
|
}
|
|
|
|
/**
|
|
* Check for `prefix`.
|
|
*/
|
|
|
|
function is(prefix) {
|
|
return xml.indexOf(prefix) == 0
|
|
}
|
|
}
|
|
|
|
module.exports = parse
|
|
|
|
|
|
/***/ }),
|
|
/* 3 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
const Block = __webpack_require__(4)
|
|
const {splitLineToCamelCase} = __webpack_require__(0)
|
|
|
|
class Element extends Block {
|
|
constructor(prop) {
|
|
super(prop.style)
|
|
this.name = prop.name
|
|
this.attributes = prop.attributes
|
|
}
|
|
}
|
|
|
|
|
|
class Widget {
|
|
constructor(xom, style) {
|
|
this.xom = xom
|
|
this.style = style
|
|
|
|
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color']
|
|
}
|
|
|
|
init() {
|
|
this.container = this.create(this.xom)
|
|
this.container.layout()
|
|
|
|
this.inheritStyle(this.container)
|
|
return this.container
|
|
}
|
|
|
|
// 继承父节点的样式
|
|
inheritStyle(node) {
|
|
const parent = node.parent || null
|
|
const children = node.children || {}
|
|
const computedStyle = node.computedStyle
|
|
|
|
if (parent) {
|
|
this.inheritProps.forEach(prop => {
|
|
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop]
|
|
})
|
|
}
|
|
|
|
Object.values(children).forEach(child => {
|
|
this.inheritStyle(child)
|
|
})
|
|
}
|
|
|
|
create(node) {
|
|
let classNames = (node.attributes.class || '').split(' ')
|
|
classNames = classNames.map(item => splitLineToCamelCase(item.trim()))
|
|
const style = {}
|
|
classNames.forEach(item => {
|
|
Object.assign(style, this.style[item] || {})
|
|
})
|
|
|
|
const args = {name: node.name, style}
|
|
|
|
const attrs = Object.keys(node.attributes)
|
|
const attributes = {}
|
|
for (const attr of attrs) {
|
|
const value = node.attributes[attr]
|
|
const CamelAttr = splitLineToCamelCase(attr)
|
|
|
|
if (value === '' || value === 'true') {
|
|
attributes[CamelAttr] = true
|
|
} else if (value === 'false') {
|
|
attributes[CamelAttr] = false
|
|
} else {
|
|
attributes[CamelAttr] = value
|
|
}
|
|
}
|
|
attributes.text = node.content
|
|
args.attributes = attributes
|
|
const element = new Element(args)
|
|
node.children.forEach(childNode => {
|
|
const childElement = this.create(childNode)
|
|
element.add(childElement)
|
|
})
|
|
return element
|
|
}
|
|
}
|
|
|
|
module.exports = {Widget}
|
|
|
|
|
|
/***/ }),
|
|
/* 4 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = require("widget-ui");
|
|
|
|
/***/ }),
|
|
/* 5 */
|
|
/***/ (function(module, exports) {
|
|
|
|
class Draw {
|
|
constructor(context, canvas, use2dCanvas = false) {
|
|
this.ctx = context
|
|
this.canvas = canvas || null
|
|
this.use2dCanvas = use2dCanvas
|
|
}
|
|
|
|
roundRect(x, y, w, h, r, fill = true, stroke = false) {
|
|
if (r < 0) return
|
|
const ctx = this.ctx
|
|
|
|
ctx.beginPath()
|
|
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
|
|
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
|
|
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
|
|
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
|
|
ctx.lineTo(x, y + r)
|
|
if (stroke) ctx.stroke()
|
|
if (fill) ctx.fill()
|
|
}
|
|
|
|
drawView(box, style) {
|
|
const ctx = this.ctx
|
|
const {
|
|
left: x, top: y, width: w, height: h
|
|
} = box
|
|
const {
|
|
borderRadius = 0,
|
|
borderWidth = 0,
|
|
borderColor,
|
|
color = '#000',
|
|
backgroundColor = 'transparent',
|
|
} = style
|
|
ctx.save()
|
|
// 外环
|
|
if (borderWidth > 0) {
|
|
ctx.fillStyle = borderColor || color
|
|
this.roundRect(x, y, w, h, borderRadius)
|
|
}
|
|
|
|
// 内环
|
|
ctx.fillStyle = backgroundColor
|
|
const innerWidth = w - 2 * borderWidth
|
|
const innerHeight = h - 2 * borderWidth
|
|
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
|
|
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius)
|
|
ctx.restore()
|
|
}
|
|
|
|
async drawImage(img, box, style) {
|
|
await new Promise((resolve, reject) => {
|
|
const ctx = this.ctx
|
|
const canvas = this.canvas
|
|
|
|
const {
|
|
borderRadius = 0
|
|
} = style
|
|
const {
|
|
left: x, top: y, width: w, height: h
|
|
} = box
|
|
ctx.save()
|
|
this.roundRect(x, y, w, h, borderRadius, false, false)
|
|
ctx.clip()
|
|
|
|
const _drawImage = (img) => {
|
|
if (this.use2dCanvas) {
|
|
const Image = canvas.createImage()
|
|
Image.onload = () => {
|
|
ctx.drawImage(Image, x, y, w, h)
|
|
ctx.restore()
|
|
resolve()
|
|
}
|
|
Image.onerror = () => { reject(new Error(`createImage fail: ${img}`)) }
|
|
Image.src = img
|
|
} else {
|
|
ctx.drawImage(img, x, y, w, h)
|
|
ctx.restore()
|
|
resolve()
|
|
}
|
|
}
|
|
|
|
const isTempFile = /^wxfile:\/\//.test(img)
|
|
const isNetworkFile = /^https?:\/\//.test(img)
|
|
|
|
if (isTempFile) {
|
|
_drawImage(img)
|
|
} else if (isNetworkFile) {
|
|
wx.downloadFile({
|
|
url: img,
|
|
success(res) {
|
|
if (res.statusCode === 200) {
|
|
_drawImage(res.tempFilePath)
|
|
} else {
|
|
reject(new Error(`downloadFile:fail ${img}`))
|
|
}
|
|
},
|
|
fail() {
|
|
reject(new Error(`downloadFile:fail ${img}`))
|
|
}
|
|
})
|
|
} else {
|
|
reject(new Error(`image format error: ${img}`))
|
|
}
|
|
})
|
|
}
|
|
|
|
// eslint-disable-next-line complexity
|
|
drawText(text, box, style) {
|
|
const ctx = this.ctx
|
|
let {
|
|
left: x, top: y, width: w, height: h
|
|
} = box
|
|
let {
|
|
color = '#000',
|
|
lineHeight = '1.4em',
|
|
fontSize = 14,
|
|
textAlign = 'left',
|
|
verticalAlign = 'top',
|
|
backgroundColor = 'transparent'
|
|
} = style
|
|
|
|
if (typeof lineHeight === 'string') { // 2em
|
|
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize)
|
|
}
|
|
if (!text || (lineHeight > h)) return
|
|
|
|
ctx.save()
|
|
ctx.textBaseline = 'top'
|
|
ctx.font = `${fontSize}px sans-serif`
|
|
ctx.textAlign = textAlign
|
|
|
|
// 背景色
|
|
ctx.fillStyle = backgroundColor
|
|
this.roundRect(x, y, w, h, 0)
|
|
|
|
// 文字颜色
|
|
ctx.fillStyle = color
|
|
|
|
// 水平布局
|
|
switch (textAlign) {
|
|
case 'left':
|
|
break
|
|
case 'center':
|
|
x += 0.5 * w
|
|
break
|
|
case 'right':
|
|
x += w
|
|
break
|
|
default: break
|
|
}
|
|
|
|
const textWidth = ctx.measureText(text).width
|
|
const actualHeight = Math.ceil(textWidth / w) * lineHeight
|
|
let paddingTop = Math.ceil((h - actualHeight) / 2)
|
|
if (paddingTop < 0) paddingTop = 0
|
|
|
|
// 垂直布局
|
|
switch (verticalAlign) {
|
|
case 'top':
|
|
break
|
|
case 'middle':
|
|
y += paddingTop
|
|
break
|
|
case 'bottom':
|
|
y += 2 * paddingTop
|
|
break
|
|
default: break
|
|
}
|
|
|
|
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
|
|
|
|
// 不超过一行
|
|
if (textWidth <= w) {
|
|
ctx.fillText(text, x, y + inlinePaddingTop)
|
|
return
|
|
}
|
|
|
|
// 多行文本
|
|
const chars = text.split('')
|
|
const _y = y
|
|
|
|
// 逐行绘制
|
|
let line = ''
|
|
for (const ch of chars) {
|
|
const testLine = line + ch
|
|
const testWidth = ctx.measureText(testLine).width
|
|
|
|
if (testWidth > w) {
|
|
ctx.fillText(line, x, y + inlinePaddingTop)
|
|
y += lineHeight
|
|
line = ch
|
|
if ((y + lineHeight) > (_y + h)) break
|
|
} else {
|
|
line = testLine
|
|
}
|
|
}
|
|
|
|
// 避免溢出
|
|
if ((y + lineHeight) <= (_y + h)) {
|
|
ctx.fillText(line, x, y + inlinePaddingTop)
|
|
}
|
|
ctx.restore()
|
|
}
|
|
|
|
async drawNode(element) {
|
|
const {layoutBox, computedStyle, name} = element
|
|
const {src, text} = element.attributes
|
|
if (name === 'view') {
|
|
this.drawView(layoutBox, computedStyle)
|
|
} else if (name === 'image') {
|
|
await this.drawImage(src, layoutBox, computedStyle)
|
|
} else if (name === 'text') {
|
|
this.drawText(text, layoutBox, computedStyle)
|
|
}
|
|
const childs = Object.values(element.children)
|
|
for (const child of childs) {
|
|
await this.drawNode(child)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
Draw
|
|
}
|
|
|
|
|
|
/***/ })
|
|
/******/ ]);
|
|
}); |