'use strict'; function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var React = require('react'); var PropTypes = require('prop-types'); // qr.js doesn't handle error level of zero (M) so we need to do it right, // thus the deep require. var QRCodeImpl = require('qr.js/lib/QRCode'); var ErrorCorrectLevel = require('qr.js/lib/ErrorCorrectLevel'); // TODO: pull this off of the QRCode class type so it matches. // Convert from UTF-16, forcing the use of byte-mode encoding in our QR Code. // This allows us to encode Hanji, Kanji, emoji, etc. Ideally we'd do more // detection and not resort to byte-mode if possible, but we're trading off // a smaller library for a smaller amount of data we can potentially encode. // Based on http://jonisalonen.com/2012/from-utf-16-to-utf-8-in-javascript/ function convertStr(str) { var out = ''; for (var i = 0; i < str.length; i++) { var charcode = str.charCodeAt(i); if (charcode < 0x0080) { out += String.fromCharCode(charcode); } else if (charcode < 0x0800) { out += String.fromCharCode(0xc0 | charcode >> 6); out += String.fromCharCode(0x80 | charcode & 0x3f); } else if (charcode < 0xd800 || charcode >= 0xe000) { out += String.fromCharCode(0xe0 | charcode >> 12); out += String.fromCharCode(0x80 | charcode >> 6 & 0x3f); out += String.fromCharCode(0x80 | charcode & 0x3f); } else { // This is a surrogate pair, so we'll reconsitute the pieces and work // from that i++; charcode = 0x10000 + ((charcode & 0x3ff) << 10 | str.charCodeAt(i) & 0x3ff); out += String.fromCharCode(0xf0 | charcode >> 18); out += String.fromCharCode(0x80 | charcode >> 12 & 0x3f); out += String.fromCharCode(0x80 | charcode >> 6 & 0x3f); out += String.fromCharCode(0x80 | charcode & 0x3f); } } return out; } var DEFAULT_PROPS = { size: 128, level: 'L', bgColor: '#FFFFFF', fgColor: '#000000', includeMargin: false }; var PROP_TYPES = process.env.NODE_ENV !== 'production' ? { value: PropTypes.string.isRequired, size: PropTypes.number, level: PropTypes.oneOf(['L', 'M', 'Q', 'H']), bgColor: PropTypes.string, fgColor: PropTypes.string, includeMargin: PropTypes.bool, imageSettings: PropTypes.shape({ src: PropTypes.string.isRequired, height: PropTypes.number.isRequired, width: PropTypes.number.isRequired, excavate: PropTypes.bool, x: PropTypes.number, y: PropTypes.number }) } : {}; var MARGIN_SIZE = 4; // This is *very* rough estimate of max amount of QRCode allowed to be covered. // It is "wrong" in a lot of ways (area is a terrible way to estimate, it // really should be number of modules covered), but if for some reason we don't // get an explicit height or width, I'd rather default to something than throw. var DEFAULT_IMG_SCALE = 0.1; function generatePath(modules) { var margin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var ops = []; modules.forEach(function (row, y) { var start = null; row.forEach(function (cell, x) { if (!cell && start !== null) { // M0 0h7v1H0z injects the space with the move and drops the comma, // saving a char per operation ops.push("M".concat(start + margin, " ").concat(y + margin, "h").concat(x - start, "v1H").concat(start + margin, "z")); start = null; return; } // end of row, clean up or skip if (x === row.length - 1) { if (!cell) { // We would have closed the op above already so this can only mean // 2+ light modules in a row. return; } if (start === null) { // Just a single dark module. ops.push("M".concat(x + margin, ",").concat(y + margin, " h1v1H").concat(x + margin, "z")); } else { // Otherwise finish the current line. ops.push("M".concat(start + margin, ",").concat(y + margin, " h").concat(x + 1 - start, "v1H").concat(start + margin, "z")); } return; } if (cell && start === null) { start = x; } }); }); return ops.join(''); } // We could just do this in generatePath, except that we want to support // non-Path2D canvas, so we need to keep it an explicit step. function excavateModules(modules, excavation) { return modules.slice().map(function (row, y) { if (y < excavation.y || y >= excavation.y + excavation.h) { return row; } return row.map(function (cell, x) { if (x < excavation.x || x >= excavation.x + excavation.w) { return cell; } return false; }); }); } function getImageSettings(props, cells) { var imageSettings = props.imageSettings, size = props.size, includeMargin = props.includeMargin; if (imageSettings == null) { return null; } var margin = includeMargin ? MARGIN_SIZE : 0; var numCells = cells.length + margin * 2; var defaultSize = Math.floor(size * DEFAULT_IMG_SCALE); var scale = numCells / size; var w = (imageSettings.width || defaultSize) * scale; var h = (imageSettings.height || defaultSize) * scale; var x = imageSettings.x == null ? cells.length / 2 - w / 2 : imageSettings.x * scale; var y = imageSettings.y == null ? cells.length / 2 - h / 2 : imageSettings.y * scale; var excavation = null; if (imageSettings.excavate) { var floorX = Math.floor(x); var floorY = Math.floor(y); var ceilW = Math.ceil(w + x - floorX); var ceilH = Math.ceil(h + y - floorY); excavation = { x: floorX, y: floorY, w: ceilW, h: ceilH }; } return { x: x, y: y, h: h, w: w, excavation: excavation }; } // For canvas we're going to switch our drawing mode based on whether or not // the environment supports Path2D. We only need the constructor to be // supported, but Edge doesn't actually support the path (string) type // argument. Luckily it also doesn't support the addPath() method. We can // treat that as the same thing. var SUPPORTS_PATH2D = function () { try { new Path2D().addPath(new Path2D()); } catch (e) { return false; } return true; }(); var QRCodeCanvas = /*#__PURE__*/ function (_React$PureComponent) { _inherits(QRCodeCanvas, _React$PureComponent); function QRCodeCanvas() { var _getPrototypeOf2; var _this; _classCallCheck(this, QRCodeCanvas); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(QRCodeCanvas)).call.apply(_getPrototypeOf2, [this].concat(args))); _defineProperty(_assertThisInitialized(_this), "_canvas", void 0); _defineProperty(_assertThisInitialized(_this), "_image", void 0); _defineProperty(_assertThisInitialized(_this), "state", { imgLoaded: false }); _defineProperty(_assertThisInitialized(_this), "handleImageLoad", function () { _this.setState({ imgLoaded: true }); }); return _this; } _createClass(QRCodeCanvas, [{ key: "componentDidMount", value: function componentDidMount() { this.update(); } }, { key: "componentDidUpdate", value: function componentDidUpdate() { this.update(); } }, { key: "update", value: function update() { var _this$props = this.props, value = _this$props.value, size = _this$props.size, level = _this$props.level, bgColor = _this$props.bgColor, fgColor = _this$props.fgColor, includeMargin = _this$props.includeMargin, imageSettings = _this$props.imageSettings; // We'll use type===-1 to force QRCode to automatically pick the best type var qrcode = new QRCodeImpl(-1, ErrorCorrectLevel[level]); qrcode.addData(convertStr(value)); qrcode.make(); if (this._canvas != null) { var canvas = this._canvas; var ctx = canvas.getContext('2d'); if (!ctx) { return; } var cells = qrcode.modules; if (cells === null) { return; } var margin = includeMargin ? MARGIN_SIZE : 0; var numCells = cells.length + margin * 2; var calculatedImageSettings = getImageSettings(this.props, cells); if (imageSettings != null && calculatedImageSettings != null) { if (calculatedImageSettings.excavation != null) { cells = excavateModules(cells, calculatedImageSettings.excavation); } } // We're going to scale this so that the number of drawable units // matches the number of cells. This avoids rounding issues, but does // result in some potentially unwanted single pixel issues between // blocks, only in environments that don't support Path2D. var pixelRatio = window.devicePixelRatio || 1; canvas.height = canvas.width = size * pixelRatio; var scale = size / numCells * pixelRatio; ctx.scale(scale, scale); // Draw solid background, only paint dark modules. ctx.fillStyle = bgColor; ctx.fillRect(0, 0, numCells, numCells); ctx.fillStyle = fgColor; if (SUPPORTS_PATH2D) { // $FlowFixMe: Path2D c'tor doesn't support args yet. ctx.fill(new Path2D(generatePath(cells, margin))); } else { cells.forEach(function (row, rdx) { row.forEach(function (cell, cdx) { if (cell) { ctx.fillRect(cdx + margin, rdx + margin, 1, 1); } }); }); } if (this.state.imgLoaded && this._image && calculatedImageSettings != null) { ctx.drawImage(this._image, calculatedImageSettings.x + margin, calculatedImageSettings.y + margin, calculatedImageSettings.w, calculatedImageSettings.h); } } } }, { key: "render", value: function render() { var _this2 = this; var _this$props2 = this.props, value = _this$props2.value, size = _this$props2.size, level = _this$props2.level, bgColor = _this$props2.bgColor, fgColor = _this$props2.fgColor, style = _this$props2.style, includeMargin = _this$props2.includeMargin, imageSettings = _this$props2.imageSettings, otherProps = _objectWithoutProperties(_this$props2, ["value", "size", "level", "bgColor", "fgColor", "style", "includeMargin", "imageSettings"]); var canvasStyle = _objectSpread({ height: size, width: size }, style); var img = null; var imgSrc = imageSettings && imageSettings.src; if (imageSettings != null && imgSrc != null) { img = React.createElement("img", { src: imgSrc, style: { display: 'none' }, onLoad: this.handleImageLoad, ref: function ref(_ref) { return _this2._image = _ref; } }); } return React.createElement(React.Fragment, null, React.createElement("canvas", _extends({ style: canvasStyle, height: size, width: size, ref: function ref(_ref2) { return _this2._canvas = _ref2; } }, otherProps)), img); } }]); return QRCodeCanvas; }(React.PureComponent); _defineProperty(QRCodeCanvas, "defaultProps", DEFAULT_PROPS); if (process.env.NODE_ENV !== 'production') { QRCodeCanvas.propTypes = PROP_TYPES; } var QRCodeSVG = /*#__PURE__*/ function (_React$PureComponent2) { _inherits(QRCodeSVG, _React$PureComponent2); function QRCodeSVG() { _classCallCheck(this, QRCodeSVG); return _possibleConstructorReturn(this, _getPrototypeOf(QRCodeSVG).apply(this, arguments)); } _createClass(QRCodeSVG, [{ key: "render", value: function render() { var _this$props3 = this.props, value = _this$props3.value, size = _this$props3.size, level = _this$props3.level, bgColor = _this$props3.bgColor, fgColor = _this$props3.fgColor, includeMargin = _this$props3.includeMargin, imageSettings = _this$props3.imageSettings, otherProps = _objectWithoutProperties(_this$props3, ["value", "size", "level", "bgColor", "fgColor", "includeMargin", "imageSettings"]); // We'll use type===-1 to force QRCode to automatically pick the best type var qrcode = new QRCodeImpl(-1, ErrorCorrectLevel[level]); qrcode.addData(convertStr(value)); qrcode.make(); var cells = qrcode.modules; if (cells === null) { return null; } var margin = includeMargin ? MARGIN_SIZE : 0; var numCells = cells.length + margin * 2; var calculatedImageSettings = getImageSettings(this.props, cells); var image = null; if (imageSettings != null && calculatedImageSettings != null) { if (calculatedImageSettings.excavation != null) { cells = excavateModules(cells, calculatedImageSettings.excavation); } image = React.createElement("image", { xlinkHref: imageSettings.src, height: calculatedImageSettings.h, width: calculatedImageSettings.w, x: calculatedImageSettings.x + margin, y: calculatedImageSettings.y + margin, preserveAspectRatio: "none" }); } // Drawing strategy: instead of a rect per module, we're going to create a // single path for the dark modules and layer that on top of a light rect, // for a total of 2 DOM nodes. We pay a bit more in string concat but that's // way faster than DOM ops. // For level 1, 441 nodes -> 2 // For level 40, 31329 -> 2 var fgPath = generatePath(cells, margin); return React.createElement("svg", _extends({ shapeRendering: "crispEdges", height: size, width: size, viewBox: "0 0 ".concat(numCells, " ").concat(numCells) }, otherProps), React.createElement("path", { fill: bgColor, d: "M0,0 h".concat(numCells, "v").concat(numCells, "H0z") }), React.createElement("path", { fill: fgColor, d: fgPath }), image); } }]); return QRCodeSVG; }(React.PureComponent); _defineProperty(QRCodeSVG, "defaultProps", DEFAULT_PROPS); if (process.env.NODE_ENV !== 'production') { QRCodeSVG.propTypes = PROP_TYPES; } var QRCode = function QRCode(props) { var renderAs = props.renderAs, otherProps = _objectWithoutProperties(props, ["renderAs"]); var Component = renderAs === 'svg' ? QRCodeSVG : QRCodeCanvas; return React.createElement(Component, otherProps); }; QRCode.defaultProps = _objectSpread({ renderAs: 'canvas' }, DEFAULT_PROPS); module.exports = QRCode;