Bladeren bron

新增归藏

wangfumin 1 maand geleden
bovenliggende
commit
9e623a1645
100 gewijzigde bestanden met toevoegingen van 10441 en 7 verwijderingen
  1. 13 7
      app.json
  2. BIN
      imgs/guicang/down.png
  3. BIN
      imgs/guicang/haiwaiwenwu.png
  4. BIN
      imgs/guicang/heritage_bg.png
  5. BIN
      imgs/guicang/heritage_h5_logo.png
  6. BIN
      imgs/guicang/img_share_bg.png
  7. BIN
      imgs/guicang/pengyouquan.png
  8. BIN
      imgs/guicang/share.png
  9. BIN
      imgs/guicang/share_link.png
  10. BIN
      imgs/guicang/wechat.png
  11. 349 0
      miniprogram_npm/eventemitter3/index.js
  12. 1 0
      miniprogram_npm/eventemitter3/index.js.map
  13. 130 0
      miniprogram_npm/extend/index.js
  14. 1 0
      miniprogram_npm/extend/index.js.map
  15. 18 0
      miniprogram_npm/weapp-qrcode/index.js
  16. 1 0
      miniprogram_npm/weapp-qrcode/index.js.map
  17. 13 0
      miniprogram_npm/widget-ui/index.js
  18. 1 0
      miniprogram_npm/widget-ui/index.js.map
  19. 779 0
      miniprogram_npm/wxml-to-canvas/index.js
  20. 4 0
      miniprogram_npm/wxml-to-canvas/index.json
  21. 2 0
      miniprogram_npm/wxml-to-canvas/index.wxml
  22. 0 0
      miniprogram_npm/wxml-to-canvas/index.wxss
  23. 57 0
      miniprogram_npm/wxml-to-canvas/utils.js
  24. 47 0
      node_modules/.package-lock.json
  25. 21 0
      node_modules/eventemitter3/LICENSE
  26. 94 0
      node_modules/eventemitter3/README.md
  27. 134 0
      node_modules/eventemitter3/index.d.ts
  28. 336 0
      node_modules/eventemitter3/index.js
  29. 56 0
      node_modules/eventemitter3/package.json
  30. 340 0
      node_modules/eventemitter3/umd/eventemitter3.js
  31. 1 0
      node_modules/eventemitter3/umd/eventemitter3.min.js
  32. 1 0
      node_modules/eventemitter3/umd/eventemitter3.min.js.map
  33. 20 0
      node_modules/extend/.editorconfig
  34. 17 0
      node_modules/extend/.eslintrc
  35. 175 0
      node_modules/extend/.jscs.json
  36. 230 0
      node_modules/extend/.travis.yml
  37. 83 0
      node_modules/extend/CHANGELOG.md
  38. 23 0
      node_modules/extend/LICENSE
  39. 81 0
      node_modules/extend/README.md
  40. 32 0
      node_modules/extend/component.json
  41. 117 0
      node_modules/extend/index.js
  42. 42 0
      node_modules/extend/package.json
  43. 9 0
      node_modules/weapp-qrcode/.editorconfig
  44. 2 0
      node_modules/weapp-qrcode/.eslintignore
  45. 26 0
      node_modules/weapp-qrcode/.eslintrc.js
  46. 37 0
      node_modules/weapp-qrcode/.github/ISSUE_TEMPLATE.md
  47. 23 0
      node_modules/weapp-qrcode/.travis.yml
  48. 40 0
      node_modules/weapp-qrcode/CHANGELOG.md
  49. 21 0
      node_modules/weapp-qrcode/LICENSE
  50. 96 0
      node_modules/weapp-qrcode/README.md
  51. 39 0
      node_modules/weapp-qrcode/build/rollup.dev.config.js
  52. 49 0
      node_modules/weapp-qrcode/build/rollup.prod.config.js
  53. 6 0
      node_modules/weapp-qrcode/dist/README.md
  54. 5 0
      node_modules/weapp-qrcode/dist/weapp.qrcode.common.js
  55. 5 0
      node_modules/weapp-qrcode/dist/weapp.qrcode.esm.js
  56. 1281 0
      node_modules/weapp-qrcode/dist/weapp.qrcode.js
  57. 5 0
      node_modules/weapp-qrcode/dist/weapp.qrcode.min.js
  58. 50 0
      node_modules/weapp-qrcode/package.json
  59. 94 0
      node_modules/weapp-qrcode/src/index.js
  60. 1242 0
      node_modules/weapp-qrcode/src/qrcode.js
  61. 9 0
      node_modules/widget-ui/babel.config.js
  62. 40 0
      node_modules/widget-ui/dist/element.d.ts
  63. 5 0
      node_modules/widget-ui/dist/event.d.ts
  64. 1 0
      node_modules/widget-ui/dist/index.js
  65. 36 0
      node_modules/widget-ui/dist/style.d.ts
  66. 6 0
      node_modules/widget-ui/jest.config.js
  67. 27 0
      node_modules/widget-ui/package.json
  68. 1186 0
      node_modules/widget-ui/src/css-layout.js
  69. 172 0
      node_modules/widget-ui/src/element.ts
  70. 15 0
      node_modules/widget-ui/src/event.ts
  71. 87 0
      node_modules/widget-ui/src/style.ts
  72. 183 0
      node_modules/widget-ui/test/css-layout.test.ts
  73. 47 0
      node_modules/widget-ui/tsconfig.json
  74. 206 0
      node_modules/widget-ui/tslint.json
  75. 25 0
      node_modules/widget-ui/webpack.config.js
  76. 10 0
      node_modules/wxml-to-canvas/.babelrc
  77. 99 0
      node_modules/wxml-to-canvas/.eslintrc.js
  78. 21 0
      node_modules/wxml-to-canvas/LICENSE
  79. 187 0
      node_modules/wxml-to-canvas/README.md
  80. 26 0
      node_modules/wxml-to-canvas/gulpfile.js
  81. 779 0
      node_modules/wxml-to-canvas/miniprogram_dist/index.js
  82. 4 0
      node_modules/wxml-to-canvas/miniprogram_dist/index.json
  83. 2 0
      node_modules/wxml-to-canvas/miniprogram_dist/index.wxml
  84. 0 0
      node_modules/wxml-to-canvas/miniprogram_dist/index.wxss
  85. 57 0
      node_modules/wxml-to-canvas/miniprogram_dist/utils.js
  86. 63 0
      node_modules/wxml-to-canvas/package.json
  87. 225 0
      node_modules/wxml-to-canvas/src/draw.js
  88. 117 0
      node_modules/wxml-to-canvas/src/index.js
  89. 4 0
      node_modules/wxml-to-canvas/src/index.json
  90. 2 0
      node_modules/wxml-to-canvas/src/index.wxml
  91. 0 0
      node_modules/wxml-to-canvas/src/index.wxss
  92. 57 0
      node_modules/wxml-to-canvas/src/utils.js
  93. 81 0
      node_modules/wxml-to-canvas/src/widget.js
  94. 164 0
      node_modules/wxml-to-canvas/src/xml-parser.js
  95. 56 0
      package-lock.json
  96. 15 0
      package.json
  97. 135 0
      pages/guicang/index.js
  98. 8 0
      pages/guicang/index.json
  99. 35 0
      pages/guicang/index.wxml
  100. 0 0
      pages/guicang/index.wxss

+ 13 - 7
app.json

@@ -3,6 +3,8 @@
     "pages/yuezhan/index",
     "pages/tongcheng/index",
     "pages/swkz/index",
+    "pages/zhanxun/index",
+    "pages/guicang/index",
     "pages/user/index",
     "pages/yuezhan/search/index",
     "pages/zl_detail/index",
@@ -26,7 +28,9 @@
     "common/components/drop-down/index",
     "pages/user/my_contact/index",
     "common/components/component-list/component-list",
-    "pages/yuezhan/html_detail/index"
+    "pages/yuezhan/html_detail/index",
+    "pages/guicangDetails/index",
+    "pages/webview/index"
   ],
   "requiredPrivateInfos": [
     "getLocation"
@@ -36,7 +40,9 @@
     "navigationBarBackgroundColor": "#fff",
     "navigationBarTitleText": "四维看展",
     "backgroundColor": "#ffffff",
-    "navigationBarTextStyle": "black"
+    "navigationBarTextStyle": "black",
+    "enableShareAppMessage": true,
+    "enableShareTimeline": true
   },
   "tabBar": {
     "color": "#B2B2B2",
@@ -50,16 +56,16 @@
         "selectedIconPath": "./imgs/yuezhan_active.png"
       },
       {
-        "pagePath": "pages/swkz/index",
-        "text": "线上看展",
+        "pagePath": "pages/zhanxun/index",
+        "text": "展",
         "iconPath": "imgs/swkz.png",
         "selectedIconPath": "./imgs/swkz_active.png"
       },
       {
-        "pagePath": "pages/tongcheng/index",
-        "text": "线下展讯",
+        "pagePath": "pages/guicang/index",
+        "text": "归藏",
         "iconPath": "imgs/tongcheng.png",
-        "selectedIconPath": "./imgs/tongcheng_active.png"
+        "selectedIconPath": "./imgs/tongcheng_active.png" 
       },
       {
         "pagePath": "pages/user/index",

BIN
imgs/guicang/down.png


BIN
imgs/guicang/haiwaiwenwu.png


BIN
imgs/guicang/heritage_bg.png


BIN
imgs/guicang/heritage_h5_logo.png


BIN
imgs/guicang/img_share_bg.png


BIN
imgs/guicang/pengyouquan.png


BIN
imgs/guicang/share.png


BIN
imgs/guicang/share_link.png


BIN
imgs/guicang/wechat.png


File diff suppressed because it is too large
+ 349 - 0
miniprogram_npm/eventemitter3/index.js


File diff suppressed because it is too large
+ 1 - 0
miniprogram_npm/eventemitter3/index.js.map


File diff suppressed because it is too large
+ 130 - 0
miniprogram_npm/extend/index.js


File diff suppressed because it is too large
+ 1 - 0
miniprogram_npm/extend/index.js.map


File diff suppressed because it is too large
+ 18 - 0
miniprogram_npm/weapp-qrcode/index.js


File diff suppressed because it is too large
+ 1 - 0
miniprogram_npm/weapp-qrcode/index.js.map


File diff suppressed because it is too large
+ 13 - 0
miniprogram_npm/widget-ui/index.js


File diff suppressed because it is too large
+ 1 - 0
miniprogram_npm/widget-ui/index.js.map


+ 779 - 0
miniprogram_npm/wxml-to-canvas/index.js

@@ -0,0 +1,779 @@
+(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
+}
+
+
+/***/ })
+/******/ ]);
+});

+ 4 - 0
miniprogram_npm/wxml-to-canvas/index.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 2 - 0
miniprogram_npm/wxml-to-canvas/index.wxml

@@ -0,0 +1,2 @@
+<canvas wx:if="{{use2dCanvas}}" id="weui-canvas" type="2d" style="width: {{width}}px; height: {{height}}px;"></canvas>
+<canvas wx:else canvas-id="weui-canvas" style="width: {{width}}px; height: {{height}}px;"></canvas>

+ 0 - 0
miniprogram_npm/wxml-to-canvas/index.wxss


+ 57 - 0
miniprogram_npm/wxml-to-canvas/utils.js

@@ -0,0 +1,57 @@
+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
+}

+ 47 - 0
node_modules/.package-lock.json

@@ -0,0 +1,47 @@
+{
+  "name": "swkz_wx",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "license": "MIT"
+    },
+    "node_modules/weapp-qrcode": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/weapp-qrcode/-/weapp-qrcode-1.0.0.tgz",
+      "integrity": "sha512-4sa3W0rGDVJ9QaeZpAKlAuUxVyjhDwiUqHyGK/jJMsRMXnhb4yO8qWU/pZruMo+iT5J6CraS67lDMFb1VY+RaA==",
+      "license": "MIT",
+      "dependencies": {
+        "extend": "^3.0.2"
+      }
+    },
+    "node_modules/widget-ui": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/widget-ui/-/widget-ui-1.0.2.tgz",
+      "integrity": "sha512-gDXosr5mflJdMA1weU1A47aTsTFfMJhfA4EKgO5XFebY3eVklf80KD4GODfrjo8J2WQ+9YjL1Rd9UUmKIzhShw==",
+      "license": "ISC",
+      "dependencies": {
+        "eventemitter3": "^4.0.0"
+      }
+    },
+    "node_modules/wxml-to-canvas": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/wxml-to-canvas/-/wxml-to-canvas-1.1.1.tgz",
+      "integrity": "sha512-3mDjHzujY/UgdCOXij/MnmwJYerVjwkyQHMBFBE8zh89DK7h7UTzoydWFqEBjIC0rfZM+AXl5kDh9hUcsNpSmg==",
+      "license": "MIT",
+      "dependencies": {
+        "widget-ui": "^1.0.2"
+      }
+    }
+  }
+}

+ 21 - 0
node_modules/eventemitter3/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Arnout Kazemier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

File diff suppressed because it is too large
+ 94 - 0
node_modules/eventemitter3/README.md


+ 134 - 0
node_modules/eventemitter3/index.d.ts

@@ -0,0 +1,134 @@
+/**
+ * Minimal `EventEmitter` interface that is molded against the Node.js
+ * `EventEmitter` interface.
+ */
+declare class EventEmitter<
+  EventTypes extends EventEmitter.ValidEventTypes = string | symbol,
+  Context extends any = any
+> {
+  static prefixed: string | boolean;
+
+  /**
+   * Return an array listing the events for which the emitter has registered
+   * listeners.
+   */
+  eventNames(): Array<EventEmitter.EventNames<EventTypes>>;
+
+  /**
+   * Return the listeners registered for a given event.
+   */
+  listeners<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T
+  ): Array<EventEmitter.EventListener<EventTypes, T>>;
+
+  /**
+   * Return the number of listeners listening to a given event.
+   */
+  listenerCount(event: EventEmitter.EventNames<EventTypes>): number;
+
+  /**
+   * Calls each of the listeners registered for a given event.
+   */
+  emit<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    ...args: EventEmitter.EventArgs<EventTypes, T>
+  ): boolean;
+
+  /**
+   * Add a listener for a given event.
+   */
+  on<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context
+  ): this;
+  addListener<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context
+  ): this;
+
+  /**
+   * Add a one-time listener for a given event.
+   */
+  once<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context
+  ): this;
+
+  /**
+   * Remove the listeners of a given event.
+   */
+  removeListener<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn?: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context,
+    once?: boolean
+  ): this;
+  off<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn?: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context,
+    once?: boolean
+  ): this;
+
+  /**
+   * Remove all listeners, or those of the specified event.
+   */
+  removeAllListeners(event?: EventEmitter.EventNames<EventTypes>): this;
+}
+
+declare namespace EventEmitter {
+  export interface ListenerFn<Args extends any[] = any[]> {
+    (...args: Args): void;
+  }
+
+  export interface EventEmitterStatic {
+    new <
+      EventTypes extends ValidEventTypes = string | symbol,
+      Context = any
+    >(): EventEmitter<EventTypes, Context>;
+  }
+
+  /**
+   * `object` should be in either of the following forms:
+   * ```
+   * interface EventTypes {
+   *   'event-with-parameters': any[]
+   *   'event-with-example-handler': (...args: any[]) => void
+   * }
+   * ```
+   */
+  export type ValidEventTypes = string | symbol | object;
+
+  export type EventNames<T extends ValidEventTypes> = T extends string | symbol
+    ? T
+    : keyof T;
+
+  export type ArgumentMap<T extends object> = {
+    [K in keyof T]: T[K] extends (...args: any[]) => void
+      ? Parameters<T[K]>
+      : T[K] extends any[]
+      ? T[K]
+      : any[];
+  };
+
+  export type EventListener<
+    T extends ValidEventTypes,
+    K extends EventNames<T>
+  > = T extends string | symbol
+    ? (...args: any[]) => void
+    : (
+        ...args: ArgumentMap<Exclude<T, string | symbol>>[Extract<K, keyof T>]
+      ) => void;
+
+  export type EventArgs<
+    T extends ValidEventTypes,
+    K extends EventNames<T>
+  > = Parameters<EventListener<T, K>>;
+
+  export const EventEmitter: EventEmitterStatic;
+}
+
+export = EventEmitter;

+ 336 - 0
node_modules/eventemitter3/index.js

@@ -0,0 +1,336 @@
+'use strict';
+
+var has = Object.prototype.hasOwnProperty
+  , prefix = '~';
+
+/**
+ * Constructor to create a storage for our `EE` objects.
+ * An `Events` instance is a plain object whose properties are event names.
+ *
+ * @constructor
+ * @private
+ */
+function Events() {}
+
+//
+// We try to not inherit from `Object.prototype`. In some engines creating an
+// instance in this way is faster than calling `Object.create(null)` directly.
+// If `Object.create(null)` is not supported we prefix the event names with a
+// character to make sure that the built-in object properties are not
+// overridden or used as an attack vector.
+//
+if (Object.create) {
+  Events.prototype = Object.create(null);
+
+  //
+  // This hack is needed because the `__proto__` property is still inherited in
+  // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
+  //
+  if (!new Events().__proto__) prefix = false;
+}
+
+/**
+ * Representation of a single event listener.
+ *
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
+ * @constructor
+ * @private
+ */
+function EE(fn, context, once) {
+  this.fn = fn;
+  this.context = context;
+  this.once = once || false;
+}
+
+/**
+ * Add a listener for a given event.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} once Specify if the listener is a one-time listener.
+ * @returns {EventEmitter}
+ * @private
+ */
+function addListener(emitter, event, fn, context, once) {
+  if (typeof fn !== 'function') {
+    throw new TypeError('The listener must be a function');
+  }
+
+  var listener = new EE(fn, context || emitter, once)
+    , evt = prefix ? prefix + event : event;
+
+  if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
+  else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
+  else emitter._events[evt] = [emitter._events[evt], listener];
+
+  return emitter;
+}
+
+/**
+ * Clear event by name.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} evt The Event name.
+ * @private
+ */
+function clearEvent(emitter, evt) {
+  if (--emitter._eventsCount === 0) emitter._events = new Events();
+  else delete emitter._events[evt];
+}
+
+/**
+ * Minimal `EventEmitter` interface that is molded against the Node.js
+ * `EventEmitter` interface.
+ *
+ * @constructor
+ * @public
+ */
+function EventEmitter() {
+  this._events = new Events();
+  this._eventsCount = 0;
+}
+
+/**
+ * Return an array listing the events for which the emitter has registered
+ * listeners.
+ *
+ * @returns {Array}
+ * @public
+ */
+EventEmitter.prototype.eventNames = function eventNames() {
+  var names = []
+    , events
+    , name;
+
+  if (this._eventsCount === 0) return names;
+
+  for (name in (events = this._events)) {
+    if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
+  }
+
+  if (Object.getOwnPropertySymbols) {
+    return names.concat(Object.getOwnPropertySymbols(events));
+  }
+
+  return names;
+};
+
+/**
+ * Return the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Array} The registered listeners.
+ * @public
+ */
+EventEmitter.prototype.listeners = function listeners(event) {
+  var evt = prefix ? prefix + event : event
+    , handlers = this._events[evt];
+
+  if (!handlers) return [];
+  if (handlers.fn) return [handlers.fn];
+
+  for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
+    ee[i] = handlers[i].fn;
+  }
+
+  return ee;
+};
+
+/**
+ * Return the number of listeners listening to a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Number} The number of listeners.
+ * @public
+ */
+EventEmitter.prototype.listenerCount = function listenerCount(event) {
+  var evt = prefix ? prefix + event : event
+    , listeners = this._events[evt];
+
+  if (!listeners) return 0;
+  if (listeners.fn) return 1;
+  return listeners.length;
+};
+
+/**
+ * Calls each of the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Boolean} `true` if the event had listeners, else `false`.
+ * @public
+ */
+EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
+  var evt = prefix ? prefix + event : event;
+
+  if (!this._events[evt]) return false;
+
+  var listeners = this._events[evt]
+    , len = arguments.length
+    , args
+    , i;
+
+  if (listeners.fn) {
+    if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
+
+    switch (len) {
+      case 1: return listeners.fn.call(listeners.context), true;
+      case 2: return listeners.fn.call(listeners.context, a1), true;
+      case 3: return listeners.fn.call(listeners.context, a1, a2), true;
+      case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
+      case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
+      case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
+    }
+
+    for (i = 1, args = new Array(len -1); i < len; i++) {
+      args[i - 1] = arguments[i];
+    }
+
+    listeners.fn.apply(listeners.context, args);
+  } else {
+    var length = listeners.length
+      , j;
+
+    for (i = 0; i < length; i++) {
+      if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
+
+      switch (len) {
+        case 1: listeners[i].fn.call(listeners[i].context); break;
+        case 2: listeners[i].fn.call(listeners[i].context, a1); break;
+        case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
+        case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
+        default:
+          if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
+            args[j - 1] = arguments[j];
+          }
+
+          listeners[i].fn.apply(listeners[i].context, args);
+      }
+    }
+  }
+
+  return true;
+};
+
+/**
+ * Add a listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.on = function on(event, fn, context) {
+  return addListener(this, event, fn, context, false);
+};
+
+/**
+ * Add a one-time listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.once = function once(event, fn, context) {
+  return addListener(this, event, fn, context, true);
+};
+
+/**
+ * Remove the listeners of a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn Only remove the listeners that match this function.
+ * @param {*} context Only remove the listeners that have this context.
+ * @param {Boolean} once Only remove one-time listeners.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
+  var evt = prefix ? prefix + event : event;
+
+  if (!this._events[evt]) return this;
+  if (!fn) {
+    clearEvent(this, evt);
+    return this;
+  }
+
+  var listeners = this._events[evt];
+
+  if (listeners.fn) {
+    if (
+      listeners.fn === fn &&
+      (!once || listeners.once) &&
+      (!context || listeners.context === context)
+    ) {
+      clearEvent(this, evt);
+    }
+  } else {
+    for (var i = 0, events = [], length = listeners.length; i < length; i++) {
+      if (
+        listeners[i].fn !== fn ||
+        (once && !listeners[i].once) ||
+        (context && listeners[i].context !== context)
+      ) {
+        events.push(listeners[i]);
+      }
+    }
+
+    //
+    // Reset the array, or remove it completely if we have no more listeners.
+    //
+    if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
+    else clearEvent(this, evt);
+  }
+
+  return this;
+};
+
+/**
+ * Remove all listeners, or those of the specified event.
+ *
+ * @param {(String|Symbol)} [event] The event name.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
+  var evt;
+
+  if (event) {
+    evt = prefix ? prefix + event : event;
+    if (this._events[evt]) clearEvent(this, evt);
+  } else {
+    this._events = new Events();
+    this._eventsCount = 0;
+  }
+
+  return this;
+};
+
+//
+// Alias methods names because people roll like that.
+//
+EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
+EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+//
+// Expose the prefix.
+//
+EventEmitter.prefixed = prefix;
+
+//
+// Allow `EventEmitter` to be imported as module namespace.
+//
+EventEmitter.EventEmitter = EventEmitter;
+
+//
+// Expose the module.
+//
+if ('undefined' !== typeof module) {
+  module.exports = EventEmitter;
+}

+ 56 - 0
node_modules/eventemitter3/package.json

@@ -0,0 +1,56 @@
+{
+  "name": "eventemitter3",
+  "version": "4.0.7",
+  "description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.",
+  "main": "index.js",
+  "typings": "index.d.ts",
+  "scripts": {
+    "browserify": "rm -rf umd && mkdir umd && browserify index.js -s EventEmitter3 -o umd/eventemitter3.js",
+    "minify": "uglifyjs umd/eventemitter3.js --source-map -cm -o umd/eventemitter3.min.js",
+    "benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;",
+    "test": "nyc --reporter=html --reporter=text mocha test/test.js",
+    "prepublishOnly": "npm run browserify && npm run minify",
+    "test-browser": "node test/browser.js"
+  },
+  "files": [
+    "index.js",
+    "index.d.ts",
+    "umd"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/primus/eventemitter3.git"
+  },
+  "keywords": [
+    "EventEmitter",
+    "EventEmitter2",
+    "EventEmitter3",
+    "Events",
+    "addEventListener",
+    "addListener",
+    "emit",
+    "emits",
+    "emitter",
+    "event",
+    "once",
+    "pub/sub",
+    "publish",
+    "reactor",
+    "subscribe"
+  ],
+  "author": "Arnout Kazemier",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/primus/eventemitter3/issues"
+  },
+  "devDependencies": {
+    "assume": "^2.2.0",
+    "browserify": "^16.5.0",
+    "mocha": "^8.0.1",
+    "nyc": "^15.1.0",
+    "pre-commit": "^1.2.0",
+    "sauce-browsers": "^2.0.0",
+    "sauce-test": "^1.3.3",
+    "uglify-js": "^3.9.0"
+  }
+}

File diff suppressed because it is too large
+ 340 - 0
node_modules/eventemitter3/umd/eventemitter3.js


File diff suppressed because it is too large
+ 1 - 0
node_modules/eventemitter3/umd/eventemitter3.min.js


File diff suppressed because it is too large
+ 1 - 0
node_modules/eventemitter3/umd/eventemitter3.min.js.map


+ 20 - 0
node_modules/extend/.editorconfig

@@ -0,0 +1,20 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 150
+
+[CHANGELOG.md]
+indent_style = space
+indent_size = 2
+
+[*.json]
+max_line_length = off
+
+[Makefile]
+max_line_length = off

+ 17 - 0
node_modules/extend/.eslintrc

@@ -0,0 +1,17 @@
+{
+	"root": true,
+
+	"extends": "@ljharb",
+
+	"rules": {
+		"complexity": [2, 20],
+		"eqeqeq": [2, "allow-null"],
+		"func-name-matching": [1],
+		"max-depth": [1, 4],
+		"max-statements": [2, 26],
+		"no-extra-parens": [1],
+		"no-magic-numbers": [0],
+		"no-restricted-syntax": [2, "BreakStatement", "ContinueStatement", "DebuggerStatement", "LabeledStatement", "WithStatement"],
+		"sort-keys": [0],
+	}
+}

+ 175 - 0
node_modules/extend/.jscs.json

@@ -0,0 +1,175 @@
+{
+	"es3": true,
+
+	"additionalRules": [],
+
+	"requireSemicolons": true,
+
+	"disallowMultipleSpaces": true,
+
+	"disallowIdentifierNames": [],
+
+	"requireCurlyBraces": {
+		"allExcept": [],
+		"keywords": ["if", "else", "for", "while", "do", "try", "catch"]
+	},
+
+	"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"],
+
+	"disallowSpaceAfterKeywords": [],
+
+	"disallowSpaceBeforeComma": true,
+	"disallowSpaceAfterComma": false,
+	"disallowSpaceBeforeSemicolon": true,
+
+	"disallowNodeTypes": [
+		"DebuggerStatement",
+		"LabeledStatement",
+		"SwitchCase",
+		"SwitchStatement",
+		"WithStatement"
+	],
+
+	"requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] },
+
+	"requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true },
+	"requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true },
+	"disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true },
+	"requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true },
+	"disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true },
+
+	"requireSpaceBetweenArguments": true,
+
+	"disallowSpacesInsideParentheses": true,
+
+	"disallowSpacesInsideArrayBrackets": true,
+
+	"disallowQuotedKeysInObjects": { "allExcept": ["reserved"] },
+
+	"disallowSpaceAfterObjectKeys": true,
+
+	"requireCommaBeforeLineBreak": true,
+
+	"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
+	"requireSpaceAfterPrefixUnaryOperators": [],
+
+	"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
+	"requireSpaceBeforePostfixUnaryOperators": [],
+
+	"disallowSpaceBeforeBinaryOperators": [],
+	"requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
+
+	"requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
+	"disallowSpaceAfterBinaryOperators": [],
+
+	"disallowImplicitTypeConversion": ["binary", "string"],
+
+	"disallowKeywords": ["with", "eval"],
+
+	"requireKeywordsOnNewLine": [],
+	"disallowKeywordsOnNewLine": ["else"],
+
+	"requireLineFeedAtFileEnd": true,
+
+	"disallowTrailingWhitespace": true,
+
+	"disallowTrailingComma": true,
+
+	"excludeFiles": ["node_modules/**", "vendor/**"],
+
+	"disallowMultipleLineStrings": true,
+
+	"requireDotNotation": { "allExcept": ["keywords"] },
+
+	"requireParenthesesAroundIIFE": true,
+
+	"validateLineBreaks": "LF",
+
+	"validateQuoteMarks": {
+		"escape": true,
+		"mark": "'"
+	},
+
+	"disallowOperatorBeforeLineBreak": [],
+
+	"requireSpaceBeforeKeywords": [
+		"do",
+		"for",
+		"if",
+		"else",
+		"switch",
+		"case",
+		"try",
+		"catch",
+		"finally",
+		"while",
+		"with",
+		"return"
+	],
+
+	"validateAlignedFunctionParameters": {
+		"lineBreakAfterOpeningBraces": true,
+		"lineBreakBeforeClosingBraces": true
+	},
+
+	"requirePaddingNewLinesBeforeExport": true,
+
+	"validateNewlineAfterArrayElements": {
+		"maximum": 6
+	},
+
+	"requirePaddingNewLinesAfterUseStrict": true,
+
+	"disallowArrowFunctions": true,
+
+	"disallowMultiLineTernary": true,
+
+	"validateOrderInObjectKeys": false,
+
+	"disallowIdenticalDestructuringNames": true,
+
+	"disallowNestedTernaries": { "maxLevel": 1 },
+
+	"requireSpaceAfterComma": { "allExcept": ["trailing"] },
+	"requireAlignedMultilineParams": false,
+
+	"requireSpacesInGenerator": {
+		"afterStar": true
+	},
+
+	"disallowSpacesInGenerator": {
+		"beforeStar": true
+	},
+
+	"disallowVar": false,
+
+	"requireArrayDestructuring": false,
+
+	"requireEnhancedObjectLiterals": false,
+
+	"requireObjectDestructuring": false,
+
+	"requireEarlyReturn": false,
+
+	"requireCapitalizedConstructorsNew": {
+		"allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"]
+	},
+
+	"requireImportAlphabetized": false,
+
+	"requireSpaceBeforeObjectValues": true,
+	"requireSpaceBeforeDestructuredValues": true,
+
+	"disallowSpacesInsideTemplateStringPlaceholders": true,
+
+	"disallowArrayDestructuringReturn": false,
+
+	"requireNewlineBeforeSingleStatementsInIf": false,
+
+	"disallowUnusedVariables": true,
+
+	"requireSpacesInsideImportedObjectBraces": true,
+
+	"requireUseStrict": true
+}
+

+ 230 - 0
node_modules/extend/.travis.yml

@@ -0,0 +1,230 @@
+language: node_js
+os:
+ - linux
+node_js:
+  - "10.7"
+  - "9.11"
+  - "8.11"
+  - "7.10"
+  - "6.14"
+  - "5.12"
+  - "4.9"
+  - "iojs-v3.3"
+  - "iojs-v2.5"
+  - "iojs-v1.8"
+  - "0.12"
+  - "0.10"
+  - "0.8"
+before_install:
+  - 'case "${TRAVIS_NODE_VERSION}" in 0.*) export NPM_CONFIG_STRICT_SSL=false ;; esac'
+  - 'nvm install-latest-npm'
+install:
+  - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.9" ]; then nvm install --latest-npm 0.8 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;'
+script:
+  - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
+  - 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi'
+  - 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi'
+  - 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi'
+sudo: false
+env:
+  - TEST=true
+matrix:
+  fast_finish: true
+  include:
+    - node_js: "lts/*"
+      env: PRETEST=true
+    - node_js: "lts/*"
+      env: POSTTEST=true
+    - node_js: "4"
+      env: COVERAGE=true
+    - node_js: "10.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "10.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "10.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "10.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "10.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "10.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "10.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.10"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.9"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.8"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "9.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.10"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.9"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.8"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "8.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.9"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.8"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "7.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.13"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.12"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.11"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.10"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.9"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.8"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "6.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.11"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.10"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.9"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.8"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "5.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.8"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "4.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v3.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v3.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v3.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v2.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v2.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v2.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v2.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v2.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.7"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.5"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.4"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.3"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.2"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.1"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "iojs-v1.0"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "0.11"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "0.9"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "0.6"
+      env: TEST=true ALLOW_FAILURE=true
+    - node_js: "0.4"
+      env: TEST=true ALLOW_FAILURE=true
+  allow_failures:
+    - os: osx
+    - env: TEST=true ALLOW_FAILURE=true

+ 83 - 0
node_modules/extend/CHANGELOG.md

@@ -0,0 +1,83 @@
+3.0.2 / 2018-07-19
+==================
+  * [Fix] Prevent merging `__proto__` property (#48)
+  * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`
+  * [Tests] up to `node` `v10.7`, `v9.11`, `v8.11`, `v7.10`, `v6.14`, `v4.9`; use `nvm install-latest-npm`
+
+3.0.1 / 2017-04-27
+==================
+  * [Fix] deep extending should work with a non-object (#46)
+  * [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`
+  * [Tests] up to `node` `v7.9`, `v6.10`, `v4.8`; improve matrix
+  * [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG.
+  * [Docs] Add example to readme (#34)
+
+3.0.0 / 2015-07-01
+==================
+  * [Possible breaking change] Use global "strict" directive (#32)
+  * [Tests] `int` is an ES3 reserved word
+  * [Tests] Test up to `io.js` `v2.3`
+  * [Tests] Add `npm run eslint`
+  * [Dev Deps] Update `covert`, `jscs`
+
+2.0.1 / 2015-04-25
+==================
+  * Use an inline `isArray` check, for ES3 browsers. (#27)
+  * Some old browsers fail when an identifier is `toString`
+  * Test latest `node` and `io.js` versions on `travis-ci`; speed up builds
+  * Add license info to package.json (#25)
+  * Update `tape`, `jscs`
+  * Adding a CHANGELOG
+
+2.0.0 / 2014-10-01
+==================
+  * Increase code coverage to 100%; run code coverage as part of tests
+  * Add `npm run lint`; Run linter as part of tests
+  * Remove nodeType and setInterval checks in isPlainObject
+  * Updating `tape`, `jscs`, `covert`
+  * General style and README cleanup
+
+1.3.0 / 2014-06-20
+==================
+  * Add component.json for browser support (#18)
+  * Use SVG for badges in README (#16)
+  * Updating `tape`, `covert`
+  * Updating travis-ci to work with multiple node versions
+  * Fix `deep === false` bug (returning target as {}) (#14)
+  * Fixing constructor checks in isPlainObject
+  * Adding additional test coverage
+  * Adding `npm run coverage`
+  * Add LICENSE (#13)
+  * Adding a warning about `false`, per #11
+  * General style and whitespace cleanup
+
+1.2.1 / 2013-09-14
+==================
+  * Fixing hasOwnProperty bugs that would only have shown up in specific browsers. Fixes #8
+  * Updating `tape`
+
+1.2.0 / 2013-09-02
+==================
+  * Updating the README: add badges
+  * Adding a missing variable reference.
+  * Using `tape` instead of `buster` for tests; add more tests (#7)
+  * Adding node 0.10 to Travis CI (#6)
+  * Enabling "npm test" and cleaning up package.json (#5)
+  * Add Travis CI.
+
+1.1.3 / 2012-12-06
+==================
+  * Added unit tests.
+  * Ensure extend function is named. (Looks nicer in a stack trace.)
+  * README cleanup.
+
+1.1.1 / 2012-11-07
+==================
+  * README cleanup.
+  * Added installation instructions.
+  * Added a missing semicolon
+
+1.0.0 / 2012-04-08
+==================
+  * Initial commit
+

+ 23 - 0
node_modules/extend/LICENSE

@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Stefan Thomas
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+

+ 81 - 0
node_modules/extend/README.md

@@ -0,0 +1,81 @@
+[![Build Status][travis-svg]][travis-url]
+[![dependency status][deps-svg]][deps-url]
+[![dev dependency status][dev-deps-svg]][dev-deps-url]
+
+# extend() for Node.js <sup>[![Version Badge][npm-version-png]][npm-url]</sup>
+
+`node-extend` is a port of the classic extend() method from jQuery. It behaves as you expect. It is simple, tried and true.
+
+Notes:
+
+* Since Node.js >= 4,
+  [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
+  now offers the same functionality natively (but without the "deep copy" option).
+  See [ECMAScript 2015 (ES6) in Node.js](https://nodejs.org/en/docs/es6).
+* Some native implementations of `Object.assign` in both Node.js and many
+  browsers (since NPM modules are for the browser too) may not be fully
+  spec-compliant.
+  Check [`object.assign`](https://www.npmjs.com/package/object.assign) module for
+  a compliant candidate.
+
+## Installation
+
+This package is available on [npm][npm-url] as: `extend`
+
+``` sh
+npm install extend
+```
+
+## Usage
+
+**Syntax:** extend **(** [`deep`], `target`, `object1`, [`objectN`] **)**
+
+*Extend one object with one or more others, returning the modified object.*
+
+**Example:**
+
+``` js
+var extend = require('extend');
+extend(targetObject, object1, object2);
+```
+
+Keep in mind that the target object will be modified, and will be returned from extend().
+
+If a boolean true is specified as the first argument, extend performs a deep copy, recursively copying any objects it finds. Otherwise, the copy will share structure with the original object(s).
+Undefined properties are not copied. However, properties inherited from the object's prototype will be copied over.
+Warning: passing `false` as the first argument is not supported.
+
+### Arguments
+
+* `deep` *Boolean* (optional)
+If set, the merge becomes recursive (i.e. deep copy).
+* `target`	*Object*
+The object to extend.
+* `object1`	*Object*
+The object that will be merged into the first.
+* `objectN` *Object* (Optional)
+More objects to merge into the first.
+
+## License
+
+`node-extend` is licensed under the [MIT License][mit-license-url].
+
+## Acknowledgements
+
+All credit to the jQuery authors for perfecting this amazing utility.
+
+Ported to Node.js by [Stefan Thomas][github-justmoon] with contributions by [Jonathan Buchanan][github-insin] and [Jordan Harband][github-ljharb].
+
+[travis-svg]: https://travis-ci.org/justmoon/node-extend.svg
+[travis-url]: https://travis-ci.org/justmoon/node-extend
+[npm-url]: https://npmjs.org/package/extend
+[mit-license-url]: http://opensource.org/licenses/MIT
+[github-justmoon]: https://github.com/justmoon
+[github-insin]: https://github.com/insin
+[github-ljharb]: https://github.com/ljharb
+[npm-version-png]: http://versionbadg.es/justmoon/node-extend.svg
+[deps-svg]: https://david-dm.org/justmoon/node-extend.svg
+[deps-url]: https://david-dm.org/justmoon/node-extend
+[dev-deps-svg]: https://david-dm.org/justmoon/node-extend/dev-status.svg
+[dev-deps-url]: https://david-dm.org/justmoon/node-extend#info=devDependencies
+

+ 32 - 0
node_modules/extend/component.json

@@ -0,0 +1,32 @@
+{
+	"name": "extend",
+	"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)",
+	"version": "3.0.0",
+	"description": "Port of jQuery.extend for node.js and the browser.",
+	"scripts": [
+		"index.js"
+	],
+	"contributors": [
+		{
+			"name": "Jordan Harband",
+			"url": "https://github.com/ljharb"
+		}
+	],
+	"keywords": [
+		"extend",
+		"clone",
+		"merge"
+	],
+	"repository" : {
+		"type": "git",
+		"url": "https://github.com/justmoon/node-extend.git"
+	},
+	"dependencies": {
+	},
+	"devDependencies": {
+		"tape" : "~3.0.0",
+		"covert": "~0.4.0",
+		"jscs": "~1.6.2"
+	}
+}
+

+ 117 - 0
node_modules/extend/index.js

@@ -0,0 +1,117 @@
+'use strict';
+
+var hasOwn = Object.prototype.hasOwnProperty;
+var toStr = Object.prototype.toString;
+var defineProperty = Object.defineProperty;
+var gOPD = Object.getOwnPropertyDescriptor;
+
+var isArray = function isArray(arr) {
+	if (typeof Array.isArray === 'function') {
+		return Array.isArray(arr);
+	}
+
+	return toStr.call(arr) === '[object Array]';
+};
+
+var isPlainObject = function isPlainObject(obj) {
+	if (!obj || toStr.call(obj) !== '[object Object]') {
+		return false;
+	}
+
+	var hasOwnConstructor = hasOwn.call(obj, 'constructor');
+	var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
+	// Not own constructor property must be Object
+	if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
+		return false;
+	}
+
+	// Own properties are enumerated firstly, so to speed up,
+	// if last one is own, then all properties are own.
+	var key;
+	for (key in obj) { /**/ }
+
+	return typeof key === 'undefined' || hasOwn.call(obj, key);
+};
+
+// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target
+var setProperty = function setProperty(target, options) {
+	if (defineProperty && options.name === '__proto__') {
+		defineProperty(target, options.name, {
+			enumerable: true,
+			configurable: true,
+			value: options.newValue,
+			writable: true
+		});
+	} else {
+		target[options.name] = options.newValue;
+	}
+};
+
+// Return undefined instead of __proto__ if '__proto__' is not an own property
+var getProperty = function getProperty(obj, name) {
+	if (name === '__proto__') {
+		if (!hasOwn.call(obj, name)) {
+			return void 0;
+		} else if (gOPD) {
+			// In early versions of node, obj['__proto__'] is buggy when obj has
+			// __proto__ as an own property. Object.getOwnPropertyDescriptor() works.
+			return gOPD(obj, name).value;
+		}
+	}
+
+	return obj[name];
+};
+
+module.exports = function extend() {
+	var options, name, src, copy, copyIsArray, clone;
+	var target = arguments[0];
+	var i = 1;
+	var length = arguments.length;
+	var deep = false;
+
+	// Handle a deep copy situation
+	if (typeof target === 'boolean') {
+		deep = target;
+		target = arguments[1] || {};
+		// skip the boolean and the target
+		i = 2;
+	}
+	if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
+		target = {};
+	}
+
+	for (; i < length; ++i) {
+		options = arguments[i];
+		// Only deal with non-null/undefined values
+		if (options != null) {
+			// Extend the base object
+			for (name in options) {
+				src = getProperty(target, name);
+				copy = getProperty(options, name);
+
+				// Prevent never-ending loop
+				if (target !== copy) {
+					// Recurse if we're merging plain objects or arrays
+					if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
+						if (copyIsArray) {
+							copyIsArray = false;
+							clone = src && isArray(src) ? src : [];
+						} else {
+							clone = src && isPlainObject(src) ? src : {};
+						}
+
+						// Never move original objects, clone them
+						setProperty(target, { name: name, newValue: extend(deep, clone, copy) });
+
+					// Don't bring in undefined values
+					} else if (typeof copy !== 'undefined') {
+						setProperty(target, { name: name, newValue: copy });
+					}
+				}
+			}
+		}
+	}
+
+	// Return the modified object
+	return target;
+};

+ 42 - 0
node_modules/extend/package.json

@@ -0,0 +1,42 @@
+{
+	"name": "extend",
+	"author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)",
+	"version": "3.0.2",
+	"description": "Port of jQuery.extend for node.js and the browser",
+	"main": "index",
+	"scripts": {
+		"pretest": "npm run lint",
+		"test": "npm run tests-only",
+		"posttest": "npm run coverage-quiet",
+		"tests-only": "node test",
+		"coverage": "covert test/index.js",
+		"coverage-quiet": "covert test/index.js --quiet",
+		"lint": "npm run jscs && npm run eslint",
+		"jscs": "jscs *.js */*.js",
+		"eslint": "eslint *.js */*.js"
+	},
+	"contributors": [
+		{
+			"name": "Jordan Harband",
+			"url": "https://github.com/ljharb"
+		}
+	],
+	"keywords": [
+		"extend",
+		"clone",
+		"merge"
+	],
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/justmoon/node-extend.git"
+	},
+	"dependencies": {},
+	"devDependencies": {
+		"@ljharb/eslint-config": "^12.2.1",
+		"covert": "^1.1.0",
+		"eslint": "^4.19.1",
+		"jscs": "^3.0.7",
+		"tape": "^4.9.1"
+	},
+	"license": "MIT"
+}

+ 9 - 0
node_modules/weapp-qrcode/.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 2 - 0
node_modules/weapp-qrcode/.eslintignore

@@ -0,0 +1,2 @@
+build/*.js
+src/qrcode.js

+ 26 - 0
node_modules/weapp-qrcode/.eslintrc.js

@@ -0,0 +1,26 @@
+module.exports = {
+  root: true,
+  parser: 'babel-eslint',
+  parserOptions: {
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+  },
+  // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
+  extends: 'standard',
+  "globals": {
+    __VERSION__: false,
+    ENV: false,
+    wx: false
+  },
+  // add your custom rules here
+  'rules': {
+    // allow paren-less arrow functions
+    'arrow-parens': 0,
+    // allow async-await
+    'generator-star-spacing': 0,
+    // allow debugger during development
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
+  }
+}

+ 37 - 0
node_modules/weapp-qrcode/.github/ISSUE_TEMPLATE.md

@@ -0,0 +1,37 @@
+# Description
+
+[问题简单描述]
+
+# Environment
+
+* Platform: [开发者工具/iOS/Andriod/Web]
+* Platform version: [对应工具或者iOS或者Andriod的版本号]
+* Wechat version: [微信版本号]
+* weapp-qrcode version: [在package.json里]
+* other version: [如果在某一设备下出现该问题,请填写设备号]
+
+# Reproduce
+
+**问题复现步骤:**
+
+1. [第一步]
+2. [第二步]
+3. [其他步骤...]
+
+**期望的表现:**
+
+[在这里描述期望的表现]
+
+**观察到的表现:**
+
+[在这里描述观察到的表现]
+
+**屏幕截图和动态 GIF 图**
+
+![复现步骤的屏幕截图和动态 GIF 图](图片的 url)
+
+# Relevant Code / Logs
+
+```
+// TODO(you): code or logs here to reproduce the problem
+```

+ 23 - 0
node_modules/weapp-qrcode/.travis.yml

@@ -0,0 +1,23 @@
+language: node_js
+sudo: required
+node_js:
+  - 10.0.0
+cache:
+  directories:
+    - node_modules
+before_install:
+  - export TZ='Asia/Shanghai'
+install:
+  - npm install
+script:
+  - npm run publish
+
+after_script:
+  - git init
+  - git config user.name "${USER_NAME}"
+  - git config user.email "${USER_EMAIL}"
+  - git add .
+  - git commit -m "publish"
+  - git push -f https://${access_token}@github.com/yingye/weapp-qrcode HEAD:master
+
+branch: master

+ 40 - 0
node_modules/weapp-qrcode/CHANGELOG.md

@@ -0,0 +1,40 @@
+# Change Log
+
+Change log for weapp-qrcode. [Details at Github](https://github.com/yingye/weapp-qrcode)
+
+## [1.0.0] - 2018-12-25
+
+- 支持传入绘图上下文(CanvasContext).
+
+## [1.0.0-beta] - 2018-12-13
+
+- 支持二维码在 canvas 上绘制的起始位置.
+- 支持在二维码上绘制图片及绘制位置.
+
+## [0.9.0] - 2018-05-31
+
+- 支持绘制带中文的二维码.
+
+## [0.8.0] - 2018-05-15
+
+- 绘制二维码后添加回调函数.
+
+## [0.7.0] - 2018-05-11
+
+- 支持在小程序组件中绘制二维码.
+
+## [0.6.0] - 2018-04-16
+
+- Add multi-output.
+
+## [0.5.0] - 2018-03-11
+
+- Add version in weapp.qrcode.js.
+
+## [0.4.0] - 2018-03-10
+
+- Fix options.
+
+## [0.3.0] - 2018-02-03
+
+- Initial release.

+ 21 - 0
node_modules/weapp-qrcode/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 yingye
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 96 - 0
node_modules/weapp-qrcode/README.md

@@ -0,0 +1,96 @@
+# weapp-qrcode
+
+[![npm version](https://badge.fury.io/js/weapp-qrcode.svg)](https://badge.fury.io/js/weapp-qrcode)
+[![change-log](https://img.shields.io/badge/changelog-md-blue.svg)](https://github.com/yingye/weapp-qrcode/blob/master/CHANGELOG.md)
+
+weapp.qrcode.js 在 微信小程序 中,快速生成二维码
+
+## Usage
+
+先在 wxml 文件中,创建绘制的 `canvas`,并定义好 `width`, `height`, `canvasId` 。
+
+```html
+<canvas style="width: 200px; height: 200px;" canvas-id="myQrcode"></canvas>
+```
+
+直接引入 js 文件,使用 `drawQrcode()` 绘制二维码。!!!在 调用 `drawQrcode()` 方法之前,一定要确保可以获取到 `canvas context` 。
+
+在 v0.6.0 版本构建出多个文件,详情移步[Build Files说明](https://github.com/yingye/weapp-qrcode/blob/master/dist/README.md)。
+
+```js
+// 将 dist 目录下,weapp.qrcode.esm.js 复制到项目目录中
+import drawQrcode from '../../utils/weapp.qrcode.esm.js'
+
+drawQrcode({
+  width: 200,
+  height: 200,
+  canvasId: 'myQrcode',
+  // ctx: wx.createCanvasContext('myQrcode'),
+  text: 'https://github.com/yingye',
+  // v1.0.0+版本支持在二维码上绘制图片
+  image: {
+    imageResource: '../../images/icon.png',
+    dx: 70,
+    dy: 70,
+    dWidth: 60,
+    dHeight: 60
+  }
+})
+```
+
+如果项目使用了 wepy 框架,可直接安装 `weapp-qrcode` npm包。
+
+```
+npm install weapp-qrcode --save
+```
+
+```js
+import drawQrcode from 'weapp-qrcode'
+
+drawQrcode({
+  width: 200,
+  height: 200,
+  canvasId: 'myQrcode',
+  text: 'https://github.com/yingye'
+})
+```
+
+## DEMO
+
+<img src="./examples/demo.jpg" width=300 >
+
+更多 demo 可以参考 [examples目录](https://github.com/yingye/weapp-qrcode/tree/master/examples),示例包含原生语法及WePY、mpvue、Taro框架。
+
+## API
+
+### drawQrcode([options])
+
+#### options
+
+Type: Object
+
+| 参数 | 说明 | 示例|
+| ------ | ------ | ------ |
+| width | 必须,二维码宽度,与`canvas`的`width`保持一致 | 200 |
+| height | 必须,二维码高度,与`canvas`的`height`保持一致 | 200 |
+| canvasId | 非必须,绘制的`canvasId` | `'myQrcode'` |
+| ctx | 非必须,绘图上下文,可通过 `wx.createCanvasContext('canvasId')` 获取,v1.0.0+版本支持 | `'wx.createCanvasContext('canvasId')'` |
+| text | 必须,二维码内容 | 'https://github.com/yingye' |
+| typeNumber | 非必须,二维码的计算模式,默认值-1 | 8 |
+| correctLevel | 非必须,二维码纠错级别,默认值为高级,取值:`{ L: 1, M: 0, Q: 3, H: 2 }` | 1 |
+| background | 非必须,二维码背景颜色,默认值白色 | `'#ffffff'` |
+| foreground | 非必须,二维码前景色,默认值黑色 | `'#000000'` |
+| _this | 非必须,若在组件中使用,需要传入,v0.7.0+版本支持 | this |
+| callback | 非必须,绘制完成后的回调函数,v0.8.0+版本支持。安卓手机兼容性问题,可通过自行设置计时器来解决,更多可以参考 [issue #18](https://github.com/yingye/weapp-qrcode/issues/18) | `function (e) { console.log('e', e) }` |
+| x | 非必须,二维码绘制的 x 轴起始位置,默认值0,v1.0.0+版本支持 | 100 |
+| y | 非必须,二维码绘制的 y 轴起始位置,默认值0,v1.0.0+版本支持 | 100 |
+| image | 非必须,在 canvas 上绘制图片,**层级高于二维码**,v1.0.0+版本支持,更多可参考[drawImage](https://developers.weixin.qq.com/miniprogram/dev/api/CanvasContext.drawImage.html) | `{ imageResource: '', dx: 0, dy: 0, dWidth: 100, dHeight: 100 }` |
+
+
+**位置信息可以参见下图:**
+
+<image src="./examples/api.png" width=500 height=500>
+
+## TIPS
+
+weapp.qrcode.js 二维码生成部分借鉴了 jquery-qrcode 源码,可以参考 [jquery-qrcode](https://github.com/jeromeetienne/jquery-qrcode)。

+ 39 - 0
node_modules/weapp-qrcode/build/rollup.dev.config.js

@@ -0,0 +1,39 @@
+var babel = require('rollup-plugin-babel')
+var resolve = require('rollup-plugin-node-resolve')
+var commonjs = require('rollup-plugin-commonjs')
+var eslint = require('rollup-plugin-eslint')
+var license = require('rollup-plugin-license')
+
+var path = require('path')
+
+var pkg = require('../package.json')
+
+module.exports = {
+  input: path.resolve(__dirname, '../src/index.js'),
+  output: [
+    {
+      file: path.resolve(__dirname, '../dist/weapp.qrcode.js'),
+      format: 'umd'
+    },
+    {
+      file: path.resolve(__dirname, '../examples/wechat-app/utils/weapp.qrcode.js'),
+      format: 'umd'
+    }
+  ],
+  moduleName: 'drawQrcode',
+  plugins: [
+    eslint(),
+    resolve({
+      jsnext: true,
+      main: true,
+      browser: true
+    }),
+    commonjs(),
+    babel({
+      exclude: 'node_modules/**'
+    }),
+    license({
+      banner: 'weapp.qrcode.js v' + pkg.version + ' (' + pkg.homepage + ')'
+    })
+  ]
+}

+ 49 - 0
node_modules/weapp-qrcode/build/rollup.prod.config.js

@@ -0,0 +1,49 @@
+var babel = require('rollup-plugin-babel')
+var babel = require('rollup-plugin-babel')
+var resolve = require('rollup-plugin-node-resolve')
+var commonjs = require('rollup-plugin-commonjs')
+var eslint = require('rollup-plugin-eslint')
+var license = require('rollup-plugin-license')
+var uglify = require('rollup-plugin-uglify')
+
+var path = require('path')
+var pkg = require('../package.json')
+
+module.exports = {
+  input: path.resolve(__dirname, '../src/index.js'),
+  output: [
+    {
+      file: path.resolve(__dirname, '../dist/weapp.qrcode.min.js'),
+      format: 'umd'
+    },
+    {
+      file: path.resolve(__dirname, '../dist/weapp.qrcode.common.js'),
+      format: 'cjs'
+    },
+    {
+      file: path.resolve(__dirname, '../dist/weapp.qrcode.esm.js'),
+      format: 'es'
+    }
+  ],
+  moduleName: 'drawQrcode',
+  plugins: [
+    eslint(),
+    resolve({
+      jsnext: true,
+      main: true,
+      browser: true
+    }),
+    commonjs(),
+    babel({
+      exclude: 'node_modules/**'
+    }),
+    uglify({
+      compress: {
+        // 'drop_console': true
+      }  
+    }),
+    license({
+      banner: 'weapp.qrcode.js v' + pkg.version + ' (' + pkg.homepage + ')'
+    })
+  ]
+}

+ 6 - 0
node_modules/weapp-qrcode/dist/README.md

@@ -0,0 +1,6 @@
+## Explanation of Build Files
+
+| | UMD | CommonJS | ES Module |
+| --- | --- | --- | --- |
+| **Develpment** | weapp.qrcode.js | weapp.qrcode.common.js | weapp.qrcode.esm.js |
+| **Production** | weapp.qrcode.min.js | | |

File diff suppressed because it is too large
+ 5 - 0
node_modules/weapp-qrcode/dist/weapp.qrcode.common.js


File diff suppressed because it is too large
+ 5 - 0
node_modules/weapp-qrcode/dist/weapp.qrcode.esm.js


File diff suppressed because it is too large
+ 1281 - 0
node_modules/weapp-qrcode/dist/weapp.qrcode.js


File diff suppressed because it is too large
+ 5 - 0
node_modules/weapp-qrcode/dist/weapp.qrcode.min.js


+ 50 - 0
node_modules/weapp-qrcode/package.json

@@ -0,0 +1,50 @@
+{
+  "name": "weapp-qrcode",
+  "version": "1.0.0",
+  "description": "generate qrcode in weapp",
+  "main": "dist/weapp.qrcode.common.js",
+  "module": "dist/weapp.qrcode.esm.js",
+  "scripts": {
+    "dev": "rollup --config build/rollup.dev.config.js -w",
+    "build": "rollup --config build/rollup.prod.config.js",
+    "publish": "rollup --config build/rollup.dev.config.js & npm run build",
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/yingye/weapp-qrcode.git"
+  },
+  "keywords": [
+    "wechat",
+    "weapp",
+    "qrcode",
+    "canvas"
+  ],
+  "author": "yingye",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/yingye/weapp-qrcode/issues"
+  },
+  "homepage": "https://github.com/yingye/weapp-qrcode#readme",
+  "devDependencies": {
+    "babel-core": "^6.26.0",
+    "babel-eslint": "^8.2.1",
+    "eslint": "^4.16.0",
+    "eslint-config-standard": "^11.0.0-beta.0",
+    "eslint-plugin-import": "^2.8.0",
+    "eslint-plugin-node": "^5.2.1",
+    "eslint-plugin-promise": "^3.6.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "rollup": "^0.55.1",
+    "rollup-plugin-babel": "^3.0.3",
+    "rollup-plugin-commonjs": "^8.3.0",
+    "rollup-plugin-eslint": "^4.0.0",
+    "rollup-plugin-license": "^0.6.0",
+    "rollup-plugin-node-resolve": "^3.0.2",
+    "rollup-plugin-replace": "^2.0.0",
+    "rollup-plugin-uglify": "^3.0.0"
+  },
+  "dependencies": {
+    "extend": "^3.0.2"
+  }
+}

+ 94 - 0
node_modules/weapp-qrcode/src/index.js

@@ -0,0 +1,94 @@
+import extend from 'extend'
+import {
+  QRCode,
+  QRErrorCorrectLevel
+} from './qrcode'
+
+// support Chinese
+function utf16to8 (str) {
+  var out, i, len, c
+  out = ''
+  len = str.length
+  for (i = 0; i < len; i++) {
+    c = str.charCodeAt(i)
+    if ((c >= 0x0001) && (c <= 0x007F)) {
+      out += str.charAt(i)
+    } else if (c > 0x07FF) {
+      out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F))
+      out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F))
+      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))
+    } else {
+      out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F))
+      out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))
+    }
+  }
+  return out
+}
+
+function drawQrcode (options) {
+  options = options || {}
+  options = extend(true, {
+    width: 256,
+    height: 256,
+    x: 0,
+    y: 0,
+    typeNumber: -1,
+    correctLevel: QRErrorCorrectLevel.H,
+    background: '#ffffff',
+    foreground: '#000000',
+    image: {
+      imageResource: '',
+      dx: 0,
+      dy: 0,
+      dWidth: 100,
+      dHeight: 100
+    }
+  }, options)
+
+  if (!options.canvasId && !options.ctx) {
+    console.warn('please set canvasId or ctx!')
+    return
+  }
+
+  createCanvas()
+
+  function createCanvas () {
+    // create the qrcode itself
+    var qrcode = new QRCode(options.typeNumber, options.correctLevel)
+    qrcode.addData(utf16to8(options.text))
+    qrcode.make()
+
+    // get canvas context
+    var ctx
+    if (options.ctx) {
+      ctx = options.ctx
+    } else {
+      ctx = options._this ? wx.createCanvasContext && wx.createCanvasContext(options.canvasId, options._this) : wx.createCanvasContext && wx.createCanvasContext(options.canvasId)
+    }
+
+    // compute tileW/tileH based on options.width/options.height
+    var tileW = options.width / qrcode.getModuleCount()
+    var tileH = options.height / qrcode.getModuleCount()
+
+    // draw in the canvas
+    for (var row = 0; row < qrcode.getModuleCount(); row++) {
+      for (var col = 0; col < qrcode.getModuleCount(); col++) {
+        var style = qrcode.isDark(row, col) ? options.foreground : options.background
+        ctx.setFillStyle(style)
+        var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW))
+        var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW))
+        ctx.fillRect(Math.round(col * tileW) + options.x, Math.round(row * tileH) + options.y, w, h)
+      }
+    }
+
+    if (options.image.imageResource) {
+      ctx.drawImage(options.image.imageResource, options.image.dx, options.image.dy, options.image.dWidth, options.image.dHeight)
+    }
+
+    ctx.draw(false, function (e) {
+      options.callback && options.callback(e)
+    })
+  }
+}
+
+export default drawQrcode

File diff suppressed because it is too large
+ 1242 - 0
node_modules/weapp-qrcode/src/qrcode.js


+ 9 - 0
node_modules/widget-ui/babel.config.js

@@ -0,0 +1,9 @@
+module.exports = {
+  presets: [
+    ["@babel/preset-env", {
+      targets: {
+        node: "current"
+      }
+    }]
+  ]
+};

+ 40 - 0
node_modules/widget-ui/dist/element.d.ts

@@ -0,0 +1,40 @@
+declare type LayoutData = {
+    left: number;
+    top: number;
+    width: number;
+    height: number;
+};
+declare type LayoutNode = {
+    id: number;
+    style: Object;
+    children: LayoutNode[];
+    layout?: LayoutData;
+};
+declare class Element {
+    static uuid(): number;
+    parent: Element | null;
+    id: number;
+    style: {
+        [key: string]: any;
+    };
+    computedStyle: {
+        [key: string]: any;
+    };
+    lastComputedStyle: {
+        [key: string]: any;
+    };
+    children: {
+        [key: string]: Element;
+    };
+    layoutBox: LayoutData;
+    constructor(style?: {
+        [key: string]: any;
+    });
+    getAbsolutePosition(element: Element): any;
+    add(element: Element): void;
+    remove(element?: Element): void;
+    getNodeTree(): LayoutNode;
+    applyLayout(layoutNode: LayoutNode): void;
+    layout(): void;
+}
+export default Element;

+ 5 - 0
node_modules/widget-ui/dist/event.d.ts

@@ -0,0 +1,5 @@
+export default class EventEmitter {
+    emit(event: string, data?: any): void;
+    on(event: string, callback: any): void;
+    off(event: string, callback: any): void;
+}

File diff suppressed because it is too large
+ 1 - 0
node_modules/widget-ui/dist/index.js


+ 36 - 0
node_modules/widget-ui/dist/style.d.ts

@@ -0,0 +1,36 @@
+declare const textStyles: string[];
+declare const scalableStyles: string[];
+declare const layoutAffectedStyles: string[];
+declare const getDefaultStyle: () => {
+    left: undefined;
+    top: undefined;
+    right: undefined;
+    bottom: undefined;
+    width: undefined;
+    height: undefined;
+    maxWidth: undefined;
+    maxHeight: undefined;
+    minWidth: undefined;
+    minHeight: undefined;
+    margin: undefined;
+    marginLeft: undefined;
+    marginRight: undefined;
+    marginTop: undefined;
+    marginBottom: undefined;
+    padding: undefined;
+    paddingLeft: undefined;
+    paddingRight: undefined;
+    paddingTop: undefined;
+    paddingBottom: undefined;
+    borderWidth: undefined;
+    flexDirection: undefined;
+    justifyContent: undefined;
+    alignItems: undefined;
+    alignSelf: undefined;
+    flex: undefined;
+    flexWrap: undefined;
+    position: undefined;
+    hidden: boolean;
+    scale: number;
+};
+export { getDefaultStyle, scalableStyles, textStyles, layoutAffectedStyles };

+ 6 - 0
node_modules/widget-ui/jest.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+  transform: {
+    "^.+\\.js$": "babel-jest",
+    "^.+\\.ts$": "ts-jest"
+  }
+};

+ 27 - 0
node_modules/widget-ui/package.json

@@ -0,0 +1,27 @@
+{
+  "name": "widget-ui",
+  "version": "1.0.2",
+  "description": "",
+  "main": "dist/index.js",
+  "scripts": {
+    "test": "jest",
+    "build": "webpack"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "eventemitter3": "^4.0.0"
+  },
+  "devDependencies": {
+    "@babel/preset-env": "^7.6.3",
+    "@babel/preset-typescript": "^7.6.0",
+    "@types/jest": "^24.0.18",
+    "babel-jest": "^24.9.0",
+    "jest": "^24.9.0",
+    "ts-jest": "^24.1.0",
+    "ts-loader": "^6.2.0",
+    "typescript": "^3.6.4",
+    "webpack": "^4.41.1",
+    "webpack-cli": "^3.3.9"
+  }
+}

File diff suppressed because it is too large
+ 1186 - 0
node_modules/widget-ui/src/css-layout.js


+ 172 - 0
node_modules/widget-ui/src/element.ts

@@ -0,0 +1,172 @@
+
+import computeLayout from "./css-layout";
+import { getDefaultStyle, scalableStyles, layoutAffectedStyles } from "./style";
+
+type LayoutData = {
+  left: number,
+  top: number,
+  width: number,
+  height: number
+};
+
+type LayoutNode = {
+  id: number,
+  style: Object,
+  children: LayoutNode[],
+  layout?: LayoutData
+};
+
+let uuid = 0;
+
+class Element {
+  public static uuid(): number {
+    return uuid++;
+  }
+
+  public parent: Element | null = null;
+  public id: number = Element.uuid();
+  public style: { [key: string]: any } = {};
+  public computedStyle: { [key: string]: any } = {};
+  public lastComputedStyle: { [key: string]: any } = {};
+  public children: { [key: string]: Element } = {};
+  public layoutBox: LayoutData = { left: 0, top: 0, width: 0, height: 0 };
+
+  constructor(style: { [key: string]: any } = {}) {
+    // 拷贝一份,防止被外部逻辑修改
+    style = Object.assign(getDefaultStyle(), style);
+    this.computedStyle = Object.assign(getDefaultStyle(), style);
+    this.lastComputedStyle = Object.assign(getDefaultStyle(), style);
+
+    Object.keys(style).forEach(key => {
+      Object.defineProperty(this.style, key, {
+        configurable: true,
+        enumerable: true,
+        get: () => style[key],
+        set: (value: any) => {
+          if (value === style[key] || value === undefined) {
+            return;
+          }
+
+          this.lastComputedStyle = this.computedStyle[key]
+          style[key] = value
+          this.computedStyle[key] = value
+
+          // 如果设置的是一个可缩放的属性, 计算自己
+          if (scalableStyles.includes(key) && this.style.scale) {
+            this.computedStyle[key] = value * this.style.scale
+          }
+
+          // 如果设置的是 scale, 则把所有可缩放的属性计算
+          if (key === "scale") {
+            scalableStyles.forEach(prop => {
+              if (style[prop]) {
+                this.computedStyle[prop] = style[prop] * value
+              }
+            })
+          }
+
+          if (key === "hidden") {
+            if (value) {
+              layoutAffectedStyles.forEach((key: string) => {
+                this.computedStyle[key] = 0;
+              });
+            } else {
+              layoutAffectedStyles.forEach((key: string) => {
+                this.computedStyle[key] = this.lastComputedStyle[key];
+              });
+            }
+          }
+        }
+      })
+    })
+
+    if (this.style.scale) {
+      scalableStyles.forEach((key: string) => {
+        if (this.style[key]) {
+          const computedValue = this.style[key] * this.style.scale;
+          this.computedStyle[key] = computedValue;
+        }
+      });
+    }
+
+    if (style.hidden) {
+      layoutAffectedStyles.forEach((key: string) => {
+        this.computedStyle[key] = 0;
+      });
+    }
+  }
+
+  getAbsolutePosition(element: Element) {
+    if (!element) {
+      return this.getAbsolutePosition(this)
+    }
+
+    if (!element.parent) {
+      return {
+        left: 0,
+        top: 0
+      }
+    }
+
+    const {left, top} = this.getAbsolutePosition(element.parent)
+
+    return {
+      left: left + element.layoutBox.left,
+      top: top + element.layoutBox.top
+    }
+  }
+
+  public add(element: Element) {
+    element.parent = this;
+    this.children[element.id] = element;
+  }
+
+  public remove(element?: Element) {
+    // 删除自己
+    if (!element) {
+      Object.keys(this.children).forEach(id => {
+        const child = this.children[id]
+        child.remove()
+        delete this.children[id]
+      })
+    } else if (this.children[element.id]) {
+      // 是自己的子节点才删除
+      element.remove()
+      delete this.children[element.id];
+    }
+  }
+
+  public getNodeTree(): LayoutNode {
+    return {
+      id: this.id,
+      style: this.computedStyle,
+      children: Object.keys(this.children).map((id: string) => {
+        const child = this.children[id];
+        return child.getNodeTree();
+      })
+    }
+  }
+
+  public applyLayout(layoutNode: LayoutNode) {
+    ["left", "top", "width", "height"].forEach((key: string) => {
+      if (layoutNode.layout && typeof layoutNode.layout[key] === "number") {
+        this.layoutBox[key] = layoutNode.layout[key];
+        if (this.parent && (key === "left" || key === "top")) {
+          this.layoutBox[key] += this.parent.layoutBox[key];
+        }
+      }
+    });
+
+    layoutNode.children.forEach((child: LayoutNode) => {
+      this.children[child.id].applyLayout(child);
+    });
+  }
+
+  layout() {
+    const nodeTree = this.getNodeTree();
+    computeLayout(nodeTree);
+    this.applyLayout(nodeTree);
+  }
+}
+
+export default Element;

+ 15 - 0
node_modules/widget-ui/src/event.ts

@@ -0,0 +1,15 @@
+import _EventEmitter from "eventemitter3";
+const emitter = new _EventEmitter();
+export default class EventEmitter {
+  public emit(event: string, data?: any) {
+    emitter.emit(event, data);
+  }
+  
+  public on(event: string, callback) {
+    emitter.on(event, callback);
+  }
+
+  public off(event: string, callback) {
+    emitter.off(event, callback);
+  }
+}

+ 87 - 0
node_modules/widget-ui/src/style.ts

@@ -0,0 +1,87 @@
+const textStyles: string[] = ["color", "fontSize", "textAlign", "fontWeight", "lineHeight", "lineBreak"];
+
+const scalableStyles: string[] = ["left", "top", "right", "bottom", "width", "height",
+  "margin", "marginLeft", "marginRight", "marginTop", "marginBottom",
+  "padding", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
+  "borderWidth", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth"];
+
+const layoutAffectedStyles: string[] = [
+  "margin", "marginTop", "marginBottom", "marginLeft", "marginRight",
+  "padding", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight",
+  "width", "height"];
+
+type Style = {
+  left: number,
+  top: number,
+  right: number,
+  bottom: number,
+  width: number,
+  height: number,
+  maxWidth: number,
+  maxHeight: number,
+  minWidth: number,
+  minHeight: number,
+  margin: number,
+  marginLeft: number,
+  marginRight: number,
+  marginTop: number,
+  marginBottom: number,
+  padding: number,
+  paddingLeft: number,
+  paddingRight: number,
+  paddingTop: number,
+  paddingBottom: number,
+  borderWidth: number,
+  borderLeftWidth: number,
+  borderRightWidth: number,
+  borderTopWidth: number,
+  borderBottomWidth: number,
+  flexDirection: "column" | "row",
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around",
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch",
+  alignSelf: "flex-start" | "center" | "flex-end" | "stretch",
+  flex: number,
+  flexWrap: "wrap" | "nowrap",
+  position: "relative" | "absolute",
+
+  hidden: boolean,
+  scale: number
+}
+
+const getDefaultStyle = () => ({
+  left: undefined,
+  top: undefined,
+  right: undefined,
+  bottom: undefined,
+  width: undefined,
+  height: undefined,
+  maxWidth: undefined,
+  maxHeight: undefined,
+  minWidth: undefined,
+  minHeight: undefined,
+  margin: undefined,
+  marginLeft: undefined,
+  marginRight: undefined,
+  marginTop: undefined,
+  marginBottom: undefined,
+  padding: undefined,
+  paddingLeft: undefined,
+  paddingRight: undefined,
+  paddingTop: undefined,
+  paddingBottom: undefined,
+  borderWidth: undefined,
+  flexDirection: undefined,
+  justifyContent: undefined,
+  alignItems: undefined,
+  alignSelf: undefined,
+  flex: undefined,
+  flexWrap: undefined,
+  position: undefined,
+
+  hidden: false,
+  scale: 1
+})
+
+export {
+  getDefaultStyle, scalableStyles, textStyles, layoutAffectedStyles
+}

+ 183 - 0
node_modules/widget-ui/test/css-layout.test.ts

@@ -0,0 +1,183 @@
+
+import Element from "../src/element";
+
+test("layout", () => {
+  const container = new Element({
+    width: 100,
+    height: 100,
+    padding: 10,
+    borderWidth: 2
+  })
+
+  const div1 = new Element({
+    left: 5,
+    top: 5,
+    width: 14,
+    height: 14
+  })
+
+  container.add(div1);
+  container.layout();
+  // css-layout 是 border-box
+  expect(container.layoutBox.left).toBe(0);
+  expect(container.layoutBox.top).toBe(0);
+  expect(container.layoutBox.width).toBe(100);
+  expect(container.layoutBox.height).toBe(100);
+
+  expect(div1.layoutBox.left).toBe(10 + 2 + 5);
+  expect(div1.layoutBox.top).toBe(10 + 2 + 5);
+  expect(div1.layoutBox.width).toBe(14);
+  expect(div1.layoutBox.height).toBe(14);
+});
+
+test("overflow", () => {
+  const container = new Element({
+    width: 100,
+    height: 100,
+    padding: 10,
+    borderWidth: 2
+  })
+
+  const div1 = new Element({
+    width: 114,
+    height: 114,
+  })
+
+  container.add(div1);
+  container.layout();
+
+  // 写死尺寸的情况下子元素不收缩父元素不撑开
+  expect(container.layoutBox.width).toBe(100);
+  expect(container.layoutBox.height).toBe(100);
+
+  expect(div1.layoutBox.left).toBe(10 + 2);
+  expect(div1.layoutBox.top).toBe(10 + 2);
+  expect(div1.layoutBox.width).toBe(114);
+  expect(div1.layoutBox.height).toBe(114);
+});
+
+test("right bottom", () => {
+  const container = new Element({
+    width: 100,
+    height: 100,
+    padding: 10,
+    borderWidth: 2
+  })
+
+  const div1 = new Element({
+    width: 14,
+    height: 14,
+    right: 13,
+    bottom: 9,
+    position: "absolute"
+  })
+
+  container.add(div1);
+  container.layout();
+
+  // right bottom 只有在 position 为 absolute 的情况下才有用
+  expect(container.layoutBox.width).toBe(100);
+  expect(container.layoutBox.height).toBe(100);
+
+  // 但这时就是以整个父元素为边界,而不是 border + padding 后的边界
+  expect(div1.layoutBox.left).toBe(100 - 13 - 14);
+  expect(div1.layoutBox.top).toBe(100 - 9 - 14);
+});
+
+test("flex center", () => {
+  const container = new Element({
+    width: 100,
+    height: 100,
+    padding: 10,
+    borderWidth: 2,
+    flexDirection: "row",
+    justifyContent: "center",
+    alignItems: "center"
+  })
+
+  const div1 = new Element({
+    width: 14,
+    height: 14
+  })
+
+  container.add(div1);
+  container.layout();
+  // 使用 flex 水平垂直居中
+  expect(div1.layoutBox.left).toBe((100 - 14)/2);
+  expect(div1.layoutBox.top).toBe((100 - 14)/2);
+})
+
+test("flex top bottom", () => {
+  const container = new Element({
+    width: 100,
+    height: 100,
+    padding: 10,
+    borderWidth: 2,
+    flexDirection: "column",
+    justifyContent: "space-between",
+    alignItems: "stretch"
+  })
+
+  // flex 实现一上一下两行水平填满
+  const div1 = new Element({
+    height: 10
+  })
+
+  const div2 = new Element({
+    height: 20
+  })
+
+  container.add(div1);
+  container.add(div2);
+  container.layout();
+
+  expect(div1.layoutBox.left).toBe(10 + 2);
+  expect(div1.layoutBox.top).toBe(10 + 2);
+  expect(div1.layoutBox.width).toBe(100 - 10*2 - 2*2);
+
+  expect(div2.layoutBox.left).toBe(10 + 2);
+  expect(div2.layoutBox.top).toBe(100 - 10 - 2 - 20);
+  expect(div2.layoutBox.width).toBe(100 - 10*2 - 2*2);
+})
+
+test("rewrite uuid", () => {
+  // 小程序为了保证 webview 和 service 侧的 coverview 不冲突,所以设置了不同的自增起点
+  // uuid 静态方法就是为了根据不同的需求去覆写
+  let uuid = 79648527;
+  Element.uuid = () =>  uuid++;
+  const container = new Element();
+  expect(container.id).toEqual(79648527);
+  const div = new Element();
+  expect(div.id).toEqual(79648528);
+});
+
+test("absolute left top", () => {
+  const container = new Element({
+    width: 300,
+    height: 200,
+    flexDirection: 'row',
+    justifyContent: 'center',
+    alignItems: 'center'
+  })
+  
+
+  const div1 = new Element({
+    width: 80,
+    height: 60
+  })
+  
+  const div2 = new Element({
+    width: 40,
+    height: 30
+  })
+
+  div1.add(div2)
+  container.add(div1)
+  container.layout()
+
+  expect(div1.layoutBox.left).toBe(110)
+  expect(div1.layoutBox.top).toBe(70)
+
+  expect(div2.layoutBox.left).toBe(110)
+  expect(div2.layoutBox.top).toBe(70)
+})

+ 47 - 0
node_modules/widget-ui/tsconfig.json

@@ -0,0 +1,47 @@
+{
+  "compilerOptions": {
+    "baseUrl": "src",
+    "resolveJsonModule": true,
+    "downlevelIteration": false,
+    "target": "es5",
+    "module": "commonjs",
+    "lib": [
+      "es5",
+      "es2015.promise",
+      "es2016",
+      "dom"
+    ],
+    "outDir": "./dist",
+    "paths": {
+      "@/*": [
+        "*"
+      ],
+      "*": [
+        "*"
+      ]
+    },
+    "typeRoots": [
+      "./node_modules/@types"
+    ],
+    "allowSyntheticDefaultImports": true,
+    "esModuleInterop": true,
+    "declaration": true,
+    "stripInternal": true,
+    "experimentalDecorators": true,
+    "noImplicitReturns": true,
+    "alwaysStrict": true,
+    "noFallthroughCasesInSwitch": true,
+    "removeComments": false,
+    "strictNullChecks": true,
+    "strictFunctionTypes": true,
+    "skipLibCheck": true,
+    "pretty": true,
+    "strictPropertyInitialization": true
+  },
+  "include": [
+    "src/**/*.ts"
+  ],
+  "exclude": [
+    "node_modules"
+  ]
+}

+ 206 - 0
node_modules/widget-ui/tslint.json

@@ -0,0 +1,206 @@
+{
+  "defaultSeverity": "error",
+  "extends": [],
+  "rules": {
+    "adjacent-overload-signatures": true,
+    "align": {
+      "options": [
+        "parameters",
+        "statements"
+      ]
+    },
+    "arrow-return-shorthand": true,
+    "ban-types": {
+      "options": [
+        [
+          "Object",
+          "Avoid using the `Object` type. Did you mean `object`?"
+        ],
+        [
+          "Function",
+          "Avoid using the `Function` type. Prefer a specific function type, like `() => void`."
+        ],
+        [
+          "Boolean",
+          "Avoid using the `Boolean` type. Did you mean `boolean`?"
+        ],
+        [
+          "Number",
+          "Avoid using the `Number` type. Did you mean `number`?"
+        ],
+        [
+          "String",
+          "Avoid using the `String` type. Did you mean `string`?"
+        ],
+        [
+          "Symbol",
+          "Avoid using the `Symbol` type. Did you mean `symbol`?"
+        ]
+      ]
+    },
+    "comment-format": {
+      "options": [
+        "check-space"
+      ]
+    },
+    "curly": {
+      "options": [
+        "ignore-same-line"
+      ]
+    },
+    "cyclomatic-complexity": false,
+    "import-spacing": true,
+    "indent": {
+      "options": [
+        "spaces"
+      ]
+    },
+    "interface-over-type-literal": true,
+    "member-ordering": [
+      true,
+      {
+        "order": [
+          "public-static-field",
+          "public-instance-field",
+          "private-static-field",
+          "private-instance-field",
+          "public-constructor",
+          "private-constructor",
+          "public-instance-method",
+          "protected-instance-method",
+          "private-instance-method"
+        ],
+        "alphabetize": false
+      }
+    ],
+    "no-angle-bracket-type-assertion": true,
+    "no-arg": true,
+    "no-conditional-assignment": true,
+    "no-debugger": true,
+    "no-duplicate-super": true,
+    "no-eval": true,
+    "no-internal-module": true,
+    "no-misused-new": true,
+    "no-reference-import": true,
+    "no-string-literal": true,
+    "no-string-throw": true,
+    "no-unnecessary-initializer": true,
+    "no-unsafe-finally": true,
+    "no-unused-expression": true,
+    "no-use-before-declare": false,
+    "no-var-keyword": true,
+    "no-var-requires": true,
+    "one-line": {
+      "options": [
+        "check-catch",
+        "check-else",
+        "check-finally",
+        "check-open-brace",
+        "check-whitespace"
+      ]
+    },
+    "one-variable-per-declaration": {
+      "options": [
+        "ignore-for-loop"
+      ]
+    },
+    "ordered-imports": {
+      "options": {
+        "import-sources-order": "case-insensitive",
+        "module-source-path": "full",
+        "named-imports-order": "case-insensitive"
+      }
+    },
+    "prefer-const": true,
+    "prefer-for-of": false,
+    "quotemark": {
+      "options": [
+        "double",
+        "avoid-escape"
+      ]
+    },
+    "radix": true,
+    "semicolon": {
+      "options": [
+        "always"
+      ]
+    },
+    "space-before-function-paren": {
+      "options": {
+        "anonymous": "never",
+        "asyncArrow": "always",
+        "constructor": "never",
+        "method": "never",
+        "named": "never"
+      }
+    },
+    "trailing-comma": {
+      "options": {
+        "esSpecCompliant": true,
+        "multiline": {
+          "objects": "always",
+          "arrays": "always",
+          "functions": "always",
+          "typeLiterals": "always"
+        },
+        "singleline": "never"
+      }
+    },
+    "triple-equals": {
+      "options": [
+        "allow-null-check"
+      ]
+    },
+    "typedef": false,
+    "typedef-whitespace": {
+      "options": [
+        {
+          "call-signature": "nospace",
+          "index-signature": "nospace",
+          "parameter": "nospace",
+          "property-declaration": "nospace",
+          "variable-declaration": "nospace"
+        },
+        {
+          "call-signature": "onespace",
+          "index-signature": "onespace",
+          "parameter": "onespace",
+          "property-declaration": "onespace",
+          "variable-declaration": "onespace"
+        }
+      ]
+    },
+    "typeof-compare": false,
+    "unified-signatures": true,
+    "use-isnan": true,
+    "whitespace": {
+      "options": [
+        "check-branch",
+        "check-decl",
+        "check-operator",
+        "check-separator",
+        "check-type",
+        "check-typecast"
+      ]
+    }
+  },
+  "jsRules": {},
+  "rulesDirectory": [],
+  "no-var-requires": false,
+  "trailing-comma": [
+    true,
+    {
+      "multiline": {
+        "objects": "always",
+        "arrays": "always",
+        "functions": "always",
+        "typeLiterals": "ignore"
+      },
+      "esSpecCompliant": true
+    }
+  ],
+  "no-unused-expression": [
+    true,
+    "allow-fast-null-checks"
+  ]
+}

+ 25 - 0
node_modules/widget-ui/webpack.config.js

@@ -0,0 +1,25 @@
+const path = require("path");
+
+module.exports = {
+  mode: "production",
+  entry: path.resolve(__dirname, "src/element.ts"),
+  module: {
+    rules: [
+      {
+        test: /\.ts$/,
+        use: "ts-loader",
+        exclude: /node_modules/
+      }
+    ]
+  },
+  resolve: {
+    extensions: [".js", ".ts"]
+  },
+  output: {
+    filename: "index.js",
+    path: path.resolve(__dirname, "dist"),
+    libraryTarget: "umd", // 采用通用模块定义
+    libraryExport: "default", // 兼容 ES6(ES2015) 的模块系统、CommonJS 和 AMD 模块规范
+    globalObject: "this" // 兼容node和浏览器运行,避免window is not undefined情况
+  }
+};

+ 10 - 0
node_modules/wxml-to-canvas/.babelrc

@@ -0,0 +1,10 @@
+{
+  "plugins": [
+      ["module-resolver", {
+          "root": ["./src"],
+          "alias": {}
+      }],
+      "@babel/transform-runtime"
+  ],
+  "presets": ["@babel/preset-env"]
+}

+ 99 - 0
node_modules/wxml-to-canvas/.eslintrc.js

@@ -0,0 +1,99 @@
+module.exports = {
+  'extends': [
+    'airbnb-base',
+    'plugin:promise/recommended'
+  ],
+  'parserOptions': {
+    'ecmaVersion': 9,
+    'ecmaFeatures': {
+      'jsx': false
+    },
+    'sourceType': 'module'
+  },
+  'env': {
+    'es6': true,
+    'node': true,
+    'jest': true
+  },
+  'plugins': [
+    'import',
+    'node',
+    'promise'
+  ],
+  'rules': {
+    'arrow-parens': 'off',
+    'comma-dangle': [
+      'error',
+      'only-multiline'
+    ],
+    'complexity': ['error', 10],
+    'func-names': 'off',
+    'global-require': 'off',
+    'handle-callback-err': [
+      'error',
+      '^(err|error)$'
+    ],
+    'import/no-unresolved': [
+      'error',
+      {
+        'caseSensitive': true,
+        'commonjs': true,
+        'ignore': ['^[^.]']
+      }
+    ],
+    'import/prefer-default-export': 'off',
+    'linebreak-style': 'off',
+    'no-catch-shadow': 'error',
+    'no-continue': 'off',
+    'no-div-regex': 'warn',
+    'no-else-return': 'off',
+    'no-param-reassign': 'off',
+    'no-plusplus': 'off',
+    'no-shadow': 'off',
+    'no-multi-assign': 'off',
+    'no-underscore-dangle': 'off',
+    'node/no-deprecated-api': 'error',
+    'node/process-exit-as-throw': 'error',
+    'object-curly-spacing': [
+      'error',
+      'never'
+    ],
+    'operator-linebreak': [
+      'error',
+      'after',
+      {
+        'overrides': {
+          ':': 'before',
+          '?': 'before'
+        }
+      }
+    ],
+    'prefer-arrow-callback': 'off',
+    'prefer-destructuring': 'off',
+    'prefer-template': 'off',
+    'quote-props': [
+      1,
+      'as-needed',
+      {
+        'unnecessary': true
+      }
+    ],
+    'semi': [
+      'error',
+      'never'
+    ],
+    'no-await-in-loop': 'off',
+    'no-restricted-syntax': 'off',
+    'promise/always-return': 'off',
+  },
+  'globals': {
+    'window': true,
+    'document': true,
+    'App': true,
+    'Page': true,
+    'Component': true,
+    'Behavior': true,
+    'wx': true,
+    'getCurrentPages': true,
+  }
+}

+ 21 - 0
node_modules/wxml-to-canvas/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 wechat-miniprogram
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 187 - 0
node_modules/wxml-to-canvas/README.md

@@ -0,0 +1,187 @@
+# wxml-to-canvas
+
+[![](https://img.shields.io/npm/v/wxml-to-canvas)](https://www.npmjs.com/package/wxml-to-canvas)
+[![](https://img.shields.io/npm/l/wxml-to-canvas)](https://github.com/wechat-miniprogram/wxml-to-canvas)
+
+小程序内通过静态模板和样式绘制 canvas ,导出图片,可用于生成分享图等场景。[代码片段](https://developers.weixin.qq.com/s/r6UBlEm17pc6)
+
+
+## 使用方法
+
+#### Step1. npm 安装,参考 [小程序 npm 支持](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)
+
+```
+npm install --save wxml-to-canvas
+```
+
+#### Step2. JSON 组件声明
+
+```
+{
+  "usingComponents": {
+    "wxml-to-canvas": "wxml-to-canvas",
+  }
+}
+```
+
+#### Step3. wxml 引入组件
+
+```
+<video class="video" src="{{src}}">
+  <wxml-to-canvas class="widget"></wxml-to-canvas>
+</video>
+<image src="{{src}}" style="width: {{width}}px; height: {{height}}px"></image>
+```
+
+##### 属性列表
+
+| 属性            | 类型    | 默认值  | 必填 | 说明                   |
+| --------------- | ------- | ------- | ---- | ---------------------- |
+| width           | Number  |   400      | 否   | 画布宽度           |
+| height           | Number  |   300      | 否   | 画布高度           |
+
+
+#### Step4. js 获取实例
+
+```
+const {wxml, style} = require('./demo.js')
+Page({
+  data: {
+    src: ''
+  },
+  onLoad() {
+    this.widget = this.selectComponent('.widget')
+  },
+  renderToCanvas() {
+    const p1 = this.widget.renderToCanvas({ wxml, style })
+    p1.then((res) => {
+      this.container = res
+      this.extraImage()
+    })
+  },
+  extraImage() {
+    const p2 = this.widget.canvasToTempFilePath()
+    p2.then(res => {
+      this.setData({
+        src: res.tempFilePath,
+        width: this.container.layoutBox.width,
+        height: this.container.layoutBox.height
+      })
+    })
+  }
+})
+```
+
+## wxml 模板
+
+支持 `view`、`text`、`image` 三种标签,通过 class 匹配 style 对象中的样式。
+
+```
+<view class="container" >
+  <view class="item-box red">
+  </view>
+  <view class="item-box green" >
+    <text class="text">yeah!</text>
+  </view>
+  <view class="item-box blue">
+      <image class="img" src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3582589792,4046843010&fm=26&gp=0.jpg"></image>
+  </view>
+</view>
+```
+
+## 样式
+
+对象属性值为对应 wxml 标签的 cass 驼峰形式。**需为每个元素指定 width 和 height 属性**,否则会导致布局错误。
+
+存在多个 className 时,位置靠后的优先级更高,子元素会继承父级元素的可继承属性。
+
+元素均为 flex 布局。left/top 等 仅在 absolute 定位下生效。
+
+```
+const style = {
+  container: {
+    width: 300,
+    height: 200,
+    flexDirection: 'row',
+    justifyContent: 'space-around',
+    backgroundColor: '#ccc',
+    alignItems: 'center',
+  },
+  itemBox: {
+    width: 80,
+    height: 60,
+  },
+  red: {
+    backgroundColor: '#ff0000'
+  },
+  green: {
+    backgroundColor: '#00ff00'
+  },
+  blue: {
+    backgroundColor: '#0000ff'
+  },
+  text: {
+    width: 80,
+    height: 60,
+    textAlign: 'center',
+    verticalAlign: 'middle',
+  }
+}
+```
+
+## 接口
+
+#### f1. `renderToCanvas({wxml, style}): Promise`
+
+渲染到 canvas,传入 wxml 模板 和 style 对象,返回的容器对象包含布局和样式信息。
+
+#### f2. `canvasToTempFilePath({fileType, quality}): Promise`
+
+提取画布中容器所在区域内容生成相同大小的图片,返回临时文件地址。
+
+`fileType` 支持 `jpg`、`png` 两种格式,quality 为图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
+
+## 支持的 css 属性
+
+### 布局相关
+
+| 属性名                | 支持的值或类型                                            | 默认值     |
+| --------------------- | --------------------------------------------------------- | ---------- |
+| width                 | number                                                    | 0          |
+| height                | number                                                    | 0          |
+| position              | relative, absolute                                        | relative   |
+| left                  | number                                                    | 0          |
+| top                   | number                                                    | 0          |
+| right                 | number                                                    | 0          |
+| bottom                | number                                                    | 0          |
+| margin                | number                                                    | 0          |
+| padding               | number                                                    | 0          |
+| borderWidth           | number                                                    | 0          |
+| borderRadius          | number                                                    | 0          |
+| flexDirection         | column, row                                               | row        |
+| flexShrink            | number                                                    | 1          |
+| flexGrow              | number                                                    |            |
+| flexWrap              | wrap, nowrap                                              | nowrap     |
+| justifyContent        | flex-start, center, flex-end, space-between, space-around | flex-start |
+| alignItems, alignSelf | flex-start, center, flex-end, stretch                     | flex-start |
+
+支持 marginLeft、paddingLeft 等
+
+### 文字
+
+| 属性名          | 支持的值或类型      | 默认值      |
+| --------------- | ------------------- | ----------- |
+| fontSize        | number              | 14          |
+| lineHeight      | number / string     | '1.4em'     |
+| textAlign       | left, center, right | left        |
+| verticalAlign   | top, middle, bottom | top         |
+| color           | string              | #000000     |
+| backgroundColor | string              | transparent |
+
+lineHeight 可取带 em 单位的字符串或数字类型。
+
+### 变形
+
+| 属性名 | 支持的值或类型 | 默认值 |
+| ------ | -------------- | ------ |
+| scale  | number         | 1      |

+ 26 - 0
node_modules/wxml-to-canvas/gulpfile.js

@@ -0,0 +1,26 @@
+const gulp = require('gulp')
+const clean = require('gulp-clean')
+
+const config = require('./tools/config')
+const BuildTask = require('./tools/build')
+const id = require('./package.json').name || 'miniprogram-custom-component'
+
+// 构建任务实例
+// eslint-disable-next-line no-new
+new BuildTask(id, config.entry)
+
+// 清空生成目录和文件
+gulp.task('clean', gulp.series(() => gulp.src(config.distPath, {read: false, allowEmpty: true}).pipe(clean()), done => {
+  if (config.isDev) {
+    return gulp.src(config.demoDist, {read: false, allowEmpty: true})
+      .pipe(clean())
+  }
+
+  return done()
+}))
+// 监听文件变化并进行开发模式构建
+gulp.task('watch', gulp.series(`${id}-watch`))
+// 开发模式构建
+gulp.task('dev', gulp.series(`${id}-dev`))
+// 生产模式构建
+gulp.task('default', gulp.series(`${id}-default`))

+ 779 - 0
node_modules/wxml-to-canvas/miniprogram_dist/index.js

@@ -0,0 +1,779 @@
+(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
+}
+
+
+/***/ })
+/******/ ]);
+});

+ 4 - 0
node_modules/wxml-to-canvas/miniprogram_dist/index.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 2 - 0
node_modules/wxml-to-canvas/miniprogram_dist/index.wxml

@@ -0,0 +1,2 @@
+<canvas wx:if="{{use2dCanvas}}" id="weui-canvas" type="2d" style="width: {{width}}px; height: {{height}}px;"></canvas>
+<canvas wx:else canvas-id="weui-canvas" style="width: {{width}}px; height: {{height}}px;"></canvas>

+ 0 - 0
node_modules/wxml-to-canvas/miniprogram_dist/index.wxss


+ 57 - 0
node_modules/wxml-to-canvas/miniprogram_dist/utils.js

@@ -0,0 +1,57 @@
+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
+}

+ 63 - 0
node_modules/wxml-to-canvas/package.json

@@ -0,0 +1,63 @@
+{
+  "name": "wxml-to-canvas",
+  "version": "1.1.1",
+  "description": "",
+  "main": "miniprogram_dist/index.js",
+  "scripts": {
+    "dev": "gulp dev --develop",
+    "watch": "gulp watch --develop --watch",
+    "build": "gulp",
+    "dist": "npm run build",
+    "clean-dev": "gulp clean --develop",
+    "clean": "gulp clean",
+    "test": "jest --bail",
+    "test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --bail",
+    "coverage": "jest ./test/* --coverage --bail",
+    "lint": "eslint \"src/**/*.js\" --fix",
+    "lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\" --fix"
+  },
+  "miniprogram": "miniprogram_dist",
+  "jest": {
+    "testEnvironment": "jsdom",
+    "testURL": "https://jest.test",
+    "collectCoverageFrom": [
+      "src/**/*.js"
+    ],
+    "moduleDirectories": [
+      "node_modules",
+      "src"
+    ]
+  },
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "author": "sanfordsun",
+  "license": "MIT",
+  "devDependencies": {
+    "colors": "^1.3.1",
+    "eslint": "^5.14.1",
+    "eslint-config-airbnb-base": "13.1.0",
+    "eslint-loader": "^2.1.2",
+    "eslint-plugin-import": "^2.16.0",
+    "eslint-plugin-node": "^7.0.1",
+    "eslint-plugin-promise": "^3.8.0",
+    "gulp": "^4.0.0",
+    "gulp-clean": "^0.4.0",
+    "gulp-if": "^2.0.2",
+    "gulp-install": "^1.1.0",
+    "gulp-less": "^4.0.1",
+    "gulp-rename": "^1.4.0",
+    "gulp-sourcemaps": "^2.6.5",
+    "jest": "^23.5.0",
+    "miniprogram-simulate": "^1.0.0",
+    "through2": "^2.0.3",
+    "vinyl": "^2.2.0",
+    "webpack": "^4.29.5",
+    "webpack-cli": "^3.3.10",
+    "webpack-node-externals": "^1.7.2"
+  },
+  "dependencies": {
+    "widget-ui": "^1.0.2"
+  }
+}

+ 225 - 0
node_modules/wxml-to-canvas/src/draw.js

@@ -0,0 +1,225 @@
+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
+}

+ 117 - 0
node_modules/wxml-to-canvas/src/index.js

@@ -0,0 +1,117 @@
+
+const xmlParse = require('./xml-parser')
+const {Widget} = require('./widget')
+const {Draw} = require('./draw')
+const {compareVersion} = require('./utils')
+
+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)
+      })
+    }
+  }
+})

+ 4 - 0
node_modules/wxml-to-canvas/src/index.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 2 - 0
node_modules/wxml-to-canvas/src/index.wxml

@@ -0,0 +1,2 @@
+<canvas wx:if="{{use2dCanvas}}" id="weui-canvas" type="2d" style="width: {{width}}px; height: {{height}}px;"></canvas>
+<canvas wx:else canvas-id="weui-canvas" style="width: {{width}}px; height: {{height}}px;"></canvas>

+ 0 - 0
node_modules/wxml-to-canvas/src/index.wxss


+ 57 - 0
node_modules/wxml-to-canvas/src/utils.js

@@ -0,0 +1,57 @@
+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
+}

+ 81 - 0
node_modules/wxml-to-canvas/src/widget.js

@@ -0,0 +1,81 @@
+const Block = require('widget-ui')
+const {splitLineToCamelCase} = require('./utils')
+
+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}

+ 164 - 0
node_modules/wxml-to-canvas/src/xml-parser.js

@@ -0,0 +1,164 @@
+
+/**
+ * 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

+ 56 - 0
package-lock.json

@@ -0,0 +1,56 @@
+{
+  "name": "swkz_wx",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "swkz_wx",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "weapp-qrcode": "^1.0.0",
+        "wxml-to-canvas": "^1.1.1"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "license": "MIT"
+    },
+    "node_modules/weapp-qrcode": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/weapp-qrcode/-/weapp-qrcode-1.0.0.tgz",
+      "integrity": "sha512-4sa3W0rGDVJ9QaeZpAKlAuUxVyjhDwiUqHyGK/jJMsRMXnhb4yO8qWU/pZruMo+iT5J6CraS67lDMFb1VY+RaA==",
+      "license": "MIT",
+      "dependencies": {
+        "extend": "^3.0.2"
+      }
+    },
+    "node_modules/widget-ui": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/widget-ui/-/widget-ui-1.0.2.tgz",
+      "integrity": "sha512-gDXosr5mflJdMA1weU1A47aTsTFfMJhfA4EKgO5XFebY3eVklf80KD4GODfrjo8J2WQ+9YjL1Rd9UUmKIzhShw==",
+      "license": "ISC",
+      "dependencies": {
+        "eventemitter3": "^4.0.0"
+      }
+    },
+    "node_modules/wxml-to-canvas": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/wxml-to-canvas/-/wxml-to-canvas-1.1.1.tgz",
+      "integrity": "sha512-3mDjHzujY/UgdCOXij/MnmwJYerVjwkyQHMBFBE8zh89DK7h7UTzoydWFqEBjIC0rfZM+AXl5kDh9hUcsNpSmg==",
+      "license": "MIT",
+      "dependencies": {
+        "widget-ui": "^1.0.2"
+      }
+    }
+  }
+}

+ 15 - 0
package.json

@@ -0,0 +1,15 @@
+{
+  "name": "swkz_wx",
+  "version": "1.0.0",
+  "main": "app.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC",
+  "description": "",
+  "dependencies": {
+    "weapp-qrcode": "^1.0.0",
+    "wxml-to-canvas": "^1.1.1"
+  }
+}

+ 135 - 0
pages/guicang/index.js

@@ -0,0 +1,135 @@
+// index.js
+Page({
+  data: {
+    // 轮播图配置
+    indicatorDots: true,
+    autoplay: true,
+    interval: 5000,
+    duration: 1000,
+    circular: true,
+    
+    // 轮播图数据
+    swiperList: [
+      {
+        id: 1,
+        imageUrl: '../../imgs/guicang/haiwaiwenwu.png',
+        title: '唐代三彩骆驼骑俑',
+        date: '2023-06-30',
+        source: '珍藏馆'
+      },
+      {
+        id: 2,
+        imageUrl: '../../imgs/guicang/haiwaiwenwu.png',
+        title: '明代青花瓷器',
+        date: '2023-06-29',
+        source: '国家博物馆'
+      },
+      {
+        id: 3,
+        imageUrl: '../../imgs/guicang/haiwaiwenwu.png',
+        title: '清代玉器',
+        date: '2023-06-28',
+        source: '故宫博物院'
+      },
+      {
+        id: 4,
+        imageUrl: '../../imgs/guicang/haiwaiwenwu.png',
+        title: '宋代瓷器',
+        date: '2023-06-27',
+        source: '上海博物馆'
+      }
+    ],
+    
+    // 最新上传数据
+    latestUploads: [],
+    
+    // 分页数据
+    currentPage: 1,
+    pageSize: 9,
+    hasMoreData: true,
+    loading: false
+  },
+  
+  onLoad: function(options) {
+    // 初始化最新上传数据
+    this.loadLatestUploads();
+  },
+  
+  // 加载最新上传数据
+  loadLatestUploads: function() {
+    if (!this.data.hasMoreData) return;
+    
+    this.setData({ loading: true });
+    
+    // 模拟数据加载
+    setTimeout(() => {
+      const newItems = this.getMockData(this.data.currentPage, this.data.pageSize);
+      const hasMore = this.data.currentPage < 3; // 模拟只有3页数据
+      
+      this.setData({
+        latestUploads: [...this.data.latestUploads, ...newItems],
+        currentPage: this.data.currentPage + 1,
+        hasMoreData: hasMore,
+        loading: false
+      });
+    }, 1000);
+  },
+  
+  // 生成模拟数据
+  getMockData: function(page, pageSize) {
+    const result = [];
+    const startIndex = (page - 1) * pageSize;
+    
+    for (let i = 0; i < pageSize; i++) {
+      result.push({
+        id: startIndex + i + 1,
+        imageUrl: '../../imgs/guicang/haiwaiwenwu.png'
+      });
+    }
+    
+    return result;
+  },
+  
+  // 跳转到详情页
+  goToDetail: function(e) {
+    const id = e.currentTarget.dataset.id;
+    wx.navigateTo({
+      url: '../guicangDetails/index?id=' + id
+    });
+  },
+  
+  // 下拉刷新
+  onPullDownRefresh: function() {
+    this.setData({
+      latestUploads: [],
+      currentPage: 1,
+      hasMoreData: true
+    });
+    
+    this.loadLatestUploads();
+    wx.stopPullDownRefresh();
+  },
+  
+  // 上拉加载更多
+  onReachBottom: function() {
+    if (!this.data.loading && this.data.hasMoreData) {
+      this.loadLatestUploads();
+    }
+  },
+  
+  onReady: function() {
+    // 页面初次渲染完成时执行的函数
+  },
+  
+  onShow: function() {
+    // 页面显示时执行的函数
+  },
+  
+  onHide: function() {
+    // 页面隐藏时执行的函数
+  },
+  
+  onUnload: function() {
+    // 页面卸载时执行的函数
+  }
+})

+ 8 - 0
pages/guicang/index.json

@@ -0,0 +1,8 @@
+{
+  "navigationBarTitleText": "归藏",
+  "navigationBarTextStyle": "white",
+  "navigationBarBackgroundColor": "#111",
+  "enablePullDownRefresh": true,
+  "backgroundColor": "#f5f5f5",
+  "backgroundTextStyle": "light"
+}

+ 35 - 0
pages/guicang/index.wxml

@@ -0,0 +1,35 @@
+<!-- index.wxml -->
+<view class="container">
+  <!-- 顶部轮播图 -->
+  <view class="swiper-container">
+    <swiper class="swiper" indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}" circular="{{circular}}">
+      <swiper-item wx:for="{{swiperList}}" wx:key="index">
+        <view class="swiper-item">
+          <image src="{{item.imageUrl}}" mode="aspectFill" class="slide-image" bindtap="goToDetail" data-id="{{item.id}}"></image>
+          <view class="swiper-info" style="background: url('../../imgs/guicang/heritage_bg.png')">
+            <view class="swiper-title">{{item.title}}</view>
+            <view class="swiper-date">{{item.date}} · {{item.source}}</view>
+          </view>
+        </view>
+      </swiper-item>
+    </swiper>
+  </view>
+  
+  <!-- 最新上传标题 -->
+  <view class="section-title">最新上传</view>
+  
+  <!-- 最新上传列表 -->
+  <view class="latest-uploads">
+    <view class="upload-grid">
+      <view class="upload-item" wx:for="{{latestUploads}}" wx:key="index" bindtap="goToDetail" data-id="{{item.id}}">
+        <image src="{{item.imageUrl}}" mode="aspectFill" class="upload-image"></image>
+      </view>
+    </view>
+  </view>
+  
+  <!-- 加载更多提示 -->
+  <view class="loading" wx:if="{{loading}}">
+    <view class="weui-loading"></view>
+    <view class="loading-text">正在加载</view>
+  </view>
+</view>

+ 0 - 0
pages/guicang/index.wxss


Some files were not shown because too many files changed in this diff