scratch-paint 4.2.2 → 4.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -1
- package/dist/web/scratch-paint.js +1 -1
- package/dist/web/scratch-paint.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scratch-paint.js","mappings":";AAAA","sources":["webpack://scratch-paint/webpack/universalModuleDefinition"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"prop-types\"), require(\"minilog\"), require(\"react\"), require(\"react-redux\"), require(\"react-intl\"), require(\"@scratch/scratch-svg-renderer\"), require(\"react-style-proptype\"), require(\"react-popover\"), require(\"react-responsive\"), require(\"redux\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"prop-types\", \"minilog\", \"react\", \"react-redux\", \"react-intl\", \"@scratch/scratch-svg-renderer\", \"react-style-proptype\", \"react-popover\", \"react-responsive\", \"redux\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"scratch-paint\"] = factory(require(\"prop-types\"), require(\"minilog\"), require(\"react\"), require(\"react-redux\"), require(\"react-intl\"), require(\"@scratch/scratch-svg-renderer\"), require(\"react-style-proptype\"), require(\"react-popover\"), require(\"react-responsive\"), require(\"redux\"));\n\telse\n\t\troot[\"scratch-paint\"] = factory(root[\"prop-types\"], root[\"minilog\"], root[\"react\"], root[\"react-redux\"], root[\"react-intl\"], root[\"@scratch/scratch-svg-renderer\"], root[\"react-style-proptype\"], root[\"react-popover\"], root[\"react-responsive\"], root[\"redux\"]);\n})(self, function(__WEBPACK_EXTERNAL_MODULE__863__, __WEBPACK_EXTERNAL_MODULE__749__, __WEBPACK_EXTERNAL_MODULE__629__, __WEBPACK_EXTERNAL_MODULE__850__, __WEBPACK_EXTERNAL_MODULE__923__, __WEBPACK_EXTERNAL_MODULE__962__, __WEBPACK_EXTERNAL_MODULE__17__, __WEBPACK_EXTERNAL_MODULE__53__, __WEBPACK_EXTERNAL_MODULE__582__, __WEBPACK_EXTERNAL_MODULE__0__) {\nreturn "],"names":[],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"scratch-paint.js","mappings":";AAAA","sources":["webpack://scratch-paint/webpack/universalModuleDefinition","webpack://scratch-paint/./node_modules/@scratch/paper/dist/paper-full.js","webpack://scratch-paint/./node_modules/acorn/dist/acorn.js","webpack://scratch-paint/./src/components/button-group/button-group.css","webpack://scratch-paint/./src/components/button/button.css","webpack://scratch-paint/./src/components/color-button/color-button.css","webpack://scratch-paint/./src/components/color-picker/color-picker.css","webpack://scratch-paint/./src/components/dropdown/dropdown.css","webpack://scratch-paint/./src/components/fixed-tools/fixed-tools.css","webpack://scratch-paint/./src/components/font-dropdown/font-dropdown.css","webpack://scratch-paint/./src/components/forms/input.css","webpack://scratch-paint/./src/components/forms/label.css","webpack://scratch-paint/./src/components/forms/slider.css","webpack://scratch-paint/./src/components/input-group/input-group.css","webpack://scratch-paint/./src/components/labeled-icon-button/labeled-icon-button.css","webpack://scratch-paint/./src/components/loupe/loupe.css","webpack://scratch-paint/./src/components/mode-tools/mode-tools.css","webpack://scratch-paint/./src/components/paint-editor/paint-editor.css","webpack://scratch-paint/./src/components/scrollable-canvas/scrollable-canvas.css","webpack://scratch-paint/./src/components/tool-select-base/tool-select-base.css","webpack://scratch-paint/./src/containers/paper-canvas.css","webpack://scratch-paint/./node_modules/css-loader/dist/runtime/api.js","webpack://scratch-paint/./node_modules/css-loader/dist/runtime/cssWithMappingToString.js","webpack://scratch-paint/./node_modules/keymirror/index.js","webpack://scratch-paint/./node_modules/lodash.bindall/index.js","webpack://scratch-paint/./node_modules/lodash.omit/index.js","webpack://scratch-paint/./node_modules/parse-color/index.js","webpack://scratch-paint/./node_modules/parse-color/node_modules/color-convert/conversions.js","webpack://scratch-paint/./node_modules/parse-color/node_modules/color-convert/index.js","webpack://scratch-paint/./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js","webpack://scratch-paint/./node_modules/style-loader/dist/runtime/insertBySelector.js","webpack://scratch-paint/./node_modules/style-loader/dist/runtime/insertStyleElement.js","webpack://scratch-paint/./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js","webpack://scratch-paint/./node_modules/style-loader/dist/runtime/styleDomAPI.js","webpack://scratch-paint/./node_modules/style-loader/dist/runtime/styleTagTransform.js","webpack://scratch-paint/external umd2 \"@scratch/scratch-svg-renderer\"","webpack://scratch-paint/external umd2 \"minilog\"","webpack://scratch-paint/external umd2 \"prop-types\"","webpack://scratch-paint/external umd2 \"react\"","webpack://scratch-paint/external umd2 \"react-intl\"","webpack://scratch-paint/external umd2 \"react-popover\"","webpack://scratch-paint/external umd2 \"react-redux\"","webpack://scratch-paint/external umd2 \"react-responsive\"","webpack://scratch-paint/external umd2 \"react-style-proptype\"","webpack://scratch-paint/external umd2 \"redux\"","webpack://scratch-paint/./node_modules/classnames/index.js","webpack://scratch-paint/webpack/bootstrap","webpack://scratch-paint/webpack/runtime/compat get default export","webpack://scratch-paint/webpack/runtime/define property getters","webpack://scratch-paint/webpack/runtime/global","webpack://scratch-paint/webpack/runtime/hasOwnProperty shorthand","webpack://scratch-paint/webpack/runtime/make namespace object","webpack://scratch-paint/webpack/runtime/nonce","webpack://scratch-paint/./src/log/log.js","webpack://scratch-paint/./src/lib/format.js","webpack://scratch-paint/./src/lib/modes.js","webpack://scratch-paint/./src/helper/item.js","webpack://scratch-paint/./src/helper/group.js","webpack://scratch-paint/./src/helper/compound-path.js","webpack://scratch-paint/./src/helper/math.js","webpack://scratch-paint/./src/helper/selection.js","webpack://scratch-paint/./src/helper/guides.js","webpack://scratch-paint/./src/helper/bitmap.js","webpack://scratch-paint/./src/helper/view.js","webpack://scratch-paint/./src/helper/layer.js","webpack://scratch-paint/./src/helper/undo.js","webpack://scratch-paint/./src/reducers/undo.js","webpack://scratch-paint/./src/reducers/selected-items.js","webpack://scratch-paint/./src/reducers/hover.js","webpack://scratch-paint/./src/reducers/clipboard.js","webpack://scratch-paint/./src/reducers/format.js","webpack://scratch-paint/./src/reducers/view-bounds.js","webpack://scratch-paint/./src/reducers/zoom-levels.js","webpack://scratch-paint/./src/containers/paper-canvas.css?f62d","webpack://scratch-paint/./src/containers/paper-canvas.jsx","webpack://scratch-paint/./src/helper/strip-invalid-paper-data.js","webpack://scratch-paint/./src/components/scrollable-canvas/scrollable-canvas.css?3f10","webpack://scratch-paint/./src/components/scrollable-canvas/scrollable-canvas.jsx","webpack://scratch-paint/./src/lib/touch-utils.js","webpack://scratch-paint/./src/containers/scrollable-canvas.jsx","webpack://scratch-paint/./src/lib/gradient-types.js","webpack://scratch-paint/./src/lib/make-color-style-reducer.js","webpack://scratch-paint/./src/reducers/fill-style.js","webpack://scratch-paint/./src/helper/style-path.js","webpack://scratch-paint/./src/reducers/modes.js","webpack://scratch-paint/./src/components/button/button.css?505c","webpack://scratch-paint/./src/components/button/button.jsx","webpack://scratch-paint/./src/components/tool-select-base/tool-select-base.css?aad5","webpack://scratch-paint/./src/components/tool-select-base/tool-select-base.jsx","webpack://scratch-paint/./src/lib/messages.js","webpack://scratch-paint/./src/components/bit-brush-mode/bit-brush-mode.jsx","webpack://scratch-paint/./src/helper/bit-tools/brush-tool.js","webpack://scratch-paint/./src/containers/bit-brush-mode.jsx","webpack://scratch-paint/./src/components/bit-line-mode/bit-line-mode.jsx","webpack://scratch-paint/./src/helper/bit-tools/line-tool.js","webpack://scratch-paint/./src/containers/bit-line-mode.jsx","webpack://scratch-paint/./src/lib/color-style-proptype.js","webpack://scratch-paint/./src/lib/cursors.js","webpack://scratch-paint/./src/reducers/eye-dropper.js","webpack://scratch-paint/./src/reducers/cursor.js","webpack://scratch-paint/./src/helper/selection-tools/scale-tool.js","webpack://scratch-paint/./src/helper/selection-tools/rotate-tool.js","webpack://scratch-paint/./src/helper/selection-tools/move-tool.js","webpack://scratch-paint/./src/helper/selection-tools/bounding-box-tool.js","webpack://scratch-paint/./src/helper/selection-tools/nudge-tool.js","webpack://scratch-paint/./src/helper/bit-tools/oval-tool.js","webpack://scratch-paint/./src/components/bit-oval-mode/bit-oval-mode.jsx","webpack://scratch-paint/./src/containers/bit-oval-mode.jsx","webpack://scratch-paint/./src/helper/bit-tools/rect-tool.js","webpack://scratch-paint/./src/components/bit-rect-mode/bit-rect-mode.jsx","webpack://scratch-paint/./src/containers/bit-rect-mode.jsx","webpack://scratch-paint/./src/components/bit-fill-mode/bit-fill-mode.jsx","webpack://scratch-paint/./src/reducers/fill-mode-gradient-type.js","webpack://scratch-paint/./src/helper/bit-tools/fill-tool.js","webpack://scratch-paint/./src/containers/bit-fill-mode.jsx","webpack://scratch-paint/./src/components/bit-eraser-mode/bit-eraser-mode.jsx","webpack://scratch-paint/./src/containers/bit-eraser-mode.jsx","webpack://scratch-paint/./src/helper/selection-tools/selection-box-tool.js","webpack://scratch-paint/./src/helper/bit-tools/select-tool.js","webpack://scratch-paint/./src/components/bit-select-mode/bit-select-mode.jsx","webpack://scratch-paint/./src/containers/bit-select-mode.jsx","webpack://scratch-paint/./src/components/box/box.jsx","webpack://scratch-paint/./src/components/button-group/button-group.css?324a","webpack://scratch-paint/./src/components/button-group/button-group.jsx","webpack://scratch-paint/./src/helper/blob-tools/broad-brush-helper.js","webpack://scratch-paint/./src/helper/blob-tools/segment-brush-helper.js","webpack://scratch-paint/./src/helper/blob-tools/blob.js","webpack://scratch-paint/./src/components/brush-mode/brush-mode.jsx","webpack://scratch-paint/./src/containers/brush-mode.jsx","webpack://scratch-paint/./src/reducers/eraser-mode.js","webpack://scratch-paint/./src/components/eraser-mode/eraser-mode.jsx","webpack://scratch-paint/./src/containers/eraser-mode.jsx","webpack://scratch-paint/./src/reducers/color-index.js","webpack://scratch-paint/./src/reducers/modals.js","webpack://scratch-paint/./src/lib/intl-shape.js","webpack://scratch-paint/./src/components/color-button/color-button.css?22e3","webpack://scratch-paint/./src/components/color-button/color-button.jsx","webpack://scratch-paint/./src/components/forms/slider.css?138d","webpack://scratch-paint/./src/components/forms/slider.jsx","webpack://scratch-paint/./src/components/labeled-icon-button/labeled-icon-button.css?fd13","webpack://scratch-paint/./src/components/labeled-icon-button/labeled-icon-button.jsx","webpack://scratch-paint/./src/components/color-picker/color-picker.css?c2a3","webpack://scratch-paint/./src/components/color-picker/color-picker.jsx","webpack://scratch-paint/./src/containers/color-picker.jsx","webpack://scratch-paint/./src/components/input-group/input-group.css?eaab","webpack://scratch-paint/./src/components/input-group/input-group.jsx","webpack://scratch-paint/./src/components/forms/label.css?9214","webpack://scratch-paint/./src/components/forms/label.jsx","webpack://scratch-paint/./src/components/color-indicator.jsx","webpack://scratch-paint/./src/containers/color-indicator.jsx","webpack://scratch-paint/./src/containers/fill-color-indicator.jsx","webpack://scratch-paint/./src/helper/hover.js","webpack://scratch-paint/./src/helper/tools/fill-tool.js","webpack://scratch-paint/./src/components/fill-mode/fill-mode.jsx","webpack://scratch-paint/./src/containers/fill-mode.jsx","webpack://scratch-paint/./src/helper/snapping.js","webpack://scratch-paint/./src/reducers/stroke-width.js","webpack://scratch-paint/./src/reducers/stroke-style.js","webpack://scratch-paint/./src/components/line-mode/line-mode.jsx","webpack://scratch-paint/./src/containers/line-mode.jsx","webpack://scratch-paint/./src/helper/tools/eye-dropper.js","webpack://scratch-paint/./src/components/loupe/loupe.css?4b37","webpack://scratch-paint/./src/components/loupe/loupe.jsx","webpack://scratch-paint/./src/helper/order.js","webpack://scratch-paint/./src/components/dropdown/dropdown.css?43c3","webpack://scratch-paint/./src/components/dropdown/dropdown.jsx","webpack://scratch-paint/./src/components/forms/input.css?0662","webpack://scratch-paint/./src/components/forms/input.jsx","webpack://scratch-paint/./src/lib/layout-constants.js","webpack://scratch-paint/./src/lib/hide-label.js","webpack://scratch-paint/./src/components/fixed-tools/fixed-tools.css?310f","webpack://scratch-paint/./src/components/fixed-tools/fixed-tools.jsx","webpack://scratch-paint/./src/components/forms/buffered-input-hoc.jsx","webpack://scratch-paint/./src/reducers/text-edit-target.js","webpack://scratch-paint/./src/reducers/layout.js","webpack://scratch-paint/./src/containers/fixed-tools.jsx","webpack://scratch-paint/./src/hocs/copy-paste-hoc.jsx","webpack://scratch-paint/./src/reducers/brush-mode.js","webpack://scratch-paint/./src/reducers/bit-brush-size.js","webpack://scratch-paint/./src/reducers/bit-eraser-size.js","webpack://scratch-paint/./src/reducers/fill-bitmap-shapes.js","webpack://scratch-paint/./src/lib/fonts.js","webpack://scratch-paint/./src/components/font-dropdown/font-dropdown.css?3e31","webpack://scratch-paint/./src/components/font-dropdown/font-dropdown.jsx","webpack://scratch-paint/./src/reducers/font.js","webpack://scratch-paint/./src/containers/font-dropdown.jsx","webpack://scratch-paint/./src/components/forms/live-input-hoc.jsx","webpack://scratch-paint/./src/components/mode-tools/mode-tools.css?2d48","webpack://scratch-paint/./src/components/mode-tools/mode-tools.jsx","webpack://scratch-paint/./src/containers/mode-tools.jsx","webpack://scratch-paint/./src/helper/tools/oval-tool.js","webpack://scratch-paint/./src/components/oval-mode/oval-mode.jsx","webpack://scratch-paint/./src/containers/oval-mode.jsx","webpack://scratch-paint/./src/helper/tools/rect-tool.js","webpack://scratch-paint/./src/components/rect-mode/rect-mode.jsx","webpack://scratch-paint/./src/containers/rect-mode.jsx","webpack://scratch-paint/./src/helper/selection-tools/point-tool.js","webpack://scratch-paint/./src/helper/selection-tools/handle-tool.js","webpack://scratch-paint/./src/helper/selection-tools/reshape-tool.js","webpack://scratch-paint/./src/components/reshape-mode/reshape-mode.jsx","webpack://scratch-paint/./src/containers/reshape-mode.jsx","webpack://scratch-paint/./src/helper/selection-tools/select-tool.js","webpack://scratch-paint/./src/components/select-mode/select-mode.jsx","webpack://scratch-paint/./src/containers/select-mode.jsx","webpack://scratch-paint/./src/containers/stroke-color-indicator.jsx","webpack://scratch-paint/./src/components/stroke-width-indicator.jsx","webpack://scratch-paint/./src/containers/stroke-width-indicator.jsx","webpack://scratch-paint/./src/helper/tools/text-tool.js","webpack://scratch-paint/./src/components/text-mode/text-mode.jsx","webpack://scratch-paint/./src/components/bit-text-mode/bit-text-mode.jsx","webpack://scratch-paint/./src/containers/text-mode.jsx","webpack://scratch-paint/./src/components/paint-editor/paint-editor.css?e8d2","webpack://scratch-paint/./src/components/paint-editor/paint-editor.jsx","webpack://scratch-paint/./src/hocs/keyboard-shortcuts-hoc.jsx","webpack://scratch-paint/./src/hocs/selection-hoc.jsx","webpack://scratch-paint/./src/hocs/undo-hoc.jsx","webpack://scratch-paint/./src/hocs/update-image-hoc.jsx","webpack://scratch-paint/./src/containers/paint-editor.jsx","webpack://scratch-paint/./src/reducers/color.js","webpack://scratch-paint/./src/reducers/fill-mode.js","webpack://scratch-paint/./src/reducers/scratch-paint-reducer.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"prop-types\"), require(\"minilog\"), require(\"react\"), require(\"react-redux\"), require(\"react-intl\"), require(\"@scratch/scratch-svg-renderer\"), require(\"react-style-proptype\"), require(\"react-popover\"), require(\"react-responsive\"), require(\"redux\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"prop-types\", \"minilog\", \"react\", \"react-redux\", \"react-intl\", \"@scratch/scratch-svg-renderer\", \"react-style-proptype\", \"react-popover\", \"react-responsive\", \"redux\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"scratch-paint\"] = factory(require(\"prop-types\"), require(\"minilog\"), require(\"react\"), require(\"react-redux\"), require(\"react-intl\"), require(\"@scratch/scratch-svg-renderer\"), require(\"react-style-proptype\"), require(\"react-popover\"), require(\"react-responsive\"), require(\"redux\"));\n\telse\n\t\troot[\"scratch-paint\"] = factory(root[\"prop-types\"], root[\"minilog\"], root[\"react\"], root[\"react-redux\"], root[\"react-intl\"], root[\"@scratch/scratch-svg-renderer\"], root[\"react-style-proptype\"], root[\"react-popover\"], root[\"react-responsive\"], root[\"redux\"]);\n})(self, function(__WEBPACK_EXTERNAL_MODULE__863__, __WEBPACK_EXTERNAL_MODULE__749__, __WEBPACK_EXTERNAL_MODULE__629__, __WEBPACK_EXTERNAL_MODULE__850__, __WEBPACK_EXTERNAL_MODULE__923__, __WEBPACK_EXTERNAL_MODULE__962__, __WEBPACK_EXTERNAL_MODULE__17__, __WEBPACK_EXTERNAL_MODULE__53__, __WEBPACK_EXTERNAL_MODULE__582__, __WEBPACK_EXTERNAL_MODULE__0__) {\nreturn ","/*!\n * Paper.js v0.12.7 - The Swiss Army Knife of Vector Graphics Scripting.\n * http://paperjs.org/\n *\n * Copyright (c) 2011 - 2020, Jürg Lehni & Jonathan Puckey\n * http://juerglehni.com/ & https://puckey.studio/\n *\n * Distributed under the MIT license. See LICENSE file for details.\n *\n * All rights reserved.\n *\n * Date: Thu Dec 1 12:02:03 2022 -0800\n *\n ***\n *\n * Straps.js - Class inheritance library with support for bean-style accessors\n *\n * Copyright (c) 2006 - 2020 Jürg Lehni\n * http://juerglehni.com/\n *\n * Distributed under the MIT license.\n *\n ***\n *\n * Acorn.js\n * https://marijnhaverbeke.nl/acorn/\n *\n * Acorn is a tiny, fast JavaScript parser written in JavaScript,\n * created by Marijn Haverbeke and released under an MIT license.\n *\n */\n\nvar paper = function(self, undefined) {\n\nself = self || require('./node/self.js');\nvar window = self.window ? self.window : self,\n\tdocument = self.document;\n\nvar Base = new function() {\n\tvar hidden = /^(statics|enumerable|beans|preserve)$/,\n\t\tarray = [],\n\t\tslice = array.slice,\n\t\tcreate = Object.create,\n\t\tdescribe = Object.getOwnPropertyDescriptor,\n\t\tdefine = Object.defineProperty,\n\n\t\tforEach = array.forEach || function(iter, bind) {\n\t\t\tfor (var i = 0, l = this.length; i < l; i++) {\n\t\t\t\titer.call(bind, this[i], i, this);\n\t\t\t}\n\t\t},\n\n\t\tforIn = function(iter, bind) {\n\t\t\tfor (var i in this) {\n\t\t\t\tif (this.hasOwnProperty(i))\n\t\t\t\t\titer.call(bind, this[i], i, this);\n\t\t\t}\n\t\t},\n\n\t\tset = Object.assign || function(dst) {\n\t\t\tfor (var i = 1, l = arguments.length; i < l; i++) {\n\t\t\t\tvar src = arguments[i];\n\t\t\t\tfor (var key in src) {\n\t\t\t\t\tif (src.hasOwnProperty(key))\n\t\t\t\t\t\tdst[key] = src[key];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn dst;\n\t\t},\n\n\t\teach = function(obj, iter, bind) {\n\t\t\tif (obj) {\n\t\t\t\tvar desc = describe(obj, 'length');\n\t\t\t\t(desc && typeof desc.value === 'number' ? forEach : forIn)\n\t\t\t\t\t.call(obj, iter, bind = bind || obj);\n\t\t\t}\n\t\t\treturn bind;\n\t\t};\n\n\tfunction inject(dest, src, enumerable, beans, preserve) {\n\t\tvar beansNames = {};\n\n\t\tfunction field(name, val) {\n\t\t\tval = val || (val = describe(src, name))\n\t\t\t\t\t&& (val.get ? val : val.value);\n\t\t\tif (typeof val === 'string' && val[0] === '#')\n\t\t\t\tval = dest[val.substring(1)] || val;\n\t\t\tvar isFunc = typeof val === 'function',\n\t\t\t\tres = val,\n\t\t\t\tprev = preserve || isFunc && !val.base\n\t\t\t\t\t\t? (val && val.get ? name in dest : dest[name])\n\t\t\t\t\t\t: null,\n\t\t\t\tbean;\n\t\t\tif (!preserve || !prev) {\n\t\t\t\tif (isFunc && prev)\n\t\t\t\t\tval.base = prev;\n\t\t\t\tif (isFunc && beans !== false\n\t\t\t\t\t\t&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))\n\t\t\t\t\tbeansNames[bean[3].toLowerCase() + bean[4]] = bean[2];\n\t\t\t\tif (!res || isFunc || !res.get || typeof res.get !== 'function'\n\t\t\t\t\t\t|| !Base.isPlainObject(res)) {\n\t\t\t\t\tres = { value: res, writable: true };\n\t\t\t\t}\n\t\t\t\tif ((describe(dest, name)\n\t\t\t\t\t\t|| { configurable: true }).configurable) {\n\t\t\t\t\tres.configurable = true;\n\t\t\t\t\tres.enumerable = enumerable != null ? enumerable : !bean;\n\t\t\t\t}\n\t\t\t\tdefine(dest, name, res);\n\t\t\t}\n\t\t}\n\t\tif (src) {\n\t\t\tfor (var name in src) {\n\t\t\t\tif (src.hasOwnProperty(name) && !hidden.test(name))\n\t\t\t\t\tfield(name);\n\t\t\t}\n\t\t\tfor (var name in beansNames) {\n\t\t\t\tvar part = beansNames[name],\n\t\t\t\t\tset = dest['set' + part],\n\t\t\t\t\tget = dest['get' + part] || set && dest['is' + part];\n\t\t\t\tif (get && (beans === true || get.length === 0))\n\t\t\t\t\tfield(name, { get: get, set: set });\n\t\t\t}\n\t\t}\n\t\treturn dest;\n\t}\n\n\tfunction Base() {\n\t\tfor (var i = 0, l = arguments.length; i < l; i++) {\n\t\t\tvar src = arguments[i];\n\t\t\tif (src)\n\t\t\t\tset(this, src);\n\t\t}\n\t\treturn this;\n\t}\n\n\treturn inject(Base, {\n\t\tinject: function(src) {\n\t\t\tif (src) {\n\t\t\t\tvar statics = src.statics === true ? src : src.statics,\n\t\t\t\t\tbeans = src.beans,\n\t\t\t\t\tpreserve = src.preserve;\n\t\t\t\tif (statics !== src)\n\t\t\t\t\tinject(this.prototype, src, src.enumerable, beans, preserve);\n\t\t\t\tinject(this, statics, null, beans, preserve);\n\t\t\t}\n\t\t\tfor (var i = 1, l = arguments.length; i < l; i++)\n\t\t\t\tthis.inject(arguments[i]);\n\t\t\treturn this;\n\t\t},\n\n\t\textend: function() {\n\t\t\tvar base = this,\n\t\t\t\tctor,\n\t\t\t\tproto;\n\t\t\tfor (var i = 0, obj, l = arguments.length;\n\t\t\t\t\ti < l && !(ctor && proto); i++) {\n\t\t\t\tobj = arguments[i];\n\t\t\t\tctor = ctor || obj.initialize;\n\t\t\t\tproto = proto || obj.prototype;\n\t\t\t}\n\t\t\tctor = ctor || function() {\n\t\t\t\tbase.apply(this, arguments);\n\t\t\t};\n\t\t\tproto = ctor.prototype = proto || create(this.prototype);\n\t\t\tdefine(proto, 'constructor',\n\t\t\t\t\t{ value: ctor, writable: true, configurable: true });\n\t\t\tinject(ctor, this);\n\t\t\tif (arguments.length)\n\t\t\t\tthis.inject.apply(ctor, arguments);\n\t\t\tctor.base = base;\n\t\t\treturn ctor;\n\t\t}\n\t}).inject({\n\t\tenumerable: false,\n\n\t\tinitialize: Base,\n\n\t\tset: Base,\n\n\t\tinject: function() {\n\t\t\tfor (var i = 0, l = arguments.length; i < l; i++) {\n\t\t\t\tvar src = arguments[i];\n\t\t\t\tif (src) {\n\t\t\t\t\tinject(this, src, src.enumerable, src.beans, src.preserve);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\n\t\textend: function() {\n\t\t\tvar res = create(this);\n\t\t\treturn res.inject.apply(res, arguments);\n\t\t},\n\n\t\teach: function(iter, bind) {\n\t\t\treturn each(this, iter, bind);\n\t\t},\n\n\t\tclone: function() {\n\t\t\treturn new this.constructor(this);\n\t\t},\n\n\t\tstatics: {\n\t\t\tset: set,\n\t\t\teach: each,\n\t\t\tcreate: create,\n\t\t\tdefine: define,\n\t\t\tdescribe: describe,\n\n\t\t\tclone: function(obj) {\n\t\t\t\treturn set(new obj.constructor(), obj);\n\t\t\t},\n\n\t\t\tisPlainObject: function(obj) {\n\t\t\t\tvar ctor = obj != null && obj.constructor;\n\t\t\t\treturn ctor && (ctor === Object || ctor === Base\n\t\t\t\t\t\t|| ctor.name === 'Object');\n\t\t\t},\n\n\t\t\tpick: function(a, b) {\n\t\t\t\treturn a !== undefined ? a : b;\n\t\t\t},\n\n\t\t\tslice: function(list, begin, end) {\n\t\t\t\treturn slice.call(list, begin, end);\n\t\t\t}\n\t\t}\n\t});\n};\n\nif (typeof module !== 'undefined')\n\tmodule.exports = Base;\n\nBase.inject({\n\tenumerable: false,\n\n\ttoString: function() {\n\t\treturn this._id != null\n\t\t\t? (this._class || 'Object') + (this._name\n\t\t\t\t? \" '\" + this._name + \"'\"\n\t\t\t\t: ' @' + this._id)\n\t\t\t: '{ ' + Base.each(this, function(value, key) {\n\t\t\t\tif (!/^_/.test(key)) {\n\t\t\t\t\tvar type = typeof value;\n\t\t\t\t\tthis.push(key + ': ' + (type === 'number'\n\t\t\t\t\t\t\t? Formatter.instance.number(value)\n\t\t\t\t\t\t\t: type === 'string' ? \"'\" + value + \"'\" : value));\n\t\t\t\t}\n\t\t\t}, []).join(', ') + ' }';\n\t},\n\n\tgetClassName: function() {\n\t\treturn this._class || '';\n\t},\n\n\timportJSON: function(json) {\n\t\treturn Base.importJSON(json, this);\n\t},\n\n\texportJSON: function(options) {\n\t\treturn Base.exportJSON(this, options);\n\t},\n\n\ttoJSON: function() {\n\t\treturn Base.serialize(this);\n\t},\n\n\tset: function(props, exclude) {\n\t\tif (props)\n\t\t\tBase.filter(this, props, exclude, this._prioritize);\n\t\treturn this;\n\t}\n}, {\n\nbeans: false,\nstatics: {\n\texports: {},\n\n\textend: function extend() {\n\t\tvar res = extend.base.apply(this, arguments),\n\t\t\tname = res.prototype._class;\n\t\tif (name && !Base.exports[name])\n\t\t\tBase.exports[name] = res;\n\t\treturn res;\n\t},\n\n\tequals: function(obj1, obj2) {\n\t\tif (obj1 === obj2)\n\t\t\treturn true;\n\t\tif (obj1 && obj1.equals)\n\t\t\treturn obj1.equals(obj2);\n\t\tif (obj2 && obj2.equals)\n\t\t\treturn obj2.equals(obj1);\n\t\tif (obj1 && obj2\n\t\t\t\t&& typeof obj1 === 'object' && typeof obj2 === 'object') {\n\t\t\tif (Array.isArray(obj1) && Array.isArray(obj2)) {\n\t\t\t\tvar length = obj1.length;\n\t\t\t\tif (length !== obj2.length)\n\t\t\t\t\treturn false;\n\t\t\t\twhile (length--) {\n\t\t\t\t\tif (!Base.equals(obj1[length], obj2[length]))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar keys = Object.keys(obj1),\n\t\t\t\t\tlength = keys.length;\n\t\t\t\tif (length !== Object.keys(obj2).length)\n\t\t\t\t\treturn false;\n\t\t\t\twhile (length--) {\n\t\t\t\t\tvar key = keys[length];\n\t\t\t\t\tif (!(obj2.hasOwnProperty(key)\n\t\t\t\t\t\t\t&& Base.equals(obj1[key], obj2[key])))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tread: function(list, start, options, amount) {\n\t\tif (this === Base) {\n\t\t\tvar value = this.peek(list, start);\n\t\t\tlist.__index++;\n\t\t\treturn value;\n\t\t}\n\t\tvar proto = this.prototype,\n\t\t\treadIndex = proto._readIndex,\n\t\t\tbegin = start || readIndex && list.__index || 0,\n\t\t\tlength = list.length,\n\t\t\tobj = list[begin];\n\t\tamount = amount || length - begin;\n\t\tif (obj instanceof this\n\t\t\t|| options && options.readNull && obj == null && amount <= 1) {\n\t\t\tif (readIndex)\n\t\t\t\tlist.__index = begin + 1;\n\t\t\treturn obj && options && options.clone ? obj.clone() : obj;\n\t\t}\n\t\tobj = Base.create(proto);\n\t\tif (readIndex)\n\t\t\tobj.__read = true;\n\t\tobj = obj.initialize.apply(obj, begin > 0 || begin + amount < length\n\t\t\t\t? Base.slice(list, begin, begin + amount)\n\t\t\t\t: list) || obj;\n\t\tif (readIndex) {\n\t\t\tlist.__index = begin + obj.__read;\n\t\t\tvar filtered = obj.__filtered;\n\t\t\tif (filtered) {\n\t\t\t\tlist.__filtered = filtered;\n\t\t\t\tobj.__filtered = undefined;\n\t\t\t}\n\t\t\tobj.__read = undefined;\n\t\t}\n\t\treturn obj;\n\t},\n\n\tpeek: function(list, start) {\n\t\treturn list[list.__index = start || list.__index || 0];\n\t},\n\n\tremain: function(list) {\n\t\treturn list.length - (list.__index || 0);\n\t},\n\n\treadList: function(list, start, options, amount) {\n\t\tvar res = [],\n\t\t\tentry,\n\t\t\tbegin = start || 0,\n\t\t\tend = amount ? begin + amount : list.length;\n\t\tfor (var i = begin; i < end; i++) {\n\t\t\tres.push(Array.isArray(entry = list[i])\n\t\t\t\t\t? this.read(entry, 0, options)\n\t\t\t\t\t: this.read(list, i, options, 1));\n\t\t}\n\t\treturn res;\n\t},\n\n\treadNamed: function(list, name, start, options, amount) {\n\t\tvar value = this.getNamed(list, name),\n\t\t\thasValue = value !== undefined;\n\t\tif (hasValue) {\n\t\t\tvar filtered = list.__filtered;\n\t\t\tif (!filtered) {\n\t\t\t\tvar source = this.getSource(list);\n\t\t\t\tfiltered = list.__filtered = Base.create(source);\n\t\t\t\tfiltered.__unfiltered = source;\n\t\t\t}\n\t\t\tfiltered[name] = undefined;\n\t\t}\n\t\treturn this.read(hasValue ? [value] : list, start, options, amount);\n\t},\n\n\treadSupported: function(list, dest) {\n\t\tvar source = this.getSource(list),\n\t\t\tthat = this,\n\t\t\tread = false;\n\t\tif (source) {\n\t\t\tObject.keys(source).forEach(function(key) {\n\t\t\t\tif (key in dest) {\n\t\t\t\t\tvar value = that.readNamed(list, key);\n\t\t\t\t\tif (value !== undefined) {\n\t\t\t\t\t\tdest[key] = value;\n\t\t\t\t\t}\n\t\t\t\t\tread = true;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn read;\n\t},\n\n\tgetSource: function(list) {\n\t\tvar source = list.__source;\n\t\tif (source === undefined) {\n\t\t\tvar arg = list.length === 1 && list[0];\n\t\t\tsource = list.__source = arg && Base.isPlainObject(arg)\n\t\t\t\t? arg : null;\n\t\t}\n\t\treturn source;\n\t},\n\n\tgetNamed: function(list, name) {\n\t\tvar source = this.getSource(list);\n\t\tif (source) {\n\t\t\treturn name ? source[name] : list.__filtered || source;\n\t\t}\n\t},\n\n\thasNamed: function(list, name) {\n\t\treturn !!this.getNamed(list, name);\n\t},\n\n\tfilter: function(dest, source, exclude, prioritize) {\n\t\tvar processed;\n\n\t\tfunction handleKey(key) {\n\t\t\tif (!(exclude && key in exclude) &&\n\t\t\t\t!(processed && key in processed)) {\n\t\t\t\tvar value = source[key];\n\t\t\t\tif (value !== undefined)\n\t\t\t\t\tdest[key] = value;\n\t\t\t}\n\t\t}\n\n\t\tif (prioritize) {\n\t\t\tvar keys = {};\n\t\t\tfor (var i = 0, key, l = prioritize.length; i < l; i++) {\n\t\t\t\tif ((key = prioritize[i]) in source) {\n\t\t\t\t\thandleKey(key);\n\t\t\t\t\tkeys[key] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocessed = keys;\n\t\t}\n\n\t\tObject.keys(source.__unfiltered || source).forEach(handleKey);\n\t\treturn dest;\n\t},\n\n\tisPlainValue: function(obj, asString) {\n\t\treturn Base.isPlainObject(obj) || Array.isArray(obj)\n\t\t\t\t|| asString && typeof obj === 'string';\n\t},\n\n\tserialize: function(obj, options, compact, dictionary) {\n\t\toptions = options || {};\n\n\t\tvar isRoot = !dictionary,\n\t\t\tres;\n\t\tif (isRoot) {\n\t\t\toptions.formatter = new Formatter(options.precision);\n\t\t\tdictionary = {\n\t\t\t\tlength: 0,\n\t\t\t\tdefinitions: {},\n\t\t\t\treferences: {},\n\t\t\t\tadd: function(item, create) {\n\t\t\t\t\tvar id = '#' + item._id,\n\t\t\t\t\t\tref = this.references[id];\n\t\t\t\t\tif (!ref) {\n\t\t\t\t\t\tthis.length++;\n\t\t\t\t\t\tvar res = create.call(item),\n\t\t\t\t\t\t\tname = item._class;\n\t\t\t\t\t\tif (name && res[0] !== name)\n\t\t\t\t\t\t\tres.unshift(name);\n\t\t\t\t\t\tthis.definitions[id] = res;\n\t\t\t\t\t\tref = this.references[id] = [id];\n\t\t\t\t\t}\n\t\t\t\t\treturn ref;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tif (obj && obj._serialize) {\n\t\t\tres = obj._serialize(options, dictionary);\n\t\t\tvar name = obj._class;\n\t\t\tif (name && !obj._compactSerialize && (isRoot || !compact)\n\t\t\t\t\t&& res[0] !== name) {\n\t\t\t\tres.unshift(name);\n\t\t\t}\n\t\t} else if (Array.isArray(obj)) {\n\t\t\tres = [];\n\t\t\tfor (var i = 0, l = obj.length; i < l; i++)\n\t\t\t\tres[i] = Base.serialize(obj[i], options, compact, dictionary);\n\t\t} else if (Base.isPlainObject(obj)) {\n\t\t\tres = {};\n\t\t\tvar keys = Object.keys(obj);\n\t\t\tfor (var i = 0, l = keys.length; i < l; i++) {\n\t\t\t\tvar key = keys[i];\n\t\t\t\tres[key] = Base.serialize(obj[key], options, compact,\n\t\t\t\t\t\tdictionary);\n\t\t\t}\n\t\t} else if (typeof obj === 'number') {\n\t\t\tres = options.formatter.number(obj, options.precision);\n\t\t} else {\n\t\t\tres = obj;\n\t\t}\n\t\treturn isRoot && dictionary.length > 0\n\t\t\t\t? [['dictionary', dictionary.definitions], res]\n\t\t\t\t: res;\n\t},\n\n\tdeserialize: function(json, create, _data, _setDictionary, _isRoot) {\n\t\tvar res = json,\n\t\t\tisFirst = !_data,\n\t\t\thasDictionary = isFirst && json && json.length\n\t\t\t\t&& json[0][0] === 'dictionary';\n\t\t_data = _data || {};\n\t\tif (Array.isArray(json)) {\n\t\t\tvar type = json[0],\n\t\t\t\tisDictionary = type === 'dictionary';\n\t\t\tif (json.length == 1 && /^#/.test(type)) {\n\t\t\t\treturn _data.dictionary[type];\n\t\t\t}\n\t\t\ttype = Base.exports[type];\n\t\t\tres = [];\n\t\t\tfor (var i = type ? 1 : 0, l = json.length; i < l; i++) {\n\t\t\t\tres.push(Base.deserialize(json[i], create, _data,\n\t\t\t\t\t\tisDictionary, hasDictionary));\n\t\t\t}\n\t\t\tif (type) {\n\t\t\t\tvar args = res;\n\t\t\t\tif (create) {\n\t\t\t\t\tres = create(type, args, isFirst || _isRoot);\n\t\t\t\t} else {\n\t\t\t\t\tres = new type(args);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (Base.isPlainObject(json)) {\n\t\t\tres = {};\n\t\t\tif (_setDictionary)\n\t\t\t\t_data.dictionary = res;\n\t\t\tfor (var key in json)\n\t\t\t\tres[key] = Base.deserialize(json[key], create, _data);\n\t\t}\n\t\treturn hasDictionary ? res[1] : res;\n\t},\n\n\texportJSON: function(obj, options) {\n\t\tvar json = Base.serialize(obj, options);\n\t\treturn options && options.asString == false\n\t\t\t\t? json\n\t\t\t\t: JSON.stringify(json);\n\t},\n\n\timportJSON: function(json, target) {\n\t\treturn Base.deserialize(\n\t\t\t\ttypeof json === 'string' ? JSON.parse(json) : json,\n\t\t\t\tfunction(ctor, args, isRoot) {\n\t\t\t\t\tvar useTarget = isRoot && target\n\t\t\t\t\t\t\t&& target.constructor === ctor,\n\t\t\t\t\t\tobj = useTarget ? target\n\t\t\t\t\t\t\t: Base.create(ctor.prototype);\n\t\t\t\t\tif (args.length === 1 && obj instanceof Item\n\t\t\t\t\t\t\t&& (useTarget || !(obj instanceof Layer))) {\n\t\t\t\t\t\tvar arg = args[0];\n\t\t\t\t\t\tif (Base.isPlainObject(arg)) {\n\t\t\t\t\t\t\targ.insert = false;\n\t\t\t\t\t\t\tif (useTarget) {\n\t\t\t\t\t\t\t\targs = args.concat([{ insert: true }]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t(useTarget ? obj.set : ctor).apply(obj, args);\n\t\t\t\t\tif (useTarget)\n\t\t\t\t\t\ttarget = null;\n\t\t\t\t\treturn obj;\n\t\t\t\t});\n\t},\n\n\tpush: function(list, items) {\n\t\tvar itemsLength = items.length;\n\t\tif (itemsLength < 4096) {\n\t\t\tlist.push.apply(list, items);\n\t\t} else {\n\t\t\tvar startLength = list.length;\n\t\t\tlist.length += itemsLength;\n\t\t\tfor (var i = 0; i < itemsLength; i++) {\n\t\t\t\tlist[startLength + i] = items[i];\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t},\n\n\tsplice: function(list, items, index, remove) {\n\t\tvar amount = items && items.length,\n\t\t\tappend = index === undefined;\n\t\tindex = append ? list.length : index;\n\t\tif (index > list.length)\n\t\t\tindex = list.length;\n\t\tfor (var i = 0; i < amount; i++)\n\t\t\titems[i]._index = index + i;\n\t\tif (append) {\n\t\t\tBase.push(list, items);\n\t\t\treturn [];\n\t\t} else {\n\t\t\tvar args = [index, remove];\n\t\t\tif (items)\n\t\t\t\tBase.push(args, items);\n\t\t\tvar removed = list.splice.apply(list, args);\n\t\t\tfor (var i = 0, l = removed.length; i < l; i++)\n\t\t\t\tremoved[i]._index = undefined;\n\t\t\tfor (var i = index + amount, l = list.length; i < l; i++)\n\t\t\t\tlist[i]._index = i;\n\t\t\treturn removed;\n\t\t}\n\t},\n\n\tcapitalize: function(str) {\n\t\treturn str.replace(/\\b[a-z]/g, function(match) {\n\t\t\treturn match.toUpperCase();\n\t\t});\n\t},\n\n\tcamelize: function(str) {\n\t\treturn str.replace(/-(.)/g, function(match, chr) {\n\t\t\treturn chr.toUpperCase();\n\t\t});\n\t},\n\n\thyphenate: function(str) {\n\t\treturn str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n\t}\n}});\n\nvar Emitter = {\n\ton: function(type, func) {\n\t\tif (typeof type !== 'string') {\n\t\t\tBase.each(type, function(value, key) {\n\t\t\t\tthis.on(key, value);\n\t\t\t}, this);\n\t\t} else {\n\t\t\tvar types = this._eventTypes,\n\t\t\t\tentry = types && types[type],\n\t\t\t\thandlers = this._callbacks = this._callbacks || {};\n\t\t\thandlers = handlers[type] = handlers[type] || [];\n\t\t\tif (handlers.indexOf(func) === -1) {\n\t\t\t\thandlers.push(func);\n\t\t\t\tif (entry && entry.install && handlers.length === 1)\n\t\t\t\t\tentry.install.call(this, type);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t},\n\n\toff: function(type, func) {\n\t\tif (typeof type !== 'string') {\n\t\t\tBase.each(type, function(value, key) {\n\t\t\t\tthis.off(key, value);\n\t\t\t}, this);\n\t\t\treturn;\n\t\t}\n\t\tvar types = this._eventTypes,\n\t\t\tentry = types && types[type],\n\t\t\thandlers = this._callbacks && this._callbacks[type],\n\t\t\tindex;\n\t\tif (handlers) {\n\t\t\tif (!func || (index = handlers.indexOf(func)) !== -1\n\t\t\t\t\t&& handlers.length === 1) {\n\t\t\t\tif (entry && entry.uninstall)\n\t\t\t\t\tentry.uninstall.call(this, type);\n\t\t\t\tdelete this._callbacks[type];\n\t\t\t} else if (index !== -1) {\n\t\t\t\thandlers.splice(index, 1);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t},\n\n\tonce: function(type, func) {\n\t\treturn this.on(type, function handler() {\n\t\t\tfunc.apply(this, arguments);\n\t\t\tthis.off(type, handler);\n\t\t});\n\t},\n\n\temit: function(type, event) {\n\t\tvar handlers = this._callbacks && this._callbacks[type];\n\t\tif (!handlers)\n\t\t\treturn false;\n\t\tvar args = Base.slice(arguments, 1),\n\t\t\tsetTarget = event && event.target && !event.currentTarget;\n\t\thandlers = handlers.slice();\n\t\tif (setTarget)\n\t\t\tevent.currentTarget = this;\n\t\tfor (var i = 0, l = handlers.length; i < l; i++) {\n\t\t\tif (handlers[i].apply(this, args) == false) {\n\t\t\t\tif (event && event.stop)\n\t\t\t\t\tevent.stop();\n\t\t\t\tbreak;\n\t\t }\n\t\t}\n\t\tif (setTarget)\n\t\t\tdelete event.currentTarget;\n\t\treturn true;\n\t},\n\n\tresponds: function(type) {\n\t\treturn !!(this._callbacks && this._callbacks[type]);\n\t},\n\n\tattach: '#on',\n\tdetach: '#off',\n\tfire: '#emit',\n\n\t_installEvents: function(install) {\n\t\tvar types = this._eventTypes,\n\t\t\thandlers = this._callbacks,\n\t\t\tkey = install ? 'install' : 'uninstall';\n\t\tif (types) {\n\t\t\tfor (var type in handlers) {\n\t\t\t\tif (handlers[type].length > 0) {\n\t\t\t\t\tvar entry = types[type],\n\t\t\t\t\t\tfunc = entry && entry[key];\n\t\t\t\t\tif (func)\n\t\t\t\t\t\tfunc.call(this, type);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tstatics: {\n\t\tinject: function inject(src) {\n\t\t\tvar events = src._events;\n\t\t\tif (events) {\n\t\t\t\tvar types = {};\n\t\t\t\tBase.each(events, function(entry, key) {\n\t\t\t\t\tvar isString = typeof entry === 'string',\n\t\t\t\t\t\tname = isString ? entry : key,\n\t\t\t\t\t\tpart = Base.capitalize(name),\n\t\t\t\t\t\ttype = name.substring(2).toLowerCase();\n\t\t\t\t\ttypes[type] = isString ? {} : entry;\n\t\t\t\t\tname = '_' + name;\n\t\t\t\t\tsrc['get' + part] = function() {\n\t\t\t\t\t\treturn this[name];\n\t\t\t\t\t};\n\t\t\t\t\tsrc['set' + part] = function(func) {\n\t\t\t\t\t\tvar prev = this[name];\n\t\t\t\t\t\tif (prev)\n\t\t\t\t\t\t\tthis.off(type, prev);\n\t\t\t\t\t\tif (func)\n\t\t\t\t\t\t\tthis.on(type, func);\n\t\t\t\t\t\tthis[name] = func;\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t\t\tsrc._eventTypes = types;\n\t\t\t}\n\t\t\treturn inject.base.apply(this, arguments);\n\t\t}\n\t}\n};\n\nvar PaperScope = Base.extend({\n\t_class: 'PaperScope',\n\n\tinitialize: function PaperScope() {\n\t\tpaper = this;\n\t\tthis.settings = new Base({\n\t\t\tapplyMatrix: true,\n\t\t\tinsertItems: true,\n\t\t\thandleSize: 4,\n\t\t\thitTolerance: 0\n\t\t});\n\t\tthis.project = null;\n\t\tthis.projects = [];\n\t\tthis.tools = [];\n\t\tthis._id = PaperScope._id++;\n\t\tPaperScope._scopes[this._id] = this;\n\t\tvar proto = PaperScope.prototype;\n\t\tif (!this.support) {\n\t\t\tvar ctx = CanvasProvider.getContext(1, 1) || {};\n\t\t\tproto.support = {\n\t\t\t\tnativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,\n\t\t\t\tnativeBlendModes: BlendMode.nativeModes\n\t\t\t};\n\t\t\tCanvasProvider.release(ctx);\n\t\t}\n\t\tif (!this.agent) {\n\t\t\tvar user = self.navigator.userAgent.toLowerCase(),\n\t\t\t\tos = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user)||[])[0],\n\t\t\t\tplatform = os === 'darwin' ? 'mac' : os,\n\t\t\t\tagent = proto.agent = proto.browser = { platform: platform };\n\t\t\tif (platform)\n\t\t\t\tagent[platform] = true;\n\t\t\tuser.replace(\n\t\t\t\t/(opera|chrome|safari|webkit|firefox|msie|trident|atom|node|jsdom)\\/?\\s*([.\\d]+)(?:.*version\\/([.\\d]+))?(?:.*rv\\:v?([.\\d]+))?/g,\n\t\t\t\tfunction(match, n, v1, v2, rv) {\n\t\t\t\t\tif (!agent.chrome) {\n\t\t\t\t\t\tvar v = n === 'opera' ? v2 :\n\t\t\t\t\t\t\t\t/^(node|trident)$/.test(n) ? rv : v1;\n\t\t\t\t\t\tagent.version = v;\n\t\t\t\t\t\tagent.versionNumber = parseFloat(v);\n\t\t\t\t\t\tn = { trident: 'msie', jsdom: 'node' }[n] || n;\n\t\t\t\t\t\tagent.name = n;\n\t\t\t\t\t\tagent[n] = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t\tif (agent.chrome)\n\t\t\t\tdelete agent.webkit;\n\t\t\tif (agent.atom)\n\t\t\t\tdelete agent.chrome;\n\t\t}\n\t},\n\n\tversion: \"0.12.7\",\n\n\tgetView: function() {\n\t\tvar project = this.project;\n\t\treturn project && project._view;\n\t},\n\n\tgetPaper: function() {\n\t\treturn this;\n\t},\n\n\texecute: function(code, options) {\n\t\t\tvar exports = paper.PaperScript.execute(code, this, options);\n\t\t\tView.updateFocus();\n\t\t\treturn exports;\n\t},\n\n\tinstall: function(scope) {\n\t\tvar that = this;\n\t\tBase.each(['project', 'view', 'tool'], function(key) {\n\t\t\tBase.define(scope, key, {\n\t\t\t\tconfigurable: true,\n\t\t\t\tget: function() {\n\t\t\t\t\treturn that[key];\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tfor (var key in this)\n\t\t\tif (!/^_/.test(key) && this[key])\n\t\t\t\tscope[key] = this[key];\n\t},\n\n\tsetup: function(element) {\n\t\tpaper = this;\n\t\tthis.project = new Project(element);\n\t\treturn this;\n\t},\n\n\tcreateCanvas: function(width, height) {\n\t\treturn CanvasProvider.getCanvas(width, height);\n\t},\n\n\tactivate: function() {\n\t\tpaper = this;\n\t},\n\n\tclear: function() {\n\t\tvar projects = this.projects,\n\t\t\ttools = this.tools;\n\t\tfor (var i = projects.length - 1; i >= 0; i--)\n\t\t\tprojects[i].remove();\n\t\tfor (var i = tools.length - 1; i >= 0; i--)\n\t\t\ttools[i].remove();\n\t},\n\n\tremove: function() {\n\t\tthis.clear();\n\t\tdelete PaperScope._scopes[this._id];\n\t},\n\n\tstatics: new function() {\n\t\tfunction handleAttribute(name) {\n\t\t\tname += 'Attribute';\n\t\t\treturn function(el, attr) {\n\t\t\t\treturn el[name](attr) || el[name]('data-paper-' + attr);\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\t_scopes: {},\n\t\t\t_id: 0,\n\n\t\t\tget: function(id) {\n\t\t\t\treturn this._scopes[id] || null;\n\t\t\t},\n\n\t\t\tgetAttribute: handleAttribute('get'),\n\t\t\thasAttribute: handleAttribute('has')\n\t\t};\n\t}\n});\n\nvar PaperScopeItem = Base.extend(Emitter, {\n\n\tinitialize: function(activate) {\n\t\tthis._scope = paper;\n\t\tthis._index = this._scope[this._list].push(this) - 1;\n\t\tif (activate || !this._scope[this._reference])\n\t\t\tthis.activate();\n\t},\n\n\tactivate: function() {\n\t\tif (!this._scope)\n\t\t\treturn false;\n\t\tvar prev = this._scope[this._reference];\n\t\tif (prev && prev !== this)\n\t\t\tprev.emit('deactivate');\n\t\tthis._scope[this._reference] = this;\n\t\tthis.emit('activate', prev);\n\t\treturn true;\n\t},\n\n\tisActive: function() {\n\t\treturn this._scope[this._reference] === this;\n\t},\n\n\tremove: function() {\n\t\tif (this._index == null)\n\t\t\treturn false;\n\t\tBase.splice(this._scope[this._list], null, this._index, 1);\n\t\tif (this._scope[this._reference] == this)\n\t\t\tthis._scope[this._reference] = null;\n\t\tthis._scope = null;\n\t\treturn true;\n\t},\n\n\tgetView: function() {\n\t\treturn this._scope.getView();\n\t}\n});\n\nvar CollisionDetection = {\n\tfindItemBoundsCollisions: function(items1, items2, tolerance) {\n\t\tfunction getBounds(items) {\n\t\t\tvar bounds = new Array(items.length);\n\t\t\tfor (var i = 0; i < items.length; i++) {\n\t\t\t\tvar rect = items[i].getBounds();\n\t\t\t\tbounds[i] = [rect.left, rect.top, rect.right, rect.bottom];\n\t\t\t}\n\t\t\treturn bounds;\n\t\t}\n\n\t\tvar bounds1 = getBounds(items1),\n\t\t\tbounds2 = !items2 || items2 === items1\n\t\t\t\t? bounds1\n\t\t\t\t: getBounds(items2);\n\t\treturn this.findBoundsCollisions(bounds1, bounds2, tolerance || 0);\n\t},\n\n\tfindCurveBoundsCollisions: function(curves1, curves2, tolerance, bothAxis) {\n\t\tfunction getBounds(curves) {\n\t\t\tvar min = Math.min,\n\t\t\t\tmax = Math.max,\n\t\t\t\tbounds = new Array(curves.length);\n\t\t\tfor (var i = 0; i < curves.length; i++) {\n\t\t\t\tvar v = curves[i];\n\t\t\t\tbounds[i] = [\n\t\t\t\t\tmin(v[0], v[2], v[4], v[6]),\n\t\t\t\t\tmin(v[1], v[3], v[5], v[7]),\n\t\t\t\t\tmax(v[0], v[2], v[4], v[6]),\n\t\t\t\t\tmax(v[1], v[3], v[5], v[7])\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn bounds;\n\t\t}\n\n\t\tvar bounds1 = getBounds(curves1),\n\t\t\tbounds2 = !curves2 || curves2 === curves1\n\t\t\t\t? bounds1\n\t\t\t\t: getBounds(curves2);\n\t\tif (bothAxis) {\n\t\t\tvar hor = this.findBoundsCollisions(\n\t\t\t\t\tbounds1, bounds2, tolerance || 0, false, true),\n\t\t\t\tver = this.findBoundsCollisions(\n\t\t\t\t\tbounds1, bounds2, tolerance || 0, true, true),\n\t\t\t\tlist = [];\n\t\t\tfor (var i = 0, l = hor.length; i < l; i++) {\n\t\t\t\tlist[i] = { hor: hor[i], ver: ver[i] };\n\t\t\t}\n\t\t\treturn list;\n\t\t}\n\t\treturn this.findBoundsCollisions(bounds1, bounds2, tolerance || 0);\n\t},\n\n\tfindBoundsCollisions: function(boundsA, boundsB, tolerance,\n\t\tsweepVertical, onlySweepAxisCollisions) {\n\t\tvar self = !boundsB || boundsA === boundsB,\n\t\t\tallBounds = self ? boundsA : boundsA.concat(boundsB),\n\t\t\tlengthA = boundsA.length,\n\t\t\tlengthAll = allBounds.length;\n\n\t\tfunction binarySearch(indices, coord, value) {\n\t\t\tvar lo = 0,\n\t\t\t\thi = indices.length;\n\t\t\twhile (lo < hi) {\n\t\t\t\tvar mid = (hi + lo) >>> 1;\n\t\t\t\tif (allBounds[indices[mid]][coord] < value) {\n\t\t\t\t\tlo = mid + 1;\n\t\t\t\t} else {\n\t\t\t\t\thi = mid;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn lo - 1;\n\t\t}\n\n\t\tvar pri0 = sweepVertical ? 1 : 0,\n\t\t\tpri1 = pri0 + 2,\n\t\t\tsec0 = sweepVertical ? 0 : 1,\n\t\t\tsec1 = sec0 + 2;\n\t\tvar allIndicesByPri0 = new Array(lengthAll);\n\t\tfor (var i = 0; i < lengthAll; i++) {\n\t\t\tallIndicesByPri0[i] = i;\n\t\t}\n\t\tallIndicesByPri0.sort(function(i1, i2) {\n\t\t\treturn allBounds[i1][pri0] - allBounds[i2][pri0];\n\t\t});\n\t\tvar activeIndicesByPri1 = [],\n\t\t\tallCollisions = new Array(lengthA);\n\t\tfor (var i = 0; i < lengthAll; i++) {\n\t\t\tvar curIndex = allIndicesByPri0[i],\n\t\t\t\tcurBounds = allBounds[curIndex],\n\t\t\t\torigIndex = self ? curIndex : curIndex - lengthA,\n\t\t\t\tisCurrentA = curIndex < lengthA,\n\t\t\t\tisCurrentB = self || !isCurrentA,\n\t\t\t\tcurCollisions = isCurrentA ? [] : null;\n\t\t\tif (activeIndicesByPri1.length) {\n\t\t\t\tvar pruneCount = binarySearch(activeIndicesByPri1, pri1,\n\t\t\t\t\t\tcurBounds[pri0] - tolerance) + 1;\n\t\t\t\tactiveIndicesByPri1.splice(0, pruneCount);\n\t\t\t\tif (self && onlySweepAxisCollisions) {\n\t\t\t\t\tcurCollisions = curCollisions.concat(activeIndicesByPri1);\n\t\t\t\t\tfor (var j = 0; j < activeIndicesByPri1.length; j++) {\n\t\t\t\t\t\tvar activeIndex = activeIndicesByPri1[j];\n\t\t\t\t\t\tallCollisions[activeIndex].push(origIndex);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvar curSec1 = curBounds[sec1],\n\t\t\t\t\t\tcurSec0 = curBounds[sec0];\n\t\t\t\t\tfor (var j = 0; j < activeIndicesByPri1.length; j++) {\n\t\t\t\t\t\tvar activeIndex = activeIndicesByPri1[j],\n\t\t\t\t\t\t\tactiveBounds = allBounds[activeIndex],\n\t\t\t\t\t\t\tisActiveA = activeIndex < lengthA,\n\t\t\t\t\t\t\tisActiveB = self || activeIndex >= lengthA;\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tonlySweepAxisCollisions ||\n\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\tisCurrentA && isActiveB ||\n\t\t\t\t\t\t\t\tisCurrentB && isActiveA\n\t\t\t\t\t\t\t) && (\n\t\t\t\t\t\t\t\tcurSec1 >= activeBounds[sec0] - tolerance &&\n\t\t\t\t\t\t\t\tcurSec0 <= activeBounds[sec1] + tolerance\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tif (isCurrentA && isActiveB) {\n\t\t\t\t\t\t\t\tcurCollisions.push(\n\t\t\t\t\t\t\t\t\tself ? activeIndex : activeIndex - lengthA);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isCurrentB && isActiveA) {\n\t\t\t\t\t\t\t\tallCollisions[activeIndex].push(origIndex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isCurrentA) {\n\t\t\t\tif (boundsA === boundsB) {\n\t\t\t\t\tcurCollisions.push(curIndex);\n\t\t\t\t}\n\t\t\t\tallCollisions[curIndex] = curCollisions;\n\t\t\t}\n\t\t\tif (activeIndicesByPri1.length) {\n\t\t\t\tvar curPri1 = curBounds[pri1],\n\t\t\t\t\tindex = binarySearch(activeIndicesByPri1, pri1, curPri1);\n\t\t\t\tactiveIndicesByPri1.splice(index + 1, 0, curIndex);\n\t\t\t} else {\n\t\t\t\tactiveIndicesByPri1.push(curIndex);\n\t\t\t}\n\t\t}\n\t\tfor (var i = 0; i < allCollisions.length; i++) {\n\t\t\tvar collisions = allCollisions[i];\n\t\t\tif (collisions) {\n\t\t\t\tcollisions.sort(function(i1, i2) { return i1 - i2; });\n\t\t\t}\n\t\t}\n\t\treturn allCollisions;\n\t}\n};\n\nvar Formatter = Base.extend({\n\tinitialize: function(precision) {\n\t\tthis.precision = Base.pick(precision, 5);\n\t\tthis.multiplier = Math.pow(10, this.precision);\n\t},\n\n\tnumber: function(val) {\n\t\treturn this.precision < 16\n\t\t\t\t? Math.round(val * this.multiplier) / this.multiplier : val;\n\t},\n\n\tpair: function(val1, val2, separator) {\n\t\treturn this.number(val1) + (separator || ',') + this.number(val2);\n\t},\n\n\tpoint: function(val, separator) {\n\t\treturn this.number(val.x) + (separator || ',') + this.number(val.y);\n\t},\n\n\tsize: function(val, separator) {\n\t\treturn this.number(val.width) + (separator || ',')\n\t\t\t\t+ this.number(val.height);\n\t},\n\n\trectangle: function(val, separator) {\n\t\treturn this.point(val, separator) + (separator || ',')\n\t\t\t\t+ this.size(val, separator);\n\t}\n});\n\nFormatter.instance = new Formatter();\n\nvar Numerical = new function() {\n\n\tvar abscissas = [\n\t\t[ 0.5773502691896257645091488],\n\t\t[0,0.7745966692414833770358531],\n\t\t[ 0.3399810435848562648026658,0.8611363115940525752239465],\n\t\t[0,0.5384693101056830910363144,0.9061798459386639927976269],\n\t\t[ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016],\n\t\t[0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897],\n\t\t[ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609],\n\t\t[0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762],\n\t\t[ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640],\n\t\t[0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380],\n\t\t[ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491],\n\t\t[0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294],\n\t\t[ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973],\n\t\t[0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657],\n\t\t[ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542]\n\t];\n\n\tvar weights = [\n\t\t[1],\n\t\t[0.8888888888888888888888889,0.5555555555555555555555556],\n\t\t[0.6521451548625461426269361,0.3478548451374538573730639],\n\t\t[0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640],\n\t\t[0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],\n\t\t[0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114],\n\t\t[0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314],\n\t\t[0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922],\n\t\t[0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688],\n\t\t[0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537],\n\t\t[0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160],\n\t\t[0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216],\n\t\t[0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329],\n\t\t[0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284],\n\t\t[0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806]\n\t];\n\n\tvar abs = Math.abs,\n\t\tsqrt = Math.sqrt,\n\t\tpow = Math.pow,\n\t\tlog2 = Math.log2 || function(x) {\n\t\t\treturn Math.log(x) * Math.LOG2E;\n\t\t},\n\t\tEPSILON = 1e-12,\n\t\tMACHINE_EPSILON = 1.12e-16;\n\n\tfunction clamp(value, min, max) {\n\t\treturn value < min ? min : value > max ? max : value;\n\t}\n\n\tfunction getDiscriminant(a, b, c) {\n\t\tfunction split(v) {\n\t\t\tvar x = v * 134217729,\n\t\t\t\ty = v - x,\n\t\t\t\thi = y + x,\n\t\t\t\tlo = v - hi;\n\t\t\treturn [hi, lo];\n\t\t}\n\n\t\tvar D = b * b - a * c,\n\t\t\tE = b * b + a * c;\n\t\tif (abs(D) * 3 < E) {\n\t\t\tvar ad = split(a),\n\t\t\t\tbd = split(b),\n\t\t\t\tcd = split(c),\n\t\t\t\tp = b * b,\n\t\t\t\tdp = (bd[0] * bd[0] - p + 2 * bd[0] * bd[1]) + bd[1] * bd[1],\n\t\t\t\tq = a * c,\n\t\t\t\tdq = (ad[0] * cd[0] - q + ad[0] * cd[1] + ad[1] * cd[0])\n\t\t\t\t\t\t+ ad[1] * cd[1];\n\t\t\tD = (p - q) + (dp - dq);\n\t\t}\n\t\treturn D;\n\t}\n\n\tfunction getNormalizationFactor() {\n\t\tvar norm = Math.max.apply(Math, arguments);\n\t\treturn norm && (norm < 1e-8 || norm > 1e8)\n\t\t\t\t? pow(2, -Math.round(log2(norm)))\n\t\t\t\t: 0;\n\t}\n\n\treturn {\n\t\tEPSILON: EPSILON,\n\t\tMACHINE_EPSILON: MACHINE_EPSILON,\n\t\tCURVETIME_EPSILON: 1e-8,\n\t\tGEOMETRIC_EPSILON: 1e-7,\n\t\tTRIGONOMETRIC_EPSILON: 1e-8,\n\t\tKAPPA: 4 * (sqrt(2) - 1) / 3,\n\n\t\tisZero: function(val) {\n\t\t\treturn val >= -EPSILON && val <= EPSILON;\n\t\t},\n\n\t\tisMachineZero: function(val) {\n\t\t\treturn val >= -MACHINE_EPSILON && val <= MACHINE_EPSILON;\n\t\t},\n\n\t\tclamp: clamp,\n\n\t\tintegrate: function(f, a, b, n) {\n\t\t\tvar x = abscissas[n - 2],\n\t\t\t\tw = weights[n - 2],\n\t\t\t\tA = (b - a) * 0.5,\n\t\t\t\tB = A + a,\n\t\t\t\ti = 0,\n\t\t\t\tm = (n + 1) >> 1,\n\t\t\t\tsum = n & 1 ? w[i++] * f(B) : 0;\n\t\t\twhile (i < m) {\n\t\t\t\tvar Ax = A * x[i];\n\t\t\t\tsum += w[i++] * (f(B + Ax) + f(B - Ax));\n\t\t\t}\n\t\t\treturn A * sum;\n\t\t},\n\n\t\tfindRoot: function(f, df, x, a, b, n, tolerance) {\n\t\t\tfor (var i = 0; i < n; i++) {\n\t\t\t\tvar fx = f(x),\n\t\t\t\t\tdx = fx / df(x),\n\t\t\t\t\tnx = x - dx;\n\t\t\t\tif (abs(dx) < tolerance) {\n\t\t\t\t\tx = nx;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (fx > 0) {\n\t\t\t\t\tb = x;\n\t\t\t\t\tx = nx <= a ? (a + b) * 0.5 : nx;\n\t\t\t\t} else {\n\t\t\t\t\ta = x;\n\t\t\t\t\tx = nx >= b ? (a + b) * 0.5 : nx;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn clamp(x, a, b);\n\t\t},\n\n\t\tsolveQuadratic: function(a, b, c, roots, min, max) {\n\t\t\tvar x1, x2 = Infinity;\n\t\t\tif (abs(a) < EPSILON) {\n\t\t\t\tif (abs(b) < EPSILON)\n\t\t\t\t\treturn abs(c) < EPSILON ? -1 : 0;\n\t\t\t\tx1 = -c / b;\n\t\t\t} else {\n\t\t\t\tb *= -0.5;\n\t\t\t\tvar D = getDiscriminant(a, b, c);\n\t\t\t\tif (D && abs(D) < MACHINE_EPSILON) {\n\t\t\t\t\tvar f = getNormalizationFactor(abs(a), abs(b), abs(c));\n\t\t\t\t\tif (f) {\n\t\t\t\t\t\ta *= f;\n\t\t\t\t\t\tb *= f;\n\t\t\t\t\t\tc *= f;\n\t\t\t\t\t\tD = getDiscriminant(a, b, c);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (D >= -MACHINE_EPSILON) {\n\t\t\t\t\tvar Q = D < 0 ? 0 : sqrt(D),\n\t\t\t\t\t\tR = b + (b < 0 ? -Q : Q);\n\t\t\t\t\tif (R === 0) {\n\t\t\t\t\t\tx1 = c / a;\n\t\t\t\t\t\tx2 = -x1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tx1 = R / a;\n\t\t\t\t\t\tx2 = c / R;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar count = 0,\n\t\t\t\tboundless = min == null,\n\t\t\t\tminB = min - EPSILON,\n\t\t\t\tmaxB = max + EPSILON;\n\t\t\tif (isFinite(x1) && (boundless || x1 > minB && x1 < maxB))\n\t\t\t\troots[count++] = boundless ? x1 : clamp(x1, min, max);\n\t\t\tif (x2 !== x1\n\t\t\t\t\t&& isFinite(x2) && (boundless || x2 > minB && x2 < maxB))\n\t\t\t\troots[count++] = boundless ? x2 : clamp(x2, min, max);\n\t\t\treturn count;\n\t\t},\n\n\t\tsolveCubic: function(a, b, c, d, roots, min, max) {\n\t\t\tvar f = getNormalizationFactor(abs(a), abs(b), abs(c), abs(d)),\n\t\t\t\tx, b1, c2, qd, q;\n\t\t\tif (f) {\n\t\t\t\ta *= f;\n\t\t\t\tb *= f;\n\t\t\t\tc *= f;\n\t\t\t\td *= f;\n\t\t\t}\n\n\t\t\tfunction evaluate(x0) {\n\t\t\t\tx = x0;\n\t\t\t\tvar tmp = a * x;\n\t\t\t\tb1 = tmp + b;\n\t\t\t\tc2 = b1 * x + c;\n\t\t\t\tqd = (tmp + b1) * x + c2;\n\t\t\t\tq = c2 * x + d;\n\t\t\t}\n\n\t\t\tif (abs(a) < EPSILON) {\n\t\t\t\ta = b;\n\t\t\t\tb1 = c;\n\t\t\t\tc2 = d;\n\t\t\t\tx = Infinity;\n\t\t\t} else if (abs(d) < EPSILON) {\n\t\t\t\tb1 = b;\n\t\t\t\tc2 = c;\n\t\t\t\tx = 0;\n\t\t\t} else {\n\t\t\t\tevaluate(-(b / a) / 3);\n\t\t\t\tvar t = q / a,\n\t\t\t\t\tr = pow(abs(t), 1/3),\n\t\t\t\t\ts = t < 0 ? -1 : 1,\n\t\t\t\t\ttd = -qd / a,\n\t\t\t\t\trd = td > 0 ? 1.324717957244746 * Math.max(r, sqrt(td)) : r,\n\t\t\t\t\tx0 = x - s * rd;\n\t\t\t\tif (x0 !== x) {\n\t\t\t\t\tdo {\n\t\t\t\t\t\tevaluate(x0);\n\t\t\t\t\t\tx0 = qd === 0 ? x : x - q / qd / (1 + MACHINE_EPSILON);\n\t\t\t\t\t} while (s * x0 > s * x);\n\t\t\t\t\tif (abs(a) * x * x > abs(d / x)) {\n\t\t\t\t\t\tc2 = -d / x;\n\t\t\t\t\t\tb1 = (c2 - c) / x;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar count = Numerical.solveQuadratic(a, b1, c2, roots, min, max),\n\t\t\t\tboundless = min == null;\n\t\t\tif (isFinite(x) && (count === 0\n\t\t\t\t\t|| count > 0 && x !== roots[0] && x !== roots[1])\n\t\t\t\t\t&& (boundless || x > min - EPSILON && x < max + EPSILON))\n\t\t\t\troots[count++] = boundless ? x : clamp(x, min, max);\n\t\t\treturn count;\n\t\t}\n\t};\n};\n\nvar UID = {\n\t_id: 1,\n\t_pools: {},\n\n\tget: function(name) {\n\t\tif (name) {\n\t\t\tvar pool = this._pools[name];\n\t\t\tif (!pool)\n\t\t\t\tpool = this._pools[name] = { _id: 1 };\n\t\t\treturn pool._id++;\n\t\t} else {\n\t\t\treturn this._id++;\n\t\t}\n\t}\n};\n\nvar Point = Base.extend({\n\t_class: 'Point',\n\t_readIndex: true,\n\n\tinitialize: function Point(arg0, arg1) {\n\t\tvar type = typeof arg0,\n\t\t\treading = this.__read,\n\t\t\tread = 0;\n\t\tif (type === 'number') {\n\t\t\tvar hasY = typeof arg1 === 'number';\n\t\t\tthis._set(arg0, hasY ? arg1 : arg0);\n\t\t\tif (reading)\n\t\t\t\tread = hasY ? 2 : 1;\n\t\t} else if (type === 'undefined' || arg0 === null) {\n\t\t\tthis._set(0, 0);\n\t\t\tif (reading)\n\t\t\t\tread = arg0 === null ? 1 : 0;\n\t\t} else {\n\t\t\tvar obj = type === 'string' ? arg0.split(/[\\s,]+/) || [] : arg0;\n\t\t\tread = 1;\n\t\t\tif (Array.isArray(obj)) {\n\t\t\t\tthis._set(+obj[0], +(obj.length > 1 ? obj[1] : obj[0]));\n\t\t\t} else if ('x' in obj) {\n\t\t\t\tthis._set(obj.x || 0, obj.y || 0);\n\t\t\t} else if ('width' in obj) {\n\t\t\t\tthis._set(obj.width || 0, obj.height || 0);\n\t\t\t} else if ('angle' in obj) {\n\t\t\t\tthis._set(obj.length || 0, 0);\n\t\t\t\tthis.setAngle(obj.angle || 0);\n\t\t\t} else {\n\t\t\t\tthis._set(0, 0);\n\t\t\t\tread = 0;\n\t\t\t}\n\t\t}\n\t\tif (reading)\n\t\t\tthis.__read = read;\n\t\treturn this;\n\t},\n\n\tset: '#initialize',\n\n\t_set: function(x, y) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\treturn this;\n\t},\n\n\tequals: function(point) {\n\t\treturn this === point || point\n\t\t\t\t&& (this.x === point.x && this.y === point.y\n\t\t\t\t\t|| Array.isArray(point)\n\t\t\t\t\t\t&& this.x === point[0] && this.y === point[1])\n\t\t\t\t|| false;\n\t},\n\n\tclone: function() {\n\t\treturn new Point(this.x, this.y);\n\t},\n\n\ttoString: function() {\n\t\tvar f = Formatter.instance;\n\t\treturn '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';\n\t},\n\n\t_serialize: function(options) {\n\t\tvar f = options.formatter;\n\t\treturn [f.number(this.x), f.number(this.y)];\n\t},\n\n\tgetLength: function() {\n\t\treturn Math.sqrt(this.x * this.x + this.y * this.y);\n\t},\n\n\tsetLength: function(length) {\n\t\tif (this.isZero()) {\n\t\t\tvar angle = this._angle || 0;\n\t\t\tthis._set(\n\t\t\t\tMath.cos(angle) * length,\n\t\t\t\tMath.sin(angle) * length\n\t\t\t);\n\t\t} else {\n\t\t\tvar scale = length / this.getLength();\n\t\t\tif (Numerical.isZero(scale))\n\t\t\t\tthis.getAngle();\n\t\t\tthis._set(\n\t\t\t\tthis.x * scale,\n\t\t\t\tthis.y * scale\n\t\t\t);\n\t\t}\n\t},\n\tgetAngle: function() {\n\t\treturn this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI;\n\t},\n\n\tsetAngle: function(angle) {\n\t\tthis.setAngleInRadians.call(this, angle * Math.PI / 180);\n\t},\n\n\tgetAngleInDegrees: '#getAngle',\n\tsetAngleInDegrees: '#setAngle',\n\n\tgetAngleInRadians: function() {\n\t\tif (!arguments.length) {\n\t\t\treturn this.isZero()\n\t\t\t\t\t? this._angle || 0\n\t\t\t\t\t: this._angle = Math.atan2(this.y, this.x);\n\t\t} else {\n\t\t\tvar point = Point.read(arguments),\n\t\t\t\tdiv = this.getLength() * point.getLength();\n\t\t\tif (Numerical.isZero(div)) {\n\t\t\t\treturn NaN;\n\t\t\t} else {\n\t\t\t\tvar a = this.dot(point) / div;\n\t\t\t\treturn Math.acos(a < -1 ? -1 : a > 1 ? 1 : a);\n\t\t\t}\n\t\t}\n\t},\n\n\tsetAngleInRadians: function(angle) {\n\t\tthis._angle = angle;\n\t\tif (!this.isZero()) {\n\t\t\tvar length = this.getLength();\n\t\t\tthis._set(\n\t\t\t\tMath.cos(angle) * length,\n\t\t\t\tMath.sin(angle) * length\n\t\t\t);\n\t\t}\n\t},\n\n\tgetQuadrant: function() {\n\t\treturn this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;\n\t}\n}, {\n\tbeans: false,\n\n\tgetDirectedAngle: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;\n\t},\n\n\tgetDistance: function() {\n\t\tvar args = arguments,\n\t\t\tpoint = Point.read(args),\n\t\t\tx = point.x - this.x,\n\t\t\ty = point.y - this.y,\n\t\t\td = x * x + y * y,\n\t\t\tsquared = Base.read(args);\n\t\treturn squared ? d : Math.sqrt(d);\n\t},\n\n\tnormalize: function(length) {\n\t\tif (length === undefined)\n\t\t\tlength = 1;\n\t\tvar current = this.getLength(),\n\t\t\tscale = current !== 0 ? length / current : 0,\n\t\t\tpoint = new Point(this.x * scale, this.y * scale);\n\t\tif (scale >= 0)\n\t\t\tpoint._angle = this._angle;\n\t\treturn point;\n\t},\n\n\trotate: function(angle, center) {\n\t\tif (angle === 0)\n\t\t\treturn this.clone();\n\t\tangle = angle * Math.PI / 180;\n\t\tvar point = center ? this.subtract(center) : this,\n\t\t\tsin = Math.sin(angle),\n\t\t\tcos = Math.cos(angle);\n\t\tpoint = new Point(\n\t\t\tpoint.x * cos - point.y * sin,\n\t\t\tpoint.x * sin + point.y * cos\n\t\t);\n\t\treturn center ? point.add(center) : point;\n\t},\n\n\ttransform: function(matrix) {\n\t\treturn matrix ? matrix._transformPoint(this) : this;\n\t},\n\n\tadd: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn new Point(this.x + point.x, this.y + point.y);\n\t},\n\n\tsubtract: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn new Point(this.x - point.x, this.y - point.y);\n\t},\n\n\tmultiply: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn new Point(this.x * point.x, this.y * point.y);\n\t},\n\n\tdivide: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn new Point(this.x / point.x, this.y / point.y);\n\t},\n\n\tmodulo: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn new Point(this.x % point.x, this.y % point.y);\n\t},\n\n\tnegate: function() {\n\t\treturn new Point(-this.x, -this.y);\n\t},\n\n\tisInside: function() {\n\t\treturn Rectangle.read(arguments).contains(this);\n\t},\n\n\tisClose: function() {\n\t\tvar args = arguments,\n\t\t\tpoint = Point.read(args),\n\t\t\ttolerance = Base.read(args);\n\t\treturn this.getDistance(point) <= tolerance;\n\t},\n\n\tisCollinear: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn Point.isCollinear(this.x, this.y, point.x, point.y);\n\t},\n\n\tisColinear: '#isCollinear',\n\n\tisOrthogonal: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn Point.isOrthogonal(this.x, this.y, point.x, point.y);\n\t},\n\n\tisZero: function() {\n\t\tvar isZero = Numerical.isZero;\n\t\treturn isZero(this.x) && isZero(this.y);\n\t},\n\n\tisNaN: function() {\n\t\treturn isNaN(this.x) || isNaN(this.y);\n\t},\n\n\tisInQuadrant: function(q) {\n\t\treturn this.x * (q > 1 && q < 4 ? -1 : 1) >= 0\n\t\t\t&& this.y * (q > 2 ? -1 : 1) >= 0;\n\t},\n\n\tdot: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn this.x * point.x + this.y * point.y;\n\t},\n\n\tcross: function() {\n\t\tvar point = Point.read(arguments);\n\t\treturn this.x * point.y - this.y * point.x;\n\t},\n\n\tproject: function() {\n\t\tvar point = Point.read(arguments),\n\t\t\tscale = point.isZero() ? 0 : this.dot(point) / point.dot(point);\n\t\treturn new Point(\n\t\t\tpoint.x * scale,\n\t\t\tpoint.y * scale\n\t\t);\n\t},\n\n\tstatics: {\n\t\tmin: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tpoint1 = Point.read(args),\n\t\t\t\tpoint2 = Point.read(args);\n\t\t\treturn new Point(\n\t\t\t\tMath.min(point1.x, point2.x),\n\t\t\t\tMath.min(point1.y, point2.y)\n\t\t\t);\n\t\t},\n\n\t\tmax: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tpoint1 = Point.read(args),\n\t\t\t\tpoint2 = Point.read(args);\n\t\t\treturn new Point(\n\t\t\t\tMath.max(point1.x, point2.x),\n\t\t\t\tMath.max(point1.y, point2.y)\n\t\t\t);\n\t\t},\n\n\t\trandom: function() {\n\t\t\treturn new Point(Math.random(), Math.random());\n\t\t},\n\n\t\tisCollinear: function(x1, y1, x2, y2) {\n\t\t\treturn Math.abs(x1 * y2 - y1 * x2)\n\t\t\t\t\t<= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))\n\t\t\t\t\t\t* 1e-8;\n\t\t},\n\n\t\tisOrthogonal: function(x1, y1, x2, y2) {\n\t\t\treturn Math.abs(x1 * x2 + y1 * y2)\n\t\t\t\t\t<= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))\n\t\t\t\t\t\t* 1e-8;\n\t\t}\n\t}\n}, Base.each(['round', 'ceil', 'floor', 'abs'], function(key) {\n\tvar op = Math[key];\n\tthis[key] = function() {\n\t\treturn new Point(op(this.x), op(this.y));\n\t};\n}, {}));\n\nvar LinkedPoint = Point.extend({\n\tinitialize: function Point(x, y, owner, setter) {\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._owner = owner;\n\t\tthis._setter = setter;\n\t},\n\n\t_set: function(x, y, _dontNotify) {\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tif (!_dontNotify)\n\t\t\tthis._owner[this._setter](this);\n\t\treturn this;\n\t},\n\n\tgetX: function() {\n\t\treturn this._x;\n\t},\n\n\tsetX: function(x) {\n\t\tthis._x = x;\n\t\tthis._owner[this._setter](this);\n\t},\n\n\tgetY: function() {\n\t\treturn this._y;\n\t},\n\n\tsetY: function(y) {\n\t\tthis._y = y;\n\t\tthis._owner[this._setter](this);\n\t},\n\n\tisSelected: function() {\n\t\treturn !!(this._owner._selection & this._getSelection());\n\t},\n\n\tsetSelected: function(selected) {\n\t\tthis._owner._changeSelection(this._getSelection(), selected);\n\t},\n\n\t_getSelection: function() {\n\t\treturn this._setter === 'setPosition' ? 4 : 0;\n\t}\n});\n\nvar Size = Base.extend({\n\t_class: 'Size',\n\t_readIndex: true,\n\n\tinitialize: function Size(arg0, arg1) {\n\t\tvar type = typeof arg0,\n\t\t\treading = this.__read,\n\t\t\tread = 0;\n\t\tif (type === 'number') {\n\t\t\tvar hasHeight = typeof arg1 === 'number';\n\t\t\tthis._set(arg0, hasHeight ? arg1 : arg0);\n\t\t\tif (reading)\n\t\t\t\tread = hasHeight ? 2 : 1;\n\t\t} else if (type === 'undefined' || arg0 === null) {\n\t\t\tthis._set(0, 0);\n\t\t\tif (reading)\n\t\t\t\tread = arg0 === null ? 1 : 0;\n\t\t} else {\n\t\t\tvar obj = type === 'string' ? arg0.split(/[\\s,]+/) || [] : arg0;\n\t\t\tread = 1;\n\t\t\tif (Array.isArray(obj)) {\n\t\t\t\tthis._set(+obj[0], +(obj.length > 1 ? obj[1] : obj[0]));\n\t\t\t} else if ('width' in obj) {\n\t\t\t\tthis._set(obj.width || 0, obj.height || 0);\n\t\t\t} else if ('x' in obj) {\n\t\t\t\tthis._set(obj.x || 0, obj.y || 0);\n\t\t\t} else {\n\t\t\t\tthis._set(0, 0);\n\t\t\t\tread = 0;\n\t\t\t}\n\t\t}\n\t\tif (reading)\n\t\t\tthis.__read = read;\n\t\treturn this;\n\t},\n\n\tset: '#initialize',\n\n\t_set: function(width, height) {\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\treturn this;\n\t},\n\n\tequals: function(size) {\n\t\treturn size === this || size && (this.width === size.width\n\t\t\t\t&& this.height === size.height\n\t\t\t\t|| Array.isArray(size) && this.width === size[0]\n\t\t\t\t\t&& this.height === size[1]) || false;\n\t},\n\n\tclone: function() {\n\t\treturn new Size(this.width, this.height);\n\t},\n\n\ttoString: function() {\n\t\tvar f = Formatter.instance;\n\t\treturn '{ width: ' + f.number(this.width)\n\t\t\t\t+ ', height: ' + f.number(this.height) + ' }';\n\t},\n\n\t_serialize: function(options) {\n\t\tvar f = options.formatter;\n\t\treturn [f.number(this.width),\n\t\t\t\tf.number(this.height)];\n\t},\n\n\tadd: function() {\n\t\tvar size = Size.read(arguments);\n\t\treturn new Size(this.width + size.width, this.height + size.height);\n\t},\n\n\tsubtract: function() {\n\t\tvar size = Size.read(arguments);\n\t\treturn new Size(this.width - size.width, this.height - size.height);\n\t},\n\n\tmultiply: function() {\n\t\tvar size = Size.read(arguments);\n\t\treturn new Size(this.width * size.width, this.height * size.height);\n\t},\n\n\tdivide: function() {\n\t\tvar size = Size.read(arguments);\n\t\treturn new Size(this.width / size.width, this.height / size.height);\n\t},\n\n\tmodulo: function() {\n\t\tvar size = Size.read(arguments);\n\t\treturn new Size(this.width % size.width, this.height % size.height);\n\t},\n\n\tnegate: function() {\n\t\treturn new Size(-this.width, -this.height);\n\t},\n\n\tisZero: function() {\n\t\tvar isZero = Numerical.isZero;\n\t\treturn isZero(this.width) && isZero(this.height);\n\t},\n\n\tisNaN: function() {\n\t\treturn isNaN(this.width) || isNaN(this.height);\n\t},\n\n\tstatics: {\n\t\tmin: function(size1, size2) {\n\t\t\treturn new Size(\n\t\t\t\tMath.min(size1.width, size2.width),\n\t\t\t\tMath.min(size1.height, size2.height));\n\t\t},\n\n\t\tmax: function(size1, size2) {\n\t\t\treturn new Size(\n\t\t\t\tMath.max(size1.width, size2.width),\n\t\t\t\tMath.max(size1.height, size2.height));\n\t\t},\n\n\t\trandom: function() {\n\t\t\treturn new Size(Math.random(), Math.random());\n\t\t}\n\t}\n}, Base.each(['round', 'ceil', 'floor', 'abs'], function(key) {\n\tvar op = Math[key];\n\tthis[key] = function() {\n\t\treturn new Size(op(this.width), op(this.height));\n\t};\n}, {}));\n\nvar LinkedSize = Size.extend({\n\tinitialize: function Size(width, height, owner, setter) {\n\t\tthis._width = width;\n\t\tthis._height = height;\n\t\tthis._owner = owner;\n\t\tthis._setter = setter;\n\t},\n\n\t_set: function(width, height, _dontNotify) {\n\t\tthis._width = width;\n\t\tthis._height = height;\n\t\tif (!_dontNotify)\n\t\t\tthis._owner[this._setter](this);\n\t\treturn this;\n\t},\n\n\tgetWidth: function() {\n\t\treturn this._width;\n\t},\n\n\tsetWidth: function(width) {\n\t\tthis._width = width;\n\t\tthis._owner[this._setter](this);\n\t},\n\n\tgetHeight: function() {\n\t\treturn this._height;\n\t},\n\n\tsetHeight: function(height) {\n\t\tthis._height = height;\n\t\tthis._owner[this._setter](this);\n\t}\n});\n\nvar Rectangle = Base.extend({\n\t_class: 'Rectangle',\n\t_readIndex: true,\n\tbeans: true,\n\n\tinitialize: function Rectangle(arg0, arg1, arg2, arg3) {\n\t\tvar args = arguments,\n\t\t\ttype = typeof arg0,\n\t\t\tread;\n\t\tif (type === 'number') {\n\t\t\tthis._set(arg0, arg1, arg2, arg3);\n\t\t\tread = 4;\n\t\t} else if (type === 'undefined' || arg0 === null) {\n\t\t\tthis._set(0, 0, 0, 0);\n\t\t\tread = arg0 === null ? 1 : 0;\n\t\t} else if (args.length === 1) {\n\t\t\tif (Array.isArray(arg0)) {\n\t\t\t\tthis._set.apply(this, arg0);\n\t\t\t\tread = 1;\n\t\t\t} else if (arg0.x !== undefined || arg0.width !== undefined) {\n\t\t\t\tthis._set(arg0.x || 0, arg0.y || 0,\n\t\t\t\t\t\targ0.width || 0, arg0.height || 0);\n\t\t\t\tread = 1;\n\t\t\t} else if (arg0.from === undefined && arg0.to === undefined) {\n\t\t\t\tthis._set(0, 0, 0, 0);\n\t\t\t\tif (Base.readSupported(args, this)) {\n\t\t\t\t\tread = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (read === undefined) {\n\t\t\tvar frm = Point.readNamed(args, 'from'),\n\t\t\t\tnext = Base.peek(args),\n\t\t\t\tx = frm.x,\n\t\t\t\ty = frm.y,\n\t\t\t\twidth,\n\t\t\t\theight;\n\t\t\tif (next && next.x !== undefined || Base.hasNamed(args, 'to')) {\n\t\t\t\tvar to = Point.readNamed(args, 'to');\n\t\t\t\twidth = to.x - x;\n\t\t\t\theight = to.y - y;\n\t\t\t\tif (width < 0) {\n\t\t\t\t\tx = to.x;\n\t\t\t\t\twidth = -width;\n\t\t\t\t}\n\t\t\t\tif (height < 0) {\n\t\t\t\t\ty = to.y;\n\t\t\t\t\theight = -height;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar size = Size.read(args);\n\t\t\t\twidth = size.width;\n\t\t\t\theight = size.height;\n\t\t\t}\n\t\t\tthis._set(x, y, width, height);\n\t\t\tread = args.__index;\n\t\t}\n\t\tvar filtered = args.__filtered;\n\t\tif (filtered)\n\t\t\tthis.__filtered = filtered;\n\t\tif (this.__read)\n\t\t\tthis.__read = read;\n\t\treturn this;\n\t},\n\n\tset: '#initialize',\n\n\t_set: function(x, y, width, height) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\treturn this;\n\t},\n\n\tclone: function() {\n\t\treturn new Rectangle(this.x, this.y, this.width, this.height);\n\t},\n\n\tequals: function(rect) {\n\t\tvar rt = Base.isPlainValue(rect)\n\t\t\t\t? Rectangle.read(arguments)\n\t\t\t\t: rect;\n\t\treturn rt === this\n\t\t\t\t|| rt && this.x === rt.x && this.y === rt.y\n\t\t\t\t\t&& this.width === rt.width && this.height === rt.height\n\t\t\t\t|| false;\n\t},\n\n\ttoString: function() {\n\t\tvar f = Formatter.instance;\n\t\treturn '{ x: ' + f.number(this.x)\n\t\t\t\t+ ', y: ' + f.number(this.y)\n\t\t\t\t+ ', width: ' + f.number(this.width)\n\t\t\t\t+ ', height: ' + f.number(this.height)\n\t\t\t\t+ ' }';\n\t},\n\n\t_serialize: function(options) {\n\t\tvar f = options.formatter;\n\t\treturn [f.number(this.x),\n\t\t\t\tf.number(this.y),\n\t\t\t\tf.number(this.width),\n\t\t\t\tf.number(this.height)];\n\t},\n\n\tgetPoint: function(_dontLink) {\n\t\tvar ctor = _dontLink ? Point : LinkedPoint;\n\t\treturn new ctor(this.x, this.y, this, 'setPoint');\n\t},\n\n\tsetPoint: function() {\n\t\tvar point = Point.read(arguments);\n\t\tthis.x = point.x;\n\t\tthis.y = point.y;\n\t},\n\n\tgetSize: function(_dontLink) {\n\t\tvar ctor = _dontLink ? Size : LinkedSize;\n\t\treturn new ctor(this.width, this.height, this, 'setSize');\n\t},\n\n\t_fw: 1,\n\t_fh: 1,\n\n\tsetSize: function() {\n\t\tvar size = Size.read(arguments),\n\t\t\tsx = this._sx,\n\t\t\tsy = this._sy,\n\t\t\tw = size.width,\n\t\t\th = size.height;\n\t\tif (sx) {\n\t\t\tthis.x += (this.width - w) * sx;\n\t\t}\n\t\tif (sy) {\n\t\t\tthis.y += (this.height - h) * sy;\n\t\t}\n\t\tthis.width = w;\n\t\tthis.height = h;\n\t\tthis._fw = this._fh = 1;\n\t},\n\n\tgetLeft: function() {\n\t\treturn this.x;\n\t},\n\n\tsetLeft: function(left) {\n\t\tif (!this._fw) {\n\t\t\tvar amount = left - this.x;\n\t\t\tthis.width -= this._sx === 0.5 ? amount * 2 : amount;\n\t\t}\n\t\tthis.x = left;\n\t\tthis._sx = this._fw = 0;\n\t},\n\n\tgetTop: function() {\n\t\treturn this.y;\n\t},\n\n\tsetTop: function(top) {\n\t\tif (!this._fh) {\n\t\t\tvar amount = top - this.y;\n\t\t\tthis.height -= this._sy === 0.5 ? amount * 2 : amount;\n\t\t}\n\t\tthis.y = top;\n\t\tthis._sy = this._fh = 0;\n\t},\n\n\tgetRight: function() {\n\t\treturn this.x + this.width;\n\t},\n\n\tsetRight: function(right) {\n\t\tif (!this._fw) {\n\t\t\tvar amount = right - this.x;\n\t\t\tthis.width = this._sx === 0.5 ? amount * 2 : amount;\n\t\t}\n\t\tthis.x = right - this.width;\n\t\tthis._sx = 1;\n\t\tthis._fw = 0;\n\t},\n\n\tgetBottom: function() {\n\t\treturn this.y + this.height;\n\t},\n\n\tsetBottom: function(bottom) {\n\t\tif (!this._fh) {\n\t\t\tvar amount = bottom - this.y;\n\t\t\tthis.height = this._sy === 0.5 ? amount * 2 : amount;\n\t\t}\n\t\tthis.y = bottom - this.height;\n\t\tthis._sy = 1;\n\t\tthis._fh = 0;\n\t},\n\n\tgetCenterX: function() {\n\t\treturn this.x + this.width / 2;\n\t},\n\n\tsetCenterX: function(x) {\n\t\tif (this._fw || this._sx === 0.5) {\n\t\t\tthis.x = x - this.width / 2;\n\t\t} else {\n\t\t\tif (this._sx) {\n\t\t\t\tthis.x += (x - this.x) * 2 * this._sx;\n\t\t\t}\n\t\t\tthis.width = (x - this.x) * 2;\n\t\t}\n\t\tthis._sx = 0.5;\n\t\tthis._fw = 0;\n\t},\n\n\tgetCenterY: function() {\n\t\treturn this.y + this.height / 2;\n\t},\n\n\tsetCenterY: function(y) {\n\t\tif (this._fh || this._sy === 0.5) {\n\t\t\tthis.y = y - this.height / 2;\n\t\t} else {\n\t\t\tif (this._sy) {\n\t\t\t\tthis.y += (y - this.y) * 2 * this._sy;\n\t\t\t}\n\t\t\tthis.height = (y - this.y) * 2;\n\t\t}\n\t\tthis._sy = 0.5;\n\t\tthis._fh = 0;\n\t},\n\n\tgetCenter: function(_dontLink) {\n\t\tvar ctor = _dontLink ? Point : LinkedPoint;\n\t\treturn new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');\n\t},\n\n\tsetCenter: function() {\n\t\tvar point = Point.read(arguments);\n\t\tthis.setCenterX(point.x);\n\t\tthis.setCenterY(point.y);\n\t\treturn this;\n\t},\n\n\tgetArea: function() {\n\t\treturn this.width * this.height;\n\t},\n\n\tisEmpty: function() {\n\t\treturn this.width === 0 || this.height === 0;\n\t},\n\n\tcontains: function(arg) {\n\t\treturn arg && arg.width !== undefined\n\t\t\t\t|| (Array.isArray(arg) ? arg : arguments).length === 4\n\t\t\t\t? this._containsRectangle(Rectangle.read(arguments))\n\t\t\t\t: this._containsPoint(Point.read(arguments));\n\t},\n\n\t_containsPoint: function(point) {\n\t\tvar x = point.x,\n\t\t\ty = point.y;\n\t\treturn x >= this.x && y >= this.y\n\t\t\t\t&& x <= this.x + this.width\n\t\t\t\t&& y <= this.y + this.height;\n\t},\n\n\t_containsRectangle: function(rect) {\n\t\tvar x = rect.x,\n\t\t\ty = rect.y;\n\t\treturn x >= this.x && y >= this.y\n\t\t\t\t&& x + rect.width <= this.x + this.width\n\t\t\t\t&& y + rect.height <= this.y + this.height;\n\t},\n\n\tintersects: function() {\n\t\tvar rect = Rectangle.read(arguments),\n\t\t\tepsilon = Base.read(arguments) || 0;\n\t\treturn rect.x + rect.width > this.x - epsilon\n\t\t\t\t&& rect.y + rect.height > this.y - epsilon\n\t\t\t\t&& rect.x < this.x + this.width + epsilon\n\t\t\t\t&& rect.y < this.y + this.height + epsilon;\n\t},\n\n\tintersect: function() {\n\t\tvar rect = Rectangle.read(arguments),\n\t\t\tx1 = Math.max(this.x, rect.x),\n\t\t\ty1 = Math.max(this.y, rect.y),\n\t\t\tx2 = Math.min(this.x + this.width, rect.x + rect.width),\n\t\t\ty2 = Math.min(this.y + this.height, rect.y + rect.height);\n\t\treturn new Rectangle(x1, y1, x2 - x1, y2 - y1);\n\t},\n\n\tunite: function() {\n\t\tvar rect = Rectangle.read(arguments),\n\t\t\tx1 = Math.min(this.x, rect.x),\n\t\t\ty1 = Math.min(this.y, rect.y),\n\t\t\tx2 = Math.max(this.x + this.width, rect.x + rect.width),\n\t\t\ty2 = Math.max(this.y + this.height, rect.y + rect.height);\n\t\treturn new Rectangle(x1, y1, x2 - x1, y2 - y1);\n\t},\n\n\tinclude: function() {\n\t\tvar point = Point.read(arguments);\n\t\tvar x1 = Math.min(this.x, point.x),\n\t\t\ty1 = Math.min(this.y, point.y),\n\t\t\tx2 = Math.max(this.x + this.width, point.x),\n\t\t\ty2 = Math.max(this.y + this.height, point.y);\n\t\treturn new Rectangle(x1, y1, x2 - x1, y2 - y1);\n\t},\n\n\texpand: function() {\n\t\tvar amount = Size.read(arguments),\n\t\t\thor = amount.width,\n\t\t\tver = amount.height;\n\t\treturn new Rectangle(this.x - hor / 2, this.y - ver / 2,\n\t\t\t\tthis.width + hor, this.height + ver);\n\t},\n\n\tscale: function(hor, ver) {\n\t\treturn this.expand(this.width * hor - this.width,\n\t\t\t\tthis.height * (ver === undefined ? hor : ver) - this.height);\n\t}\n}, Base.each([\n\t\t['Top', 'Left'], ['Top', 'Right'],\n\t\t['Bottom', 'Left'], ['Bottom', 'Right'],\n\t\t['Left', 'Center'], ['Top', 'Center'],\n\t\t['Right', 'Center'], ['Bottom', 'Center']\n\t],\n\tfunction(parts, index) {\n\t\tvar part = parts.join(''),\n\t\t\txFirst = /^[RL]/.test(part);\n\t\tif (index >= 4)\n\t\t\tparts[1] += xFirst ? 'Y' : 'X';\n\t\tvar x = parts[xFirst ? 0 : 1],\n\t\t\ty = parts[xFirst ? 1 : 0],\n\t\t\tgetX = 'get' + x,\n\t\t\tgetY = 'get' + y,\n\t\t\tsetX = 'set' + x,\n\t\t\tsetY = 'set' + y,\n\t\t\tget = 'get' + part,\n\t\t\tset = 'set' + part;\n\t\tthis[get] = function(_dontLink) {\n\t\t\tvar ctor = _dontLink ? Point : LinkedPoint;\n\t\t\treturn new ctor(this[getX](), this[getY](), this, set);\n\t\t};\n\t\tthis[set] = function() {\n\t\t\tvar point = Point.read(arguments);\n\t\t\tthis[setX](point.x);\n\t\t\tthis[setY](point.y);\n\t\t};\n\t}, {\n\t\tbeans: true\n\t}\n));\n\nvar LinkedRectangle = Rectangle.extend({\n\tinitialize: function Rectangle(x, y, width, height, owner, setter) {\n\t\tthis._set(x, y, width, height, true);\n\t\tthis._owner = owner;\n\t\tthis._setter = setter;\n\t},\n\n\t_set: function(x, y, width, height, _dontNotify) {\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._width = width;\n\t\tthis._height = height;\n\t\tif (!_dontNotify)\n\t\t\tthis._owner[this._setter](this);\n\t\treturn this;\n\t}\n},\nnew function() {\n\tvar proto = Rectangle.prototype;\n\n\treturn Base.each(['x', 'y', 'width', 'height'], function(key) {\n\t\tvar part = Base.capitalize(key),\n\t\t\tinternal = '_' + key;\n\t\tthis['get' + part] = function() {\n\t\t\treturn this[internal];\n\t\t};\n\n\t\tthis['set' + part] = function(value) {\n\t\t\tthis[internal] = value;\n\t\t\tif (!this._dontNotify)\n\t\t\t\tthis._owner[this._setter](this);\n\t\t};\n\t}, Base.each(['Point', 'Size', 'Center',\n\t\t\t'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',\n\t\t\t'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',\n\t\t\t'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],\n\t\tfunction(key) {\n\t\t\tvar name = 'set' + key;\n\t\t\tthis[name] = function() {\n\t\t\t\tthis._dontNotify = true;\n\t\t\t\tproto[name].apply(this, arguments);\n\t\t\t\tthis._dontNotify = false;\n\t\t\t\tthis._owner[this._setter](this);\n\t\t\t};\n\t\t}, {\n\t\t\tisSelected: function() {\n\t\t\t\treturn !!(this._owner._selection & 2);\n\t\t\t},\n\n\t\t\tsetSelected: function(selected) {\n\t\t\t\tvar owner = this._owner;\n\t\t\t\tif (owner._changeSelection) {\n\t\t\t\t\towner._changeSelection(2, selected);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t);\n});\n\nvar Matrix = Base.extend({\n\t_class: 'Matrix',\n\n\tinitialize: function Matrix(arg, _dontNotify) {\n\t\tvar args = arguments,\n\t\t\tcount = args.length,\n\t\t\tok = true;\n\t\tif (count >= 6) {\n\t\t\tthis._set.apply(this, args);\n\t\t} else if (count === 1 || count === 2) {\n\t\t\tif (arg instanceof Matrix) {\n\t\t\t\tthis._set(arg._a, arg._b, arg._c, arg._d, arg._tx, arg._ty,\n\t\t\t\t\t\t_dontNotify);\n\t\t\t} else if (Array.isArray(arg)) {\n\t\t\t\tthis._set.apply(this,\n\t\t\t\t\t\t_dontNotify ? arg.concat([_dontNotify]) : arg);\n\t\t\t} else {\n\t\t\t\tok = false;\n\t\t\t}\n\t\t} else if (!count) {\n\t\t\tthis.reset();\n\t\t} else {\n\t\t\tok = false;\n\t\t}\n\t\tif (!ok) {\n\t\t\tthrow new Error('Unsupported matrix parameters');\n\t\t}\n\t\treturn this;\n\t},\n\n\tset: '#initialize',\n\n\t_set: function(a, b, c, d, tx, ty, _dontNotify) {\n\t\tthis._a = a;\n\t\tthis._b = b;\n\t\tthis._c = c;\n\t\tthis._d = d;\n\t\tthis._tx = tx;\n\t\tthis._ty = ty;\n\t\tif (!_dontNotify)\n\t\t\tthis._changed();\n\t\treturn this;\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\treturn Base.serialize(this.getValues(), options, true, dictionary);\n\t},\n\n\t_changed: function() {\n\t\tvar owner = this._owner;\n\t\tif (owner) {\n\t\t\tif (owner._applyMatrix) {\n\t\t\t\towner.transform(null, true);\n\t\t\t} else {\n\t\t\t\towner._changed(25);\n\t\t\t}\n\t\t}\n\t},\n\n\tclone: function() {\n\t\treturn new Matrix(this._a, this._b, this._c, this._d,\n\t\t\t\tthis._tx, this._ty);\n\t},\n\n\tequals: function(mx) {\n\t\treturn mx === this || mx && this._a === mx._a && this._b === mx._b\n\t\t\t\t&& this._c === mx._c && this._d === mx._d\n\t\t\t\t&& this._tx === mx._tx && this._ty === mx._ty;\n\t},\n\n\ttoString: function() {\n\t\tvar f = Formatter.instance;\n\t\treturn '[[' + [f.number(this._a), f.number(this._c),\n\t\t\t\t\tf.number(this._tx)].join(', ') + '], ['\n\t\t\t\t+ [f.number(this._b), f.number(this._d),\n\t\t\t\t\tf.number(this._ty)].join(', ') + ']]';\n\t},\n\n\treset: function(_dontNotify) {\n\t\tthis._a = this._d = 1;\n\t\tthis._b = this._c = this._tx = this._ty = 0;\n\t\tif (!_dontNotify)\n\t\t\tthis._changed();\n\t\treturn this;\n\t},\n\n\tapply: function(recursively, _setApplyMatrix) {\n\t\tvar owner = this._owner;\n\t\tif (owner) {\n\t\t\towner.transform(null, Base.pick(recursively, true), _setApplyMatrix);\n\t\t\treturn this.isIdentity();\n\t\t}\n\t\treturn false;\n\t},\n\n\ttranslate: function() {\n\t\tvar point = Point.read(arguments),\n\t\t\tx = point.x,\n\t\t\ty = point.y;\n\t\tthis._tx += x * this._a + y * this._c;\n\t\tthis._ty += x * this._b + y * this._d;\n\t\tthis._changed();\n\t\treturn this;\n\t},\n\n\tscale: function() {\n\t\tvar args = arguments,\n\t\t\tscale = Point.read(args),\n\t\t\tcenter = Point.read(args, 0, { readNull: true });\n\t\tif (center)\n\t\t\tthis.translate(center);\n\t\tthis._a *= scale.x;\n\t\tthis._b *= scale.x;\n\t\tthis._c *= scale.y;\n\t\tthis._d *= scale.y;\n\t\tif (center)\n\t\t\tthis.translate(center.negate());\n\t\tthis._changed();\n\t\treturn this;\n\t},\n\n\trotate: function(angle ) {\n\t\tangle *= Math.PI / 180;\n\t\tvar center = Point.read(arguments, 1),\n\t\t\tx = center.x,\n\t\t\ty = center.y,\n\t\t\tcos = Math.cos(angle),\n\t\t\tsin = Math.sin(angle),\n\t\t\ttx = x - x * cos + y * sin,\n\t\t\tty = y - x * sin - y * cos,\n\t\t\ta = this._a,\n\t\t\tb = this._b,\n\t\t\tc = this._c,\n\t\t\td = this._d;\n\t\tthis._a = cos * a + sin * c;\n\t\tthis._b = cos * b + sin * d;\n\t\tthis._c = -sin * a + cos * c;\n\t\tthis._d = -sin * b + cos * d;\n\t\tthis._tx += tx * a + ty * c;\n\t\tthis._ty += tx * b + ty * d;\n\t\tthis._changed();\n\t\treturn this;\n\t},\n\n\tshear: function() {\n\t\tvar args = arguments,\n\t\t\tshear = Point.read(args),\n\t\t\tcenter = Point.read(args, 0, { readNull: true });\n\t\tif (center)\n\t\t\tthis.translate(center);\n\t\tvar a = this._a,\n\t\t\tb = this._b;\n\t\tthis._a += shear.y * this._c;\n\t\tthis._b += shear.y * this._d;\n\t\tthis._c += shear.x * a;\n\t\tthis._d += shear.x * b;\n\t\tif (center)\n\t\t\tthis.translate(center.negate());\n\t\tthis._changed();\n\t\treturn this;\n\t},\n\n\tskew: function() {\n\t\tvar args = arguments,\n\t\t\tskew = Point.read(args),\n\t\t\tcenter = Point.read(args, 0, { readNull: true }),\n\t\t\ttoRadians = Math.PI / 180,\n\t\t\tshear = new Point(Math.tan(skew.x * toRadians),\n\t\t\t\tMath.tan(skew.y * toRadians));\n\t\treturn this.shear(shear, center);\n\t},\n\n\tappend: function(mx, _dontNotify) {\n\t\tif (mx) {\n\t\t\tvar a1 = this._a,\n\t\t\t\tb1 = this._b,\n\t\t\t\tc1 = this._c,\n\t\t\t\td1 = this._d,\n\t\t\t\ta2 = mx._a,\n\t\t\t\tb2 = mx._c,\n\t\t\t\tc2 = mx._b,\n\t\t\t\td2 = mx._d,\n\t\t\t\ttx2 = mx._tx,\n\t\t\t\tty2 = mx._ty;\n\t\t\tthis._a = a2 * a1 + c2 * c1;\n\t\t\tthis._c = b2 * a1 + d2 * c1;\n\t\t\tthis._b = a2 * b1 + c2 * d1;\n\t\t\tthis._d = b2 * b1 + d2 * d1;\n\t\t\tthis._tx += tx2 * a1 + ty2 * c1;\n\t\t\tthis._ty += tx2 * b1 + ty2 * d1;\n\t\t\tif (!_dontNotify)\n\t\t\t\tthis._changed();\n\t\t}\n\t\treturn this;\n\t},\n\n\tprepend: function(mx, _dontNotify) {\n\t\tif (mx) {\n\t\t\tvar a1 = this._a,\n\t\t\t\tb1 = this._b,\n\t\t\t\tc1 = this._c,\n\t\t\t\td1 = this._d,\n\t\t\t\ttx1 = this._tx,\n\t\t\t\tty1 = this._ty,\n\t\t\t\ta2 = mx._a,\n\t\t\t\tb2 = mx._c,\n\t\t\t\tc2 = mx._b,\n\t\t\t\td2 = mx._d,\n\t\t\t\ttx2 = mx._tx,\n\t\t\t\tty2 = mx._ty;\n\t\t\tthis._a = a2 * a1 + b2 * b1;\n\t\t\tthis._c = a2 * c1 + b2 * d1;\n\t\t\tthis._b = c2 * a1 + d2 * b1;\n\t\t\tthis._d = c2 * c1 + d2 * d1;\n\t\t\tthis._tx = a2 * tx1 + b2 * ty1 + tx2;\n\t\t\tthis._ty = c2 * tx1 + d2 * ty1 + ty2;\n\t\t\tif (!_dontNotify)\n\t\t\t\tthis._changed();\n\t\t}\n\t\treturn this;\n\t},\n\n\tappended: function(mx) {\n\t\treturn this.clone().append(mx);\n\t},\n\n\tprepended: function(mx) {\n\t\treturn this.clone().prepend(mx);\n\t},\n\n\tinvert: function() {\n\t\tvar a = this._a,\n\t\t\tb = this._b,\n\t\t\tc = this._c,\n\t\t\td = this._d,\n\t\t\ttx = this._tx,\n\t\t\tty = this._ty,\n\t\t\tdet = a * d - b * c,\n\t\t\tres = null;\n\t\tif (det && !isNaN(det) && isFinite(tx) && isFinite(ty)) {\n\t\t\tthis._a = d / det;\n\t\t\tthis._b = -b / det;\n\t\t\tthis._c = -c / det;\n\t\t\tthis._d = a / det;\n\t\t\tthis._tx = (c * ty - d * tx) / det;\n\t\t\tthis._ty = (b * tx - a * ty) / det;\n\t\t\tres = this;\n\t\t}\n\t\treturn res;\n\t},\n\n\tinverted: function() {\n\t\treturn this.clone().invert();\n\t},\n\n\tconcatenate: '#append',\n\tpreConcatenate: '#prepend',\n\tchain: '#appended',\n\n\t_shiftless: function() {\n\t\treturn new Matrix(this._a, this._b, this._c, this._d, 0, 0);\n\t},\n\n\t_orNullIfIdentity: function() {\n\t\treturn this.isIdentity() ? null : this;\n\t},\n\n\tisIdentity: function() {\n\t\treturn this._a === 1 && this._b === 0 && this._c === 0 && this._d === 1\n\t\t\t\t&& this._tx === 0 && this._ty === 0;\n\t},\n\n\tisInvertible: function() {\n\t\tvar det = this._a * this._d - this._c * this._b;\n\t\treturn det && !isNaN(det) && isFinite(this._tx) && isFinite(this._ty);\n\t},\n\n\tisSingular: function() {\n\t\treturn !this.isInvertible();\n\t},\n\n\ttransform: function( src, dst, count) {\n\t\treturn arguments.length < 3\n\t\t\t? this._transformPoint(Point.read(arguments))\n\t\t\t: this._transformCoordinates(src, dst, count);\n\t},\n\n\t_transformPoint: function(point, dest, _dontNotify) {\n\t\tvar x = point.x,\n\t\t\ty = point.y;\n\t\tif (!dest)\n\t\t\tdest = new Point();\n\t\treturn dest._set(\n\t\t\t\tx * this._a + y * this._c + this._tx,\n\t\t\t\tx * this._b + y * this._d + this._ty,\n\t\t\t\t_dontNotify);\n\t},\n\n\t_transformCoordinates: function(src, dst, count) {\n\t\tfor (var i = 0, max = 2 * count; i < max; i += 2) {\n\t\t\tvar x = src[i],\n\t\t\t\ty = src[i + 1];\n\t\t\tdst[i] = x * this._a + y * this._c + this._tx;\n\t\t\tdst[i + 1] = x * this._b + y * this._d + this._ty;\n\t\t}\n\t\treturn dst;\n\t},\n\n\t_transformCorners: function(rect) {\n\t\tvar x1 = rect.x,\n\t\t\ty1 = rect.y,\n\t\t\tx2 = x1 + rect.width,\n\t\t\ty2 = y1 + rect.height,\n\t\t\tcoords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];\n\t\treturn this._transformCoordinates(coords, coords, 4);\n\t},\n\n\t_transformBounds: function(bounds, dest, _dontNotify) {\n\t\tvar coords = this._transformCorners(bounds),\n\t\t\tmin = coords.slice(0, 2),\n\t\t\tmax = min.slice();\n\t\tfor (var i = 2; i < 8; i++) {\n\t\t\tvar val = coords[i],\n\t\t\t\tj = i & 1;\n\t\t\tif (val < min[j]) {\n\t\t\t\tmin[j] = val;\n\t\t\t} else if (val > max[j]) {\n\t\t\t\tmax[j] = val;\n\t\t\t}\n\t\t}\n\t\tif (!dest)\n\t\t\tdest = new Rectangle();\n\t\treturn dest._set(min[0], min[1], max[0] - min[0], max[1] - min[1],\n\t\t\t\t_dontNotify);\n\t},\n\n\tinverseTransform: function() {\n\t\treturn this._inverseTransform(Point.read(arguments));\n\t},\n\n\t_inverseTransform: function(point, dest, _dontNotify) {\n\t\tvar a = this._a,\n\t\t\tb = this._b,\n\t\t\tc = this._c,\n\t\t\td = this._d,\n\t\t\ttx = this._tx,\n\t\t\tty = this._ty,\n\t\t\tdet = a * d - b * c,\n\t\t\tres = null;\n\t\tif (det && !isNaN(det) && isFinite(tx) && isFinite(ty)) {\n\t\t\tvar x = point.x - this._tx,\n\t\t\t\ty = point.y - this._ty;\n\t\t\tif (!dest)\n\t\t\t\tdest = new Point();\n\t\t\tres = dest._set(\n\t\t\t\t\t(x * d - y * c) / det,\n\t\t\t\t\t(y * a - x * b) / det,\n\t\t\t\t\t_dontNotify);\n\t\t}\n\t\treturn res;\n\t},\n\n\tdecompose: function() {\n\t\tvar a = this._a,\n\t\t\tb = this._b,\n\t\t\tc = this._c,\n\t\t\td = this._d,\n\t\t\tdet = a * d - b * c,\n\t\t\tsqrt = Math.sqrt,\n\t\t\tatan2 = Math.atan2,\n\t\t\tdegrees = 180 / Math.PI,\n\t\t\trotate,\n\t\t\tscale,\n\t\t\tskew;\n\t\tif (a !== 0 || b !== 0) {\n\t\t\tvar r = sqrt(a * a + b * b);\n\t\t\trotate = Math.acos(a / r) * (b > 0 ? 1 : -1);\n\t\t\tscale = [r, det / r];\n\t\t\tskew = [atan2(a * c + b * d, r * r), 0];\n\t\t} else if (c !== 0 || d !== 0) {\n\t\t\tvar s = sqrt(c * c + d * d);\n\t\t\trotate = Math.asin(c / s) * (d > 0 ? 1 : -1);\n\t\t\tscale = [det / s, s];\n\t\t\tskew = [0, atan2(a * c + b * d, s * s)];\n\t\t} else {\n\t\t\trotate = 0;\n\t\t\tskew = scale = [0, 0];\n\t\t}\n\t\treturn {\n\t\t\ttranslation: this.getTranslation(),\n\t\t\trotation: rotate * degrees,\n\t\t\tscaling: new Point(scale),\n\t\t\tskewing: new Point(skew[0] * degrees, skew[1] * degrees)\n\t\t};\n\t},\n\n\tgetValues: function() {\n\t\treturn [ this._a, this._b, this._c, this._d, this._tx, this._ty ];\n\t},\n\n\tgetTranslation: function() {\n\t\treturn new Point(this._tx, this._ty);\n\t},\n\n\tgetScaling: function() {\n\t\treturn this.decompose().scaling;\n\t},\n\n\tgetRotation: function() {\n\t\treturn this.decompose().rotation;\n\t},\n\n\tapplyToContext: function(ctx) {\n\t\tif (!this.isIdentity()) {\n\t\t\tctx.transform(this._a, this._b, this._c, this._d,\n\t\t\t\t\tthis._tx, this._ty);\n\t\t}\n\t}\n}, Base.each(['a', 'b', 'c', 'd', 'tx', 'ty'], function(key) {\n\tvar part = Base.capitalize(key),\n\t\tprop = '_' + key;\n\tthis['get' + part] = function() {\n\t\treturn this[prop];\n\t};\n\tthis['set' + part] = function(value) {\n\t\tthis[prop] = value;\n\t\tthis._changed();\n\t};\n}, {}));\n\nvar Line = Base.extend({\n\t_class: 'Line',\n\n\tinitialize: function Line(arg0, arg1, arg2, arg3, arg4) {\n\t\tvar asVector = false;\n\t\tif (arguments.length >= 4) {\n\t\t\tthis._px = arg0;\n\t\t\tthis._py = arg1;\n\t\t\tthis._vx = arg2;\n\t\t\tthis._vy = arg3;\n\t\t\tasVector = arg4;\n\t\t} else {\n\t\t\tthis._px = arg0.x;\n\t\t\tthis._py = arg0.y;\n\t\t\tthis._vx = arg1.x;\n\t\t\tthis._vy = arg1.y;\n\t\t\tasVector = arg2;\n\t\t}\n\t\tif (!asVector) {\n\t\t\tthis._vx -= this._px;\n\t\t\tthis._vy -= this._py;\n\t\t}\n\t},\n\n\tgetPoint: function() {\n\t\treturn new Point(this._px, this._py);\n\t},\n\n\tgetVector: function() {\n\t\treturn new Point(this._vx, this._vy);\n\t},\n\n\tgetLength: function() {\n\t\treturn this.getVector().getLength();\n\t},\n\n\tintersect: function(line, isInfinite) {\n\t\treturn Line.intersect(\n\t\t\t\tthis._px, this._py, this._vx, this._vy,\n\t\t\t\tline._px, line._py, line._vx, line._vy,\n\t\t\t\ttrue, isInfinite);\n\t},\n\n\tgetSide: function(point, isInfinite) {\n\t\treturn Line.getSide(\n\t\t\t\tthis._px, this._py, this._vx, this._vy,\n\t\t\t\tpoint.x, point.y, true, isInfinite);\n\t},\n\n\tgetDistance: function(point) {\n\t\treturn Math.abs(this.getSignedDistance(point));\n\t},\n\n\tgetSignedDistance: function(point) {\n\t\treturn Line.getSignedDistance(this._px, this._py, this._vx, this._vy,\n\t\t\t\tpoint.x, point.y, true);\n\t},\n\n\tisCollinear: function(line) {\n\t\treturn Point.isCollinear(this._vx, this._vy, line._vx, line._vy);\n\t},\n\n\tisOrthogonal: function(line) {\n\t\treturn Point.isOrthogonal(this._vx, this._vy, line._vx, line._vy);\n\t},\n\n\tstatics: {\n\t\tintersect: function(p1x, p1y, v1x, v1y, p2x, p2y, v2x, v2y, asVector,\n\t\t\t\tisInfinite) {\n\t\t\tif (!asVector) {\n\t\t\t\tv1x -= p1x;\n\t\t\t\tv1y -= p1y;\n\t\t\t\tv2x -= p2x;\n\t\t\t\tv2y -= p2y;\n\t\t\t}\n\t\t\tvar cross = v1x * v2y - v1y * v2x;\n\t\t\tif (!Numerical.isMachineZero(cross)) {\n\t\t\t\tvar dx = p1x - p2x,\n\t\t\t\t\tdy = p1y - p2y,\n\t\t\t\t\tu1 = (v2x * dy - v2y * dx) / cross,\n\t\t\t\t\tu2 = (v1x * dy - v1y * dx) / cross,\n\t\t\t\t\tepsilon = 1e-12,\n\t\t\t\t\tuMin = -epsilon,\n\t\t\t\t\tuMax = 1 + epsilon;\n\t\t\t\tif (isInfinite\n\t\t\t\t\t\t|| uMin < u1 && u1 < uMax && uMin < u2 && u2 < uMax) {\n\t\t\t\t\tif (!isInfinite) {\n\t\t\t\t\t\tu1 = u1 <= 0 ? 0 : u1 >= 1 ? 1 : u1;\n\t\t\t\t\t}\n\t\t\t\t\treturn new Point(\n\t\t\t\t\t\t\tp1x + u1 * v1x,\n\t\t\t\t\t\t\tp1y + u1 * v1y);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tgetSide: function(px, py, vx, vy, x, y, asVector, isInfinite) {\n\t\t\tif (!asVector) {\n\t\t\t\tvx -= px;\n\t\t\t\tvy -= py;\n\t\t\t}\n\t\t\tvar v2x = x - px,\n\t\t\t\tv2y = y - py,\n\t\t\t\tccw = v2x * vy - v2y * vx;\n\t\t\tif (!isInfinite && Numerical.isMachineZero(ccw)) {\n\t\t\t\tccw = (v2x * vx + v2x * vx) / (vx * vx + vy * vy);\n\t\t\t\tif (ccw >= 0 && ccw <= 1)\n\t\t\t\t\tccw = 0;\n\t\t\t}\n\t\t\treturn ccw < 0 ? -1 : ccw > 0 ? 1 : 0;\n\t\t},\n\n\t\tgetSignedDistance: function(px, py, vx, vy, x, y, asVector) {\n\t\t\tif (!asVector) {\n\t\t\t\tvx -= px;\n\t\t\t\tvy -= py;\n\t\t\t}\n\t\t\t return vx === 0 ? (vy > 0 ? x - px : px - x)\n\t\t\t\t\t: vy === 0 ? (vx < 0 ? y - py : py - y)\n\t\t\t\t\t: ((x - px) * vy - (y - py) * vx) / (\n\t\t\t\t\t\tvy > vx\n\t\t\t\t\t\t\t? vy * Math.sqrt(1 + (vx * vx) / (vy * vy))\n\t\t\t\t\t\t\t: vx * Math.sqrt(1 + (vy * vy) / (vx * vx))\n\t\t\t\t\t);\n\t\t},\n\n\t\tgetDistance: function(px, py, vx, vy, x, y, asVector) {\n\t\t\treturn Math.abs(\n\t\t\t\t\tLine.getSignedDistance(px, py, vx, vy, x, y, asVector));\n\t\t}\n\t}\n});\n\nvar Project = PaperScopeItem.extend({\n\t_class: 'Project',\n\t_list: 'projects',\n\t_reference: 'project',\n\t_compactSerialize: true,\n\n\tinitialize: function Project(element) {\n\t\tPaperScopeItem.call(this, true);\n\t\tthis._children = [];\n\t\tthis._namedChildren = {};\n\t\tthis._activeLayer = null;\n\t\tthis._currentStyle = new Style(null, null, this);\n\t\tthis._view = View.create(this,\n\t\t\t\telement || CanvasProvider.getCanvas(1, 1));\n\t\tthis._selectionItems = {};\n\t\tthis._selectionCount = 0;\n\t\tthis._updateVersion = 0;\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\treturn Base.serialize(this._children, options, true, dictionary);\n\t},\n\n\t_changed: function(flags, item) {\n\t\tif (flags & 1) {\n\t\t\tvar view = this._view;\n\t\t\tif (view) {\n\t\t\t\tview._needsUpdate = true;\n\t\t\t\tif (!view._requested && view._autoUpdate)\n\t\t\t\t\tview.requestUpdate();\n\t\t\t}\n\t\t}\n\t\tvar changes = this._changes;\n\t\tif (changes && item) {\n\t\t\tvar changesById = this._changesById,\n\t\t\t\tid = item._id,\n\t\t\t\tentry = changesById[id];\n\t\t\tif (entry) {\n\t\t\t\tentry.flags |= flags;\n\t\t\t} else {\n\t\t\t\tchanges.push(changesById[id] = { item: item, flags: flags });\n\t\t\t}\n\t\t}\n\t},\n\n\tclear: function() {\n\t\tvar children = this._children;\n\t\tfor (var i = children.length - 1; i >= 0; i--)\n\t\t\tchildren[i].remove();\n\t},\n\n\tisEmpty: function() {\n\t\treturn !this._children.length;\n\t},\n\n\tremove: function remove() {\n\t\tif (!remove.base.call(this))\n\t\t\treturn false;\n\t\tif (this._view)\n\t\t\tthis._view.remove();\n\t\treturn true;\n\t},\n\n\tgetView: function() {\n\t\treturn this._view;\n\t},\n\n\tgetCurrentStyle: function() {\n\t\treturn this._currentStyle;\n\t},\n\n\tsetCurrentStyle: function(style) {\n\t\tthis._currentStyle.set(style);\n\t},\n\n\tgetIndex: function() {\n\t\treturn this._index;\n\t},\n\n\tgetOptions: function() {\n\t\treturn this._scope.settings;\n\t},\n\n\tgetLayers: function() {\n\t\treturn this._children;\n\t},\n\n\tgetActiveLayer: function() {\n\t\treturn this._activeLayer || new Layer({ project: this, insert: true });\n\t},\n\n\tgetSymbolDefinitions: function() {\n\t\tvar definitions = [],\n\t\t\tids = {};\n\t\tthis.getItems({\n\t\t\tclass: SymbolItem,\n\t\t\tmatch: function(item) {\n\t\t\t\tvar definition = item._definition,\n\t\t\t\t\tid = definition._id;\n\t\t\t\tif (!ids[id]) {\n\t\t\t\t\tids[id] = true;\n\t\t\t\t\tdefinitions.push(definition);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\treturn definitions;\n\t},\n\n\tgetSymbols: 'getSymbolDefinitions',\n\n\tgetSelectedItems: function() {\n\t\tvar selectionItems = this._selectionItems,\n\t\t\titems = [];\n\t\tfor (var id in selectionItems) {\n\t\t\tvar item = selectionItems[id],\n\t\t\t\tselection = item._selection;\n\t\t\tif ((selection & 1) && item.isInserted()) {\n\t\t\t\titems.push(item);\n\t\t\t} else if (!selection) {\n\t\t\t\tthis._updateSelection(item);\n\t\t\t}\n\t\t}\n\t\treturn items;\n\t},\n\n\t_updateSelection: function(item) {\n\t\tvar id = item._id,\n\t\t\tselectionItems = this._selectionItems;\n\t\tif (item._selection) {\n\t\t\tif (selectionItems[id] !== item) {\n\t\t\t\tthis._selectionCount++;\n\t\t\t\tselectionItems[id] = item;\n\t\t\t}\n\t\t} else if (selectionItems[id] === item) {\n\t\t\tthis._selectionCount--;\n\t\t\tdelete selectionItems[id];\n\t\t}\n\t},\n\n\tselectAll: function() {\n\t\tvar children = this._children;\n\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\tchildren[i].setFullySelected(true);\n\t},\n\n\tdeselectAll: function() {\n\t\tvar selectionItems = this._selectionItems;\n\t\tfor (var i in selectionItems)\n\t\t\tselectionItems[i].setFullySelected(false);\n\t},\n\n\taddLayer: function(layer) {\n\t\treturn this.insertLayer(undefined, layer);\n\t},\n\n\tinsertLayer: function(index, layer) {\n\t\tif (layer instanceof Layer) {\n\t\t\tlayer._remove(false, true);\n\t\t\tBase.splice(this._children, [layer], index, 0);\n\t\t\tlayer._setProject(this, true);\n\t\t\tvar name = layer._name;\n\t\t\tif (name)\n\t\t\t\tlayer.setName(name);\n\t\t\tif (this._changes)\n\t\t\t\tlayer._changed(5);\n\t\t\tif (!this._activeLayer)\n\t\t\t\tthis._activeLayer = layer;\n\t\t} else {\n\t\t\tlayer = null;\n\t\t}\n\t\treturn layer;\n\t},\n\n\t_insertItem: function(index, item, _created) {\n\t\titem = this.insertLayer(index, item)\n\t\t\t\t|| (this._activeLayer || this._insertItem(undefined,\n\t\t\t\t\t\tnew Layer(Item.NO_INSERT), true))\n\t\t\t\t\t\t.insertChild(index, item);\n\t\tif (_created && item.activate)\n\t\t\titem.activate();\n\t\treturn item;\n\t},\n\n\tgetItems: function(options) {\n\t\treturn Item._getItems(this, options);\n\t},\n\n\tgetItem: function(options) {\n\t\treturn Item._getItems(this, options, null, null, true)[0] || null;\n\t},\n\n\timportJSON: function(json) {\n\t\tthis.activate();\n\t\tvar layer = this._activeLayer;\n\t\treturn Base.importJSON(json, layer && layer.isEmpty() && layer);\n\t},\n\n\tremoveOn: function(type) {\n\t\tvar sets = this._removeSets;\n\t\tif (sets) {\n\t\t\tif (type === 'mouseup')\n\t\t\t\tsets.mousedrag = null;\n\t\t\tvar set = sets[type];\n\t\t\tif (set) {\n\t\t\t\tfor (var id in set) {\n\t\t\t\t\tvar item = set[id];\n\t\t\t\t\tfor (var key in sets) {\n\t\t\t\t\t\tvar other = sets[key];\n\t\t\t\t\t\tif (other && other != set)\n\t\t\t\t\t\t\tdelete other[item._id];\n\t\t\t\t\t}\n\t\t\t\t\titem.remove();\n\t\t\t\t}\n\t\t\t\tsets[type] = null;\n\t\t\t}\n\t\t}\n\t},\n\n\tdraw: function(ctx, matrix, pixelRatio) {\n\t\tthis._updateVersion++;\n\t\tctx.save();\n\t\tmatrix.applyToContext(ctx);\n\t\tvar children = this._children,\n\t\t\tparam = new Base({\n\t\t\t\toffset: new Point(0, 0),\n\t\t\t\tpixelRatio: pixelRatio,\n\t\t\t\tviewMatrix: matrix.isIdentity() ? null : matrix,\n\t\t\t\tmatrices: [new Matrix()],\n\t\t\t\tupdateMatrix: true\n\t\t\t});\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tchildren[i].draw(ctx, param);\n\t\t}\n\t\tctx.restore();\n\n\t\tif (this._selectionCount > 0) {\n\t\t\tctx.save();\n\t\t\tctx.strokeWidth = 1;\n\t\t\tvar items = this._selectionItems,\n\t\t\t\tsize = this._scope.settings.handleSize,\n\t\t\t\tversion = this._updateVersion;\n\t\t\tfor (var id in items) {\n\t\t\t\titems[id]._drawSelection(ctx, matrix, size, items, version);\n\t\t\t}\n\t\t\tctx.restore();\n\t\t}\n\t}\n});\n\nvar Item = Base.extend(Emitter, {\n\tstatics: {\n\t\textend: function extend(src) {\n\t\t\tif (src._serializeFields)\n\t\t\t\tsrc._serializeFields = Base.set({},\n\t\t\t\t\tthis.prototype._serializeFields, src._serializeFields);\n\t\t\treturn extend.base.apply(this, arguments);\n\t\t},\n\n\t\tNO_INSERT: { insert: false }\n\t},\n\n\t_class: 'Item',\n\t_name: null,\n\t_applyMatrix: true,\n\t_canApplyMatrix: true,\n\t_canScaleStroke: false,\n\t_pivot: null,\n\t_visible: true,\n\t_blendMode: 'normal',\n\t_opacity: 1,\n\t_locked: false,\n\t_guide: false,\n\t_clipMask: false,\n\t_selection: 0,\n\t_selectBounds: true,\n\t_selectChildren: false,\n\t_serializeFields: {\n\t\tname: null,\n\t\tapplyMatrix: null,\n\t\tmatrix: new Matrix(),\n\t\tpivot: null,\n\t\tvisible: true,\n\t\tblendMode: 'normal',\n\t\topacity: 1,\n\t\tlocked: false,\n\t\tguide: false,\n\t\tclipMask: false,\n\t\tselected: false,\n\t\tdata: {}\n\t},\n\t_prioritize: ['applyMatrix']\n},\nnew function() {\n\tvar handlers = ['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',\n\t\t\t'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'];\n\treturn Base.each(handlers,\n\t\tfunction(name) {\n\t\t\tthis._events[name] = {\n\t\t\t\tinstall: function(type) {\n\t\t\t\t\tthis.getView()._countItemEvent(type, 1);\n\t\t\t\t},\n\n\t\t\t\tuninstall: function(type) {\n\t\t\t\t\tthis.getView()._countItemEvent(type, -1);\n\t\t\t\t}\n\t\t\t};\n\t\t}, {\n\t\t\t_events: {\n\t\t\t\tonFrame: {\n\t\t\t\t\tinstall: function() {\n\t\t\t\t\t\tthis.getView()._animateItem(this, true);\n\t\t\t\t\t},\n\n\t\t\t\t\tuninstall: function() {\n\t\t\t\t\t\tthis.getView()._animateItem(this, false);\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonLoad: {},\n\t\t\t\tonError: {}\n\t\t\t},\n\t\t\tstatics: {\n\t\t\t\t_itemHandlers: handlers\n\t\t\t}\n\t\t}\n\t);\n}, {\n\tinitialize: function Item() {\n\t},\n\n\t_initialize: function(props, point) {\n\t\tvar hasProps = props && Base.isPlainObject(props),\n\t\t\tinternal = hasProps && props.internal === true,\n\t\t\tmatrix = this._matrix = new Matrix(),\n\t\t\tproject = hasProps && props.project || paper.project,\n\t\t\tsettings = paper.settings;\n\t\tthis._id = internal ? null : UID.get();\n\t\tthis._parent = this._index = null;\n\t\tthis._applyMatrix = this._canApplyMatrix && settings.applyMatrix;\n\t\tif (point)\n\t\t\tmatrix.translate(point);\n\t\tmatrix._owner = this;\n\t\tthis._style = new Style(project._currentStyle, this, project);\n\t\tif (internal || hasProps && props.insert == false\n\t\t\t|| !settings.insertItems && !(hasProps && props.insert === true)) {\n\t\t\tthis._setProject(project);\n\t\t} else {\n\t\t\t(hasProps && props.parent || project)\n\t\t\t\t\t._insertItem(undefined, this, true);\n\t\t}\n\t\tif (hasProps && props !== Item.NO_INSERT) {\n\t\t\tthis.set(props, {\n\t\t\t\tinternal: true, insert: true, project: true, parent: true\n\t\t\t});\n\t\t}\n\t\treturn hasProps;\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\tvar props = {},\n\t\t\tthat = this;\n\n\t\tfunction serialize(fields) {\n\t\t\tfor (var key in fields) {\n\t\t\t\tvar value = that[key];\n\t\t\t\tif (!Base.equals(value, key === 'leading'\n\t\t\t\t\t\t? fields.fontSize * 1.2 : fields[key])) {\n\t\t\t\t\tprops[key] = Base.serialize(value, options,\n\t\t\t\t\t\t\tkey !== 'data', dictionary);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tserialize(this._serializeFields);\n\t\tif (!(this instanceof Group))\n\t\t\tserialize(this._style._defaults);\n\t\treturn [ this._class, props ];\n\t},\n\n\t_changed: function(flags) {\n\t\tvar symbol = this._symbol,\n\t\t\tcacheParent = this._parent || symbol,\n\t\t\tproject = this._project;\n\t\tif (flags & 8) {\n\t\t\tthis._bounds = this._position = this._decomposed = undefined;\n\t\t}\n\t\tif (flags & 16) {\n\t\t\tthis._globalMatrix = undefined;\n\t\t}\n\t\tif (cacheParent\n\t\t\t\t&& (flags & 72)) {\n\t\t\tItem._clearBoundsCache(cacheParent);\n\t\t}\n\t\tif (flags & 2) {\n\t\t\tItem._clearBoundsCache(this);\n\t\t}\n\t\tif (project)\n\t\t\tproject._changed(flags, this);\n\t\tif (symbol)\n\t\t\tsymbol._changed(flags);\n\t},\n\n\tgetId: function() {\n\t\treturn this._id;\n\t},\n\n\tgetName: function() {\n\t\treturn this._name;\n\t},\n\n\tsetName: function(name) {\n\n\t\tif (this._name)\n\t\t\tthis._removeNamed();\n\t\tif (name === (+name) + '')\n\t\t\tthrow new Error(\n\t\t\t\t\t'Names consisting only of numbers are not supported.');\n\t\tvar owner = this._getOwner();\n\t\tif (name && owner) {\n\t\t\tvar children = owner._children,\n\t\t\t\tnamedChildren = owner._namedChildren;\n\t\t\t(namedChildren[name] = namedChildren[name] || []).push(this);\n\t\t\tif (!(name in children))\n\t\t\t\tchildren[name] = this;\n\t\t}\n\t\tthis._name = name || undefined;\n\t\tthis._changed(256);\n\t},\n\n\tgetStyle: function() {\n\t\treturn this._style;\n\t},\n\n\tsetStyle: function(style) {\n\t\tthis.getStyle().set(style);\n\t}\n}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],\n\tfunction(name) {\n\t\tvar part = Base.capitalize(name),\n\t\t\tkey = '_' + name,\n\t\t\tflags = {\n\t\t\t\tlocked: 256,\n\t\t\t\tvisible: 265\n\t\t\t};\n\t\tthis['get' + part] = function() {\n\t\t\treturn this[key];\n\t\t};\n\t\tthis['set' + part] = function(value) {\n\t\t\tif (value != this[key]) {\n\t\t\t\tthis[key] = value;\n\t\t\t\tthis._changed(flags[name] || 257);\n\t\t\t}\n\t\t};\n\t},\n{}), {\n\tbeans: true,\n\n\tgetSelection: function() {\n\t\treturn this._selection;\n\t},\n\n\tsetSelection: function(selection) {\n\t\tif (selection !== this._selection) {\n\t\t\tthis._selection = selection;\n\t\t\tvar project = this._project;\n\t\t\tif (project) {\n\t\t\t\tproject._updateSelection(this);\n\t\t\t\tthis._changed(257);\n\t\t\t}\n\t\t}\n\t},\n\n\t_changeSelection: function(flag, selected) {\n\t\tvar selection = this._selection;\n\t\tthis.setSelection(selected ? selection | flag : selection & ~flag);\n\t},\n\n\tisSelected: function() {\n\t\tif (this._selectChildren) {\n\t\t\tvar children = this._children;\n\t\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\t\tif (children[i].isSelected())\n\t\t\t\t\treturn true;\n\t\t}\n\t\treturn !!(this._selection & 1);\n\t},\n\n\tsetSelected: function(selected) {\n\t\tif (this._selectChildren) {\n\t\t\tvar children = this._children;\n\t\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\t\tchildren[i].setSelected(selected);\n\t\t}\n\t\tthis._changeSelection(1, selected);\n\t},\n\n\tisFullySelected: function() {\n\t\tvar children = this._children,\n\t\t\tselected = !!(this._selection & 1);\n\t\tif (children && selected) {\n\t\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\t\tif (!children[i].isFullySelected())\n\t\t\t\t\treturn false;\n\t\t\treturn true;\n\t\t}\n\t\treturn selected;\n\t},\n\n\tsetFullySelected: function(selected) {\n\t\tvar children = this._children;\n\t\tif (children) {\n\t\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\t\tchildren[i].setFullySelected(selected);\n\t\t}\n\t\tthis._changeSelection(1, selected);\n\t},\n\n\tisClipMask: function() {\n\t\treturn this._clipMask;\n\t},\n\n\tsetClipMask: function(clipMask) {\n\t\tif (this._clipMask != (clipMask = !!clipMask)) {\n\t\t\tthis._clipMask = clipMask;\n\t\t\tif (clipMask) {\n\t\t\t\tthis.setFillColor(null);\n\t\t\t\tthis.setStrokeColor(null);\n\t\t\t}\n\t\t\tthis._changed(257);\n\t\t\tif (this._parent)\n\t\t\t\tthis._parent._changed(2048);\n\t\t}\n\t},\n\n\tgetData: function() {\n\t\tif (!this._data)\n\t\t\tthis._data = {};\n\t\treturn this._data;\n\t},\n\n\tsetData: function(data) {\n\t\tthis._data = data;\n\t},\n\n\tgetPosition: function(_dontLink) {\n\t\tvar ctor = _dontLink ? Point : LinkedPoint;\n\t\tvar position = this._position ||\n\t\t\t(this._position = this._getPositionFromBounds());\n\t\treturn new ctor(position.x, position.y, this, 'setPosition');\n\t},\n\n\tsetPosition: function() {\n\t\tthis.translate(Point.read(arguments).subtract(this.getPosition(true)));\n\t},\n\n\t_getPositionFromBounds: function(bounds) {\n\t\treturn this._pivot\n\t\t\t\t? this._matrix._transformPoint(this._pivot)\n\t\t\t\t: (bounds || this.getBounds()).getCenter(true);\n\t},\n\n\tgetPivot: function() {\n\t\tvar pivot = this._pivot;\n\t\treturn pivot\n\t\t\t\t? new LinkedPoint(pivot.x, pivot.y, this, 'setPivot')\n\t\t\t\t: null;\n\t},\n\n\tsetPivot: function() {\n\t\tthis._pivot = Point.read(arguments, 0, { clone: true, readNull: true });\n\t\tthis._position = undefined;\n\t}\n}, Base.each({\n\t\tgetStrokeBounds: { stroke: true },\n\t\tgetHandleBounds: { handle: true },\n\t\tgetInternalBounds: { internal: true },\n\t\tgetDrawnBounds: { stroke: true, drawnTextBounds: true },\n\t},\n\tfunction(options, key) {\n\t\tthis[key] = function(matrix) {\n\t\t\treturn this.getBounds(matrix, options);\n\t\t};\n\t},\n{\n\tbeans: true,\n\n\tgetBounds: function(matrix, options) {\n\t\tvar hasMatrix = options || matrix instanceof Matrix,\n\t\t\topts = Base.set({}, hasMatrix ? options : matrix,\n\t\t\t\t\tthis._boundsOptions);\n\t\tif (!opts.stroke || this.getStrokeScaling())\n\t\t\topts.cacheItem = this;\n\t\tvar rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;\n\t\treturn !arguments.length\n\t\t\t\t? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,\n\t\t\t\t\tthis, 'setBounds')\n\t\t\t\t: rect;\n\t},\n\n\tsetBounds: function() {\n\t\tvar rect = Rectangle.read(arguments),\n\t\t\tbounds = this.getBounds(),\n\t\t\t_matrix = this._matrix,\n\t\t\tmatrix = new Matrix(),\n\t\t\tcenter = rect.getCenter();\n\t\tmatrix.translate(center);\n\t\tif (rect.width != bounds.width || rect.height != bounds.height) {\n\t\t\tif (!_matrix.isInvertible()) {\n\t\t\t\t_matrix.set(_matrix._backup\n\t\t\t\t\t\t|| new Matrix().translate(_matrix.getTranslation()));\n\t\t\t\tbounds = this.getBounds();\n\t\t\t}\n\t\t\tmatrix.scale(\n\t\t\t\t\tbounds.width !== 0 ? rect.width / bounds.width : 0,\n\t\t\t\t\tbounds.height !== 0 ? rect.height / bounds.height : 0);\n\t\t}\n\t\tcenter = bounds.getCenter();\n\t\tmatrix.translate(-center.x, -center.y);\n\t\tthis.transform(matrix);\n\t},\n\n\t_getBounds: function(matrix, options) {\n\t\tvar children = this._children;\n\t\tif (!children || !children.length)\n\t\t\treturn new Rectangle();\n\t\tItem._updateBoundsCache(this, options.cacheItem);\n\t\treturn Item._getBounds(children, matrix, options);\n\t},\n\n\t_getBoundsCacheKey: function(options, internal) {\n\t\treturn [\n\t\t\toptions.stroke ? 1 : 0,\n\t\t\toptions.handle ? 1 : 0,\n\t\t\toptions.drawnTextBounds? 1 : 0,\n\t\t\tinternal ? 1 : 0\n\t\t].join('');\n\t},\n\n\t_getCachedBounds: function(matrix, options, noInternal) {\n\t\tmatrix = matrix && matrix._orNullIfIdentity();\n\t\tvar internal = options.internal && !noInternal,\n\t\t\tcacheItem = options.cacheItem,\n\t\t\t_matrix = internal ? null : this._matrix._orNullIfIdentity(),\n\t\t\tcacheKey = cacheItem && (!matrix || matrix.equals(_matrix))\n\t\t\t\t&& this._getBoundsCacheKey(options, internal),\n\t\t\tbounds = this._bounds;\n\t\tItem._updateBoundsCache(this._parent || this._symbol, cacheItem);\n\t\tif (cacheKey && bounds && cacheKey in bounds) {\n\t\t\tvar cached = bounds[cacheKey];\n\t\t\treturn {\n\t\t\t\trect: cached.rect.clone(),\n\t\t\t\tnonscaling: cached.nonscaling\n\t\t\t};\n\t\t}\n\t\tvar res = this._getBounds(matrix || _matrix, options),\n\t\t\trect = res.rect || res,\n\t\t\tstyle = this._style,\n\t\t\tnonscaling = res.nonscaling || style.hasStroke()\n\t\t\t\t&& !style.getStrokeScaling();\n\t\tif (cacheKey) {\n\t\t\tif (!bounds) {\n\t\t\t\tthis._bounds = bounds = {};\n\t\t\t}\n\t\t\tvar cached = bounds[cacheKey] = {\n\t\t\t\trect: rect.clone(),\n\t\t\t\tnonscaling: nonscaling,\n\t\t\t\tinternal: internal\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\trect: rect,\n\t\t\tnonscaling: nonscaling\n\t\t};\n\t},\n\n\t_getStrokeMatrix: function(matrix, options) {\n\t\tvar parent = this.getStrokeScaling() ? null\n\t\t\t\t: options && options.internal ? this\n\t\t\t\t\t: this._parent || this._symbol && this._symbol._item,\n\t\t\tmx = parent ? parent.getViewMatrix().invert() : matrix;\n\t\treturn mx && mx._shiftless();\n\t},\n\n\tstatics: {\n\t\t_updateBoundsCache: function(parent, item) {\n\t\t\tif (parent && item) {\n\t\t\t\tvar id = item._id,\n\t\t\t\t\tref = parent._boundsCache = parent._boundsCache || {\n\t\t\t\t\t\tids: {},\n\t\t\t\t\t\tlist: []\n\t\t\t\t\t};\n\t\t\t\tif (!ref.ids[id]) {\n\t\t\t\t\tref.list.push(item);\n\t\t\t\t\tref.ids[id] = item;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t_clearBoundsCache: function(item) {\n\t\t\tvar cache = item._boundsCache;\n\t\t\tif (cache) {\n\t\t\t\titem._bounds = item._position = item._boundsCache = undefined;\n\t\t\t\tfor (var i = 0, list = cache.list, l = list.length; i < l; i++){\n\t\t\t\t\tvar other = list[i];\n\t\t\t\t\tif (other !== item) {\n\t\t\t\t\t\tother._bounds = other._position = undefined;\n\t\t\t\t\t\tif (other._boundsCache)\n\t\t\t\t\t\t\tItem._clearBoundsCache(other);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t_getBounds: function(items, matrix, options) {\n\t\t\tvar x1 = Infinity,\n\t\t\t\tx2 = -x1,\n\t\t\t\ty1 = x1,\n\t\t\t\ty2 = x2,\n\t\t\t\tnonscaling = false;\n\t\t\toptions = options || {};\n\t\t\tfor (var i = 0, l = items.length; i < l; i++) {\n\t\t\t\tvar item = items[i];\n\t\t\t\tif (item._visible && !item.isEmpty(true)) {\n\t\t\t\t\tvar bounds = item._getCachedBounds(\n\t\t\t\t\t\tmatrix && matrix.appended(item._matrix), options, true),\n\t\t\t\t\t\trect = bounds.rect;\n\t\t\t\t\tx1 = Math.min(rect.x, x1);\n\t\t\t\t\ty1 = Math.min(rect.y, y1);\n\t\t\t\t\tx2 = Math.max(rect.x + rect.width, x2);\n\t\t\t\t\ty2 = Math.max(rect.y + rect.height, y2);\n\t\t\t\t\tif (bounds.nonscaling)\n\t\t\t\t\t\tnonscaling = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\trect: isFinite(x1)\n\t\t\t\t\t? new Rectangle(x1, y1, x2 - x1, y2 - y1)\n\t\t\t\t\t: new Rectangle(),\n\t\t\t\tnonscaling: nonscaling\n\t\t\t};\n\t\t}\n\t}\n\n}), {\n\tbeans: true,\n\n\t_decompose: function() {\n\t\treturn this._applyMatrix\n\t\t\t? null\n\t\t\t: this._decomposed || (this._decomposed = this._matrix.decompose());\n\t},\n\n\tgetRotation: function() {\n\t\tvar decomposed = this._decompose();\n\t\treturn decomposed ? decomposed.rotation : 0;\n\t},\n\n\tsetRotation: function(rotation) {\n\t\tvar current = this.getRotation();\n\t\tif (current != null && rotation != null) {\n\t\t\tvar decomposed = this._decomposed;\n\t\t\tthis.rotate(rotation - current);\n\t\t\tif (decomposed) {\n\t\t\t\tdecomposed.rotation = rotation;\n\t\t\t\tthis._decomposed = decomposed;\n\t\t\t}\n\t\t}\n\t},\n\n\tgetScaling: function() {\n\t\tvar decomposed = this._decompose(),\n\t\t\ts = decomposed && decomposed.scaling;\n\t\treturn new LinkedPoint(s ? s.x : 1, s ? s.y : 1, this, 'setScaling');\n\t},\n\n\tsetScaling: function() {\n\t\tvar current = this.getScaling(),\n\t\t\tscaling = Point.read(arguments, 0, { clone: true, readNull: true });\n\t\tif (current && scaling && !current.equals(scaling)) {\n\t\t\tvar rotation = this.getRotation(),\n\t\t\t\tdecomposed = this._decomposed,\n\t\t\t\tmatrix = new Matrix(),\n\t\t\t\tcenter = this.getPosition(true);\n\t\t\tmatrix.translate(center);\n\t\t\tif (rotation)\n\t\t\t\tmatrix.rotate(rotation);\n\t\t\tmatrix.scale(scaling.x / current.x, scaling.y / current.y);\n\t\t\tif (rotation)\n\t\t\t\tmatrix.rotate(-rotation);\n\t\t\tmatrix.translate(center.negate());\n\t\t\tthis.transform(matrix);\n\t\t\tif (decomposed) {\n\t\t\t\tdecomposed.scaling = scaling;\n\t\t\t\tthis._decomposed = decomposed;\n\t\t\t}\n\t\t}\n\t},\n\n\tgetMatrix: function() {\n\t\treturn this._matrix;\n\t},\n\n\tsetMatrix: function() {\n\t\tvar matrix = this._matrix;\n\t\tmatrix.initialize.apply(matrix, arguments);\n\t},\n\n\tgetGlobalMatrix: function(_dontClone) {\n\t\tvar matrix = this._globalMatrix;\n\t\tif (matrix) {\n\t\t\tvar parent = this._parent;\n\t\t\tvar parents = [];\n\t\t\twhile (parent) {\n\t\t\t\tif (!parent._globalMatrix) {\n\t\t\t\t\tmatrix = null;\n\t\t\t\t\tfor (var i = 0, l = parents.length; i < l; i++) {\n\t\t\t\t\t\tparents[i]._globalMatrix = null;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tparents.push(parent);\n\t\t\t\tparent = parent._parent;\n\t\t\t}\n\t\t}\n\t\tif (!matrix) {\n\t\t\tmatrix = this._globalMatrix = this._matrix.clone();\n\t\t\tvar parent = this._parent;\n\t\t\tif (parent)\n\t\t\t\tmatrix.prepend(parent.getGlobalMatrix(true));\n\t\t}\n\t\treturn _dontClone ? matrix : matrix.clone();\n\t},\n\n\tgetViewMatrix: function() {\n\t\treturn this.getGlobalMatrix().prepend(this.getView()._matrix);\n\t},\n\n\tgetApplyMatrix: function() {\n\t\treturn this._applyMatrix;\n\t},\n\n\tsetApplyMatrix: function(apply) {\n\t\tif (this._applyMatrix = this._canApplyMatrix && !!apply)\n\t\t\tthis.transform(null, true);\n\t},\n\n\tgetTransformContent: '#getApplyMatrix',\n\tsetTransformContent: '#setApplyMatrix',\n}, {\n\tgetProject: function() {\n\t\treturn this._project;\n\t},\n\n\t_setProject: function(project, installEvents) {\n\t\tif (this._project !== project) {\n\t\t\tif (this._project)\n\t\t\t\tthis._installEvents(false);\n\t\t\tthis._project = project;\n\t\t\tvar children = this._children;\n\t\t\tfor (var i = 0, l = children && children.length; i < l; i++)\n\t\t\t\tchildren[i]._setProject(project);\n\t\t\tinstallEvents = true;\n\t\t}\n\t\tif (installEvents)\n\t\t\tthis._installEvents(true);\n\t},\n\n\tgetView: function() {\n\t\treturn this._project._view;\n\t},\n\n\t_installEvents: function _installEvents(install) {\n\t\t_installEvents.base.call(this, install);\n\t\tvar children = this._children;\n\t\tfor (var i = 0, l = children && children.length; i < l; i++)\n\t\t\tchildren[i]._installEvents(install);\n\t},\n\n\tgetLayer: function() {\n\t\tvar parent = this;\n\t\twhile (parent = parent._parent) {\n\t\t\tif (parent instanceof Layer)\n\t\t\t\treturn parent;\n\t\t}\n\t\treturn null;\n\t},\n\n\tgetParent: function() {\n\t\treturn this._parent;\n\t},\n\n\tsetParent: function(item) {\n\t\treturn item.addChild(this);\n\t},\n\n\t_getOwner: '#getParent',\n\n\tgetChildren: function() {\n\t\treturn this._children;\n\t},\n\n\tsetChildren: function(items) {\n\t\tthis.removeChildren();\n\t\tthis.addChildren(items);\n\t},\n\n\tgetFirstChild: function() {\n\t\treturn this._children && this._children[0] || null;\n\t},\n\n\tgetLastChild: function() {\n\t\treturn this._children && this._children[this._children.length - 1]\n\t\t\t\t|| null;\n\t},\n\n\tgetNextSibling: function() {\n\t\tvar owner = this._getOwner();\n\t\treturn owner && owner._children[this._index + 1] || null;\n\t},\n\n\tgetPreviousSibling: function() {\n\t\tvar owner = this._getOwner();\n\t\treturn owner && owner._children[this._index - 1] || null;\n\t},\n\n\tgetIndex: function() {\n\t\treturn this._index;\n\t},\n\n\tequals: function(item) {\n\t\treturn item === this || item && this._class === item._class\n\t\t\t\t&& this._style.equals(item._style)\n\t\t\t\t&& this._matrix.equals(item._matrix)\n\t\t\t\t&& this._locked === item._locked\n\t\t\t\t&& this._visible === item._visible\n\t\t\t\t&& this._blendMode === item._blendMode\n\t\t\t\t&& this._opacity === item._opacity\n\t\t\t\t&& this._clipMask === item._clipMask\n\t\t\t\t&& this._guide === item._guide\n\t\t\t\t&& this._equals(item)\n\t\t\t\t|| false;\n\t},\n\n\t_equals: function(item) {\n\t\treturn Base.equals(this._children, item._children);\n\t},\n\n\tclone: function(options) {\n\t\tvar copy = new this.constructor(Item.NO_INSERT),\n\t\t\tchildren = this._children,\n\t\t\tinsert = Base.pick(options ? options.insert : undefined,\n\t\t\t\t\toptions === undefined || options === true),\n\t\t\tdeep = Base.pick(options ? options.deep : undefined, true);\n\t\tif (children)\n\t\t\tcopy.copyAttributes(this);\n\t\tif (!children || deep)\n\t\t\tcopy.copyContent(this);\n\t\tif (!children)\n\t\t\tcopy.copyAttributes(this);\n\t\tif (insert)\n\t\t\tcopy.insertAbove(this);\n\t\tvar name = this._name,\n\t\t\tparent = this._parent;\n\t\tif (name && parent) {\n\t\t\tvar children = parent._children,\n\t\t\t\torig = name,\n\t\t\t\ti = 1;\n\t\t\twhile (children[name])\n\t\t\t\tname = orig + ' ' + (i++);\n\t\t\tif (name !== orig)\n\t\t\t\tcopy.setName(name);\n\t\t}\n\t\treturn copy;\n\t},\n\n\tcopyContent: function(source) {\n\t\tvar children = source._children;\n\t\tfor (var i = 0, l = children && children.length; i < l; i++) {\n\t\t\tthis.addChild(children[i].clone(false), true);\n\t\t}\n\t},\n\n\tcopyAttributes: function(source, excludeMatrix) {\n\t\tthis.setStyle(source._style);\n\t\tvar keys = ['_locked', '_visible', '_blendMode', '_opacity',\n\t\t\t\t'_clipMask', '_guide'];\n\t\tfor (var i = 0, l = keys.length; i < l; i++) {\n\t\t\tvar key = keys[i];\n\t\t\tif (source.hasOwnProperty(key))\n\t\t\t\tthis[key] = source[key];\n\t\t}\n\t\tif (!excludeMatrix)\n\t\t\tthis._matrix.set(source._matrix, true);\n\t\tthis.setApplyMatrix(source._applyMatrix);\n\t\tthis.setPivot(source._pivot);\n\t\tthis.setSelection(source._selection);\n\t\tvar data = source._data,\n\t\t\tname = source._name;\n\t\tthis._data = data ? Base.clone(data) : null;\n\t\tif (name)\n\t\t\tthis.setName(name);\n\t},\n\n\trasterize: function(resolution, insert, boundRect) {\n\t\tvar bounds = boundRect ? boundRect : this.getStrokeBounds(),\n\t\t\tscale = (resolution || this.getView().getResolution()) / 72,\n\t\t\ttopLeft = bounds.getTopLeft().floor(),\n\t\t\tbottomRight = bounds.getBottomRight().ceil(),\n\t\t\tsize = new Size(bottomRight.subtract(topLeft)),\n\t\t\traster = new Raster(Item.NO_INSERT);\n\t\tif (!size.isZero()) {\n\t\t\tvar canvas = CanvasProvider.getCanvas(size.multiply(scale)),\n\t\t\t\tctx = canvas.getContext('2d'),\n\t\t\t\tmatrix = new Matrix().scale(scale).translate(topLeft.negate());\n\t\t\tctx.imageSmoothingEnabled = false;\n\t\t\tctx.save();\n\t\t\tmatrix.applyToContext(ctx);\n\t\t\tthis.draw(ctx, new Base({ matrices: [matrix] }));\n\t\t\tctx.restore();\n\t\t\traster.setCanvas(canvas);\n\t\t}\n\t\traster.transform(new Matrix().translate(topLeft.add(size.divide(2)))\n\t\t\t\t.scale(1 / scale));\n\t\tif (insert === undefined || insert)\n\t\t\traster.insertAbove(this);\n\t\treturn raster;\n\t},\n\n\tcontains: function() {\n\t\tvar matrix = this._matrix;\n\t\treturn (\n\t\t\tmatrix.isInvertible() &&\n\t\t\t!!this._contains(matrix._inverseTransform(Point.read(arguments)))\n\t\t);\n\t},\n\n\t_contains: function(point) {\n\t\tvar children = this._children;\n\t\tif (children) {\n\t\t\tfor (var i = children.length - 1; i >= 0; i--) {\n\t\t\t\tif (children[i].contains(point))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\treturn point.isInside(this.getInternalBounds());\n\t},\n\n\tisInside: function() {\n\t\treturn Rectangle.read(arguments).contains(this.getBounds());\n\t},\n\n\t_asPathItem: function() {\n\t\treturn new Path.Rectangle({\n\t\t\trectangle: this.getInternalBounds(),\n\t\t\tmatrix: this._matrix,\n\t\t\tinsert: false,\n\t\t});\n\t},\n\n\tintersects: function(item, _matrix) {\n\t\tif (!(item instanceof Item))\n\t\t\treturn false;\n\t\treturn this._asPathItem().getIntersections(item._asPathItem(), null,\n\t\t\t\t_matrix, true).length > 0;\n\t}\n},\nnew function() {\n\tfunction hitTest() {\n\t\tvar args = arguments;\n\t\treturn this._hitTest(\n\t\t\t\tPoint.read(args),\n\t\t\t\tHitResult.getOptions(args));\n\t}\n\n\tfunction hitTestAll() {\n\t\tvar args = arguments,\n\t\t\tpoint = Point.read(args),\n\t\t\toptions = HitResult.getOptions(args),\n\t\t\tall = [];\n\t\tthis._hitTest(point, new Base({ all: all }, options));\n\t\treturn all;\n\t}\n\n\tfunction hitTestChildren(point, options, viewMatrix, _exclude) {\n\t\tvar children = this._children;\n\t\tif (children) {\n\t\t\tfor (var i = children.length - 1; i >= 0; i--) {\n\t\t\t\tvar child = children[i];\n\t\t\t\tvar res = child !== _exclude && child._hitTest(point, options,\n\t\t\t\t\t\tviewMatrix);\n\t\t\t\tif (res && !options.all)\n\t\t\t\t\treturn res;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tProject.inject({\n\t\thitTest: hitTest,\n\t\thitTestAll: hitTestAll,\n\t\t_hitTest: hitTestChildren\n\t});\n\n\treturn {\n\t\thitTest: hitTest,\n\t\thitTestAll: hitTestAll,\n\t\t_hitTestChildren: hitTestChildren,\n\t};\n}, {\n\n\t_hitTest: function(point, options, parentViewMatrix) {\n\t\tif (this._locked || !this._visible || this._guide && !options.guides\n\t\t\t\t|| this.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar matrix = this._matrix,\n\t\t\tviewMatrix = parentViewMatrix\n\t\t\t\t\t? parentViewMatrix.appended(matrix)\n\t\t\t\t\t: this.getGlobalMatrix().prepend(this.getView()._matrix),\n\t\t\ttolerance = Math.max(options.tolerance, 1e-12),\n\t\t\ttolerancePadding = options._tolerancePadding = new Size(\n\t\t\t\t\tPath._getStrokePadding(tolerance,\n\t\t\t\t\t\tmatrix._shiftless().invert()));\n\t\tpoint = matrix._inverseTransform(point);\n\t\tif (!point || !this._children &&\n\t\t\t!this.getBounds({ internal: true, stroke: true, handle: true })\n\t\t\t\t.expand(tolerancePadding.multiply(2))._containsPoint(point)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar checkSelf = !(options.guides && !this._guide\n\t\t\t\t|| options.selected && !this.isSelected()\n\t\t\t\t|| options.type && options.type !== Base.hyphenate(this._class)\n\t\t\t\t|| options.class && !(this instanceof options.class)),\n\t\t\tmatch = options.match,\n\t\t\tthat = this,\n\t\t\tbounds,\n\t\t\tres;\n\n\t\tfunction filter(hit) {\n\t\t\tif (hit && match && !match(hit))\n\t\t\t\thit = null;\n\t\t\tif (hit && options.all)\n\t\t\t\toptions.all.push(hit);\n\t\t\treturn hit;\n\t\t}\n\n\t\tfunction checkPoint(type, part) {\n\t\t\tvar pt = part ? bounds['get' + part]() : that.getPosition();\n\t\t\tif (point.subtract(pt).divide(tolerancePadding).length <= 1) {\n\t\t\t\treturn new HitResult(type, that, {\n\t\t\t\t\tname: part ? Base.hyphenate(part) : type,\n\t\t\t\t\tpoint: pt\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tvar checkPosition = options.position,\n\t\t\tcheckCenter = options.center,\n\t\t\tcheckBounds = options.bounds;\n\t\tif (checkSelf && this._parent\n\t\t\t\t&& (checkPosition || checkCenter || checkBounds)) {\n\t\t\tif (checkCenter || checkBounds) {\n\t\t\t\tbounds = this.getInternalBounds();\n\t\t\t}\n\t\t\tres = checkPosition && checkPoint('position') ||\n\t\t\t\t\tcheckCenter && checkPoint('center', 'Center');\n\t\t\tif (!res && checkBounds) {\n\t\t\t\tvar points = [\n\t\t\t\t\t'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',\n\t\t\t\t\t'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'\n\t\t\t\t];\n\t\t\t\tfor (var i = 0; i < 8 && !res; i++) {\n\t\t\t\t\tres = checkPoint('bounds', points[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tres = filter(res);\n\t\t}\n\n\t\tif (!res) {\n\t\t\tres = this._hitTestChildren(point, options, viewMatrix)\n\t\t\t\t|| checkSelf\n\t\t\t\t\t&& filter(this._hitTestSelf(point, options, viewMatrix,\n\t\t\t\t\t\tthis.getStrokeScaling() ? null\n\t\t\t\t\t\t\t: viewMatrix._shiftless().invert()))\n\t\t\t\t|| null;\n\t\t}\n\t\tif (res && res.point) {\n\t\t\tres.point = matrix.transform(res.point);\n\t\t}\n\t\treturn res;\n\t},\n\n\t_hitTestSelf: function(point, options) {\n\t\tif (options.fill && this.hasFill() && this._contains(point))\n\t\t\treturn new HitResult('fill', this);\n\t},\n\n\tmatches: function(name, compare) {\n\t\tfunction matchObject(obj1, obj2) {\n\t\t\tfor (var i in obj1) {\n\t\t\t\tif (obj1.hasOwnProperty(i)) {\n\t\t\t\t\tvar val1 = obj1[i],\n\t\t\t\t\t\tval2 = obj2[i];\n\t\t\t\t\tif (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {\n\t\t\t\t\t\tif (!matchObject(val1, val2))\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t} else if (!Base.equals(val1, val2)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tvar type = typeof name;\n\t\tif (type === 'object') {\n\t\t\tfor (var key in name) {\n\t\t\t\tif (name.hasOwnProperty(key) && !this.matches(key, name[key]))\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t} else if (type === 'function') {\n\t\t\treturn name(this);\n\t\t} else if (name === 'match') {\n\t\t\treturn compare(this);\n\t\t} else {\n\t\t\tvar value = /^(empty|editable)$/.test(name)\n\t\t\t\t\t? this['is' + Base.capitalize(name)]()\n\t\t\t\t\t: name === 'type'\n\t\t\t\t\t\t? Base.hyphenate(this._class)\n\t\t\t\t\t\t: this[name];\n\t\t\tif (name === 'class') {\n\t\t\t\tif (typeof compare === 'function')\n\t\t\t\t\treturn this instanceof compare;\n\t\t\t\tvalue = this._class;\n\t\t\t}\n\t\t\tif (typeof compare === 'function') {\n\t\t\t\treturn !!compare(value);\n\t\t\t} else if (compare) {\n\t\t\t\tif (compare.test) {\n\t\t\t\t\treturn compare.test(value);\n\t\t\t\t} else if (Base.isPlainObject(compare)) {\n\t\t\t\t\treturn matchObject(compare, value);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Base.equals(value, compare);\n\t\t}\n\t},\n\n\tgetItems: function(options) {\n\t\treturn Item._getItems(this, options, this._matrix);\n\t},\n\n\tgetItem: function(options) {\n\t\treturn Item._getItems(this, options, this._matrix, null, true)[0]\n\t\t\t\t|| null;\n\t},\n\n\tstatics: {\n\t\t_getItems: function _getItems(item, options, matrix, param, firstOnly) {\n\t\t\tif (!param) {\n\t\t\t\tvar obj = typeof options === 'object' && options,\n\t\t\t\t\toverlapping = obj && obj.overlapping,\n\t\t\t\t\tinside = obj && obj.inside,\n\t\t\t\t\tbounds = overlapping || inside,\n\t\t\t\t\trect = bounds && Rectangle.read([bounds]);\n\t\t\t\tparam = {\n\t\t\t\t\titems: [],\n\t\t\t\t\trecursive: obj && obj.recursive !== false,\n\t\t\t\t\tinside: !!inside,\n\t\t\t\t\toverlapping: !!overlapping,\n\t\t\t\t\trect: rect,\n\t\t\t\t\tpath: overlapping && new Path.Rectangle({\n\t\t\t\t\t\trectangle: rect,\n\t\t\t\t\t\tinsert: false\n\t\t\t\t\t})\n\t\t\t\t};\n\t\t\t\tif (obj) {\n\t\t\t\t\toptions = Base.filter({}, options, {\n\t\t\t\t\t\trecursive: true, inside: true, overlapping: true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar children = item._children,\n\t\t\t\titems = param.items,\n\t\t\t\trect = param.rect;\n\t\t\tmatrix = rect && (matrix || new Matrix());\n\t\t\tfor (var i = 0, l = children && children.length; i < l; i++) {\n\t\t\t\tvar child = children[i],\n\t\t\t\t\tchildMatrix = matrix && matrix.appended(child._matrix),\n\t\t\t\t\tadd = true;\n\t\t\t\tif (rect) {\n\t\t\t\t\tvar bounds = child.getBounds(childMatrix);\n\t\t\t\t\tif (!rect.intersects(bounds))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!(rect.contains(bounds)\n\t\t\t\t\t\t\t|| param.overlapping && (bounds.contains(rect)\n\t\t\t\t\t\t\t\t|| param.path.intersects(child, childMatrix))))\n\t\t\t\t\t\tadd = false;\n\t\t\t\t}\n\t\t\t\tif (add && child.matches(options)) {\n\t\t\t\t\titems.push(child);\n\t\t\t\t\tif (firstOnly)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (param.recursive !== false) {\n\t\t\t\t\t_getItems(child, options, childMatrix, param, firstOnly);\n\t\t\t\t}\n\t\t\t\tif (firstOnly && items.length > 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn items;\n\t\t}\n\t}\n}, {\n\n\timportJSON: function(json) {\n\t\tvar res = Base.importJSON(json, this);\n\t\treturn res !== this ? this.addChild(res) : res;\n\t},\n\n\taddChild: function(item) {\n\t\treturn this.insertChild(undefined, item);\n\t},\n\n\tinsertChild: function(index, item) {\n\t\tvar res = item ? this.insertChildren(index, [item]) : null;\n\t\treturn res && res[0];\n\t},\n\n\taddChildren: function(items) {\n\t\treturn this.insertChildren(this._children.length, items);\n\t},\n\n\tinsertChildren: function(index, items) {\n\t\tvar children = this._children;\n\t\tif (children && items && items.length > 0) {\n\t\t\titems = Base.slice(items);\n\t\t\tvar inserted = {};\n\t\t\tfor (var i = items.length - 1; i >= 0; i--) {\n\t\t\t\tvar item = items[i],\n\t\t\t\t\tid = item && item._id;\n\t\t\t\tif (!item || inserted[id]) {\n\t\t\t\t\titems.splice(i, 1);\n\t\t\t\t} else {\n\t\t\t\t\titem._remove(false, true);\n\t\t\t\t\tinserted[id] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tBase.splice(children, items, index, 0);\n\t\t\tvar project = this._project,\n\t\t\t\tnotifySelf = project._changes;\n\t\t\tfor (var i = 0, l = items.length; i < l; i++) {\n\t\t\t\tvar item = items[i],\n\t\t\t\t\tname = item._name;\n\t\t\t\titem._parent = this;\n\t\t\t\titem._setProject(project, true);\n\t\t\t\tif (name)\n\t\t\t\t\titem.setName(name);\n\t\t\t\tif (notifySelf)\n\t\t\t\t\titem._changed(5);\n\t\t\t}\n\t\t\tthis._changed(11);\n\t\t} else {\n\t\t\titems = null;\n\t\t}\n\t\treturn items;\n\t},\n\n\t_insertItem: '#insertChild',\n\n\t_insertAt: function(item, offset) {\n\t\tvar owner = item && item._getOwner(),\n\t\t\tres = item !== this && owner ? this : null;\n\t\tif (res) {\n\t\t\tres._remove(false, true);\n\t\t\towner._insertItem(item._index + offset, res);\n\t\t}\n\t\treturn res;\n\t},\n\n\tinsertAbove: function(item) {\n\t\treturn this._insertAt(item, 1);\n\t},\n\n\tinsertBelow: function(item) {\n\t\treturn this._insertAt(item, 0);\n\t},\n\n\tsendToBack: function() {\n\t\tvar owner = this._getOwner();\n\t\treturn owner ? owner._insertItem(0, this) : null;\n\t},\n\n\tbringToFront: function() {\n\t\tvar owner = this._getOwner();\n\t\treturn owner ? owner._insertItem(undefined, this) : null;\n\t},\n\n\tappendTop: '#addChild',\n\n\tappendBottom: function(item) {\n\t\treturn this.insertChild(0, item);\n\t},\n\n\tmoveAbove: '#insertAbove',\n\n\tmoveBelow: '#insertBelow',\n\n\taddTo: function(owner) {\n\t\treturn owner._insertItem(undefined, this);\n\t},\n\n\tcopyTo: function(owner) {\n\t\treturn this.clone(false).addTo(owner);\n\t},\n\n\treduce: function(options) {\n\t\tvar children = this._children;\n\t\tif (children && children.length === 1) {\n\t\t\tvar child = children[0].reduce(options);\n\t\t\tif (this._parent) {\n\t\t\t\tchild.insertAbove(this);\n\t\t\t\tthis.remove();\n\t\t\t} else {\n\t\t\t\tchild.remove();\n\t\t\t}\n\t\t\treturn child;\n\t\t}\n\t\treturn this;\n\t},\n\n\t_removeNamed: function() {\n\t\tvar owner = this._getOwner();\n\t\tif (owner) {\n\t\t\tvar children = owner._children,\n\t\t\t\tnamedChildren = owner._namedChildren,\n\t\t\t\tname = this._name,\n\t\t\t\tnamedArray = namedChildren[name],\n\t\t\t\tindex = namedArray ? namedArray.indexOf(this) : -1;\n\t\t\tif (index !== -1) {\n\t\t\t\tif (children[name] == this)\n\t\t\t\t\tdelete children[name];\n\t\t\t\tnamedArray.splice(index, 1);\n\t\t\t\tif (namedArray.length) {\n\t\t\t\t\tchildren[name] = namedArray[0];\n\t\t\t\t} else {\n\t\t\t\t\tdelete namedChildren[name];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t_remove: function(notifySelf, notifyParent) {\n\t\tvar owner = this._getOwner(),\n\t\t\tproject = this._project,\n\t\t\tindex = this._index;\n\t\tif (this._style)\n\t\t\tthis._style._dispose();\n\t\tif (owner) {\n\t\t\tif (this._name)\n\t\t\t\tthis._removeNamed();\n\t\t\tif (index != null) {\n\t\t\t\tif (project._activeLayer === this)\n\t\t\t\t\tproject._activeLayer = this.getNextSibling()\n\t\t\t\t\t\t\t|| this.getPreviousSibling();\n\t\t\t\tBase.splice(owner._children, null, index, 1);\n\t\t\t}\n\t\t\tthis._installEvents(false);\n\t\t\tif (notifySelf && project._changes)\n\t\t\t\tthis._changed(5);\n\t\t\tif (notifyParent)\n\t\t\t\towner._changed(11, this);\n\t\t\tthis._parent = null;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tremove: function() {\n\t\treturn this._remove(true, true);\n\t},\n\n\treplaceWith: function(item) {\n\t\tvar ok = item && item.insertBelow(this);\n\t\tif (ok)\n\t\t\tthis.remove();\n\t\treturn ok;\n\t},\n\n\tremoveChildren: function(start, end) {\n\t\tif (!this._children)\n\t\t\treturn null;\n\t\tstart = start || 0;\n\t\tend = Base.pick(end, this._children.length);\n\t\tvar removed = Base.splice(this._children, null, start, end - start);\n\t\tfor (var i = removed.length - 1; i >= 0; i--) {\n\t\t\tremoved[i]._remove(true, false);\n\t\t}\n\t\tif (removed.length > 0)\n\t\t\tthis._changed(11);\n\t\treturn removed;\n\t},\n\n\tclear: '#removeChildren',\n\n\treverseChildren: function() {\n\t\tif (this._children) {\n\t\t\tthis._children.reverse();\n\t\t\tfor (var i = 0, l = this._children.length; i < l; i++)\n\t\t\t\tthis._children[i]._index = i;\n\t\t\tthis._changed(11);\n\t\t}\n\t},\n\n\tisEmpty: function(recursively) {\n\t\tvar children = this._children;\n\t\tvar numChildren = children ? children.length : 0;\n\t\tif (recursively) {\n\t\t\tfor (var i = 0; i < numChildren; i++) {\n\t\t\t\tif (!children[i].isEmpty(recursively)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn !numChildren;\n\t},\n\n\tisEditable: function() {\n\t\tvar item = this;\n\t\twhile (item) {\n\t\t\tif (!item._visible || item._locked)\n\t\t\t\treturn false;\n\t\t\titem = item._parent;\n\t\t}\n\t\treturn true;\n\t},\n\n\thasFill: function() {\n\t\treturn this.getStyle().hasFill();\n\t},\n\n\thasStroke: function() {\n\t\treturn this.getStyle().hasStroke();\n\t},\n\n\thasShadow: function() {\n\t\treturn this.getStyle().hasShadow();\n\t},\n\n\t_getOrder: function(item) {\n\t\tfunction getList(item) {\n\t\t\tvar list = [];\n\t\t\tdo {\n\t\t\t\tlist.unshift(item);\n\t\t\t} while (item = item._parent);\n\t\t\treturn list;\n\t\t}\n\t\tvar list1 = getList(this),\n\t\t\tlist2 = getList(item);\n\t\tfor (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {\n\t\t\tif (list1[i] != list2[i]) {\n\t\t\t\treturn list1[i]._index < list2[i]._index ? 1 : -1;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t},\n\n\thasChildren: function() {\n\t\treturn this._children && this._children.length > 0;\n\t},\n\n\tisInserted: function() {\n\t\treturn this._parent ? this._parent.isInserted() : false;\n\t},\n\n\tisAbove: function(item) {\n\t\treturn this._getOrder(item) === -1;\n\t},\n\n\tisBelow: function(item) {\n\t\treturn this._getOrder(item) === 1;\n\t},\n\n\tisParent: function(item) {\n\t\treturn this._parent === item;\n\t},\n\n\tisChild: function(item) {\n\t\treturn item && item._parent === this;\n\t},\n\n\tisDescendant: function(item) {\n\t\tvar parent = this;\n\t\twhile (parent = parent._parent) {\n\t\t\tif (parent === item)\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tisAncestor: function(item) {\n\t\treturn item ? item.isDescendant(this) : false;\n\t},\n\n\tisSibling: function(item) {\n\t\treturn this._parent === item._parent;\n\t},\n\n\tisGroupedWith: function(item) {\n\t\tvar parent = this._parent;\n\t\twhile (parent) {\n\t\t\tif (parent._parent\n\t\t\t\t&& /^(Group|Layer|CompoundPath)$/.test(parent._class)\n\t\t\t\t&& item.isDescendant(parent))\n\t\t\t\t\treturn true;\n\t\t\tparent = parent._parent;\n\t\t}\n\t\treturn false;\n\t},\n\n}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {\n\tvar rotate = key === 'rotate';\n\tthis[key] = function() {\n\t\tvar args = arguments,\n\t\t\tvalue = (rotate ? Base : Point).read(args),\n\t\t\tcenter = Point.read(args, 0, { readNull: true });\n\t\treturn this.transform(new Matrix()[key](value,\n\t\t\t\tcenter || this.getPosition(true)));\n\t};\n}, {\n\ttranslate: function() {\n\t\tvar mx = new Matrix();\n\t\treturn this.transform(mx.translate.apply(mx, arguments));\n\t},\n\n\ttransform: function(matrix, _applyRecursively, _setApplyMatrix) {\n\t\tvar _matrix = this._matrix,\n\t\t\ttransformMatrix = matrix && !matrix.isIdentity(),\n\t\t\tapplyMatrix = (\n\t\t\t\t_setApplyMatrix && this._canApplyMatrix ||\n\t\t\t\tthis._applyMatrix && (\n\t\t\t\t\ttransformMatrix || !_matrix.isIdentity() ||\n\t\t\t\t\t_applyRecursively && this._children\n\t\t\t\t)\n\t\t\t);\n\t\tif (!transformMatrix && !applyMatrix)\n\t\t\treturn this;\n\t\tif (transformMatrix) {\n\t\t\tif (!matrix.isInvertible() && _matrix.isInvertible())\n\t\t\t\t_matrix._backup = _matrix.getValues();\n\t\t\t_matrix.prepend(matrix, true);\n\t\t\tvar style = this._style,\n\t\t\t\tfillColor = style.getFillColor(true),\n\t\t\t\tstrokeColor = style.getStrokeColor(true);\n\t\t\tif (fillColor)\n\t\t\t\tfillColor.transform(matrix);\n\t\t\tif (strokeColor)\n\t\t\t\tstrokeColor.transform(matrix);\n\t\t}\n\n\t\tif (applyMatrix && (applyMatrix = this._transformContent(\n\t\t\t\t_matrix, _applyRecursively, _setApplyMatrix))) {\n\t\t\tvar pivot = this._pivot;\n\t\t\tif (pivot)\n\t\t\t\t_matrix._transformPoint(pivot, pivot, true);\n\t\t\t_matrix.reset(true);\n\t\t\tif (_setApplyMatrix && this._canApplyMatrix)\n\t\t\t\tthis._applyMatrix = true;\n\t\t}\n\t\tvar bounds = this._bounds,\n\t\t\tposition = this._position;\n\t\tif (transformMatrix || applyMatrix) {\n\t\t\tthis._changed(25);\n\t\t}\n\t\tvar decomp = transformMatrix && bounds && matrix.decompose();\n\t\tif (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {\n\t\t\tfor (var key in bounds) {\n\t\t\t\tvar cache = bounds[key];\n\t\t\t\tif (cache.nonscaling) {\n\t\t\t\t\tdelete bounds[key];\n\t\t\t\t} else if (applyMatrix || !cache.internal) {\n\t\t\t\t\tvar rect = cache.rect;\n\t\t\t\t\tmatrix._transformBounds(rect, rect);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._bounds = bounds;\n\t\t\tvar cached = bounds[this._getBoundsCacheKey(\n\t\t\t\tthis._boundsOptions || {})];\n\t\t\tif (cached) {\n\t\t\t\tthis._position = this._getPositionFromBounds(cached.rect);\n\t\t\t}\n\t\t} else if (transformMatrix && position && this._pivot) {\n\t\t\tthis._position = matrix._transformPoint(position, position);\n\t\t}\n\t\treturn this;\n\t},\n\n\t_transformContent: function(matrix, applyRecursively, setApplyMatrix) {\n\t\tvar children = this._children;\n\t\tif (children) {\n\t\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\t\tchildren[i].transform(matrix, applyRecursively, setApplyMatrix);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t},\n\n\tglobalToLocal: function() {\n\t\treturn this.getGlobalMatrix(true)._inverseTransform(\n\t\t\t\tPoint.read(arguments));\n\t},\n\n\tlocalToGlobal: function() {\n\t\treturn this.getGlobalMatrix(true)._transformPoint(\n\t\t\t\tPoint.read(arguments));\n\t},\n\n\tparentToLocal: function() {\n\t\treturn this._matrix._inverseTransform(Point.read(arguments));\n\t},\n\n\tlocalToParent: function() {\n\t\treturn this._matrix._transformPoint(Point.read(arguments));\n\t},\n\n\tfitBounds: function(rectangle, fill) {\n\t\trectangle = Rectangle.read(arguments);\n\t\tvar bounds = this.getBounds(),\n\t\t\titemRatio = bounds.height / bounds.width,\n\t\t\trectRatio = rectangle.height / rectangle.width,\n\t\t\tscale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)\n\t\t\t\t\t? rectangle.width / bounds.width\n\t\t\t\t\t: rectangle.height / bounds.height,\n\t\t\tnewBounds = new Rectangle(new Point(),\n\t\t\t\t\tnew Size(bounds.width * scale, bounds.height * scale));\n\t\tnewBounds.setCenter(rectangle.getCenter());\n\t\tthis.setBounds(newBounds);\n\t}\n}), {\n\n\t_setStyles: function(ctx, param, viewMatrix, strokeMatrix) {\n\t\tvar style = this._style,\n\t\t\tmatrix = this._matrix;\n\t\tif (style.hasFill()) {\n\t\t\tctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix, strokeMatrix);\n\t\t}\n\t\tif (style.hasStroke()) {\n\t\t\tctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx, matrix, strokeMatrix);\n\t\t\tctx.lineWidth = style.getStrokeWidth();\n\t\t\tvar strokeJoin = style.getStrokeJoin(),\n\t\t\t\tstrokeCap = style.getStrokeCap(),\n\t\t\t\tmiterLimit = style.getMiterLimit();\n\t\t\tif (strokeJoin)\n\t\t\t\tctx.lineJoin = strokeJoin;\n\t\t\tif (strokeCap)\n\t\t\t\tctx.lineCap = strokeCap;\n\t\t\tif (miterLimit)\n\t\t\t\tctx.miterLimit = miterLimit;\n\t\t\tif (paper.support.nativeDash) {\n\t\t\t\tvar dashArray = style.getDashArray(),\n\t\t\t\t\tdashOffset = style.getDashOffset();\n\t\t\t\tif (dashArray && dashArray.length) {\n\t\t\t\t\tif ('setLineDash' in ctx) {\n\t\t\t\t\t\tctx.setLineDash(dashArray);\n\t\t\t\t\t\tctx.lineDashOffset = dashOffset;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tctx.mozDash = dashArray;\n\t\t\t\t\t\tctx.mozDashOffset = dashOffset;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (style.hasShadow()) {\n\t\t\tvar pixelRatio = param.pixelRatio || 1,\n\t\t\t\tmx = viewMatrix._shiftless().prepend(\n\t\t\t\t\tnew Matrix().scale(pixelRatio, pixelRatio)),\n\t\t\t\tblur = mx.transform(new Point(style.getShadowBlur(), 0)),\n\t\t\t\toffset = mx.transform(this.getShadowOffset());\n\t\t\tctx.shadowColor = style.getShadowColor().toCanvasStyle(ctx);\n\t\t\tctx.shadowBlur = blur.getLength();\n\t\t\tctx.shadowOffsetX = offset.x;\n\t\t\tctx.shadowOffsetY = offset.y;\n\t\t}\n\t},\n\n\tdraw: function(ctx, param, parentStrokeMatrix) {\n\t\tvar updateVersion = this._updateVersion = this._project._updateVersion;\n\t\tif (!this._visible || this._opacity === 0)\n\t\t\treturn;\n\t\tvar matrices = param.matrices,\n\t\t\tviewMatrix = param.viewMatrix,\n\t\t\tmatrix = this._matrix,\n\t\t\tglobalMatrix = matrices[matrices.length - 1].appended(matrix);\n\t\tif (!globalMatrix.isInvertible())\n\t\t\treturn;\n\n\t\tviewMatrix = viewMatrix ? viewMatrix.appended(globalMatrix)\n\t\t\t\t: globalMatrix;\n\n\t\tmatrices.push(globalMatrix);\n\t\tif (param.updateMatrix) {\n\t\t\tthis._globalMatrix = globalMatrix;\n\t\t}\n\n\t\tvar blendMode = this._blendMode,\n\t\t\topacity = Numerical.clamp(this._opacity, 0, 1),\n\t\t\tnormalBlend = blendMode === 'normal',\n\t\t\tnativeBlend = BlendMode.nativeModes[blendMode],\n\t\t\tdirect = normalBlend && opacity === 1\n\t\t\t\t\t|| param.dontStart\n\t\t\t\t\t|| param.clip\n\t\t\t\t\t|| (nativeBlend || normalBlend && opacity < 1)\n\t\t\t\t\t\t&& this._canComposite(),\n\t\t\tpixelRatio = param.pixelRatio || 1,\n\t\t\tmainCtx, itemOffset, prevOffset;\n\t\tif (!direct) {\n\t\t\tvar bounds = this.getStrokeBounds(viewMatrix);\n\t\t\tif (!bounds.width || !bounds.height) {\n\t\t\t\tmatrices.pop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprevOffset = param.offset;\n\t\t\titemOffset = param.offset = bounds.getTopLeft().floor();\n\t\t\tmainCtx = ctx;\n\t\t\tctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1)\n\t\t\t\t\t.multiply(pixelRatio));\n\t\t\tif (pixelRatio !== 1)\n\t\t\t\tctx.scale(pixelRatio, pixelRatio);\n\t\t}\n\t\tctx.save();\n\t\tvar strokeMatrix = parentStrokeMatrix\n\t\t\t\t? parentStrokeMatrix.appended(matrix)\n\t\t\t\t: this._canScaleStroke && !this.getStrokeScaling(true)\n\t\t\t\t\t&& viewMatrix,\n\t\t\tclip = !direct && param.clipItem,\n\t\t\ttransform = !strokeMatrix || clip;\n\t\tif (direct) {\n\t\t\tctx.globalAlpha = opacity;\n\t\t\tif (nativeBlend)\n\t\t\t\tctx.globalCompositeOperation = blendMode;\n\t\t} else if (transform) {\n\t\t\tctx.translate(-itemOffset.x, -itemOffset.y);\n\t\t}\n\t\tif (transform) {\n\t\t\t(direct ? matrix : viewMatrix).applyToContext(ctx);\n\t\t}\n\t\tif (clip) {\n\t\t\tparam.clipItem.draw(ctx, param.extend({ clip: true }));\n\t\t}\n\t\tif (strokeMatrix) {\n\t\t\tctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);\n\t\t\tvar offset = param.offset;\n\t\t\tif (offset)\n\t\t\t\tctx.translate(-offset.x, -offset.y);\n\t\t}\n\t\tthis._draw(ctx, param, viewMatrix, strokeMatrix);\n\t\tctx.restore();\n\t\tmatrices.pop();\n\t\tif (param.clip && !param.dontFinish) {\n\t\t\tctx.clip(this.getFillRule());\n\t\t}\n\t\tif (!direct) {\n\t\t\tBlendMode.process(blendMode, ctx, mainCtx, opacity,\n\t\t\t\t\titemOffset.subtract(prevOffset).multiply(pixelRatio));\n\t\t\tCanvasProvider.release(ctx);\n\t\t\tparam.offset = prevOffset;\n\t\t}\n\t},\n\n\t_isUpdated: function(updateVersion) {\n\t\tvar parent = this._parent;\n\t\tif (parent instanceof CompoundPath)\n\t\t\treturn parent._isUpdated(updateVersion);\n\t\tvar updated = this._updateVersion === updateVersion;\n\t\tif (!updated && parent && parent._visible\n\t\t\t\t&& parent._isUpdated(updateVersion)) {\n\t\t\tthis._updateVersion = updateVersion;\n\t\t\tupdated = true;\n\t\t}\n\t\treturn updated;\n\t},\n\n\t_drawSelection: function(ctx, matrix, size, selectionItems, updateVersion) {\n\t\tvar selection = this._selection,\n\t\t\titemSelected = selection & 1,\n\t\t\tboundsSelected = selection & 2\n\t\t\t\t\t|| itemSelected && this._selectBounds,\n\t\t\tpositionSelected = selection & 4;\n\t\tif (!this._drawSelected)\n\t\t\titemSelected = false;\n\t\tif ((itemSelected || boundsSelected || positionSelected)\n\t\t\t\t&& this._isUpdated(updateVersion)) {\n\t\t\tvar layer,\n\t\t\t\tcolor = this.getSelectedColor(true) || (layer = this.getLayer())\n\t\t\t\t\t&& layer.getSelectedColor(true),\n\t\t\t\tmx = matrix.appended(this.getGlobalMatrix(true)),\n\t\t\t\thalf = size / 2;\n\t\t\tctx.strokeStyle = ctx.fillStyle = color\n\t\t\t\t\t? color.toCanvasStyle(ctx) : '#009dec';\n\t\t\tctx.lineWidth=2.5;\n\t\t\tif (itemSelected)\n\t\t\t\tthis._drawSelected(ctx, mx, selectionItems);\n\t\t\tif (positionSelected) {\n\t\t\t\tvar pos = this.getPosition(true),\n\t\t\t\t\tparent = this._parent,\n\t\t\t\t\tpoint = parent ? parent.localToGlobal(pos) : pos,\n\t\t\t\t\tx = point.x,\n\t\t\t\t\ty = point.y;\n\t\t\t\tctx.beginPath();\n\t\t\t\tctx.arc(x, y, half, 0, Math.PI * 2, true);\n\t\t\t\tctx.stroke();\n\t\t\t\tvar deltas = [[0, -1], [1, 0], [0, 1], [-1, 0]],\n\t\t\t\t\tstart = half,\n\t\t\t\t\tend = size + 1;\n\t\t\t\tfor (var i = 0; i < 4; i++) {\n\t\t\t\t\tvar delta = deltas[i],\n\t\t\t\t\t\tdx = delta[0],\n\t\t\t\t\t\tdy = delta[1];\n\t\t\t\t\tctx.moveTo(x + dx * start, y + dy * start);\n\t\t\t\t\tctx.lineTo(x + dx * end, y + dy * end);\n\t\t\t\t\tctx.stroke();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (boundsSelected) {\n\t\t\t\tvar coords = mx._transformCorners(this.getInternalBounds());\n\t\t\t\tctx.beginPath();\n\t\t\t\tfor (var i = 0; i < 8; i++) {\n\t\t\t\t\tctx[!i ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);\n\t\t\t\t}\n\t\t\t\tctx.closePath();\n\t\t\t\tctx.stroke();\n\t\t\t\tfor (var i = 0; i < 8; i++) {\n\t\t\t\t\tctx.fillRect(coords[i] - half, coords[++i] - half,\n\t\t\t\t\t\t\tsize, size);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t_canComposite: function() {\n\t\treturn false;\n\t}\n}, Base.each(['down', 'drag', 'up', 'move'], function(key) {\n\tthis['removeOn' + Base.capitalize(key)] = function() {\n\t\tvar hash = {};\n\t\thash[key] = true;\n\t\treturn this.removeOn(hash);\n\t};\n}, {\n\n\tremoveOn: function(obj) {\n\t\tfor (var name in obj) {\n\t\t\tif (obj[name]) {\n\t\t\t\tvar key = 'mouse' + name,\n\t\t\t\t\tproject = this._project,\n\t\t\t\t\tsets = project._removeSets = project._removeSets || {};\n\t\t\t\tsets[key] = sets[key] || {};\n\t\t\t\tsets[key][this._id] = this;\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n}), {\n\ttween: function(from, to, options) {\n\t\tif (!options) {\n\t\t\toptions = to;\n\t\t\tto = from;\n\t\t\tfrom = null;\n\t\t\tif (!options) {\n\t\t\t\toptions = to;\n\t\t\t\tto = null;\n\t\t\t}\n\t\t}\n\t\tvar easing = options && options.easing,\n\t\t\tstart = options && options.start,\n\t\t\tduration = options != null && (\n\t\t\t\ttypeof options === 'number' ? options : options.duration\n\t\t\t),\n\t\t\ttween = new Tween(this, from, to, duration, easing, start);\n\t\tfunction onFrame(event) {\n\t\t\ttween._handleFrame(event.time * 1000);\n\t\t\tif (!tween.running) {\n\t\t\t\tthis.off('frame', onFrame);\n\t\t\t}\n\t\t}\n\t\tif (duration) {\n\t\t\tthis.on('frame', onFrame);\n\t\t}\n\t\treturn tween;\n\t},\n\n\ttweenTo: function(to, options) {\n\t\treturn this.tween(null, to, options);\n\t},\n\n\ttweenFrom: function(from, options) {\n\t\treturn this.tween(from, null, options);\n\t}\n});\n\nvar Group = Item.extend({\n\t_class: 'Group',\n\t_selectBounds: false,\n\t_selectChildren: true,\n\t_serializeFields: {\n\t\tchildren: []\n\t},\n\n\tinitialize: function Group(arg) {\n\t\tthis._children = [];\n\t\tthis._namedChildren = {};\n\t\tif (!this._initialize(arg))\n\t\t\tthis.addChildren(Array.isArray(arg) ? arg : arguments);\n\t},\n\n\t_changed: function _changed(flags) {\n\t\t_changed.base.call(this, flags);\n\t\tif (flags & 2050) {\n\t\t\tthis._clipItem = undefined;\n\t\t}\n\t},\n\n\t_getClipItem: function() {\n\t\tvar clipItem = this._clipItem;\n\t\tif (clipItem === undefined) {\n\t\t\tclipItem = null;\n\t\t\tvar children = this._children;\n\t\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\t\tif (children[i]._clipMask) {\n\t\t\t\t\tclipItem = children[i];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._clipItem = clipItem;\n\t\t}\n\t\treturn clipItem;\n\t},\n\n\tisClipped: function() {\n\t\treturn !!this._getClipItem();\n\t},\n\n\tsetClipped: function(clipped) {\n\t\tvar child = this.getFirstChild();\n\t\tif (child)\n\t\t\tchild.setClipMask(clipped);\n\t},\n\n\t_getBounds: function _getBounds(matrix, options) {\n\t\tvar clipItem = this._getClipItem();\n\t\treturn clipItem\n\t\t\t? clipItem._getCachedBounds(clipItem._matrix.prepended(matrix),\n\t\t\t\tBase.set({}, options, { stroke: false }))\n\t\t\t: _getBounds.base.call(this, matrix, options);\n\t},\n\n\t_hitTestChildren: function _hitTestChildren(point, options, viewMatrix) {\n\t\tvar clipItem = this._getClipItem();\n\t\treturn (!clipItem || clipItem.contains(point))\n\t\t\t\t&& _hitTestChildren.base.call(this, point, options, viewMatrix,\n\t\t\t\t\tclipItem);\n\t},\n\n\t_draw: function(ctx, param) {\n\t\tvar clip = param.clip,\n\t\t\tclipItem = !clip && this._getClipItem();\n\t\tparam = param.extend({ clipItem: clipItem, clip: false });\n\t\tif (clip) {\n\t\t\tctx.beginPath();\n\t\t\tparam.dontStart = param.dontFinish = true;\n\t\t} else if (clipItem) {\n\t\t\tclipItem.draw(ctx, param.extend({ clip: true }));\n\t\t}\n\t\tvar children = this._children;\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tvar item = children[i];\n\t\t\tif (item !== clipItem)\n\t\t\t\titem.draw(ctx, param);\n\t\t}\n\t}\n});\n\nvar Layer = Group.extend({\n\t_class: 'Layer',\n\n\tinitialize: function Layer() {\n\t\tGroup.apply(this, arguments);\n\t},\n\n\t_getOwner: function() {\n\t\treturn this._parent || this._index != null && this._project;\n\t},\n\n\tisInserted: function isInserted() {\n\t\treturn this._parent ? isInserted.base.call(this) : this._index != null;\n\t},\n\n\tactivate: function() {\n\t\tthis._project._activeLayer = this;\n\t},\n\n\t_hitTestSelf: function() {\n\t}\n});\n\nvar Shape = Item.extend({\n\t_class: 'Shape',\n\t_applyMatrix: false,\n\t_canApplyMatrix: false,\n\t_canScaleStroke: true,\n\t_serializeFields: {\n\t\ttype: null,\n\t\tsize: null,\n\t\tradius: null\n\t},\n\n\tinitialize: function Shape(props, point) {\n\t\tthis._initialize(props, point);\n\t},\n\n\t_equals: function(item) {\n\t\treturn this._type === item._type\n\t\t\t&& this._size.equals(item._size)\n\t\t\t&& Base.equals(this._radius, item._radius);\n\t},\n\n\tcopyContent: function(source) {\n\t\tthis.setType(source._type);\n\t\tthis.setSize(source._size);\n\t\tthis.setRadius(source._radius);\n\t},\n\n\tgetType: function() {\n\t\treturn this._type;\n\t},\n\n\tsetType: function(type) {\n\t\tthis._type = type;\n\t},\n\n\tgetShape: '#getType',\n\tsetShape: '#setType',\n\n\tgetSize: function() {\n\t\tvar size = this._size;\n\t\treturn new LinkedSize(size.width, size.height, this, 'setSize');\n\t},\n\n\tsetSize: function() {\n\t\tvar size = Size.read(arguments);\n\t\tif (!this._size) {\n\t\t\tthis._size = size.clone();\n\t\t} else if (!this._size.equals(size)) {\n\t\t\tvar type = this._type,\n\t\t\t\twidth = size.width,\n\t\t\t\theight = size.height;\n\t\t\tif (type === 'rectangle') {\n\t\t\t\tthis._radius.set(Size.min(this._radius, size.divide(2).abs()));\n\t\t\t} else if (type === 'circle') {\n\t\t\t\twidth = height = (width + height) / 2;\n\t\t\t\tthis._radius = width / 2;\n\t\t\t} else if (type === 'ellipse') {\n\t\t\t\tthis._radius._set(width / 2, height / 2);\n\t\t\t}\n\t\t\tthis._size._set(width, height);\n\t\t\tthis._changed(9);\n\t\t}\n\t},\n\n\tgetRadius: function() {\n\t\tvar rad = this._radius;\n\t\treturn this._type === 'circle'\n\t\t\t\t? rad\n\t\t\t\t: new LinkedSize(rad.width, rad.height, this, 'setRadius');\n\t},\n\n\tsetRadius: function(radius) {\n\t\tvar type = this._type;\n\t\tif (type === 'circle') {\n\t\t\tif (radius === this._radius)\n\t\t\t\treturn;\n\t\t\tvar size = radius * 2;\n\t\t\tthis._radius = radius;\n\t\t\tthis._size._set(size, size);\n\t\t} else {\n\t\t\tradius = Size.read(arguments);\n\t\t\tif (!this._radius) {\n\t\t\t\tthis._radius = radius.clone();\n\t\t\t} else {\n\t\t\t\tif (this._radius.equals(radius))\n\t\t\t\t\treturn;\n\t\t\t\tthis._radius.set(radius);\n\t\t\t\tif (type === 'rectangle') {\n\t\t\t\t\tvar size = Size.max(this._size, radius.multiply(2));\n\t\t\t\t\tthis._size.set(size);\n\t\t\t\t} else if (type === 'ellipse') {\n\t\t\t\t\tthis._size._set(radius.width * 2, radius.height * 2);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis._changed(9);\n\t},\n\n\tisEmpty: function() {\n\t\treturn false;\n\t},\n\n\ttoPath: function(insert) {\n\t\tvar path = new Path[Base.capitalize(this._type)]({\n\t\t\tcenter: new Point(),\n\t\t\tsize: this._size,\n\t\t\tradius: this._radius,\n\t\t\tinsert: false\n\t\t});\n\t\tpath.copyAttributes(this);\n\t\tif (paper.settings.applyMatrix)\n\t\t\tpath.setApplyMatrix(true);\n\t\tif (insert === undefined || insert)\n\t\t\tpath.insertAbove(this);\n\t\treturn path;\n\t},\n\n\ttoShape: '#clone',\n\n\t_asPathItem: function() {\n\t\treturn this.toPath(false);\n\t},\n\n\t_draw: function(ctx, param, viewMatrix, strokeMatrix) {\n\t\tvar style = this._style,\n\t\t\thasFill = style.hasFill(),\n\t\t\thasStroke = style.hasStroke(),\n\t\t\tdontPaint = param.dontFinish || param.clip,\n\t\t\tuntransformed = !strokeMatrix;\n\t\tif (hasFill || hasStroke || dontPaint) {\n\t\t\tvar type = this._type,\n\t\t\t\tradius = this._radius,\n\t\t\t\tisCircle = type === 'circle';\n\t\t\tif (!param.dontStart)\n\t\t\t\tctx.beginPath();\n\t\t\tif (untransformed && isCircle) {\n\t\t\t\tctx.arc(0, 0, radius, 0, Math.PI * 2, true);\n\t\t\t} else {\n\t\t\t\tvar rx = isCircle ? radius : radius.width,\n\t\t\t\t\try = isCircle ? radius : radius.height,\n\t\t\t\t\tsize = this._size,\n\t\t\t\t\twidth = size.width,\n\t\t\t\t\theight = size.height;\n\t\t\t\tif (untransformed && type === 'rectangle' && rx === 0 && ry === 0) {\n\t\t\t\t\tctx.rect(-width / 2, -height / 2, width, height);\n\t\t\t\t} else {\n\t\t\t\t\tvar x = width / 2,\n\t\t\t\t\t\ty = height / 2,\n\t\t\t\t\t\tkappa = 1 - 0.5522847498307936,\n\t\t\t\t\t\tcx = rx * kappa,\n\t\t\t\t\t\tcy = ry * kappa,\n\t\t\t\t\t\tc = [\n\t\t\t\t\t\t\t-x, -y + ry,\n\t\t\t\t\t\t\t-x, -y + cy,\n\t\t\t\t\t\t\t-x + cx, -y,\n\t\t\t\t\t\t\t-x + rx, -y,\n\t\t\t\t\t\t\tx - rx, -y,\n\t\t\t\t\t\t\tx - cx, -y,\n\t\t\t\t\t\t\tx, -y + cy,\n\t\t\t\t\t\t\tx, -y + ry,\n\t\t\t\t\t\t\tx, y - ry,\n\t\t\t\t\t\t\tx, y - cy,\n\t\t\t\t\t\t\tx - cx, y,\n\t\t\t\t\t\t\tx - rx, y,\n\t\t\t\t\t\t\t-x + rx, y,\n\t\t\t\t\t\t\t-x + cx, y,\n\t\t\t\t\t\t\t-x, y - cy,\n\t\t\t\t\t\t\t-x, y - ry\n\t\t\t\t\t\t];\n\t\t\t\t\tif (strokeMatrix)\n\t\t\t\t\t\tstrokeMatrix.transform(c, c, 32);\n\t\t\t\t\tctx.moveTo(c[0], c[1]);\n\t\t\t\t\tctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]);\n\t\t\t\t\tif (x !== rx)\n\t\t\t\t\t\tctx.lineTo(c[8], c[9]);\n\t\t\t\t\tctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]);\n\t\t\t\t\tif (y !== ry)\n\t\t\t\t\t\tctx.lineTo(c[16], c[17]);\n\t\t\t\t\tctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]);\n\t\t\t\t\tif (x !== rx)\n\t\t\t\t\t\tctx.lineTo(c[24], c[25]);\n\t\t\t\t\tctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.closePath();\n\t\t}\n\t\tif (!dontPaint && (hasFill || hasStroke)) {\n\t\t\tthis._setStyles(ctx, param, viewMatrix, strokeMatrix);\n\t\t\tif (hasFill) {\n\t\t\t\tctx.fill(style.getFillRule());\n\t\t\t\tctx.shadowColor = 'rgba(0,0,0,0)';\n\t\t\t}\n\t\t\tif (hasStroke)\n\t\t\t\tctx.stroke();\n\t\t}\n\t},\n\n\t_canComposite: function() {\n\t\treturn !(this.hasFill() && this.hasStroke());\n\t},\n\n\t_getBounds: function(matrix, options) {\n\t\tvar rect = new Rectangle(this._size).setCenter(0, 0),\n\t\t\tstyle = this._style,\n\t\t\tstrokeWidth = options.stroke && style.hasStroke()\n\t\t\t\t\t&& style.getStrokeWidth();\n\t\tif (matrix)\n\t\t\trect = matrix._transformBounds(rect);\n\t\treturn strokeWidth\n\t\t\t\t? rect.expand(Path._getStrokePadding(strokeWidth,\n\t\t\t\t\tthis._getStrokeMatrix(matrix, options)))\n\t\t\t\t: rect;\n\t}\n},\nnew function() {\n\tfunction getCornerCenter(that, point, expand) {\n\t\tvar radius = that._radius;\n\t\tif (!radius.isZero()) {\n\t\t\tvar halfSize = that._size.divide(2);\n\t\t\tfor (var q = 1; q <= 4; q++) {\n\t\t\t\tvar dir = new Point(q > 1 && q < 4 ? -1 : 1, q > 2 ? -1 : 1),\n\t\t\t\t\tcorner = dir.multiply(halfSize),\n\t\t\t\t\tcenter = corner.subtract(dir.multiply(radius)),\n\t\t\t\t\trect = new Rectangle(\n\t\t\t\t\t\t\texpand ? corner.add(dir.multiply(expand)) : corner,\n\t\t\t\t\t\t\tcenter);\n\t\t\t\tif (rect.contains(point))\n\t\t\t\t\treturn { point: center, quadrant: q };\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction isOnEllipseStroke(point, radius, padding, quadrant) {\n\t\tvar vector = point.divide(radius);\n\t\treturn (!quadrant || vector.isInQuadrant(quadrant)) &&\n\t\t\t\tvector.subtract(vector.normalize()).multiply(radius)\n\t\t\t\t\t.divide(padding).length <= 1;\n\t}\n\n\treturn {\n\t\t_contains: function _contains(point) {\n\t\t\tif (this._type === 'rectangle') {\n\t\t\t\tvar center = getCornerCenter(this, point);\n\t\t\t\treturn center\n\t\t\t\t\t\t? point.subtract(center.point).divide(this._radius)\n\t\t\t\t\t\t\t.getLength() <= 1\n\t\t\t\t\t\t: _contains.base.call(this, point);\n\t\t\t} else {\n\t\t\t\treturn point.divide(this.size).getLength() <= 0.5;\n\t\t\t}\n\t\t},\n\n\t\t_hitTestSelf: function _hitTestSelf(point, options, viewMatrix,\n\t\t\t\tstrokeMatrix) {\n\t\t\tvar hit = false,\n\t\t\t\tstyle = this._style,\n\t\t\t\thitStroke = options.stroke && style.hasStroke(),\n\t\t\t\thitFill = options.fill && style.hasFill();\n\t\t\tif (hitStroke || hitFill) {\n\t\t\t\tvar type = this._type,\n\t\t\t\t\tradius = this._radius,\n\t\t\t\t\tstrokeRadius = hitStroke ? style.getStrokeWidth() / 2 : 0,\n\t\t\t\t\tstrokePadding = options._tolerancePadding.add(\n\t\t\t\t\t\tPath._getStrokePadding(strokeRadius,\n\t\t\t\t\t\t\t!style.getStrokeScaling() && strokeMatrix));\n\t\t\t\tif (type === 'rectangle') {\n\t\t\t\t\tvar padding = strokePadding.multiply(2),\n\t\t\t\t\t\tcenter = getCornerCenter(this, point, padding);\n\t\t\t\t\tif (center) {\n\t\t\t\t\t\thit = isOnEllipseStroke(point.subtract(center.point),\n\t\t\t\t\t\t\t\tradius, strokePadding, center.quadrant);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar rect = new Rectangle(this._size).setCenter(0, 0),\n\t\t\t\t\t\t\touter = rect.expand(padding),\n\t\t\t\t\t\t\tinner = rect.expand(padding.negate());\n\t\t\t\t\t\thit = outer._containsPoint(point)\n\t\t\t\t\t\t\t\t&& !inner._containsPoint(point);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\thit = isOnEllipseStroke(point, radius, strokePadding);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hit ? new HitResult(hitStroke ? 'stroke' : 'fill', this)\n\t\t\t\t\t: _hitTestSelf.base.apply(this, arguments);\n\t\t}\n\t};\n}, {\n\nstatics: new function() {\n\tfunction createShape(type, point, size, radius, args) {\n\t\tvar item = Base.create(Shape.prototype);\n\t\titem._type = type;\n\t\titem._size = size;\n\t\titem._radius = radius;\n\t\titem._initialize(Base.getNamed(args), point);\n\t\treturn item;\n\t}\n\n\treturn {\n\t\tCircle: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tcenter = Point.readNamed(args, 'center'),\n\t\t\t\tradius = Base.readNamed(args, 'radius');\n\t\t\treturn createShape('circle', center, new Size(radius * 2), radius,\n\t\t\t\t\targs);\n\t\t},\n\n\t\tRectangle: function() {\n\t\t\tvar args = arguments,\n\t\t\t\trect = Rectangle.readNamed(args, 'rectangle'),\n\t\t\t\tradius = Size.min(Size.readNamed(args, 'radius'),\n\t\t\t\t\t\trect.getSize(true).divide(2));\n\t\t\treturn createShape('rectangle', rect.getCenter(true),\n\t\t\t\t\trect.getSize(true), radius, args);\n\t\t},\n\n\t\tEllipse: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tellipse = Shape._readEllipse(args),\n\t\t\t\tradius = ellipse.radius;\n\t\t\treturn createShape('ellipse', ellipse.center, radius.multiply(2),\n\t\t\t\t\tradius, args);\n\t\t},\n\n\t\t_readEllipse: function(args) {\n\t\t\tvar center,\n\t\t\t\tradius;\n\t\t\tif (Base.hasNamed(args, 'radius')) {\n\t\t\t\tcenter = Point.readNamed(args, 'center');\n\t\t\t\tradius = Size.readNamed(args, 'radius');\n\t\t\t} else {\n\t\t\t\tvar rect = Rectangle.readNamed(args, 'rectangle');\n\t\t\t\tcenter = rect.getCenter(true);\n\t\t\t\tradius = rect.getSize(true).divide(2);\n\t\t\t}\n\t\t\treturn { center: center, radius: radius };\n\t\t}\n\t};\n}});\n\nvar Raster = Item.extend({\n\t_class: 'Raster',\n\t_applyMatrix: false,\n\t_canApplyMatrix: false,\n\t_boundsOptions: { stroke: false, handle: false },\n\t_serializeFields: {\n\t\tcrossOrigin: null,\n\t\tsource: null\n\t},\n\t_prioritize: ['crossOrigin'],\n\t_smoothing: false,\n\tbeans: true,\n\n\tinitialize: function Raster(source, position) {\n\t\tif (!this._initialize(source,\n\t\t\t\tposition !== undefined && Point.read(arguments))) {\n\t\t\tvar image,\n\t\t\t\ttype = typeof source,\n\t\t\t\tobject = type === 'string'\n\t\t\t\t\t? document.getElementById(source)\n\t\t\t\t\t: type === 'object'\n\t\t\t\t\t\t? source\n\t\t\t\t\t\t: null;\n\t\t\tif (object && object !== Item.NO_INSERT) {\n\t\t\t\tif (object.getContext || object.naturalHeight != null) {\n\t\t\t\t\timage = object;\n\t\t\t\t} else if (object) {\n\t\t\t\t\tvar size = Size.read(arguments);\n\t\t\t\t\tif (!size.isZero()) {\n\t\t\t\t\t\timage = CanvasProvider.getCanvas(size);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (image) {\n\t\t\t\tthis.setImage(image);\n\t\t\t} else {\n\t\t\t\tthis.setSource(source);\n\t\t\t}\n\t\t}\n\t\tif (!this._size) {\n\t\t\tthis._size = new Size();\n\t\t\tthis._loaded = false;\n\t\t}\n\t},\n\n\t_equals: function(item) {\n\t\treturn this.getSource() === item.getSource();\n\t},\n\n\tcopyContent: function(source) {\n\t\tvar image = source._image,\n\t\t\tcanvas = source._canvas;\n\t\tif (image) {\n\t\t\tthis._setImage(image);\n\t\t} else if (canvas) {\n\t\t\tvar copyCanvas = CanvasProvider.getCanvas(source._size);\n\t\t\tcopyCanvas.getContext('2d').drawImage(canvas, 0, 0);\n\t\t\tthis._setImage(copyCanvas);\n\t\t}\n\t\tthis._crossOrigin = source._crossOrigin;\n\t},\n\n\tgetSize: function() {\n\t\tvar size = this._size;\n\t\treturn new LinkedSize(size ? size.width : 0, size ? size.height : 0,\n\t\t\t\tthis, 'setSize');\n\t},\n\n\tsetSize: function() {\n\t\tvar size = Size.read(arguments);\n\t\tif (!size.equals(this._size)) {\n\t\t\tif (size.width > 0 && size.height > 0) {\n\t\t\t\tvar element = this.getElement();\n\t\t\t\tthis._setImage(CanvasProvider.getCanvas(size));\n\t\t\t\tif (element)\n\t\t\t\t\tthis.getContext(true).drawImage(element, 0, 0,\n\t\t\t\t\t\t\tsize.width, size.height);\n\t\t\t} else {\n\t\t\t\tif (this._canvas)\n\t\t\t\t\tCanvasProvider.release(this._canvas);\n\t\t\t\tthis._size = size.clone();\n\t\t\t}\n\t\t}\n\t},\n\n\tgetWidth: function() {\n\t\treturn this._size ? this._size.width : 0;\n\t},\n\n\tsetWidth: function(width) {\n\t\tthis.setSize(width, this.getHeight());\n\t},\n\n\tgetHeight: function() {\n\t\treturn this._size ? this._size.height : 0;\n\t},\n\n\tsetHeight: function(height) {\n\t\tthis.setSize(this.getWidth(), height);\n\t},\n\n\tgetLoaded: function() {\n\t\treturn this._loaded;\n\t},\n\n\tisEmpty: function() {\n\t\tvar size = this._size;\n\t\treturn !size || size.width === 0 && size.height === 0;\n\t},\n\n\tgetResolution: function() {\n\t\tvar matrix = this._matrix,\n\t\t\torig = new Point(0, 0).transform(matrix),\n\t\t\tu = new Point(1, 0).transform(matrix).subtract(orig),\n\t\t\tv = new Point(0, 1).transform(matrix).subtract(orig);\n\t\treturn new Size(\n\t\t\t72 / u.getLength(),\n\t\t\t72 / v.getLength()\n\t\t);\n\t},\n\n\tgetPpi: '#getResolution',\n\n\tgetImage: function() {\n\t\treturn this._image;\n\t},\n\n\tsetImage: function(image) {\n\t\tvar that = this;\n\n\t\tfunction emit(event) {\n\t\t\tvar view = that.getView(),\n\t\t\t\ttype = event && event.type || 'load';\n\t\t\tif (view && that.responds(type)) {\n\t\t\t\tpaper = view._scope;\n\t\t\t\tthat.emit(type, new Event(event));\n\t\t\t}\n\t\t}\n\n\t\tthis._setImage(image);\n\t\tif (this._loaded) {\n\t\t\tsetTimeout(emit, 0);\n\t\t} else if (image) {\n\t\t\tDomEvent.add(image, {\n\t\t\t\tload: function(event) {\n\t\t\t\t\tthat._setImage(image);\n\t\t\t\t\temit(event);\n\t\t\t\t},\n\t\t\t\terror: emit\n\t\t\t});\n\t\t}\n\t},\n\n\t_setImage: function(image) {\n\t\tif (this._canvas)\n\t\t\tCanvasProvider.release(this._canvas);\n\t\tif (image && image.getContext) {\n\t\t\tthis._image = null;\n\t\t\tthis._canvas = image;\n\t\t\tthis._loaded = true;\n\t\t} else {\n\t\t\tthis._image = image;\n\t\t\tthis._canvas = null;\n\t\t\tthis._loaded = !!(image && image.src && image.complete);\n\t\t}\n\t\tthis._size = new Size(\n\t\t\t\timage ? image.naturalWidth || image.width : 0,\n\t\t\t\timage ? image.naturalHeight || image.height : 0);\n\t\tthis._context = null;\n\t\tthis._changed(1033);\n\t},\n\n\tgetCanvas: function() {\n\t\tif (!this._canvas) {\n\t\t\tvar ctx = CanvasProvider.getContext(this._size);\n\t\t\ttry {\n\t\t\t\tif (this._image)\n\t\t\t\t\tctx.drawImage(this._image, 0, 0);\n\t\t\t\tthis._canvas = ctx.canvas;\n\t\t\t} catch (e) {\n\t\t\t\tCanvasProvider.release(ctx);\n\t\t\t}\n\t\t}\n\t\treturn this._canvas;\n\t},\n\n\tsetCanvas: '#setImage',\n\n\tgetContext: function(_change) {\n\t\tif (!this._context)\n\t\t\tthis._context = this.getCanvas().getContext('2d');\n\t\tif (_change) {\n\t\t\tthis._image = null;\n\t\t\tthis._changed(1025);\n\t\t}\n\t\treturn this._context;\n\t},\n\n\tsetContext: function(context) {\n\t\tthis._context = context;\n\t},\n\n\tgetSource: function() {\n\t\tvar image = this._image;\n\t\treturn image && image.src || this.toDataURL();\n\t},\n\n\tsetSource: function(src) {\n\t\tvar image = new self.Image(),\n\t\t\tcrossOrigin = this._crossOrigin;\n\t\tif (crossOrigin)\n\t\t\timage.crossOrigin = crossOrigin;\n\t\tif (src)\n\t\t\timage.src = src;\n\t\tthis.setImage(image);\n\t},\n\n\tgetCrossOrigin: function() {\n\t\tvar image = this._image;\n\t\treturn image && image.crossOrigin || this._crossOrigin || '';\n\t},\n\n\tsetCrossOrigin: function(crossOrigin) {\n\t\tthis._crossOrigin = crossOrigin;\n\t\tvar image = this._image;\n\t\tif (image)\n\t\t\timage.crossOrigin = crossOrigin;\n\t},\n\n\tgetSmoothing: function() {\n\t\treturn this._smoothing;\n\t},\n\n\tsetSmoothing: function(smoothing) {\n\t\tthis._smoothing = smoothing;\n\t\tthis._changed(257);\n\t},\n\n\tgetElement: function() {\n\t\treturn this._canvas || this._loaded && this._image;\n\t}\n}, {\n\tbeans: false,\n\n\tgetSubCanvas: function() {\n\t\tvar rect = Rectangle.read(arguments),\n\t\t\tctx = CanvasProvider.getContext(rect.getSize());\n\t\tvar clippedStartX = Math.max(0, rect.x);\n\t\tvar clippedStartY = Math.max(0, rect.y);\n\t\tvar clippedEndX = Math.min(this.getCanvas().width, rect.x + rect.width);\n\t\tvar clippedEndY = Math.min(this.getCanvas().height, rect.y + rect.height);\n\t\tctx.drawImage(this.getCanvas(),\n\t\t\tclippedStartX, clippedStartY,\n\t\t\tclippedEndX - clippedStartX, clippedEndY - clippedStartY,\n\t\t\tclippedStartX - rect.x, clippedStartY - rect.y,\n\t\t\tclippedEndX - clippedStartX, clippedEndY - clippedStartY\n\t\t);\n\t\treturn ctx.canvas;\n\t},\n\n\tgetSubRaster: function() {\n\t\tvar rect = Rectangle.read(arguments),\n\t\t\traster = new Raster(Item.NO_INSERT);\n\t\traster._setImage(this.getSubCanvas(rect));\n\t\traster.translate(rect.getCenter().subtract(this.getSize().divide(2)));\n\t\traster._matrix.prepend(this._matrix);\n\t\traster.insertAbove(this);\n\t\treturn raster;\n\t},\n\n\ttoDataURL: function() {\n\t\tvar image = this._image,\n\t\t\tsrc = image && image.src;\n\t\tif (/^data:/.test(src))\n\t\t\treturn src;\n\t\tvar canvas = this.getCanvas();\n\t\treturn canvas ? canvas.toDataURL.apply(canvas, arguments) : null;\n\t},\n\n\tdrawImage: function(image ) {\n\t\tvar point = Point.read(arguments, 1);\n\t\tthis.getContext(true).drawImage(image, point.x, point.y);\n\t},\n\n\tgetAverageColor: function(object) {\n\t\tvar bounds, path;\n\t\tif (!object) {\n\t\t\tbounds = this.getBounds();\n\t\t} else if (object instanceof PathItem) {\n\t\t\tpath = object;\n\t\t\tbounds = object.getBounds();\n\t\t} else if (typeof object === 'object') {\n\t\t\tif ('width' in object) {\n\t\t\t\tbounds = new Rectangle(object);\n\t\t\t} else if ('x' in object) {\n\t\t\t\tbounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);\n\t\t\t}\n\t\t}\n\t\tif (!bounds)\n\t\t\treturn null;\n\t\tvar sampleSize = 32,\n\t\t\twidth = Math.min(bounds.width, sampleSize),\n\t\t\theight = Math.min(bounds.height, sampleSize);\n\t\tvar ctx = Raster._sampleContext;\n\t\tif (!ctx) {\n\t\t\tctx = Raster._sampleContext = CanvasProvider.getContext(\n\t\t\t\t\tnew Size(sampleSize));\n\t\t} else {\n\t\t\tctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);\n\t\t}\n\t\tctx.save();\n\t\tvar matrix = new Matrix()\n\t\t\t\t.scale(width / bounds.width, height / bounds.height)\n\t\t\t\t.translate(-bounds.x, -bounds.y);\n\t\tmatrix.applyToContext(ctx);\n\t\tif (path)\n\t\t\tpath.draw(ctx, new Base({ clip: true, matrices: [matrix] }));\n\t\tthis._matrix.applyToContext(ctx);\n\t\tvar element = this.getElement(),\n\t\t\tsize = this._size;\n\t\tif (element)\n\t\t\tctx.drawImage(element, -size.width / 2, -size.height / 2);\n\t\tctx.restore();\n\t\tvar pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),\n\t\t\t\tMath.ceil(height)).data,\n\t\t\tchannels = [0, 0, 0],\n\t\t\ttotal = 0;\n\t\tfor (var i = 0, l = pixels.length; i < l; i += 4) {\n\t\t\tvar alpha = pixels[i + 3];\n\t\t\ttotal += alpha;\n\t\t\talpha /= 255;\n\t\t\tchannels[0] += pixels[i] * alpha;\n\t\t\tchannels[1] += pixels[i + 1] * alpha;\n\t\t\tchannels[2] += pixels[i + 2] * alpha;\n\t\t}\n\t\tfor (var i = 0; i < 3; i++)\n\t\t\tchannels[i] /= total;\n\t\treturn total ? Color.read(channels) : null;\n\t},\n\n\tgetPixel: function() {\n\t\tvar point = Point.read(arguments);\n\t\tvar data = this.getContext().getImageData(point.x, point.y, 1, 1).data;\n\t\treturn new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],\n\t\t\t\tdata[3] / 255);\n\t},\n\n\tsetPixel: function() {\n\t\tvar args = arguments,\n\t\t\tpoint = Point.read(args),\n\t\t\tcolor = Color.read(args),\n\t\t\tcomponents = color._convert('rgb'),\n\t\t\talpha = color._alpha,\n\t\t\tctx = this.getContext(true),\n\t\t\timageData = ctx.createImageData(1, 1),\n\t\t\tdata = imageData.data;\n\t\tdata[0] = components[0] * 255;\n\t\tdata[1] = components[1] * 255;\n\t\tdata[2] = components[2] * 255;\n\t\tdata[3] = alpha != null ? alpha * 255 : 255;\n\t\tctx.putImageData(imageData, point.x, point.y);\n\t},\n\n\tclear: function() {\n\t\tvar size = this._size;\n\t\tthis.getContext(true).clearRect(0, 0, size.width + 1, size.height + 1);\n\t},\n\n\tcreateImageData: function() {\n\t\tvar size = Size.read(arguments);\n\t\treturn this.getContext().createImageData(size.width, size.height);\n\t},\n\n\tgetImageData: function() {\n\t\tvar rect = Rectangle.read(arguments);\n\t\tif (rect.isEmpty())\n\t\t\trect = new Rectangle(this._size);\n\t\treturn this.getContext().getImageData(rect.x, rect.y,\n\t\t\t\trect.width, rect.height);\n\t},\n\n\tsetImageData: function(data ) {\n\t\tvar point = Point.read(arguments, 1);\n\t\tthis.getContext(true).putImageData(data, point.x, point.y);\n\t},\n\n\t_getBounds: function(matrix, options) {\n\t\tvar rect = new Rectangle(this._size).setCenter(0, 0);\n\t\treturn matrix ? matrix._transformBounds(rect) : rect;\n\t},\n\n\t_hitTestSelf: function(point) {\n\t\tif (this._contains(point)) {\n\t\t\tvar that = this;\n\t\t\treturn new HitResult('pixel', that, {\n\t\t\t\toffset: point.add(that._size.divide(2)).round(),\n\t\t\t\tcolor: {\n\t\t\t\t\tget: function() {\n\t\t\t\t\t\treturn that.getPixel(this.offset);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t},\n\n\t_draw: function(ctx, param, viewMatrix) {\n\t\tvar element = this.getElement();\n\t\tif (element && element.width > 0 && element.height > 0) {\n\t\t\tctx.globalAlpha = Numerical.clamp(this._opacity, 0, 1);\n\n\t\t\tthis._setStyles(ctx, param, viewMatrix);\n\n\t\t\tDomElement.setPrefixed(\n\t\t\t\tctx, 'imageSmoothingEnabled', this._smoothing\n\t\t\t);\n\n\t\t\tctx.drawImage(element,\n\t\t\t\t\t-this._size.width / 2, -this._size.height / 2);\n\t\t}\n\t},\n\n\t_canComposite: function() {\n\t\treturn true;\n\t}\n});\n\nvar SymbolItem = Item.extend({\n\t_class: 'SymbolItem',\n\t_applyMatrix: false,\n\t_canApplyMatrix: false,\n\t_boundsOptions: { stroke: true },\n\t_serializeFields: {\n\t\tsymbol: null\n\t},\n\n\tinitialize: function SymbolItem(arg0, arg1) {\n\t\tif (!this._initialize(arg0,\n\t\t\t\targ1 !== undefined && Point.read(arguments, 1)))\n\t\t\tthis.setDefinition(arg0 instanceof SymbolDefinition ?\n\t\t\t\t\targ0 : new SymbolDefinition(arg0));\n\t},\n\n\t_equals: function(item) {\n\t\treturn this._definition === item._definition;\n\t},\n\n\tcopyContent: function(source) {\n\t\tthis.setDefinition(source._definition);\n\t},\n\n\tgetDefinition: function() {\n\t\treturn this._definition;\n\t},\n\n\tsetDefinition: function(definition) {\n\t\tthis._definition = definition;\n\t\tthis._changed(9);\n\t},\n\n\tgetSymbol: '#getDefinition',\n\tsetSymbol: '#setDefinition',\n\n\tisEmpty: function() {\n\t\treturn this._definition._item.isEmpty();\n\t},\n\n\t_getBounds: function(matrix, options) {\n\t\tvar item = this._definition._item;\n\t\treturn item._getCachedBounds(item._matrix.prepended(matrix), options);\n\t},\n\n\t_hitTestSelf: function(point, options, viewMatrix) {\n\t\tvar opts = options.extend({ all: false });\n\t\tvar res = this._definition._item._hitTest(point, opts, viewMatrix);\n\t\tif (res)\n\t\t\tres.item = this;\n\t\treturn res;\n\t},\n\n\t_draw: function(ctx, param) {\n\t\tthis._definition._item.draw(ctx, param);\n\t}\n\n});\n\nvar SymbolDefinition = Base.extend({\n\t_class: 'SymbolDefinition',\n\n\tinitialize: function SymbolDefinition(item, dontCenter) {\n\t\tthis._id = UID.get();\n\t\tthis.project = paper.project;\n\t\tif (item)\n\t\t\tthis.setItem(item, dontCenter);\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\treturn dictionary.add(this, function() {\n\t\t\treturn Base.serialize([this._class, this._item],\n\t\t\t\t\toptions, false, dictionary);\n\t\t});\n\t},\n\n\t_changed: function(flags) {\n\t\tif (flags & 8)\n\t\t\tItem._clearBoundsCache(this);\n\t\tif (flags & 1)\n\t\t\tthis.project._changed(flags);\n\t},\n\n\tgetItem: function() {\n\t\treturn this._item;\n\t},\n\n\tsetItem: function(item, _dontCenter) {\n\t\tif (item._symbol)\n\t\t\titem = item.clone();\n\t\tif (this._item)\n\t\t\tthis._item._symbol = null;\n\t\tthis._item = item;\n\t\titem.remove();\n\t\titem.setSelected(false);\n\t\tif (!_dontCenter)\n\t\t\titem.setPosition(new Point());\n\t\titem._symbol = this;\n\t\tthis._changed(9);\n\t},\n\n\tgetDefinition: '#getItem',\n\tsetDefinition: '#setItem',\n\n\tplace: function(position) {\n\t\treturn new SymbolItem(this, position);\n\t},\n\n\tclone: function() {\n\t\treturn new SymbolDefinition(this._item.clone(false));\n\t},\n\n\tequals: function(symbol) {\n\t\treturn symbol === this\n\t\t\t\t|| symbol && this._item.equals(symbol._item)\n\t\t\t\t|| false;\n\t}\n});\n\nvar HitResult = Base.extend({\n\t_class: 'HitResult',\n\n\tinitialize: function HitResult(type, item, values) {\n\t\tthis.type = type;\n\t\tthis.item = item;\n\t\tif (values)\n\t\t\tthis.inject(values);\n\t},\n\n\tstatics: {\n\t\tgetOptions: function(args) {\n\t\t\tvar options = args && Base.read(args);\n\t\t\treturn new Base({\n\t\t\t\ttype: null,\n\t\t\t\ttolerance: paper.settings.hitTolerance,\n\t\t\t\tfill: !options,\n\t\t\t\tstroke: !options,\n\t\t\t\tsegments: !options,\n\t\t\t\thandles: false,\n\t\t\t\tends: false,\n\t\t\t\tposition: false,\n\t\t\t\tcenter: false,\n\t\t\t\tbounds: false,\n\t\t\t\tguides: false,\n\t\t\t\tselected: false\n\t\t\t}, options);\n\t\t}\n\t}\n});\n\nvar Segment = Base.extend({\n\t_class: 'Segment',\n\tbeans: true,\n\t_selection: 0,\n\n\tinitialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {\n\t\tvar count = arguments.length,\n\t\t\tpoint, handleIn, handleOut, selection;\n\t\tif (count > 0) {\n\t\t\tif (arg0 == null || typeof arg0 === 'object') {\n\t\t\t\tif (count === 1 && arg0 && 'point' in arg0) {\n\t\t\t\t\tpoint = arg0.point;\n\t\t\t\t\thandleIn = arg0.handleIn;\n\t\t\t\t\thandleOut = arg0.handleOut;\n\t\t\t\t\tselection = arg0.selection;\n\t\t\t\t} else {\n\t\t\t\t\tpoint = arg0;\n\t\t\t\t\thandleIn = arg1;\n\t\t\t\t\thandleOut = arg2;\n\t\t\t\t\tselection = arg3;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpoint = [ arg0, arg1 ];\n\t\t\t\thandleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;\n\t\t\t\thandleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;\n\t\t\t}\n\t\t}\n\t\tnew SegmentPoint(point, this, '_point');\n\t\tnew SegmentPoint(handleIn, this, '_handleIn');\n\t\tnew SegmentPoint(handleOut, this, '_handleOut');\n\t\tif (selection)\n\t\t\tthis.setSelection(selection);\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\tvar point = this._point,\n\t\t\tselection = this._selection,\n\t\t\tobj = selection || this.hasHandles()\n\t\t\t\t\t? [point, this._handleIn, this._handleOut]\n\t\t\t\t\t: point;\n\t\tif (selection)\n\t\t\tobj.push(selection);\n\t\treturn Base.serialize(obj, options, true, dictionary);\n\t},\n\n\t_changed: function(point) {\n\t\tvar path = this._path;\n\t\tif (!path)\n\t\t\treturn;\n\t\tvar curves = path._curves,\n\t\t\tindex = this._index,\n\t\t\tcurve;\n\t\tif (curves) {\n\t\t\tif ((!point || point === this._point || point === this._handleIn)\n\t\t\t\t\t&& (curve = index > 0 ? curves[index - 1] : path._closed\n\t\t\t\t\t\t? curves[curves.length - 1] : null))\n\t\t\t\tcurve._changed();\n\t\t\tif ((!point || point === this._point || point === this._handleOut)\n\t\t\t\t\t&& (curve = curves[index]))\n\t\t\t\tcurve._changed();\n\t\t}\n\t\tpath._changed(41);\n\t},\n\n\tgetPoint: function() {\n\t\treturn this._point;\n\t},\n\n\tsetPoint: function() {\n\t\tthis._point.set(Point.read(arguments));\n\t},\n\n\tgetHandleIn: function() {\n\t\treturn this._handleIn;\n\t},\n\n\tsetHandleIn: function() {\n\t\tthis._handleIn.set(Point.read(arguments));\n\t},\n\n\tgetHandleOut: function() {\n\t\treturn this._handleOut;\n\t},\n\n\tsetHandleOut: function() {\n\t\tthis._handleOut.set(Point.read(arguments));\n\t},\n\n\thasHandles: function() {\n\t\treturn !this._handleIn.isZero() || !this._handleOut.isZero();\n\t},\n\n\tisSmooth: function() {\n\t\tvar handleIn = this._handleIn,\n\t\t\thandleOut = this._handleOut;\n\t\treturn !handleIn.isZero() && !handleOut.isZero()\n\t\t\t\t&& handleIn.isCollinear(handleOut);\n\t},\n\n\tclearHandles: function() {\n\t\tthis._handleIn._set(0, 0);\n\t\tthis._handleOut._set(0, 0);\n\t},\n\n\tgetSelection: function() {\n\t\treturn this._selection;\n\t},\n\n\tsetSelection: function(selection) {\n\t\tvar oldSelection = this._selection,\n\t\t\tpath = this._path;\n\t\tthis._selection = selection = selection || 0;\n\t\tif (path && selection !== oldSelection) {\n\t\t\tpath._updateSelection(this, oldSelection, selection);\n\t\t\tpath._changed(257);\n\t\t}\n\t},\n\n\t_changeSelection: function(flag, selected) {\n\t\tvar selection = this._selection;\n\t\tthis.setSelection(selected ? selection | flag : selection & ~flag);\n\t},\n\n\tisSelected: function() {\n\t\treturn !!(this._selection & 7);\n\t},\n\n\tsetSelected: function(selected) {\n\t\tthis._changeSelection(7, selected);\n\t},\n\n\tgetIndex: function() {\n\t\treturn this._index !== undefined ? this._index : null;\n\t},\n\n\tgetPath: function() {\n\t\treturn this._path || null;\n\t},\n\n\tgetCurve: function() {\n\t\tvar path = this._path,\n\t\t\tindex = this._index;\n\t\tif (path) {\n\t\t\tif (index > 0 && !path._closed\n\t\t\t\t\t&& index === path._segments.length - 1)\n\t\t\t\tindex--;\n\t\t\treturn path.getCurves()[index] || null;\n\t\t}\n\t\treturn null;\n\t},\n\n\tgetLocation: function() {\n\t\tvar curve = this.getCurve();\n\t\treturn curve\n\t\t\t\t? new CurveLocation(curve, this === curve._segment1 ? 0 : 1)\n\t\t\t\t: null;\n\t},\n\n\tgetNext: function() {\n\t\tvar segments = this._path && this._path._segments;\n\t\treturn segments && (segments[this._index + 1]\n\t\t\t\t|| this._path._closed && segments[0]) || null;\n\t},\n\n\tsmooth: function(options, _first, _last) {\n\t\tvar opts = options || {},\n\t\t\ttype = opts.type,\n\t\t\tfactor = opts.factor,\n\t\t\tprev = this.getPrevious(),\n\t\t\tnext = this.getNext(),\n\t\t\tp0 = (prev || this)._point,\n\t\t\tp1 = this._point,\n\t\t\tp2 = (next || this)._point,\n\t\t\td1 = p0.getDistance(p1),\n\t\t\td2 = p1.getDistance(p2);\n\t\tif (!type || type === 'catmull-rom') {\n\t\t\tvar a = factor === undefined ? 0.5 : factor,\n\t\t\t\td1_a = Math.pow(d1, a),\n\t\t\t\td1_2a = d1_a * d1_a,\n\t\t\t\td2_a = Math.pow(d2, a),\n\t\t\t\td2_2a = d2_a * d2_a;\n\t\t\tif (!_first && prev) {\n\t\t\t\tvar A = 2 * d2_2a + 3 * d2_a * d1_a + d1_2a,\n\t\t\t\t\tN = 3 * d2_a * (d2_a + d1_a);\n\t\t\t\tthis.setHandleIn(N !== 0\n\t\t\t\t\t? new Point(\n\t\t\t\t\t\t(d2_2a * p0._x + A * p1._x - d1_2a * p2._x) / N - p1._x,\n\t\t\t\t\t\t(d2_2a * p0._y + A * p1._y - d1_2a * p2._y) / N - p1._y)\n\t\t\t\t\t: new Point());\n\t\t\t}\n\t\t\tif (!_last && next) {\n\t\t\t\tvar A = 2 * d1_2a + 3 * d1_a * d2_a + d2_2a,\n\t\t\t\t\tN = 3 * d1_a * (d1_a + d2_a);\n\t\t\t\tthis.setHandleOut(N !== 0\n\t\t\t\t\t? new Point(\n\t\t\t\t\t\t(d1_2a * p2._x + A * p1._x - d2_2a * p0._x) / N - p1._x,\n\t\t\t\t\t\t(d1_2a * p2._y + A * p1._y - d2_2a * p0._y) / N - p1._y)\n\t\t\t\t\t: new Point());\n\t\t\t}\n\t\t} else if (type === 'geometric') {\n\t\t\tif (prev && next) {\n\t\t\t\tvar vector = p0.subtract(p2),\n\t\t\t\t\tt = factor === undefined ? 0.4 : factor,\n\t\t\t\t\tk = t * d1 / (d1 + d2);\n\t\t\t\tif (!_first)\n\t\t\t\t\tthis.setHandleIn(vector.multiply(k));\n\t\t\t\tif (!_last)\n\t\t\t\t\tthis.setHandleOut(vector.multiply(k - t));\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error('Smoothing method \\'' + type + '\\' not supported.');\n\t\t}\n\t},\n\n\tgetPrevious: function() {\n\t\tvar segments = this._path && this._path._segments;\n\t\treturn segments && (segments[this._index - 1]\n\t\t\t\t|| this._path._closed && segments[segments.length - 1]) || null;\n\t},\n\n\tisFirst: function() {\n\t\treturn !this._index;\n\t},\n\n\tisLast: function() {\n\t\tvar path = this._path;\n\t\treturn path && this._index === path._segments.length - 1 || false;\n\t},\n\n\treverse: function() {\n\t\tvar handleIn = this._handleIn,\n\t\t\thandleOut = this._handleOut,\n\t\t\ttmp = handleIn.clone();\n\t\thandleIn.set(handleOut);\n\t\thandleOut.set(tmp);\n\t},\n\n\treversed: function() {\n\t\treturn new Segment(this._point, this._handleOut, this._handleIn);\n\t},\n\n\tremove: function() {\n\t\treturn this._path ? !!this._path.removeSegment(this._index) : false;\n\t},\n\n\tclone: function() {\n\t\treturn new Segment(this._point, this._handleIn, this._handleOut);\n\t},\n\n\tequals: function(segment) {\n\t\treturn segment === this || segment && this._class === segment._class\n\t\t\t\t&& this._point.equals(segment._point)\n\t\t\t\t&& this._handleIn.equals(segment._handleIn)\n\t\t\t\t&& this._handleOut.equals(segment._handleOut)\n\t\t\t\t|| false;\n\t},\n\n\ttoString: function() {\n\t\tvar parts = [ 'point: ' + this._point ];\n\t\tif (!this._handleIn.isZero())\n\t\t\tparts.push('handleIn: ' + this._handleIn);\n\t\tif (!this._handleOut.isZero())\n\t\t\tparts.push('handleOut: ' + this._handleOut);\n\t\treturn '{ ' + parts.join(', ') + ' }';\n\t},\n\n\ttransform: function(matrix) {\n\t\tthis._transformCoordinates(matrix, new Array(6), true);\n\t\tthis._changed();\n\t},\n\n\tinterpolate: function(from, to, factor) {\n\t\tvar u = 1 - factor,\n\t\t\tv = factor,\n\t\t\tpoint1 = from._point,\n\t\t\tpoint2 = to._point,\n\t\t\thandleIn1 = from._handleIn,\n\t\t\thandleIn2 = to._handleIn,\n\t\t\thandleOut2 = to._handleOut,\n\t\t\thandleOut1 = from._handleOut;\n\t\tthis._point._set(\n\t\t\t\tu * point1._x + v * point2._x,\n\t\t\t\tu * point1._y + v * point2._y, true);\n\t\tthis._handleIn._set(\n\t\t\t\tu * handleIn1._x + v * handleIn2._x,\n\t\t\t\tu * handleIn1._y + v * handleIn2._y, true);\n\t\tthis._handleOut._set(\n\t\t\t\tu * handleOut1._x + v * handleOut2._x,\n\t\t\t\tu * handleOut1._y + v * handleOut2._y, true);\n\t\tthis._changed();\n\t},\n\n\t_transformCoordinates: function(matrix, coords, change) {\n\t\tvar point = this._point,\n\t\t\thandleIn = !change || !this._handleIn.isZero()\n\t\t\t\t\t? this._handleIn : null,\n\t\t\thandleOut = !change || !this._handleOut.isZero()\n\t\t\t\t\t? this._handleOut : null,\n\t\t\tx = point._x,\n\t\t\ty = point._y,\n\t\t\ti = 2;\n\t\tcoords[0] = x;\n\t\tcoords[1] = y;\n\t\tif (handleIn) {\n\t\t\tcoords[i++] = handleIn._x + x;\n\t\t\tcoords[i++] = handleIn._y + y;\n\t\t}\n\t\tif (handleOut) {\n\t\t\tcoords[i++] = handleOut._x + x;\n\t\t\tcoords[i++] = handleOut._y + y;\n\t\t}\n\t\tif (matrix) {\n\t\t\tmatrix._transformCoordinates(coords, coords, i / 2);\n\t\t\tx = coords[0];\n\t\t\ty = coords[1];\n\t\t\tif (change) {\n\t\t\t\tpoint._x = x;\n\t\t\t\tpoint._y = y;\n\t\t\t\ti = 2;\n\t\t\t\tif (handleIn) {\n\t\t\t\t\thandleIn._x = coords[i++] - x;\n\t\t\t\t\thandleIn._y = coords[i++] - y;\n\t\t\t\t}\n\t\t\t\tif (handleOut) {\n\t\t\t\t\thandleOut._x = coords[i++] - x;\n\t\t\t\t\thandleOut._y = coords[i++] - y;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!handleIn) {\n\t\t\t\t\tcoords[i++] = x;\n\t\t\t\t\tcoords[i++] = y;\n\t\t\t\t}\n\t\t\t\tif (!handleOut) {\n\t\t\t\t\tcoords[i++] = x;\n\t\t\t\t\tcoords[i++] = y;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn coords;\n\t}\n});\n\nvar SegmentPoint = Point.extend({\n\tinitialize: function SegmentPoint(point, owner, key) {\n\t\tvar x, y,\n\t\t\tselected;\n\t\tif (!point) {\n\t\t\tx = y = 0;\n\t\t} else if ((x = point[0]) !== undefined) {\n\t\t\ty = point[1];\n\t\t} else {\n\t\t\tvar pt = point;\n\t\t\tif ((x = pt.x) === undefined) {\n\t\t\t\tpt = Point.read(arguments);\n\t\t\t\tx = pt.x;\n\t\t\t}\n\t\t\ty = pt.y;\n\t\t\tselected = pt.selected;\n\t\t}\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._owner = owner;\n\t\towner[key] = this;\n\t\tif (selected)\n\t\t\tthis.setSelected(true);\n\t},\n\n\t_set: function(x, y) {\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._owner._changed(this);\n\t\treturn this;\n\t},\n\n\tgetX: function() {\n\t\treturn this._x;\n\t},\n\n\tsetX: function(x) {\n\t\tthis._x = x;\n\t\tthis._owner._changed(this);\n\t},\n\n\tgetY: function() {\n\t\treturn this._y;\n\t},\n\n\tsetY: function(y) {\n\t\tthis._y = y;\n\t\tthis._owner._changed(this);\n\t},\n\n\tisZero: function() {\n\t\tvar isZero = Numerical.isZero;\n\t\treturn isZero(this._x) && isZero(this._y);\n\t},\n\n\tisSelected: function() {\n\t\treturn !!(this._owner._selection & this._getSelection());\n\t},\n\n\tsetSelected: function(selected) {\n\t\tthis._owner._changeSelection(this._getSelection(), selected);\n\t},\n\n\t_getSelection: function() {\n\t\tvar owner = this._owner;\n\t\treturn this === owner._point ? 1\n\t\t\t: this === owner._handleIn ? 2\n\t\t\t: this === owner._handleOut ? 4\n\t\t\t: 0;\n\t}\n});\n\nvar Curve = Base.extend({\n\t_class: 'Curve',\n\tbeans: true,\n\n\tinitialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {\n\t\tvar count = arguments.length,\n\t\t\tseg1, seg2,\n\t\t\tpoint1, point2,\n\t\t\thandle1, handle2;\n\t\tif (count === 3) {\n\t\t\tthis._path = arg0;\n\t\t\tseg1 = arg1;\n\t\t\tseg2 = arg2;\n\t\t} else if (!count) {\n\t\t\tseg1 = new Segment();\n\t\t\tseg2 = new Segment();\n\t\t} else if (count === 1) {\n\t\t\tif ('segment1' in arg0) {\n\t\t\t\tseg1 = new Segment(arg0.segment1);\n\t\t\t\tseg2 = new Segment(arg0.segment2);\n\t\t\t} else if ('point1' in arg0) {\n\t\t\t\tpoint1 = arg0.point1;\n\t\t\t\thandle1 = arg0.handle1;\n\t\t\t\thandle2 = arg0.handle2;\n\t\t\t\tpoint2 = arg0.point2;\n\t\t\t} else if (Array.isArray(arg0)) {\n\t\t\t\tpoint1 = [arg0[0], arg0[1]];\n\t\t\t\tpoint2 = [arg0[6], arg0[7]];\n\t\t\t\thandle1 = [arg0[2] - arg0[0], arg0[3] - arg0[1]];\n\t\t\t\thandle2 = [arg0[4] - arg0[6], arg0[5] - arg0[7]];\n\t\t\t}\n\t\t} else if (count === 2) {\n\t\t\tseg1 = new Segment(arg0);\n\t\t\tseg2 = new Segment(arg1);\n\t\t} else if (count === 4) {\n\t\t\tpoint1 = arg0;\n\t\t\thandle1 = arg1;\n\t\t\thandle2 = arg2;\n\t\t\tpoint2 = arg3;\n\t\t} else if (count === 8) {\n\t\t\tpoint1 = [arg0, arg1];\n\t\t\tpoint2 = [arg6, arg7];\n\t\t\thandle1 = [arg2 - arg0, arg3 - arg1];\n\t\t\thandle2 = [arg4 - arg6, arg5 - arg7];\n\t\t}\n\t\tthis._segment1 = seg1 || new Segment(point1, null, handle1);\n\t\tthis._segment2 = seg2 || new Segment(point2, handle2, null);\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\treturn Base.serialize(this.hasHandles()\n\t\t\t\t? [this.getPoint1(), this.getHandle1(), this.getHandle2(),\n\t\t\t\t\tthis.getPoint2()]\n\t\t\t\t: [this.getPoint1(), this.getPoint2()],\n\t\t\t\toptions, true, dictionary);\n\t},\n\n\t_changed: function() {\n\t\tthis._length = this._bounds = undefined;\n\t},\n\n\tclone: function() {\n\t\treturn new Curve(this._segment1, this._segment2);\n\t},\n\n\ttoString: function() {\n\t\tvar parts = [ 'point1: ' + this._segment1._point ];\n\t\tif (!this._segment1._handleOut.isZero())\n\t\t\tparts.push('handle1: ' + this._segment1._handleOut);\n\t\tif (!this._segment2._handleIn.isZero())\n\t\t\tparts.push('handle2: ' + this._segment2._handleIn);\n\t\tparts.push('point2: ' + this._segment2._point);\n\t\treturn '{ ' + parts.join(', ') + ' }';\n\t},\n\n\tclassify: function() {\n\t\treturn Curve.classify(this.getValues());\n\t},\n\n\tremove: function() {\n\t\tvar removed = false;\n\t\tif (this._path) {\n\t\t\tvar segment2 = this._segment2,\n\t\t\t\thandleOut = segment2._handleOut;\n\t\t\tremoved = segment2.remove();\n\t\t\tif (removed)\n\t\t\t\tthis._segment1._handleOut.set(handleOut);\n\t\t}\n\t\treturn removed;\n\t},\n\n\tgetPoint1: function() {\n\t\treturn this._segment1._point;\n\t},\n\n\tsetPoint1: function() {\n\t\tthis._segment1._point.set(Point.read(arguments));\n\t},\n\n\tgetPoint2: function() {\n\t\treturn this._segment2._point;\n\t},\n\n\tsetPoint2: function() {\n\t\tthis._segment2._point.set(Point.read(arguments));\n\t},\n\n\tgetHandle1: function() {\n\t\treturn this._segment1._handleOut;\n\t},\n\n\tsetHandle1: function() {\n\t\tthis._segment1._handleOut.set(Point.read(arguments));\n\t},\n\n\tgetHandle2: function() {\n\t\treturn this._segment2._handleIn;\n\t},\n\n\tsetHandle2: function() {\n\t\tthis._segment2._handleIn.set(Point.read(arguments));\n\t},\n\n\tgetSegment1: function() {\n\t\treturn this._segment1;\n\t},\n\n\tgetSegment2: function() {\n\t\treturn this._segment2;\n\t},\n\n\tgetPath: function() {\n\t\treturn this._path;\n\t},\n\n\tgetIndex: function() {\n\t\treturn this._segment1._index;\n\t},\n\n\tgetNext: function() {\n\t\tvar curves = this._path && this._path._curves;\n\t\treturn curves && (curves[this._segment1._index + 1]\n\t\t\t\t|| this._path._closed && curves[0]) || null;\n\t},\n\n\tgetPrevious: function() {\n\t\tvar curves = this._path && this._path._curves;\n\t\treturn curves && (curves[this._segment1._index - 1]\n\t\t\t\t|| this._path._closed && curves[curves.length - 1]) || null;\n\t},\n\n\tisFirst: function() {\n\t\treturn !this._segment1._index;\n\t},\n\n\tisLast: function() {\n\t\tvar path = this._path;\n\t\treturn path && this._segment1._index === path._curves.length - 1\n\t\t\t\t|| false;\n\t},\n\n\tisSelected: function() {\n\t\treturn this.getPoint1().isSelected()\n\t\t\t\t&& this.getHandle1().isSelected()\n\t\t\t\t&& this.getHandle2().isSelected()\n\t\t\t\t&& this.getPoint2().isSelected();\n\t},\n\n\tsetSelected: function(selected) {\n\t\tthis.getPoint1().setSelected(selected);\n\t\tthis.getHandle1().setSelected(selected);\n\t\tthis.getHandle2().setSelected(selected);\n\t\tthis.getPoint2().setSelected(selected);\n\t},\n\n\tgetValues: function(matrix) {\n\t\treturn Curve.getValues(this._segment1, this._segment2, matrix);\n\t},\n\n\tgetPoints: function() {\n\t\tvar coords = this.getValues(),\n\t\t\tpoints = [];\n\t\tfor (var i = 0; i < 8; i += 2)\n\t\t\tpoints.push(new Point(coords[i], coords[i + 1]));\n\t\treturn points;\n\t}\n}, {\n\tgetLength: function() {\n\t\tif (this._length == null)\n\t\t\tthis._length = Curve.getLength(this.getValues(), 0, 1);\n\t\treturn this._length;\n\t},\n\n\tgetArea: function() {\n\t\treturn Curve.getArea(this.getValues());\n\t},\n\n\tgetLine: function() {\n\t\treturn new Line(this._segment1._point, this._segment2._point);\n\t},\n\n\tgetPart: function(from, to) {\n\t\treturn new Curve(Curve.getPart(this.getValues(), from, to));\n\t},\n\n\tgetPartLength: function(from, to) {\n\t\treturn Curve.getLength(this.getValues(), from, to);\n\t},\n\n\tdivideAt: function(location) {\n\t\treturn this.divideAtTime(location && location.curve === this\n\t\t\t\t? location.time : this.getTimeAt(location));\n\t},\n\n\tdivideAtTime: function(time, _setHandles) {\n\t\tvar tMin = 1e-8,\n\t\t\ttMax = 1 - tMin,\n\t\t\tres = null;\n\t\tif (time >= tMin && time <= tMax) {\n\t\t\tvar parts = Curve.subdivide(this.getValues(), time),\n\t\t\t\tleft = parts[0],\n\t\t\t\tright = parts[1],\n\t\t\t\tsetHandles = _setHandles || this.hasHandles(),\n\t\t\t\tseg1 = this._segment1,\n\t\t\t\tseg2 = this._segment2,\n\t\t\t\tpath = this._path;\n\t\t\tif (setHandles) {\n\t\t\t\tseg1._handleOut._set(left[2] - left[0], left[3] - left[1]);\n\t\t\t\tseg2._handleIn._set(right[4] - right[6],right[5] - right[7]);\n\t\t\t}\n\t\t\tvar x = left[6], y = left[7],\n\t\t\t\tsegment = new Segment(new Point(x, y),\n\t\t\t\t\t\tsetHandles && new Point(left[4] - x, left[5] - y),\n\t\t\t\t\t\tsetHandles && new Point(right[2] - x, right[3] - y));\n\t\t\tif (path) {\n\t\t\t\tpath.insert(seg1._index + 1, segment);\n\t\t\t\tres = this.getNext();\n\t\t\t} else {\n\t\t\t\tthis._segment2 = segment;\n\t\t\t\tthis._changed();\n\t\t\t\tres = new Curve(segment, seg2);\n\t\t\t}\n\t\t}\n\t\treturn res;\n\t},\n\n\tsplitAt: function(location) {\n\t\tvar path = this._path;\n\t\treturn path ? path.splitAt(location) : null;\n\t},\n\n\tsplitAtTime: function(time) {\n\t\treturn this.splitAt(this.getLocationAtTime(time));\n\t},\n\n\tdivide: function(offset, isTime) {\n\t\treturn this.divideAtTime(offset === undefined ? 0.5 : isTime ? offset\n\t\t\t\t: this.getTimeAt(offset));\n\t},\n\n\tsplit: function(offset, isTime) {\n\t\treturn this.splitAtTime(offset === undefined ? 0.5 : isTime ? offset\n\t\t\t\t: this.getTimeAt(offset));\n\t},\n\n\treversed: function() {\n\t\treturn new Curve(this._segment2.reversed(), this._segment1.reversed());\n\t},\n\n\tclearHandles: function() {\n\t\tthis._segment1._handleOut._set(0, 0);\n\t\tthis._segment2._handleIn._set(0, 0);\n\t},\n\nstatics: {\n\tgetValues: function(segment1, segment2, matrix, straight) {\n\t\tvar p1 = segment1._point,\n\t\t\th1 = segment1._handleOut,\n\t\t\th2 = segment2._handleIn,\n\t\t\tp2 = segment2._point,\n\t\t\tx1 = p1.x, y1 = p1.y,\n\t\t\tx2 = p2.x, y2 = p2.y,\n\t\t\tvalues = straight\n\t\t\t\t? [ x1, y1, x1, y1, x2, y2, x2, y2 ]\n\t\t\t\t: [\n\t\t\t\t\tx1, y1,\n\t\t\t\t\tx1 + h1._x, y1 + h1._y,\n\t\t\t\t\tx2 + h2._x, y2 + h2._y,\n\t\t\t\t\tx2, y2\n\t\t\t\t];\n\t\tif (matrix)\n\t\t\tmatrix._transformCoordinates(values, values, 4);\n\t\treturn values;\n\t},\n\n\tsubdivide: function(v, t) {\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx1 = v[2], y1 = v[3],\n\t\t\tx2 = v[4], y2 = v[5],\n\t\t\tx3 = v[6], y3 = v[7];\n\t\tif (t === undefined)\n\t\t\tt = 0.5;\n\t\tvar u = 1 - t,\n\t\t\tx4 = u * x0 + t * x1, y4 = u * y0 + t * y1,\n\t\t\tx5 = u * x1 + t * x2, y5 = u * y1 + t * y2,\n\t\t\tx6 = u * x2 + t * x3, y6 = u * y2 + t * y3,\n\t\t\tx7 = u * x4 + t * x5, y7 = u * y4 + t * y5,\n\t\t\tx8 = u * x5 + t * x6, y8 = u * y5 + t * y6,\n\t\t\tx9 = u * x7 + t * x8, y9 = u * y7 + t * y8;\n\t\treturn [\n\t\t\t[x0, y0, x4, y4, x7, y7, x9, y9],\n\t\t\t[x9, y9, x8, y8, x6, y6, x3, y3]\n\t\t];\n\t},\n\n\tgetMonoCurves: function(v, dir) {\n\t\tvar curves = [],\n\t\t\tio = dir ? 0 : 1,\n\t\t\to0 = v[io + 0],\n\t\t\to1 = v[io + 2],\n\t\t\to2 = v[io + 4],\n\t\t\to3 = v[io + 6];\n\t\tif ((o0 >= o1) === (o1 >= o2) && (o1 >= o2) === (o2 >= o3)\n\t\t\t\t|| Curve.isStraight(v)) {\n\t\t\tcurves.push(v);\n\t\t} else {\n\t\t\tvar a = 3 * (o1 - o2) - o0 + o3,\n\t\t\t\tb = 2 * (o0 + o2) - 4 * o1,\n\t\t\t\tc = o1 - o0,\n\t\t\t\ttMin = 1e-8,\n\t\t\t\ttMax = 1 - tMin,\n\t\t\t\troots = [],\n\t\t\t\tn = Numerical.solveQuadratic(a, b, c, roots, tMin, tMax);\n\t\t\tif (!n) {\n\t\t\t\tcurves.push(v);\n\t\t\t} else {\n\t\t\t\troots.sort();\n\t\t\t\tvar t = roots[0],\n\t\t\t\t\tparts = Curve.subdivide(v, t);\n\t\t\t\tcurves.push(parts[0]);\n\t\t\t\tif (n > 1) {\n\t\t\t\t\tt = (roots[1] - t) / (1 - t);\n\t\t\t\t\tparts = Curve.subdivide(parts[1], t);\n\t\t\t\t\tcurves.push(parts[0]);\n\t\t\t\t}\n\t\t\t\tcurves.push(parts[1]);\n\t\t\t}\n\t\t}\n\t\treturn curves;\n\t},\n\n\tsolveCubic: function (v, coord, val, roots, min, max) {\n\t\tvar v0 = v[coord],\n\t\t\tv1 = v[coord + 2],\n\t\t\tv2 = v[coord + 4],\n\t\t\tv3 = v[coord + 6],\n\t\t\tres = 0;\n\t\tif ( !(v0 < val && v3 < val && v1 < val && v2 < val ||\n\t\t\t\tv0 > val && v3 > val && v1 > val && v2 > val)) {\n\t\t\tvar c = 3 * (v1 - v0),\n\t\t\t\tb = 3 * (v2 - v1) - c,\n\t\t\t\ta = v3 - v0 - c - b;\n\t\t\tres = Numerical.solveCubic(a, b, c, v0 - val, roots, min, max);\n\t\t}\n\t\treturn res;\n\t},\n\n\tgetTimeOf: function(v, point) {\n\t\tvar p0 = new Point(v[0], v[1]),\n\t\t\tp3 = new Point(v[6], v[7]),\n\t\t\tepsilon = 1e-12,\n\t\t\tgeomEpsilon = 1e-7,\n\t\t\tt = point.isClose(p0, epsilon) ? 0\n\t\t\t : point.isClose(p3, epsilon) ? 1\n\t\t\t : null;\n\t\tif (t === null) {\n\t\t\tvar coords = [point.x, point.y],\n\t\t\t\troots = [];\n\t\t\tfor (var c = 0; c < 2; c++) {\n\t\t\t\tvar count = Curve.solveCubic(v, c, coords[c], roots, 0, 1);\n\t\t\t\tfor (var i = 0; i < count; i++) {\n\t\t\t\t\tvar u = roots[i];\n\t\t\t\t\tif (point.isClose(Curve.getPoint(v, u), geomEpsilon))\n\t\t\t\t\t\treturn u;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn point.isClose(p0, geomEpsilon) ? 0\n\t\t\t : point.isClose(p3, geomEpsilon) ? 1\n\t\t\t : null;\n\t},\n\n\tgetNearestTime: function(v, point) {\n\t\tif (Curve.isStraight(v)) {\n\t\t\tvar x0 = v[0], y0 = v[1],\n\t\t\t\tx3 = v[6], y3 = v[7],\n\t\t\t\tvx = x3 - x0, vy = y3 - y0,\n\t\t\t\tdet = vx * vx + vy * vy;\n\t\t\tif (det === 0)\n\t\t\t\treturn 0;\n\t\t\tvar u = ((point.x - x0) * vx + (point.y - y0) * vy) / det;\n\t\t\treturn u < 1e-12 ? 0\n\t\t\t\t : u > 0.999999999999 ? 1\n\t\t\t\t : Curve.getTimeOf(v,\n\t\t\t\t\tnew Point(x0 + u * vx, y0 + u * vy));\n\t\t}\n\n\t\tvar count = 100,\n\t\t\tminDist = Infinity,\n\t\t\tminT = 0;\n\n\t\tfunction refine(t) {\n\t\t\tif (t >= 0 && t <= 1) {\n\t\t\t\tvar dist = point.getDistance(Curve.getPoint(v, t), true);\n\t\t\t\tif (dist < minDist) {\n\t\t\t\t\tminDist = dist;\n\t\t\t\t\tminT = t;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i <= count; i++)\n\t\t\trefine(i / count);\n\n\t\tvar step = 1 / (count * 2);\n\t\twhile (step > 1e-8) {\n\t\t\tif (!refine(minT - step) && !refine(minT + step))\n\t\t\t\tstep /= 2;\n\t\t}\n\t\treturn minT;\n\t},\n\n\tgetPart: function(v, from, to) {\n\t\tvar flip = from > to;\n\t\tif (flip) {\n\t\t\tvar tmp = from;\n\t\t\tfrom = to;\n\t\t\tto = tmp;\n\t\t}\n\t\tif (from > 0)\n\t\t\tv = Curve.subdivide(v, from)[1];\n\t\tif (to < 1)\n\t\t\tv = Curve.subdivide(v, (to - from) / (1 - from))[0];\n\t\treturn flip\n\t\t\t\t? [v[6], v[7], v[4], v[5], v[2], v[3], v[0], v[1]]\n\t\t\t\t: v;\n\t},\n\n\tisFlatEnough: function(v, flatness) {\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx1 = v[2], y1 = v[3],\n\t\t\tx2 = v[4], y2 = v[5],\n\t\t\tx3 = v[6], y3 = v[7],\n\t\t\tux = 3 * x1 - 2 * x0 - x3,\n\t\t\tuy = 3 * y1 - 2 * y0 - y3,\n\t\t\tvx = 3 * x2 - 2 * x3 - x0,\n\t\t\tvy = 3 * y2 - 2 * y3 - y0;\n\t\treturn Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)\n\t\t\t\t<= 16 * flatness * flatness;\n\t},\n\n\tgetArea: function(v) {\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx1 = v[2], y1 = v[3],\n\t\t\tx2 = v[4], y2 = v[5],\n\t\t\tx3 = v[6], y3 = v[7];\n\t\treturn 3 * ((y3 - y0) * (x1 + x2) - (x3 - x0) * (y1 + y2)\n\t\t\t\t+ y1 * (x0 - x2) - x1 * (y0 - y2)\n\t\t\t\t+ y3 * (x2 + x0 / 3) - x3 * (y2 + y0 / 3)) / 20;\n\t},\n\n\tgetBounds: function(v) {\n\t\tvar min = v.slice(0, 2),\n\t\t\tmax = min.slice(),\n\t\t\troots = [0, 0];\n\t\tfor (var i = 0; i < 2; i++)\n\t\t\tCurve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],\n\t\t\t\t\ti, 0, min, max, roots);\n\t\treturn new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n\t},\n\n\t_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {\n\t\tfunction add(value, padding) {\n\t\t\tvar left = value - padding,\n\t\t\t\tright = value + padding;\n\t\t\tif (left < min[coord])\n\t\t\t\tmin[coord] = left;\n\t\t\tif (right > max[coord])\n\t\t\t\tmax[coord] = right;\n\t\t}\n\n\t\tpadding /= 2;\n\t\tvar minPad = min[coord] - padding,\n\t\t\tmaxPad = max[coord] + padding;\n\t\tif ( v0 < minPad || v1 < minPad || v2 < minPad || v3 < minPad ||\n\t\t\t\tv0 > maxPad || v1 > maxPad || v2 > maxPad || v3 > maxPad) {\n\t\t\tif (v1 < v0 != v1 < v3 && v2 < v0 != v2 < v3) {\n\t\t\t\tadd(v0, padding);\n\t\t\t\tadd(v3, padding);\n\t\t\t} else {\n\t\t\t\tvar a = 3 * (v1 - v2) - v0 + v3,\n\t\t\t\t\tb = 2 * (v0 + v2) - 4 * v1,\n\t\t\t\t\tc = v1 - v0,\n\t\t\t\t\tcount = Numerical.solveQuadratic(a, b, c, roots),\n\t\t\t\t\ttMin = 1e-8,\n\t\t\t\t\ttMax = 1 - tMin;\n\t\t\t\tadd(v3, 0);\n\t\t\t\tfor (var i = 0; i < count; i++) {\n\t\t\t\t\tvar t = roots[i],\n\t\t\t\t\t\tu = 1 - t;\n\t\t\t\t\tif (tMin <= t && t <= tMax)\n\t\t\t\t\t\tadd(u * u * u * v0\n\t\t\t\t\t\t\t+ 3 * u * u * t * v1\n\t\t\t\t\t\t\t+ 3 * u * t * t * v2\n\t\t\t\t\t\t\t+ t * t * t * v3,\n\t\t\t\t\t\t\tpadding);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}}, Base.each(\n\t['getBounds', 'getStrokeBounds', 'getHandleBounds'],\n\tfunction(name) {\n\t\tthis[name] = function() {\n\t\t\tif (!this._bounds)\n\t\t\t\tthis._bounds = {};\n\t\t\tvar bounds = this._bounds[name];\n\t\t\tif (!bounds) {\n\t\t\t\tbounds = this._bounds[name] = Path[name](\n\t\t\t\t\t\t[this._segment1, this._segment2], false, this._path);\n\t\t\t}\n\t\t\treturn bounds.clone();\n\t\t};\n\t},\n{\n\n}), Base.each({\n\tisStraight: function(p1, h1, h2, p2) {\n\t\tif (h1.isZero() && h2.isZero()) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tvar v = p2.subtract(p1);\n\t\t\tif (v.isZero()) {\n\t\t\t\treturn false;\n\t\t\t} else if (v.isCollinear(h1) && v.isCollinear(h2)) {\n\t\t\t\tvar l = new Line(p1, p2),\n\t\t\t\t\tepsilon = 1e-7;\n\t\t\t\tif (l.getDistance(p1.add(h1)) < epsilon &&\n\t\t\t\t\tl.getDistance(p2.add(h2)) < epsilon) {\n\t\t\t\t\tvar div = v.dot(v),\n\t\t\t\t\t\ts1 = v.dot(h1) / div,\n\t\t\t\t\t\ts2 = v.dot(h2) / div;\n\t\t\t\t\treturn s1 >= 0 && s1 <= 1 && s2 <= 0 && s2 >= -1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t},\n\n\tisLinear: function(p1, h1, h2, p2) {\n\t\tvar third = p2.subtract(p1).divide(3);\n\t\treturn h1.equals(third) && h2.negate().equals(third);\n\t}\n}, function(test, name) {\n\tthis[name] = function(epsilon) {\n\t\tvar seg1 = this._segment1,\n\t\t\tseg2 = this._segment2;\n\t\treturn test(seg1._point, seg1._handleOut, seg2._handleIn, seg2._point,\n\t\t\t\tepsilon);\n\t};\n\n\tthis.statics[name] = function(v, epsilon) {\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx3 = v[6], y3 = v[7];\n\t\treturn test(\n\t\t\t\tnew Point(x0, y0),\n\t\t\t\tnew Point(v[2] - x0, v[3] - y0),\n\t\t\t\tnew Point(v[4] - x3, v[5] - y3),\n\t\t\t\tnew Point(x3, y3), epsilon);\n\t};\n}, {\n\tstatics: {},\n\n\thasHandles: function() {\n\t\treturn !this._segment1._handleOut.isZero()\n\t\t\t\t|| !this._segment2._handleIn.isZero();\n\t},\n\n\thasLength: function(epsilon) {\n\t\treturn (!this.getPoint1().equals(this.getPoint2()) || this.hasHandles())\n\t\t\t\t&& this.getLength() > (epsilon || 0);\n\t},\n\n\tisCollinear: function(curve) {\n\t\treturn curve && this.isStraight() && curve.isStraight()\n\t\t\t\t&& this.getLine().isCollinear(curve.getLine());\n\t},\n\n\tisHorizontal: function() {\n\t\treturn this.isStraight() && Math.abs(this.getTangentAtTime(0.5).y)\n\t\t\t\t< 1e-8;\n\t},\n\n\tisVertical: function() {\n\t\treturn this.isStraight() && Math.abs(this.getTangentAtTime(0.5).x)\n\t\t\t\t< 1e-8;\n\t}\n}), {\n\tbeans: false,\n\n\tgetLocationAt: function(offset, _isTime) {\n\t\treturn this.getLocationAtTime(\n\t\t\t\t_isTime ? offset : this.getTimeAt(offset));\n\t},\n\n\tgetLocationAtTime: function(t) {\n\t\treturn t != null && t >= 0 && t <= 1\n\t\t\t\t? new CurveLocation(this, t)\n\t\t\t\t: null;\n\t},\n\n\tgetTimeAt: function(offset, start) {\n\t\treturn Curve.getTimeAt(this.getValues(), offset, start);\n\t},\n\n\tgetParameterAt: '#getTimeAt',\n\n\tgetTimesWithTangent: function () {\n\t\tvar tangent = Point.read(arguments);\n\t\treturn tangent.isZero()\n\t\t\t\t? []\n\t\t\t\t: Curve.getTimesWithTangent(this.getValues(), tangent);\n\t},\n\n\tgetOffsetAtTime: function(t) {\n\t\treturn this.getPartLength(0, t);\n\t},\n\n\tgetLocationOf: function() {\n\t\treturn this.getLocationAtTime(this.getTimeOf(Point.read(arguments)));\n\t},\n\n\tgetOffsetOf: function() {\n\t\tvar loc = this.getLocationOf.apply(this, arguments);\n\t\treturn loc ? loc.getOffset() : null;\n\t},\n\n\tgetTimeOf: function() {\n\t\treturn Curve.getTimeOf(this.getValues(), Point.read(arguments));\n\t},\n\n\tgetParameterOf: '#getTimeOf',\n\n\tgetNearestLocation: function() {\n\t\tvar point = Point.read(arguments),\n\t\t\tvalues = this.getValues(),\n\t\t\tt = Curve.getNearestTime(values, point),\n\t\t\tpt = Curve.getPoint(values, t);\n\t\treturn new CurveLocation(this, t, pt, null, point.getDistance(pt));\n\t},\n\n\tgetNearestPoint: function() {\n\t\tvar loc = this.getNearestLocation.apply(this, arguments);\n\t\treturn loc ? loc.getPoint() : loc;\n\t}\n\n},\nnew function() {\n\tvar methods = ['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',\n\t\t'getWeightedNormal', 'getCurvature'];\n\treturn Base.each(methods,\n\t\tfunction(name) {\n\t\t\tthis[name + 'At'] = function(location, _isTime) {\n\t\t\t\tvar values = this.getValues();\n\t\t\t\treturn Curve[name](values, _isTime ? location\n\t\t\t\t\t\t: Curve.getTimeAt(values, location));\n\t\t\t};\n\n\t\t\tthis[name + 'AtTime'] = function(time) {\n\t\t\t\treturn Curve[name](this.getValues(), time);\n\t\t\t};\n\t\t}, {\n\t\t\tstatics: {\n\t\t\t\t_evaluateMethods: methods\n\t\t\t}\n\t\t}\n\t);\n},\nnew function() {\n\n\tfunction getLengthIntegrand(v) {\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx1 = v[2], y1 = v[3],\n\t\t\tx2 = v[4], y2 = v[5],\n\t\t\tx3 = v[6], y3 = v[7],\n\n\t\t\tax = 9 * (x1 - x2) + 3 * (x3 - x0),\n\t\t\tbx = 6 * (x0 + x2) - 12 * x1,\n\t\t\tcx = 3 * (x1 - x0),\n\n\t\t\tay = 9 * (y1 - y2) + 3 * (y3 - y0),\n\t\t\tby = 6 * (y0 + y2) - 12 * y1,\n\t\t\tcy = 3 * (y1 - y0);\n\n\t\treturn function(t) {\n\t\t\tvar dx = (ax * t + bx) * t + cx,\n\t\t\t\tdy = (ay * t + by) * t + cy;\n\t\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t\t};\n\t}\n\n\tfunction getIterations(a, b) {\n\t\treturn Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));\n\t}\n\n\tfunction evaluate(v, t, type, normalized) {\n\t\tif (t == null || t < 0 || t > 1)\n\t\t\treturn null;\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx1 = v[2], y1 = v[3],\n\t\t\tx2 = v[4], y2 = v[5],\n\t\t\tx3 = v[6], y3 = v[7],\n\t\t\tisZero = Numerical.isZero;\n\t\tif (isZero(x1 - x0) && isZero(y1 - y0)) {\n\t\t\tx1 = x0;\n\t\t\ty1 = y0;\n\t\t}\n\t\tif (isZero(x2 - x3) && isZero(y2 - y3)) {\n\t\t\tx2 = x3;\n\t\t\ty2 = y3;\n\t\t}\n\t\tvar cx = 3 * (x1 - x0),\n\t\t\tbx = 3 * (x2 - x1) - cx,\n\t\t\tax = x3 - x0 - cx - bx,\n\t\t\tcy = 3 * (y1 - y0),\n\t\t\tby = 3 * (y2 - y1) - cy,\n\t\t\tay = y3 - y0 - cy - by,\n\t\t\tx, y;\n\t\tif (type === 0) {\n\t\t\tx = t === 0 ? x0 : t === 1 ? x3\n\t\t\t\t\t: ((ax * t + bx) * t + cx) * t + x0;\n\t\t\ty = t === 0 ? y0 : t === 1 ? y3\n\t\t\t\t\t: ((ay * t + by) * t + cy) * t + y0;\n\t\t} else {\n\t\t\tvar tMin = 1e-8,\n\t\t\t\ttMax = 1 - tMin;\n\t\t\tif (t < tMin) {\n\t\t\t\tx = cx;\n\t\t\t\ty = cy;\n\t\t\t} else if (t > tMax) {\n\t\t\t\tx = 3 * (x3 - x2);\n\t\t\t\ty = 3 * (y3 - y2);\n\t\t\t} else {\n\t\t\t\tx = (3 * ax * t + 2 * bx) * t + cx;\n\t\t\t\ty = (3 * ay * t + 2 * by) * t + cy;\n\t\t\t}\n\t\t\tif (normalized) {\n\t\t\t\tif (x === 0 && y === 0 && (t < tMin || t > tMax)) {\n\t\t\t\t\tx = x2 - x1;\n\t\t\t\t\ty = y2 - y1;\n\t\t\t\t}\n\t\t\t\tvar len = Math.sqrt(x * x + y * y);\n\t\t\t\tif (len) {\n\t\t\t\t\tx /= len;\n\t\t\t\t\ty /= len;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (type === 3) {\n\t\t\t\tvar x2 = 6 * ax * t + 2 * bx,\n\t\t\t\t\ty2 = 6 * ay * t + 2 * by,\n\t\t\t\t\td = Math.pow(x * x + y * y, 3 / 2);\n\t\t\t\tx = d !== 0 ? (x * y2 - y * x2) / d : 0;\n\t\t\t\ty = 0;\n\t\t\t}\n\t\t}\n\t\treturn type === 2 ? new Point(y, -x) : new Point(x, y);\n\t}\n\n\treturn { statics: {\n\n\t\tclassify: function(v) {\n\n\t\t\tvar x0 = v[0], y0 = v[1],\n\t\t\t\tx1 = v[2], y1 = v[3],\n\t\t\t\tx2 = v[4], y2 = v[5],\n\t\t\t\tx3 = v[6], y3 = v[7],\n\t\t\t\ta1 = x0 * (y3 - y2) + y0 * (x2 - x3) + x3 * y2 - y3 * x2,\n\t\t\t\ta2 = x1 * (y0 - y3) + y1 * (x3 - x0) + x0 * y3 - y0 * x3,\n\t\t\t\ta3 = x2 * (y1 - y0) + y2 * (x0 - x1) + x1 * y0 - y1 * x0,\n\t\t\t\td3 = 3 * a3,\n\t\t\t\td2 = d3 - a2,\n\t\t\t\td1 = d2 - a2 + a1,\n\t\t\t\tl = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3),\n\t\t\t\ts = l !== 0 ? 1 / l : 0,\n\t\t\t\tisZero = Numerical.isZero,\n\t\t\t\tserpentine = 'serpentine';\n\t\t\td1 *= s;\n\t\t\td2 *= s;\n\t\t\td3 *= s;\n\n\t\t\tfunction type(type, t1, t2) {\n\t\t\t\tvar hasRoots = t1 !== undefined,\n\t\t\t\t\tt1Ok = hasRoots && t1 > 0 && t1 < 1,\n\t\t\t\t\tt2Ok = hasRoots && t2 > 0 && t2 < 1;\n\t\t\t\tif (hasRoots && (!(t1Ok || t2Ok)\n\t\t\t\t\t\t|| type === 'loop' && !(t1Ok && t2Ok))) {\n\t\t\t\t\ttype = 'arch';\n\t\t\t\t\tt1Ok = t2Ok = false;\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\ttype: type,\n\t\t\t\t\troots: t1Ok || t2Ok\n\t\t\t\t\t\t\t? t1Ok && t2Ok\n\t\t\t\t\t\t\t\t? t1 < t2 ? [t1, t2] : [t2, t1]\n\t\t\t\t\t\t\t\t: [t1Ok ? t1 : t2]\n\t\t\t\t\t\t\t: null\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (isZero(d1)) {\n\t\t\t\treturn isZero(d2)\n\t\t\t\t\t\t? type(isZero(d3) ? 'line' : 'quadratic')\n\t\t\t\t\t\t: type(serpentine, d3 / (3 * d2));\n\t\t\t}\n\t\t\tvar d = 3 * d2 * d2 - 4 * d1 * d3;\n\t\t\tif (isZero(d)) {\n\t\t\t\treturn type('cusp', d2 / (2 * d1));\n\t\t\t}\n\t\t\tvar f1 = d > 0 ? Math.sqrt(d / 3) : Math.sqrt(-d),\n\t\t\t\tf2 = 2 * d1;\n\t\t\treturn type(d > 0 ? serpentine : 'loop',\n\t\t\t\t\t(d2 + f1) / f2,\n\t\t\t\t\t(d2 - f1) / f2);\n\t\t},\n\n\t\tgetLength: function(v, a, b, ds) {\n\t\t\tif (a === undefined)\n\t\t\t\ta = 0;\n\t\t\tif (b === undefined)\n\t\t\t\tb = 1;\n\t\t\tif (Curve.isStraight(v)) {\n\t\t\t\tvar c = v;\n\t\t\t\tif (b < 1) {\n\t\t\t\t\tc = Curve.subdivide(c, b)[0];\n\t\t\t\t\ta /= b;\n\t\t\t\t}\n\t\t\t\tif (a > 0) {\n\t\t\t\t\tc = Curve.subdivide(c, a)[1];\n\t\t\t\t}\n\t\t\t\tvar dx = c[6] - c[0],\n\t\t\t\t\tdy = c[7] - c[1];\n\t\t\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t\t\t}\n\t\t\treturn Numerical.integrate(ds || getLengthIntegrand(v), a, b,\n\t\t\t\t\tgetIterations(a, b));\n\t\t},\n\n\t\tgetTimeAt: function(v, offset, start) {\n\t\t\tif (start === undefined)\n\t\t\t\tstart = offset < 0 ? 1 : 0;\n\t\t\tif (offset === 0)\n\t\t\t\treturn start;\n\t\t\tvar abs = Math.abs,\n\t\t\t\tepsilon = 1e-12,\n\t\t\t\tforward = offset > 0,\n\t\t\t\ta = forward ? start : 0,\n\t\t\t\tb = forward ? 1 : start,\n\t\t\t\tds = getLengthIntegrand(v),\n\t\t\t\trangeLength = Curve.getLength(v, a, b, ds),\n\t\t\t\tdiff = abs(offset) - rangeLength;\n\t\t\tif (abs(diff) < epsilon) {\n\t\t\t\treturn forward ? b : a;\n\t\t\t} else if (diff > epsilon) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tvar guess = offset / rangeLength,\n\t\t\t\tlength = 0;\n\t\t\tfunction f(t) {\n\t\t\t\tlength += Numerical.integrate(ds, start, t,\n\t\t\t\t\t\tgetIterations(start, t));\n\t\t\t\tstart = t;\n\t\t\t\treturn length - offset;\n\t\t\t}\n\t\t\treturn Numerical.findRoot(f, ds, start + guess, a, b, 32,\n\t\t\t\t\t1e-12);\n\t\t},\n\n\t\tgetPoint: function(v, t) {\n\t\t\treturn evaluate(v, t, 0, false);\n\t\t},\n\n\t\tgetTangent: function(v, t) {\n\t\t\treturn evaluate(v, t, 1, true);\n\t\t},\n\n\t\tgetWeightedTangent: function(v, t) {\n\t\t\treturn evaluate(v, t, 1, false);\n\t\t},\n\n\t\tgetNormal: function(v, t) {\n\t\t\treturn evaluate(v, t, 2, true);\n\t\t},\n\n\t\tgetWeightedNormal: function(v, t) {\n\t\t\treturn evaluate(v, t, 2, false);\n\t\t},\n\n\t\tgetCurvature: function(v, t) {\n\t\t\treturn evaluate(v, t, 3, false).x;\n\t\t},\n\n\t\tgetPeaks: function(v) {\n\t\t\tvar x0 = v[0], y0 = v[1],\n\t\t\t\tx1 = v[2], y1 = v[3],\n\t\t\t\tx2 = v[4], y2 = v[5],\n\t\t\t\tx3 = v[6], y3 = v[7],\n\t\t\t\tax = -x0 + 3 * x1 - 3 * x2 + x3,\n\t\t\t\tbx = 3 * x0 - 6 * x1 + 3 * x2,\n\t\t\t\tcx = -3 * x0 + 3 * x1,\n\t\t\t\tay = -y0 + 3 * y1 - 3 * y2 + y3,\n\t\t\t\tby = 3 * y0 - 6 * y1 + 3 * y2,\n\t\t\t\tcy = -3 * y0 + 3 * y1,\n\t\t\t\ttMin = 1e-8,\n\t\t\t\ttMax = 1 - tMin,\n\t\t\t\troots = [];\n\t\t\tNumerical.solveCubic(\n\t\t\t\t\t9 * (ax * ax + ay * ay),\n\t\t\t\t\t9 * (ax * bx + by * ay),\n\t\t\t\t\t2 * (bx * bx + by * by) + 3 * (cx * ax + cy * ay),\n\t\t\t\t\t(cx * bx + by * cy),\n\t\t\t\t\troots, tMin, tMax);\n\t\t\treturn roots.sort();\n\t\t}\n\t}};\n},\nnew function() {\n\n\tfunction addLocation(locations, include, c1, t1, c2, t2, overlap) {\n\t\tvar excludeStart = !overlap && c1.getPrevious() === c2,\n\t\t\texcludeEnd = !overlap && c1 !== c2 && c1.getNext() === c2,\n\t\t\ttMin = 1e-8,\n\t\t\ttMax = 1 - tMin;\n\t\tif (t1 !== null && t1 >= (excludeStart ? tMin : 0) &&\n\t\t\tt1 <= (excludeEnd ? tMax : 1)) {\n\t\t\tif (t2 !== null && t2 >= (excludeEnd ? tMin : 0) &&\n\t\t\t\tt2 <= (excludeStart ? tMax : 1)) {\n\t\t\t\tvar loc1 = new CurveLocation(c1, t1, null, overlap),\n\t\t\t\t\tloc2 = new CurveLocation(c2, t2, null, overlap);\n\t\t\t\tloc1._intersection = loc2;\n\t\t\t\tloc2._intersection = loc1;\n\t\t\t\tif (!include || include(loc1)) {\n\t\t\t\t\tCurveLocation.insert(locations, loc1, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction addCurveIntersections(v1, v2, c1, c2, locations, include, flip,\n\t\t\trecursion, calls, tMin, tMax, uMin, uMax) {\n\t\tif (++calls >= 4096 || ++recursion >= 40)\n\t\t\treturn calls;\n\t\tvar fatLineEpsilon = 1e-9,\n\t\t\tq0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],\n\t\t\tgetSignedDistance = Line.getSignedDistance,\n\t\t\td1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]),\n\t\t\td2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]),\n\t\t\tfactor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,\n\t\t\tdMin = factor * Math.min(0, d1, d2),\n\t\t\tdMax = factor * Math.max(0, d1, d2),\n\t\t\tdp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]),\n\t\t\tdp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),\n\t\t\tdp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),\n\t\t\tdp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),\n\t\t\thull = getConvexHull(dp0, dp1, dp2, dp3),\n\t\t\ttop = hull[0],\n\t\t\tbottom = hull[1],\n\t\t\ttMinClip,\n\t\t\ttMaxClip;\n\t\tif (d1 === 0 && d2 === 0\n\t\t\t\t&& dp0 === 0 && dp1 === 0 && dp2 === 0 && dp3 === 0\n\t\t\t|| (tMinClip = clipConvexHull(top, bottom, dMin, dMax)) == null\n\t\t\t|| (tMaxClip = clipConvexHull(top.reverse(), bottom.reverse(),\n\t\t\t\tdMin, dMax)) == null)\n\t\t\treturn calls;\n\t\tvar tMinNew = tMin + (tMax - tMin) * tMinClip,\n\t\t\ttMaxNew = tMin + (tMax - tMin) * tMaxClip;\n\t\tif (Math.max(uMax - uMin, tMaxNew - tMinNew) < fatLineEpsilon) {\n\t\t\tvar t = (tMinNew + tMaxNew) / 2,\n\t\t\t\tu = (uMin + uMax) / 2;\n\t\t\taddLocation(locations, include,\n\t\t\t\t\tflip ? c2 : c1, flip ? u : t,\n\t\t\t\t\tflip ? c1 : c2, flip ? t : u);\n\t\t} else {\n\t\t\tv1 = Curve.getPart(v1, tMinClip, tMaxClip);\n\t\t\tvar uDiff = uMax - uMin;\n\t\t\tif (tMaxClip - tMinClip > 0.8) {\n\t\t\t\tif (tMaxNew - tMinNew > uDiff) {\n\t\t\t\t\tvar parts = Curve.subdivide(v1, 0.5),\n\t\t\t\t\t\tt = (tMinNew + tMaxNew) / 2;\n\t\t\t\t\tcalls = addCurveIntersections(\n\t\t\t\t\t\t\tv2, parts[0], c2, c1, locations, include, !flip,\n\t\t\t\t\t\t\trecursion, calls, uMin, uMax, tMinNew, t);\n\t\t\t\t\tcalls = addCurveIntersections(\n\t\t\t\t\t\t\tv2, parts[1], c2, c1, locations, include, !flip,\n\t\t\t\t\t\t\trecursion, calls, uMin, uMax, t, tMaxNew);\n\t\t\t\t} else {\n\t\t\t\t\tvar parts = Curve.subdivide(v2, 0.5),\n\t\t\t\t\t\tu = (uMin + uMax) / 2;\n\t\t\t\t\tcalls = addCurveIntersections(\n\t\t\t\t\t\t\tparts[0], v1, c2, c1, locations, include, !flip,\n\t\t\t\t\t\t\trecursion, calls, uMin, u, tMinNew, tMaxNew);\n\t\t\t\t\tcalls = addCurveIntersections(\n\t\t\t\t\t\t\tparts[1], v1, c2, c1, locations, include, !flip,\n\t\t\t\t\t\t\trecursion, calls, u, uMax, tMinNew, tMaxNew);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (uDiff === 0 || uDiff >= fatLineEpsilon) {\n\t\t\t\t\tcalls = addCurveIntersections(\n\t\t\t\t\t\t\tv2, v1, c2, c1, locations, include, !flip,\n\t\t\t\t\t\t\trecursion, calls, uMin, uMax, tMinNew, tMaxNew);\n\t\t\t\t} else {\n\t\t\t\t\tcalls = addCurveIntersections(\n\t\t\t\t\t\t\tv1, v2, c1, c2, locations, include, flip,\n\t\t\t\t\t\t\trecursion, calls, tMinNew, tMaxNew, uMin, uMax);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn calls;\n\t}\n\n\tfunction getConvexHull(dq0, dq1, dq2, dq3) {\n\t\tvar p0 = [ 0, dq0 ],\n\t\t\tp1 = [ 1 / 3, dq1 ],\n\t\t\tp2 = [ 2 / 3, dq2 ],\n\t\t\tp3 = [ 1, dq3 ],\n\t\t\tdist1 = dq1 - (2 * dq0 + dq3) / 3,\n\t\t\tdist2 = dq2 - (dq0 + 2 * dq3) / 3,\n\t\t\thull;\n\t\tif (dist1 * dist2 < 0) {\n\t\t\thull = [[p0, p1, p3], [p0, p2, p3]];\n\t\t} else {\n\t\t\tvar distRatio = dist1 / dist2;\n\t\t\thull = [\n\t\t\t\tdistRatio >= 2 ? [p0, p1, p3]\n\t\t\t\t: distRatio <= 0.5 ? [p0, p2, p3]\n\t\t\t\t: [p0, p1, p2, p3],\n\t\t\t\t[p0, p3]\n\t\t\t];\n\t\t}\n\t\treturn (dist1 || dist2) < 0 ? hull.reverse() : hull;\n\t}\n\n\tfunction clipConvexHull(hullTop, hullBottom, dMin, dMax) {\n\t\tif (hullTop[0][1] < dMin) {\n\t\t\treturn clipConvexHullPart(hullTop, true, dMin);\n\t\t} else if (hullBottom[0][1] > dMax) {\n\t\t\treturn clipConvexHullPart(hullBottom, false, dMax);\n\t\t} else {\n\t\t\treturn hullTop[0][0];\n\t\t}\n\t}\n\n\tfunction clipConvexHullPart(part, top, threshold) {\n\t\tvar px = part[0][0],\n\t\t\tpy = part[0][1];\n\t\tfor (var i = 1, l = part.length; i < l; i++) {\n\t\t\tvar qx = part[i][0],\n\t\t\t\tqy = part[i][1];\n\t\t\tif (top ? qy >= threshold : qy <= threshold) {\n\t\t\t\treturn qy === threshold ? qx\n\t\t\t\t\t\t: px + (threshold - py) * (qx - px) / (qy - py);\n\t\t\t}\n\t\t\tpx = qx;\n\t\t\tpy = qy;\n\t\t}\n\t\treturn null;\n\t}\n\n\tfunction getCurveLineIntersections(v, px, py, vx, vy) {\n\t\tvar isZero = Numerical.isZero;\n\t\tif (isZero(vx) && isZero(vy)) {\n\t\t\tvar t = Curve.getTimeOf(v, new Point(px, py));\n\t\t\treturn t === null ? [] : [t];\n\t\t}\n\t\tvar angle = Math.atan2(-vy, vx),\n\t\t\tsin = Math.sin(angle),\n\t\t\tcos = Math.cos(angle),\n\t\t\trv = [],\n\t\t\troots = [];\n\t\tfor (var i = 0; i < 8; i += 2) {\n\t\t\tvar x = v[i] - px,\n\t\t\t\ty = v[i + 1] - py;\n\t\t\trv.push(\n\t\t\t\tx * cos - y * sin,\n\t\t\t\tx * sin + y * cos);\n\t\t}\n\t\tCurve.solveCubic(rv, 1, 0, roots, 0, 1);\n\t\treturn roots;\n\t}\n\n\tfunction addCurveLineIntersections(v1, v2, c1, c2, locations, include,\n\t\t\tflip) {\n\t\tvar x1 = v2[0], y1 = v2[1],\n\t\t\tx2 = v2[6], y2 = v2[7],\n\t\t\troots = getCurveLineIntersections(v1, x1, y1, x2 - x1, y2 - y1);\n\t\tfor (var i = 0, l = roots.length; i < l; i++) {\n\t\t\tvar t1 = roots[i],\n\t\t\t\tp1 = Curve.getPoint(v1, t1),\n\t\t\t\tt2 = Curve.getTimeOf(v2, p1);\n\t\t\tif (t2 !== null) {\n\t\t\t\taddLocation(locations, include,\n\t\t\t\t\t\tflip ? c2 : c1, flip ? t2 : t1,\n\t\t\t\t\t\tflip ? c1 : c2, flip ? t1 : t2);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction addLineIntersection(v1, v2, c1, c2, locations, include) {\n\t\tvar pt = Line.intersect(\n\t\t\t\tv1[0], v1[1], v1[6], v1[7],\n\t\t\t\tv2[0], v2[1], v2[6], v2[7]);\n\t\tif (pt) {\n\t\t\taddLocation(locations, include,\n\t\t\t\t\tc1, Curve.getTimeOf(v1, pt),\n\t\t\t\t\tc2, Curve.getTimeOf(v2, pt));\n\t\t}\n\t}\n\n\tfunction getCurveIntersections(v1, v2, c1, c2, locations, include) {\n\t\tvar epsilon = 1e-12,\n\t\t\tmin = Math.min,\n\t\t\tmax = Math.max;\n\n\t\tif (max(v1[0], v1[2], v1[4], v1[6]) + epsilon >\n\t\t\tmin(v2[0], v2[2], v2[4], v2[6]) &&\n\t\t\tmin(v1[0], v1[2], v1[4], v1[6]) - epsilon <\n\t\t\tmax(v2[0], v2[2], v2[4], v2[6]) &&\n\t\t\tmax(v1[1], v1[3], v1[5], v1[7]) + epsilon >\n\t\t\tmin(v2[1], v2[3], v2[5], v2[7]) &&\n\t\t\tmin(v1[1], v1[3], v1[5], v1[7]) - epsilon <\n\t\t\tmax(v2[1], v2[3], v2[5], v2[7])) {\n\t\t\tvar overlaps = getOverlaps(v1, v2);\n\t\t\tif (overlaps) {\n\t\t\t\tfor (var i = 0; i < 2; i++) {\n\t\t\t\t\tvar overlap = overlaps[i];\n\t\t\t\t\taddLocation(locations, include,\n\t\t\t\t\t\t\tc1, overlap[0],\n\t\t\t\t\t\t\tc2, overlap[1], true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar straight1 = Curve.isStraight(v1),\n\t\t\t\t\tstraight2 = Curve.isStraight(v2),\n\t\t\t\t\tstraight = straight1 && straight2,\n\t\t\t\t\tflip = straight1 && !straight2,\n\t\t\t\t\tbefore = locations.length;\n\t\t\t\t(straight\n\t\t\t\t\t? addLineIntersection\n\t\t\t\t\t: straight1 || straight2\n\t\t\t\t\t\t? addCurveLineIntersections\n\t\t\t\t\t\t: addCurveIntersections)(\n\t\t\t\t\t\t\tflip ? v2 : v1, flip ? v1 : v2,\n\t\t\t\t\t\t\tflip ? c2 : c1, flip ? c1 : c2,\n\t\t\t\t\t\t\tlocations, include, flip,\n\t\t\t\t\t\t\t0, 0, 0, 1, 0, 1);\n\t\t\t\tif (!straight || locations.length === before) {\n\t\t\t\t\tfor (var i = 0; i < 4; i++) {\n\t\t\t\t\t\tvar t1 = i >> 1,\n\t\t\t\t\t\t\tt2 = i & 1,\n\t\t\t\t\t\t\ti1 = t1 * 6,\n\t\t\t\t\t\t\ti2 = t2 * 6,\n\t\t\t\t\t\t\tp1 = new Point(v1[i1], v1[i1 + 1]),\n\t\t\t\t\t\t\tp2 = new Point(v2[i2], v2[i2 + 1]);\n\t\t\t\t\t\tif (p1.isClose(p2, epsilon)) {\n\t\t\t\t\t\t\taddLocation(locations, include,\n\t\t\t\t\t\t\t\t\tc1, t1,\n\t\t\t\t\t\t\t\t\tc2, t2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn locations;\n\t}\n\n\tfunction getSelfIntersection(v1, c1, locations, include) {\n\t\tvar info = Curve.classify(v1);\n\t\tif (info.type === 'loop') {\n\t\t\tvar roots = info.roots;\n\t\t\taddLocation(locations, include,\n\t\t\t\t\tc1, roots[0],\n\t\t\t\t\tc1, roots[1]);\n\t\t}\n\t return locations;\n\t}\n\n\tfunction getIntersections(curves1, curves2, include, matrix1, matrix2,\n\t\t\t_returnFirst) {\n\t\tvar epsilon = 1e-7,\n\t\t\tself = !curves2;\n\t\tif (self)\n\t\t\tcurves2 = curves1;\n\t\tvar length1 = curves1.length,\n\t\t\tlength2 = curves2.length,\n\t\t\tvalues1 = new Array(length1),\n\t\t\tvalues2 = self ? values1 : new Array(length2),\n\t\t\tlocations = [];\n\n\t\tfor (var i = 0; i < length1; i++) {\n\t\t\tvalues1[i] = curves1[i].getValues(matrix1);\n\t\t}\n\t\tif (!self) {\n\t\t\tfor (var i = 0; i < length2; i++) {\n\t\t\t\tvalues2[i] = curves2[i].getValues(matrix2);\n\t\t\t}\n\t\t}\n\t\tvar boundsCollisions = CollisionDetection.findCurveBoundsCollisions(\n\t\t\t\tvalues1, values2, epsilon);\n\t\tfor (var index1 = 0; index1 < length1; index1++) {\n\t\t\tvar curve1 = curves1[index1],\n\t\t\t\tv1 = values1[index1];\n\t\t\tif (self) {\n\t\t\t\tgetSelfIntersection(v1, curve1, locations, include);\n\t\t\t}\n\t\t\tvar collisions1 = boundsCollisions[index1];\n\t\t\tif (collisions1) {\n\t\t\t\tfor (var j = 0; j < collisions1.length; j++) {\n\t\t\t\t\tif (_returnFirst && locations.length)\n\t\t\t\t\t\treturn locations;\n\t\t\t\t\tvar index2 = collisions1[j];\n\t\t\t\t\tif (!self || index2 > index1) {\n\t\t\t\t\t\tvar curve2 = curves2[index2],\n\t\t\t\t\t\t\tv2 = values2[index2];\n\t\t\t\t\t\tgetCurveIntersections(\n\t\t\t\t\t\t\t\tv1, v2, curve1, curve2, locations, include);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn locations;\n\t}\n\n\tfunction getOverlaps(v1, v2) {\n\n\t\tfunction getSquaredLineLength(v) {\n\t\t\tvar x = v[6] - v[0],\n\t\t\t\ty = v[7] - v[1];\n\t\t\treturn x * x + y * y;\n\t\t}\n\n\t\tvar abs = Math.abs,\n\t\t\tgetDistance = Line.getDistance,\n\t\t\ttimeEpsilon = 1e-8,\n\t\t\tgeomEpsilon = 1e-7,\n\t\t\tstraight1 = Curve.isStraight(v1),\n\t\t\tstraight2 = Curve.isStraight(v2),\n\t\t\tstraightBoth = straight1 && straight2,\n\t\t\tflip = getSquaredLineLength(v1) < getSquaredLineLength(v2),\n\t\t\tl1 = flip ? v2 : v1,\n\t\t\tl2 = flip ? v1 : v2,\n\t\t\tpx = l1[0], py = l1[1],\n\t\t\tvx = l1[6] - px, vy = l1[7] - py;\n\t\tif (getDistance(px, py, vx, vy, l2[0], l2[1], true) < geomEpsilon &&\n\t\t\tgetDistance(px, py, vx, vy, l2[6], l2[7], true) < geomEpsilon) {\n\t\t\tif (!straightBoth &&\n\t\t\t\tgetDistance(px, py, vx, vy, l1[2], l1[3], true) < geomEpsilon &&\n\t\t\t\tgetDistance(px, py, vx, vy, l1[4], l1[5], true) < geomEpsilon &&\n\t\t\t\tgetDistance(px, py, vx, vy, l2[2], l2[3], true) < geomEpsilon &&\n\t\t\t\tgetDistance(px, py, vx, vy, l2[4], l2[5], true) < geomEpsilon) {\n\t\t\t\tstraight1 = straight2 = straightBoth = true;\n\t\t\t}\n\t\t} else if (straightBoth) {\n\t\t\treturn null;\n\t\t}\n\t\tif (straight1 ^ straight2) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar v = [v1, v2],\n\t\t\tpairs = [];\n\t\tfor (var i = 0; i < 4 && pairs.length < 2; i++) {\n\t\t\tvar i1 = i & 1,\n\t\t\t\ti2 = i1 ^ 1,\n\t\t\t\tt1 = i >> 1,\n\t\t\t\tt2 = Curve.getTimeOf(v[i1], new Point(\n\t\t\t\t\tv[i2][t1 ? 6 : 0],\n\t\t\t\t\tv[i2][t1 ? 7 : 1]));\n\t\t\tif (t2 != null) {\n\t\t\t\tvar pair = i1 ? [t1, t2] : [t2, t1];\n\t\t\t\tif (!pairs.length ||\n\t\t\t\t\tabs(pair[0] - pairs[0][0]) > timeEpsilon &&\n\t\t\t\t\tabs(pair[1] - pairs[0][1]) > timeEpsilon) {\n\t\t\t\t\tpairs.push(pair);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i > 2 && !pairs.length)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (pairs.length !== 2) {\n\t\t\tpairs = null;\n\t\t} else if (!straightBoth) {\n\t\t\tvar o1 = Curve.getPart(v1, pairs[0][0], pairs[1][0]),\n\t\t\t\to2 = Curve.getPart(v2, pairs[0][1], pairs[1][1]);\n\t\t\tif (abs(o2[2] - o1[2]) > geomEpsilon ||\n\t\t\t\tabs(o2[3] - o1[3]) > geomEpsilon ||\n\t\t\t\tabs(o2[4] - o1[4]) > geomEpsilon ||\n\t\t\t\tabs(o2[5] - o1[5]) > geomEpsilon)\n\t\t\t\tpairs = null;\n\t\t}\n\t\treturn pairs;\n\t}\n\n\tfunction getTimesWithTangent(v, tangent) {\n\t\tvar x0 = v[0], y0 = v[1],\n\t\t\tx1 = v[2], y1 = v[3],\n\t\t\tx2 = v[4], y2 = v[5],\n\t\t\tx3 = v[6], y3 = v[7],\n\t\t\tnormalized = tangent.normalize(),\n\t\t\ttx = normalized.x,\n\t\t\tty = normalized.y,\n\t\t\tax = 3 * x3 - 9 * x2 + 9 * x1 - 3 * x0,\n\t\t\tay = 3 * y3 - 9 * y2 + 9 * y1 - 3 * y0,\n\t\t\tbx = 6 * x2 - 12 * x1 + 6 * x0,\n\t\t\tby = 6 * y2 - 12 * y1 + 6 * y0,\n\t\t\tcx = 3 * x1 - 3 * x0,\n\t\t\tcy = 3 * y1 - 3 * y0,\n\t\t\tden = 2 * ax * ty - 2 * ay * tx,\n\t\t\ttimes = [];\n\t\tif (Math.abs(den) < Numerical.CURVETIME_EPSILON) {\n\t\t\tvar num = ax * cy - ay * cx,\n\t\t\t\tden = ax * by - ay * bx;\n\t\t\tif (den != 0) {\n\t\t\t\tvar t = -num / den;\n\t\t\t\tif (t >= 0 && t <= 1) times.push(t);\n\t\t\t}\n\t\t} else {\n\t\t\tvar delta = (bx * bx - 4 * ax * cx) * ty * ty +\n\t\t\t\t(-2 * bx * by + 4 * ay * cx + 4 * ax * cy) * tx * ty +\n\t\t\t\t(by * by - 4 * ay * cy) * tx * tx,\n\t\t\t\tk = bx * ty - by * tx;\n\t\t\tif (delta >= 0 && den != 0) {\n\t\t\t\tvar d = Math.sqrt(delta),\n\t\t\t\t\tt0 = -(k + d) / den,\n\t\t\t\t\tt1 = (-k + d) / den;\n\t\t\t\tif (t0 >= 0 && t0 <= 1) times.push(t0);\n\t\t\t\tif (t1 >= 0 && t1 <= 1) times.push(t1);\n\t\t\t}\n\t\t}\n\t\treturn times;\n\t}\n\n\treturn {\n\t\tgetIntersections: function(curve) {\n\t\t\tvar v1 = this.getValues(),\n\t\t\t\tv2 = curve && curve !== this && curve.getValues();\n\t\t\treturn v2 ? getCurveIntersections(v1, v2, this, curve, [])\n\t\t\t\t\t : getSelfIntersection(v1, this, []);\n\t\t},\n\n\t\tstatics: {\n\t\t\tgetOverlaps: getOverlaps,\n\t\t\tgetIntersections: getIntersections,\n\t\t\tgetCurveLineIntersections: getCurveLineIntersections,\n\t\t\tgetTimesWithTangent: getTimesWithTangent\n\t\t}\n\t};\n});\n\nvar CurveLocation = Base.extend({\n\t_class: 'CurveLocation',\n\n\tinitialize: function CurveLocation(curve, time, point, _overlap, _distance) {\n\t\tif (time >= 0.99999999) {\n\t\t\tvar next = curve.getNext();\n\t\t\tif (next) {\n\t\t\t\ttime = 0;\n\t\t\t\tcurve = next;\n\t\t\t}\n\t\t}\n\t\tthis._setCurve(curve);\n\t\tthis._time = time;\n\t\tthis._point = point || curve.getPointAtTime(time);\n\t\tthis._overlap = _overlap;\n\t\tthis._distance = _distance;\n\t\tthis._intersection = this._next = this._previous = null;\n\t},\n\n\t_setPath: function(path) {\n\t\tthis._path = path;\n\t\tthis._version = path ? path._version : 0;\n\t},\n\n\t_setCurve: function(curve) {\n\t\tthis._setPath(curve._path);\n\t\tthis._curve = curve;\n\t\tthis._segment = null;\n\t\tthis._segment1 = curve._segment1;\n\t\tthis._segment2 = curve._segment2;\n\t},\n\n\t_setSegment: function(segment) {\n\t\tvar curve = segment.getCurve();\n\t\tif (curve) {\n\t\t\tthis._setCurve(curve);\n\t\t} else {\n\t\t\tthis._setPath(segment._path);\n\t\t\tthis._segment1 = segment;\n\t\t\tthis._segment2 = null;\n\t\t}\n\t\tthis._segment = segment;\n\t\tthis._time = segment === this._segment1 ? 0 : 1;\n\t\tthis._point = segment._point.clone();\n\t},\n\n\tgetSegment: function() {\n\t\tvar segment = this._segment;\n\t\tif (!segment) {\n\t\t\tvar curve = this.getCurve(),\n\t\t\t\ttime = this.getTime();\n\t\t\tif (time === 0) {\n\t\t\t\tsegment = curve._segment1;\n\t\t\t} else if (time === 1) {\n\t\t\t\tsegment = curve._segment2;\n\t\t\t} else if (time != null) {\n\t\t\t\tsegment = curve.getPartLength(0, time)\n\t\t\t\t\t< curve.getPartLength(time, 1)\n\t\t\t\t\t\t? curve._segment1\n\t\t\t\t\t\t: curve._segment2;\n\t\t\t}\n\t\t\tthis._segment = segment;\n\t\t}\n\t\treturn segment;\n\t},\n\n\tgetCurve: function() {\n\t\tvar path = this._path,\n\t\t\tthat = this;\n\t\tif (path && path._version !== this._version) {\n\t\t\tthis._time = this._offset = this._curveOffset = this._curve = null;\n\t\t}\n\n\t\tfunction trySegment(segment) {\n\t\t\tvar curve = segment && segment.getCurve();\n\t\t\tif (curve && (that._time = curve.getTimeOf(that._point)) != null) {\n\t\t\t\tthat._setCurve(curve);\n\t\t\t\treturn curve;\n\t\t\t}\n\t\t}\n\n\t\treturn this._curve\n\t\t\t|| trySegment(this._segment)\n\t\t\t|| trySegment(this._segment1)\n\t\t\t|| trySegment(this._segment2.getPrevious());\n\t},\n\n\tgetPath: function() {\n\t\tvar curve = this.getCurve();\n\t\treturn curve && curve._path;\n\t},\n\n\tgetIndex: function() {\n\t\tvar curve = this.getCurve();\n\t\treturn curve && curve.getIndex();\n\t},\n\n\tgetTime: function() {\n\t\tvar curve = this.getCurve(),\n\t\t\ttime = this._time;\n\t\treturn curve && time == null\n\t\t\t? this._time = curve.getTimeOf(this._point)\n\t\t\t: time;\n\t},\n\n\tgetParameter: '#getTime',\n\n\tgetPoint: function() {\n\t\treturn this._point;\n\t},\n\n\tgetOffset: function() {\n\t\tvar offset = this._offset;\n\t\tif (offset == null) {\n\t\t\toffset = 0;\n\t\t\tvar path = this.getPath(),\n\t\t\t\tindex = this.getIndex();\n\t\t\tif (path && index != null) {\n\t\t\t\tvar curves = path.getCurves();\n\t\t\t\tfor (var i = 0; i < index; i++)\n\t\t\t\t\toffset += curves[i].getLength();\n\t\t\t}\n\t\t\tthis._offset = offset += this.getCurveOffset();\n\t\t}\n\t\treturn offset;\n\t},\n\n\tgetCurveOffset: function() {\n\t\tvar offset = this._curveOffset;\n\t\tif (offset == null) {\n\t\t\tvar curve = this.getCurve(),\n\t\t\t\ttime = this.getTime();\n\t\t\tthis._curveOffset = offset = time != null && curve\n\t\t\t\t\t&& curve.getPartLength(0, time);\n\t\t}\n\t\treturn offset;\n\t},\n\n\tgetIntersection: function() {\n\t\treturn this._intersection;\n\t},\n\n\tgetDistance: function() {\n\t\treturn this._distance;\n\t},\n\n\tdivide: function() {\n\t\tvar curve = this.getCurve(),\n\t\t\tres = curve && curve.divideAtTime(this.getTime());\n\t\tif (res) {\n\t\t\tthis._setSegment(res._segment1);\n\t\t}\n\t\treturn res;\n\t},\n\n\tsplit: function() {\n\t\tvar curve = this.getCurve(),\n\t\t\tpath = curve._path,\n\t\t\tres = curve && curve.splitAtTime(this.getTime());\n\t\tif (res) {\n\t\t\tthis._setSegment(path.getLastSegment());\n\t\t}\n\t\treturn res;\n\t},\n\n\tequals: function(loc, _ignoreOther) {\n\t\tvar res = this === loc;\n\t\tif (!res && loc instanceof CurveLocation) {\n\t\t\tvar c1 = this.getCurve(),\n\t\t\t\tc2 = loc.getCurve(),\n\t\t\t\tp1 = c1._path,\n\t\t\t\tp2 = c2._path;\n\t\t\tif (p1 === p2) {\n\t\t\t\tvar abs = Math.abs,\n\t\t\t\t\tepsilon = 1e-7,\n\t\t\t\t\tdiff = abs(this.getOffset() - loc.getOffset()),\n\t\t\t\t\ti1 = !_ignoreOther && this._intersection,\n\t\t\t\t\ti2 = !_ignoreOther && loc._intersection;\n\t\t\t\tres = (diff < epsilon\n\t\t\t\t\t\t|| p1 && abs(p1.getLength() - diff) < epsilon)\n\t\t\t\t\t&& (!i1 && !i2 || i1 && i2 && i1.equals(i2, true));\n\t\t\t}\n\t\t}\n\t\treturn res;\n\t},\n\n\ttoString: function() {\n\t\tvar parts = [],\n\t\t\tpoint = this.getPoint(),\n\t\t\tf = Formatter.instance;\n\t\tif (point)\n\t\t\tparts.push('point: ' + point);\n\t\tvar index = this.getIndex();\n\t\tif (index != null)\n\t\t\tparts.push('index: ' + index);\n\t\tvar time = this.getTime();\n\t\tif (time != null)\n\t\t\tparts.push('time: ' + f.number(time));\n\t\tif (this._distance != null)\n\t\t\tparts.push('distance: ' + f.number(this._distance));\n\t\treturn '{ ' + parts.join(', ') + ' }';\n\t},\n\n\tisTouching: function() {\n\t\tvar inter = this._intersection;\n\t\tif (inter && this.getTangent().isCollinear(inter.getTangent())) {\n\t\t\tvar curve1 = this.getCurve(),\n\t\t\t\tcurve2 = inter.getCurve();\n\t\t\treturn !(curve1.isStraight() && curve2.isStraight()\n\t\t\t\t\t&& curve1.getLine().intersect(curve2.getLine()));\n\t\t}\n\t\treturn false;\n\t},\n\n\tisCrossing: function() {\n\t\tvar inter = this._intersection;\n\t\tif (!inter)\n\t\t\treturn false;\n\t\tvar t1 = this.getTime(),\n\t\t\tt2 = inter.getTime(),\n\t\t\ttMin = 1e-8,\n\t\t\ttMax = 1 - tMin,\n\t\t\tt1Inside = t1 >= tMin && t1 <= tMax,\n\t\t\tt2Inside = t2 >= tMin && t2 <= tMax;\n\t\tif (t1Inside && t2Inside)\n\t\t\treturn !this.isTouching();\n\t\tvar c2 = this.getCurve(),\n\t\t\tc1 = c2 && t1 < tMin ? c2.getPrevious() : c2,\n\t\t\tc4 = inter.getCurve(),\n\t\t\tc3 = c4 && t2 < tMin ? c4.getPrevious() : c4;\n\t\tif (t1 > tMax)\n\t\t\tc2 = c2.getNext();\n\t\tif (t2 > tMax)\n\t\t\tc4 = c4.getNext();\n\t\tif (!c1 || !c2 || !c3 || !c4)\n\t\t\treturn false;\n\n\t\tvar offsets = [];\n\n\t\tfunction addOffsets(curve, end) {\n\t\t\tvar v = curve.getValues(),\n\t\t\t\troots = Curve.classify(v).roots || Curve.getPeaks(v),\n\t\t\t\tcount = roots.length,\n\t\t\t\toffset = Curve.getLength(v,\n\t\t\t\t\tend && count ? roots[count - 1] : 0,\n\t\t\t\t\t!end && count ? roots[0] : 1);\n\t\t\toffsets.push(count ? offset : offset / 32);\n\t\t}\n\n\t\tfunction isInRange(angle, min, max) {\n\t\t\treturn min < max\n\t\t\t\t\t? angle > min && angle < max\n\t\t\t\t\t: angle > min || angle < max;\n\t\t}\n\n\t\tif (!t1Inside) {\n\t\t\taddOffsets(c1, true);\n\t\t\taddOffsets(c2, false);\n\t\t}\n\t\tif (!t2Inside) {\n\t\t\taddOffsets(c3, true);\n\t\t\taddOffsets(c4, false);\n\t\t}\n\t\tvar pt = this.getPoint(),\n\t\t\toffset = Math.min.apply(Math, offsets),\n\t\t\tv2 = t1Inside ? c2.getTangentAtTime(t1)\n\t\t\t\t\t: c2.getPointAt(offset).subtract(pt),\n\t\t\tv1 = t1Inside ? v2.negate()\n\t\t\t\t\t: c1.getPointAt(-offset).subtract(pt),\n\t\t\tv4 = t2Inside ? c4.getTangentAtTime(t2)\n\t\t\t\t\t: c4.getPointAt(offset).subtract(pt),\n\t\t\tv3 = t2Inside ? v4.negate()\n\t\t\t\t\t: c3.getPointAt(-offset).subtract(pt),\n\t\t\ta1 = v1.getAngle(),\n\t\t\ta2 = v2.getAngle(),\n\t\t\ta3 = v3.getAngle(),\n\t\t\ta4 = v4.getAngle();\n\t\treturn !!(t1Inside\n\t\t\t\t? (isInRange(a1, a3, a4) ^ isInRange(a2, a3, a4)) &&\n\t\t\t\t (isInRange(a1, a4, a3) ^ isInRange(a2, a4, a3))\n\t\t\t\t: (isInRange(a3, a1, a2) ^ isInRange(a4, a1, a2)) &&\n\t\t\t\t (isInRange(a3, a2, a1) ^ isInRange(a4, a2, a1)));\n\t},\n\n\thasOverlap: function() {\n\t\treturn !!this._overlap;\n\t}\n}, Base.each(Curve._evaluateMethods, function(name) {\n\tvar get = name + 'At';\n\tthis[name] = function() {\n\t\tvar curve = this.getCurve(),\n\t\t\ttime = this.getTime();\n\t\treturn time != null && curve && curve[get](time, true);\n\t};\n}, {\n\tpreserve: true\n}),\nnew function() {\n\n\tfunction insert(locations, loc, merge) {\n\t\tvar length = locations.length,\n\t\t\tl = 0,\n\t\t\tr = length - 1;\n\n\t\tfunction search(index, dir) {\n\t\t\tfor (var i = index + dir; i >= -1 && i <= length; i += dir) {\n\t\t\t\tvar loc2 = locations[((i % length) + length) % length];\n\t\t\t\tif (!loc.getPoint().isClose(loc2.getPoint(),\n\t\t\t\t\t\t1e-7))\n\t\t\t\t\tbreak;\n\t\t\t\tif (loc.equals(loc2))\n\t\t\t\t\treturn loc2;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\twhile (l <= r) {\n\t\t\tvar m = (l + r) >>> 1,\n\t\t\t\tloc2 = locations[m],\n\t\t\t\tfound;\n\t\t\tif (merge && (found = loc.equals(loc2) ? loc2\n\t\t\t\t\t: (search(m, -1) || search(m, 1)))) {\n\t\t\t\tif (loc._overlap) {\n\t\t\t\t\tfound._overlap = found._intersection._overlap = true;\n\t\t\t\t}\n\t\t\t\treturn found;\n\t\t\t}\n\t\tvar path1 = loc.getPath(),\n\t\t\tpath2 = loc2.getPath(),\n\t\t\tdiff = path1 !== path2\n\t\t\t\t? path1._id - path2._id\n\t\t\t\t: (loc.getIndex() + loc.getTime())\n\t\t\t\t- (loc2.getIndex() + loc2.getTime());\n\t\t\tif (diff < 0) {\n\t\t\t\tr = m - 1;\n\t\t\t} else {\n\t\t\t\tl = m + 1;\n\t\t\t}\n\t\t}\n\t\tlocations.splice(l, 0, loc);\n\t\treturn loc;\n\t}\n\n\treturn { statics: {\n\t\tinsert: insert,\n\n\t\texpand: function(locations) {\n\t\t\tvar expanded = locations.slice();\n\t\t\tfor (var i = locations.length - 1; i >= 0; i--) {\n\t\t\t\tinsert(expanded, locations[i]._intersection, false);\n\t\t\t}\n\t\t\treturn expanded;\n\t\t}\n\t}};\n});\n\nvar PathItem = Item.extend({\n\t_class: 'PathItem',\n\t_selectBounds: false,\n\t_canScaleStroke: true,\n\tbeans: true,\n\n\tinitialize: function PathItem() {\n\t},\n\n\tstatics: {\n\t\tcreate: function(arg) {\n\t\t\tvar data,\n\t\t\t\tsegments,\n\t\t\t\tcompound;\n\t\t\tif (Base.isPlainObject(arg)) {\n\t\t\t\tsegments = arg.segments;\n\t\t\t\tdata = arg.pathData;\n\t\t\t} else if (Array.isArray(arg)) {\n\t\t\t\tsegments = arg;\n\t\t\t} else if (typeof arg === 'string') {\n\t\t\t\tdata = arg;\n\t\t\t}\n\t\t\tif (segments) {\n\t\t\t\tvar first = segments[0];\n\t\t\t\tcompound = first && Array.isArray(first[0]);\n\t\t\t} else if (data) {\n\t\t\t\tcompound = (data.match(/m/gi) || []).length > 1\n\t\t\t\t\t\t|| /z\\s*\\S+/i.test(data);\n\t\t\t}\n\t\t\tvar ctor = compound ? CompoundPath : Path;\n\t\t\treturn new ctor(arg);\n\t\t}\n\t},\n\n\t_asPathItem: function() {\n\t\treturn this;\n\t},\n\n\tisClockwise: function() {\n\t\treturn this.getArea() >= 0;\n\t},\n\n\tsetClockwise: function(clockwise) {\n\t\tif (this.isClockwise() != (clockwise = !!clockwise))\n\t\t\tthis.reverse();\n\t},\n\n\tsetPathData: function(data) {\n\n\t\tvar parts = data && data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),\n\t\t\tcoords,\n\t\t\trelative = false,\n\t\t\tprevious,\n\t\t\tcontrol,\n\t\t\tcurrent = new Point(),\n\t\t\tstart = new Point();\n\n\t\tfunction getCoord(index, coord) {\n\t\t\tvar val = +coords[index];\n\t\t\tif (relative)\n\t\t\t\tval += current[coord];\n\t\t\treturn val;\n\t\t}\n\n\t\tfunction getPoint(index) {\n\t\t\treturn new Point(\n\t\t\t\tgetCoord(index, 'x'),\n\t\t\t\tgetCoord(index + 1, 'y')\n\t\t\t);\n\t\t}\n\n\t\tthis.clear();\n\n\t\tfor (var i = 0, l = parts && parts.length; i < l; i++) {\n\t\t\tvar part = parts[i],\n\t\t\t\tcommand = part[0],\n\t\t\t\tlower = command.toLowerCase();\n\t\t\tcoords = part.match(/[+-]?(?:\\d*\\.\\d+|\\d+\\.?)(?:[eE][+-]?\\d+)?/g);\n\t\t\tvar length = coords && coords.length;\n\t\t\trelative = command === lower;\n\t\t\tif (previous === 'z' && !/[mz]/.test(lower))\n\t\t\t\tthis.moveTo(current);\n\t\t\tswitch (lower) {\n\t\t\tcase 'm':\n\t\t\tcase 'l':\n\t\t\t\tvar move = lower === 'm';\n\t\t\t\tfor (var j = 0; j < length; j += 2) {\n\t\t\t\t\tthis[move ? 'moveTo' : 'lineTo'](current = getPoint(j));\n\t\t\t\t\tif (move) {\n\t\t\t\t\t\tstart = current;\n\t\t\t\t\t\tmove = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontrol = current;\n\t\t\t\tbreak;\n\t\t\tcase 'h':\n\t\t\tcase 'v':\n\t\t\t\tvar coord = lower === 'h' ? 'x' : 'y';\n\t\t\t\tcurrent = current.clone();\n\t\t\t\tfor (var j = 0; j < length; j++) {\n\t\t\t\t\tcurrent[coord] = getCoord(j, coord);\n\t\t\t\t\tthis.lineTo(current);\n\t\t\t\t}\n\t\t\t\tcontrol = current;\n\t\t\t\tbreak;\n\t\t\tcase 'c':\n\t\t\t\tfor (var j = 0; j < length; j += 6) {\n\t\t\t\t\tthis.cubicCurveTo(\n\t\t\t\t\t\t\tgetPoint(j),\n\t\t\t\t\t\t\tcontrol = getPoint(j + 2),\n\t\t\t\t\t\t\tcurrent = getPoint(j + 4));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tfor (var j = 0; j < length; j += 4) {\n\t\t\t\t\tthis.cubicCurveTo(\n\t\t\t\t\t\t\t/[cs]/.test(previous)\n\t\t\t\t\t\t\t\t\t? current.multiply(2).subtract(control)\n\t\t\t\t\t\t\t\t\t: current,\n\t\t\t\t\t\t\tcontrol = getPoint(j),\n\t\t\t\t\t\t\tcurrent = getPoint(j + 2));\n\t\t\t\t\tprevious = lower;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'q':\n\t\t\t\tfor (var j = 0; j < length; j += 4) {\n\t\t\t\t\tthis.quadraticCurveTo(\n\t\t\t\t\t\t\tcontrol = getPoint(j),\n\t\t\t\t\t\t\tcurrent = getPoint(j + 2));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 't':\n\t\t\t\tfor (var j = 0; j < length; j += 2) {\n\t\t\t\t\tthis.quadraticCurveTo(\n\t\t\t\t\t\t\tcontrol = (/[qt]/.test(previous)\n\t\t\t\t\t\t\t\t\t? current.multiply(2).subtract(control)\n\t\t\t\t\t\t\t\t\t: current),\n\t\t\t\t\t\t\tcurrent = getPoint(j));\n\t\t\t\t\tprevious = lower;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'a':\n\t\t\t\tfor (var j = 0; j < length; j += 7) {\n\t\t\t\t\tthis.arcTo(current = getPoint(j + 5),\n\t\t\t\t\t\t\tnew Size(+coords[j], +coords[j + 1]),\n\t\t\t\t\t\t\t+coords[j + 2], +coords[j + 4], +coords[j + 3]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'z':\n\t\t\t\tthis.closePath(1e-12);\n\t\t\t\tcurrent = start;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprevious = lower;\n\t\t}\n\t},\n\n\t_canComposite: function() {\n\t\treturn !(this.hasFill() && this.hasStroke());\n\t},\n\n\t_contains: function(point) {\n\t\tvar winding = point.isInside(\n\t\t\t\tthis.getBounds({ internal: true, handle: true }))\n\t\t\t\t\t? this._getWinding(point)\n\t\t\t\t\t: {};\n\t\treturn winding.onPath || !!(this.getFillRule() === 'evenodd'\n\t\t\t\t? winding.windingL & 1 || winding.windingR & 1\n\t\t\t\t: winding.winding);\n\t},\n\n\tgetIntersections: function(path, include, _matrix, _returnFirst) {\n\t\tvar self = this === path || !path,\n\t\t\tmatrix1 = this._matrix._orNullIfIdentity(),\n\t\t\tmatrix2 = self ? matrix1\n\t\t\t\t: (_matrix || path._matrix)._orNullIfIdentity();\n\t\treturn self || this.getBounds(matrix1).intersects(\n\t\t\t\tpath.getBounds(matrix2), 1e-12)\n\t\t\t\t? Curve.getIntersections(\n\t\t\t\t\t\tthis.getCurves(), !self && path.getCurves(), include,\n\t\t\t\t\t\tmatrix1, matrix2, _returnFirst)\n\t\t\t\t: [];\n\t},\n\n\tgetCrossings: function(path) {\n\t\treturn this.getIntersections(path, function(inter) {\n\t\t\treturn inter.isCrossing();\n\t\t});\n\t},\n\n\tgetNearestLocation: function() {\n\t\tvar point = Point.read(arguments),\n\t\t\tcurves = this.getCurves(),\n\t\t\tminDist = Infinity,\n\t\t\tminLoc = null;\n\t\tfor (var i = 0, l = curves.length; i < l; i++) {\n\t\t\tvar loc = curves[i].getNearestLocation(point);\n\t\t\tif (loc._distance < minDist) {\n\t\t\t\tminDist = loc._distance;\n\t\t\t\tminLoc = loc;\n\t\t\t}\n\t\t}\n\t\treturn minLoc;\n\t},\n\n\tgetNearestPoint: function() {\n\t\tvar loc = this.getNearestLocation.apply(this, arguments);\n\t\treturn loc ? loc.getPoint() : loc;\n\t},\n\n\tinterpolate: function(from, to, factor) {\n\t\tvar isPath = !this._children,\n\t\t\tname = isPath ? '_segments' : '_children',\n\t\t\titemsFrom = from[name],\n\t\t\titemsTo = to[name],\n\t\t\titems = this[name];\n\t\tif (!itemsFrom || !itemsTo || itemsFrom.length !== itemsTo.length) {\n\t\t\tthrow new Error('Invalid operands in interpolate() call: ' +\n\t\t\t\t\tfrom + ', ' + to);\n\t\t}\n\t\tvar current = items.length,\n\t\t\tlength = itemsTo.length;\n\t\tif (current < length) {\n\t\t\tvar ctor = isPath ? Segment : Path;\n\t\t\tfor (var i = current; i < length; i++) {\n\t\t\t\tthis.add(new ctor());\n\t\t\t}\n\t\t} else if (current > length) {\n\t\t\tthis[isPath ? 'removeSegments' : 'removeChildren'](length, current);\n\t\t}\n\t\tfor (var i = 0; i < length; i++) {\n\t\t\titems[i].interpolate(itemsFrom[i], itemsTo[i], factor);\n\t\t}\n\t\tif (isPath) {\n\t\t\tthis.setClosed(from._closed);\n\t\t\tthis._changed(9);\n\t\t}\n\t},\n\n\tcompare: function(path) {\n\t\tvar ok = false;\n\t\tif (path) {\n\t\t\tvar paths1 = this._children || [this],\n\t\t\t\tpaths2 = path._children ? path._children.slice() : [path],\n\t\t\t\tlength1 = paths1.length,\n\t\t\t\tlength2 = paths2.length,\n\t\t\t\tmatched = [],\n\t\t\t\tcount = 0;\n\t\t\tok = true;\n\t\t\tvar boundsOverlaps = CollisionDetection.findItemBoundsCollisions(paths1, paths2, Numerical.GEOMETRIC_EPSILON);\n\t\t\tfor (var i1 = length1 - 1; i1 >= 0 && ok; i1--) {\n\t\t\t\tvar path1 = paths1[i1];\n\t\t\t\tok = false;\n\t\t\t\tvar pathBoundsOverlaps = boundsOverlaps[i1];\n\t\t\t\tif (pathBoundsOverlaps) {\n\t\t\t\t\tfor (var i2 = pathBoundsOverlaps.length - 1; i2 >= 0 && !ok; i2--) {\n\t\t\t\t\t\tif (path1.compare(paths2[pathBoundsOverlaps[i2]])) {\n\t\t\t\t\t\t\tif (!matched[pathBoundsOverlaps[i2]]) {\n\t\t\t\t\t\t\t\tmatched[pathBoundsOverlaps[i2]] = true;\n\t\t\t\t\t\t\t\tcount++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tok = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tok = ok && count === length2;\n\t\t}\n\t\treturn ok;\n\t},\n\n});\n\nvar Path = PathItem.extend({\n\t_class: 'Path',\n\t_serializeFields: {\n\t\tsegments: [],\n\t\tclosed: false\n\t},\n\n\tinitialize: function Path(arg) {\n\t\tthis._closed = false;\n\t\tthis._segments = [];\n\t\tthis._version = 0;\n\t\tvar args = arguments,\n\t\t\tsegments = Array.isArray(arg)\n\t\t\t? typeof arg[0] === 'object'\n\t\t\t\t? arg\n\t\t\t\t: args\n\t\t\t: arg && (arg.size === undefined && (arg.x !== undefined\n\t\t\t\t\t|| arg.point !== undefined))\n\t\t\t\t? args\n\t\t\t\t: null;\n\t\tif (segments && segments.length > 0) {\n\t\t\tthis.setSegments(segments);\n\t\t} else {\n\t\t\tthis._curves = undefined;\n\t\t\tthis._segmentSelection = 0;\n\t\t\tif (!segments && typeof arg === 'string') {\n\t\t\t\tthis.setPathData(arg);\n\t\t\t\targ = null;\n\t\t\t}\n\t\t}\n\t\tthis._initialize(!segments && arg);\n\t},\n\n\t_equals: function(item) {\n\t\treturn this._closed === item._closed\n\t\t\t\t&& Base.equals(this._segments, item._segments);\n\t},\n\n\tcopyContent: function(source) {\n\t\tthis.setSegments(source._segments);\n\t\tthis._closed = source._closed;\n\t},\n\n\t_changed: function _changed(flags) {\n\t\t_changed.base.call(this, flags);\n\t\tif (flags & 8) {\n\t\t\tthis._length = this._area = undefined;\n\t\t\tif (flags & 32) {\n\t\t\t\tthis._version++;\n\t\t\t} else if (this._curves) {\n\t\t\t for (var i = 0, l = this._curves.length; i < l; i++)\n\t\t\t\t\tthis._curves[i]._changed();\n\t\t\t}\n\t\t} else if (flags & 64) {\n\t\t\tthis._bounds = undefined;\n\t\t}\n\t},\n\n\tgetStyle: function() {\n\t\tvar parent = this._parent;\n\t\treturn (parent instanceof CompoundPath ? parent : this)._style;\n\t},\n\n\tgetSegments: function() {\n\t\treturn this._segments;\n\t},\n\n\tsetSegments: function(segments) {\n\t\tvar fullySelected = this.isFullySelected(),\n\t\t\tlength = segments && segments.length;\n\t\tthis._segments.length = 0;\n\t\tthis._segmentSelection = 0;\n\t\tthis._curves = undefined;\n\t\tif (length) {\n\t\t\tvar last = segments[length - 1];\n\t\t\tif (typeof last === 'boolean') {\n\t\t\t\tthis.setClosed(last);\n\t\t\t\tlength--;\n\t\t\t}\n\t\t\tthis._add(Segment.readList(segments, 0, {}, length));\n\t\t}\n\t\tif (fullySelected)\n\t\t\tthis.setFullySelected(true);\n\t},\n\n\tgetFirstSegment: function() {\n\t\treturn this._segments[0];\n\t},\n\n\tgetLastSegment: function() {\n\t\treturn this._segments[this._segments.length - 1];\n\t},\n\n\tgetCurves: function() {\n\t\tvar curves = this._curves,\n\t\t\tsegments = this._segments;\n\t\tif (!curves) {\n\t\t\tvar length = this._countCurves();\n\t\t\tcurves = this._curves = new Array(length);\n\t\t\tfor (var i = 0; i < length; i++)\n\t\t\t\tcurves[i] = new Curve(this, segments[i],\n\t\t\t\t\tsegments[i + 1] || segments[0]);\n\t\t}\n\t\treturn curves;\n\t},\n\n\tgetFirstCurve: function() {\n\t\treturn this.getCurves()[0];\n\t},\n\n\tgetLastCurve: function() {\n\t\tvar curves = this.getCurves();\n\t\treturn curves[curves.length - 1];\n\t},\n\n\tisClosed: function() {\n\t\treturn this._closed;\n\t},\n\n\tsetClosed: function(closed) {\n\t\tif (this._closed != (closed = !!closed)) {\n\t\t\tthis._closed = closed;\n\t\t\tif (this._curves) {\n\t\t\t\tvar length = this._curves.length = this._countCurves();\n\t\t\t\tif (closed)\n\t\t\t\t\tthis._curves[length - 1] = new Curve(this,\n\t\t\t\t\t\tthis._segments[length - 1], this._segments[0]);\n\t\t\t}\n\t\t\tthis._changed(41);\n\t\t}\n\t}\n}, {\n\tbeans: true,\n\n\tgetPathData: function(_matrix, _precision) {\n\t\tvar segments = this._segments,\n\t\t\tlength = segments.length,\n\t\t\tf = new Formatter(_precision),\n\t\t\tcoords = new Array(6),\n\t\t\tfirst = true,\n\t\t\tcurX, curY,\n\t\t\tprevX, prevY,\n\t\t\tinX, inY,\n\t\t\toutX, outY,\n\t\t\tparts = [];\n\n\t\tfunction addSegment(segment, skipLine) {\n\t\t\tsegment._transformCoordinates(_matrix, coords);\n\t\t\tcurX = coords[0];\n\t\t\tcurY = coords[1];\n\t\t\tif (first) {\n\t\t\t\tparts.push('M' + f.pair(curX, curY));\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tinX = coords[2];\n\t\t\t\tinY = coords[3];\n\t\t\t\tif (inX === curX && inY === curY\n\t\t\t\t\t\t&& outX === prevX && outY === prevY) {\n\t\t\t\t\tif (!skipLine) {\n\t\t\t\t\t\tvar dx = curX - prevX,\n\t\t\t\t\t\t\tdy = curY - prevY;\n\t\t\t\t\t\tparts.push(\n\t\t\t\t\t\t\t dx === 0 ? 'v' + f.number(dy)\n\t\t\t\t\t\t\t: dy === 0 ? 'h' + f.number(dx)\n\t\t\t\t\t\t\t: 'l' + f.pair(dx, dy));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tparts.push('c' + f.pair(outX - prevX, outY - prevY)\n\t\t\t\t\t\t\t + ' ' + f.pair( inX - prevX, inY - prevY)\n\t\t\t\t\t\t\t + ' ' + f.pair(curX - prevX, curY - prevY));\n\t\t\t\t}\n\t\t\t}\n\t\t\tprevX = curX;\n\t\t\tprevY = curY;\n\t\t\toutX = coords[4];\n\t\t\toutY = coords[5];\n\t\t}\n\n\t\tif (!length)\n\t\t\treturn '';\n\n\t\tfor (var i = 0; i < length; i++)\n\t\t\taddSegment(segments[i]);\n\t\tif (this._closed && length > 0) {\n\t\t\taddSegment(segments[0], true);\n\t\t\tparts.push('z');\n\t\t}\n\t\treturn parts.join('');\n\t},\n\n\tisEmpty: function() {\n\t\treturn !this._segments.length;\n\t},\n\n\t_transformContent: function(matrix) {\n\t\tvar segments = this._segments,\n\t\t\tcoords = new Array(6);\n\t\tfor (var i = 0, l = segments.length; i < l; i++)\n\t\t\tsegments[i]._transformCoordinates(matrix, coords, true);\n\t\treturn true;\n\t},\n\n\t_add: function(segs, index) {\n\t\tvar segments = this._segments,\n\t\t\tcurves = this._curves,\n\t\t\tamount = segs.length,\n\t\t\tappend = index == null,\n\t\t\tindex = append ? segments.length : index;\n\t\tfor (var i = 0; i < amount; i++) {\n\t\t\tvar segment = segs[i];\n\t\t\tif (segment._path)\n\t\t\t\tsegment = segs[i] = segment.clone();\n\t\t\tsegment._path = this;\n\t\t\tsegment._index = index + i;\n\t\t\tif (segment._selection)\n\t\t\t\tthis._updateSelection(segment, 0, segment._selection);\n\t\t}\n\t\tif (append) {\n\t\t\tBase.push(segments, segs);\n\t\t} else {\n\t\t\tsegments.splice.apply(segments, [index, 0].concat(segs));\n\t\t\tfor (var i = index + amount, l = segments.length; i < l; i++)\n\t\t\t\tsegments[i]._index = i;\n\t\t}\n\t\tif (curves) {\n\t\t\tvar total = this._countCurves(),\n\t\t\t\tstart = index > 0 && index + amount - 1 === total ? index - 1\n\t\t\t\t\t: index,\n\t\t\t\tinsert = start,\n\t\t\t\tend = Math.min(start + amount, total);\n\t\t\tif (segs._curves) {\n\t\t\t\tcurves.splice.apply(curves, [start, 0].concat(segs._curves));\n\t\t\t\tinsert += segs._curves.length;\n\t\t\t}\n\t\t\tfor (var i = insert; i < end; i++)\n\t\t\t\tcurves.splice(i, 0, new Curve(this, null, null));\n\t\t\tthis._adjustCurves(start, end);\n\t\t}\n\t\tthis._changed(41);\n\t\treturn segs;\n\t},\n\n\t_adjustCurves: function(start, end) {\n\t\tvar segments = this._segments,\n\t\t\tcurves = this._curves,\n\t\t\tcurve;\n\t\tfor (var i = start; i < end; i++) {\n\t\t\tcurve = curves[i];\n\t\t\tcurve._path = this;\n\t\t\tcurve._segment1 = segments[i];\n\t\t\tcurve._segment2 = segments[i + 1] || segments[0];\n\t\t\tcurve._changed();\n\t\t}\n\t\tif (curve = curves[this._closed && !start ? segments.length - 1\n\t\t\t\t: start - 1]) {\n\t\t\tcurve._segment2 = segments[start] || segments[0];\n\t\t\tcurve._changed();\n\t\t}\n\t\tif (curve = curves[end]) {\n\t\t\tcurve._segment1 = segments[end];\n\t\t\tcurve._changed();\n\t\t}\n\t},\n\n\t_countCurves: function() {\n\t\tvar length = this._segments.length;\n\t\treturn !this._closed && length > 0 ? length - 1 : length;\n\t},\n\n\tadd: function(segment1 ) {\n\t\tvar args = arguments;\n\t\treturn args.length > 1 && typeof segment1 !== 'number'\n\t\t\t? this._add(Segment.readList(args))\n\t\t\t: this._add([ Segment.read(args) ])[0];\n\t},\n\n\tinsert: function(index, segment1 ) {\n\t\tvar args = arguments;\n\t\treturn args.length > 2 && typeof segment1 !== 'number'\n\t\t\t? this._add(Segment.readList(args, 1), index)\n\t\t\t: this._add([ Segment.read(args, 1) ], index)[0];\n\t},\n\n\taddSegment: function() {\n\t\treturn this._add([ Segment.read(arguments) ])[0];\n\t},\n\n\tinsertSegment: function(index ) {\n\t\treturn this._add([ Segment.read(arguments, 1) ], index)[0];\n\t},\n\n\taddSegments: function(segments) {\n\t\treturn this._add(Segment.readList(segments));\n\t},\n\n\tinsertSegments: function(index, segments) {\n\t\treturn this._add(Segment.readList(segments), index);\n\t},\n\n\tremoveSegment: function(index) {\n\t\treturn this.removeSegments(index, index + 1)[0] || null;\n\t},\n\n\tremoveSegments: function(start, end, _includeCurves) {\n\t\tstart = start || 0;\n\t\tend = Base.pick(end, this._segments.length);\n\t\tvar segments = this._segments,\n\t\t\tcurves = this._curves,\n\t\t\tcount = segments.length,\n\t\t\tremoved = segments.splice(start, end - start),\n\t\t\tamount = removed.length;\n\t\tif (!amount)\n\t\t\treturn removed;\n\t\tfor (var i = 0; i < amount; i++) {\n\t\t\tvar segment = removed[i];\n\t\t\tif (segment._selection)\n\t\t\t\tthis._updateSelection(segment, segment._selection, 0);\n\t\t\tsegment._index = segment._path = null;\n\t\t}\n\t\tfor (var i = start, l = segments.length; i < l; i++)\n\t\t\tsegments[i]._index = i;\n\t\tif (curves) {\n\t\t\tvar index = start > 0 && end === count + (this._closed ? 1 : 0)\n\t\t\t\t\t? start - 1\n\t\t\t\t\t: start,\n\t\t\t\tcurves = curves.splice(index, amount);\n\t\t\tfor (var i = curves.length - 1; i >= 0; i--)\n\t\t\t\tcurves[i]._path = null;\n\t\t\tif (_includeCurves)\n\t\t\t\tremoved._curves = curves.slice(1);\n\t\t\tthis._adjustCurves(index, index);\n\t\t}\n\t\tthis._changed(41);\n\t\treturn removed;\n\t},\n\n\tclear: '#removeSegments',\n\n\thasHandles: function() {\n\t\tvar segments = this._segments;\n\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\tif (segments[i].hasHandles())\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\n\tclearHandles: function() {\n\t\tvar segments = this._segments;\n\t\tfor (var i = 0, l = segments.length; i < l; i++)\n\t\t\tsegments[i].clearHandles();\n\t},\n\n\tgetLength: function() {\n\t\tif (this._length == null) {\n\t\t\tvar curves = this.getCurves(),\n\t\t\t\tlength = 0;\n\t\t\tfor (var i = 0, l = curves.length; i < l; i++)\n\t\t\t\tlength += curves[i].getLength();\n\t\t\tthis._length = length;\n\t\t}\n\t\treturn this._length;\n\t},\n\n\tgetArea: function() {\n\t\tvar area = this._area;\n\t\tif (area == null) {\n\t\t\tvar segments = this._segments,\n\t\t\t\tclosed = this._closed;\n\t\t\tarea = 0;\n\t\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\t\tvar last = i + 1 === l;\n\t\t\t\tarea += Curve.getArea(Curve.getValues(\n\t\t\t\t\t\tsegments[i], segments[last ? 0 : i + 1],\n\t\t\t\t\t\tnull, last && !closed));\n\t\t\t}\n\t\t\tthis._area = area;\n\t\t}\n\t\treturn area;\n\t},\n\n\tisFullySelected: function() {\n\t\tvar length = this._segments.length;\n\t\treturn this.isSelected() && length > 0 && this._segmentSelection\n\t\t\t\t=== length * 7;\n\t},\n\n\tsetFullySelected: function(selected) {\n\t\tif (selected)\n\t\t\tthis._selectSegments(true);\n\t\tthis.setSelected(selected);\n\t},\n\n\tsetSelection: function setSelection(selection) {\n\t\tif (!(selection & 1))\n\t\t\tthis._selectSegments(false);\n\t\tsetSelection.base.call(this, selection);\n\t},\n\n\t_selectSegments: function(selected) {\n\t\tvar segments = this._segments,\n\t\t\tlength = segments.length,\n\t\t\tselection = selected ? 7 : 0;\n\t\tthis._segmentSelection = selection * length;\n\t\tfor (var i = 0; i < length; i++)\n\t\t\tsegments[i]._selection = selection;\n\t},\n\n\t_updateSelection: function(segment, oldSelection, newSelection) {\n\t\tsegment._selection = newSelection;\n\t\tvar selection = this._segmentSelection += newSelection - oldSelection;\n\t\tif (selection > 0)\n\t\t\tthis.setSelected(true);\n\t},\n\n\tdivideAt: function(location) {\n\t\tvar loc = this.getLocationAt(location),\n\t\t\tcurve;\n\t\treturn loc && (curve = loc.getCurve().divideAt(loc.getCurveOffset()))\n\t\t\t\t? curve._segment1\n\t\t\t\t: null;\n\t},\n\n\tsplitAt: function(location) {\n\t\tvar loc = this.getLocationAt(location),\n\t\t\tindex = loc && loc.index,\n\t\t\ttime = loc && loc.time,\n\t\t\ttMin = 1e-8,\n\t\t\ttMax = 1 - tMin;\n\t\tif (time > tMax) {\n\t\t\tindex++;\n\t\t\ttime = 0;\n\t\t}\n\t\tvar curves = this.getCurves();\n\t\tif (index >= 0 && index < curves.length) {\n\t\t\tif (time >= tMin) {\n\t\t\t\tcurves[index++].divideAtTime(time);\n\t\t\t}\n\t\t\tvar segs = this.removeSegments(index, this._segments.length, true),\n\t\t\t\tpath;\n\t\t\tif (this._closed) {\n\t\t\t\tthis.setClosed(false);\n\t\t\t\tpath = this;\n\t\t\t} else {\n\t\t\t\tpath = new Path(Item.NO_INSERT);\n\t\t\t\tpath.insertAbove(this);\n\t\t\t\tpath.copyAttributes(this);\n\t\t\t}\n\t\t\tpath._add(segs, 0);\n\t\t\tthis.addSegment(segs[0]);\n\t\t\treturn path;\n\t\t}\n\t\treturn null;\n\t},\n\n\tsplit: function(index, time) {\n\t\tvar curve,\n\t\t\tlocation = time === undefined ? index\n\t\t\t\t: (curve = this.getCurves()[index])\n\t\t\t\t\t&& curve.getLocationAtTime(time);\n\t\treturn location != null ? this.splitAt(location) : null;\n\t},\n\n\tjoin: function(path, tolerance) {\n\t\tvar epsilon = tolerance || 0;\n\t\tif (path && path !== this) {\n\t\t\tvar segments = path._segments,\n\t\t\t\tlast1 = this.getLastSegment(),\n\t\t\t\tlast2 = path.getLastSegment();\n\t\t\tif (!last2)\n\t\t\t\treturn this;\n\t\t\tif (last1 && last1._point.isClose(last2._point, epsilon))\n\t\t\t\tpath.reverse();\n\t\t\tvar first2 = path.getFirstSegment();\n\t\t\tif (last1 && last1._point.isClose(first2._point, epsilon)) {\n\t\t\t\tlast1.setHandleOut(first2._handleOut);\n\t\t\t\tthis._add(segments.slice(1));\n\t\t\t} else {\n\t\t\t\tvar first1 = this.getFirstSegment();\n\t\t\t\tif (first1 && first1._point.isClose(first2._point, epsilon))\n\t\t\t\t\tpath.reverse();\n\t\t\t\tlast2 = path.getLastSegment();\n\t\t\t\tif (first1 && first1._point.isClose(last2._point, epsilon)) {\n\t\t\t\t\tfirst1.setHandleIn(last2._handleIn);\n\t\t\t\t\tthis._add(segments.slice(0, segments.length - 1), 0);\n\t\t\t\t} else {\n\t\t\t\t\tthis._add(segments.slice());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (path._closed)\n\t\t\t\tthis._add([segments[0]]);\n\t\t\tpath.remove();\n\t\t}\n\t\tvar first = this.getFirstSegment(),\n\t\t\tlast = this.getLastSegment();\n\t\tif (first !== last && first._point.isClose(last._point, epsilon)) {\n\t\t\tfirst.setHandleIn(last._handleIn);\n\t\t\tlast.remove();\n\t\t\tthis.setClosed(true);\n\t\t}\n\t\treturn this;\n\t},\n\n\treduce: function(options) {\n\t\tvar curves = this.getCurves(),\n\t\t\tsimplify = options && options.simplify,\n\t\t\ttolerance = simplify ? 1e-7 : 0;\n\t\tfor (var i = curves.length - 1; i >= 0; i--) {\n\t\t\tvar curve = curves[i];\n\t\t\tif (!curve.hasHandles() && (!curve.hasLength(tolerance)\n\t\t\t\t\t|| simplify && curve.isCollinear(curve.getNext())))\n\t\t\t\tcurve.remove();\n\t\t}\n\t\treturn this;\n\t},\n\n\treverse: function() {\n\t\tthis._segments.reverse();\n\t\tfor (var i = 0, l = this._segments.length; i < l; i++) {\n\t\t\tvar segment = this._segments[i];\n\t\t\tvar handleIn = segment._handleIn;\n\t\t\tsegment._handleIn = segment._handleOut;\n\t\t\tsegment._handleOut = handleIn;\n\t\t\tsegment._index = i;\n\t\t}\n\t\tthis._curves = null;\n\t\tthis._changed(9);\n\t},\n\n\tflatten: function(flatness) {\n\t\tvar flattener = new PathFlattener(this, flatness || 0.25, 256, true),\n\t\t\tparts = flattener.parts,\n\t\t\tlength = parts.length,\n\t\t\tsegments = [];\n\t\tfor (var i = 0; i < length; i++) {\n\t\t\tsegments.push(new Segment(parts[i].curve.slice(0, 2)));\n\t\t}\n\t\tif (!this._closed && length > 0) {\n\t\t\tsegments.push(new Segment(parts[length - 1].curve.slice(6)));\n\t\t}\n\t\tthis.setSegments(segments);\n\t},\n\n\tsimplify: function(tolerance) {\n\t\tvar segments = new PathFitter(this).fit(tolerance || 2.5);\n\t\tif (segments)\n\t\t\tthis.setSegments(segments);\n\t\treturn !!segments;\n\t},\n\n\tsmooth: function(options) {\n\t\tvar that = this,\n\t\t\topts = options || {},\n\t\t\ttype = opts.type || 'asymmetric',\n\t\t\tsegments = this._segments,\n\t\t\tlength = segments.length,\n\t\t\tclosed = this._closed;\n\n\t\tfunction getIndex(value, _default) {\n\t\t\tvar index = value && value.index;\n\t\t\tif (index != null) {\n\t\t\t\tvar path = value.path;\n\t\t\t\tif (path && path !== that)\n\t\t\t\t\tthrow new Error(value._class + ' ' + index + ' of ' + path\n\t\t\t\t\t\t\t+ ' is not part of ' + that);\n\t\t\t\tif (_default && value instanceof Curve)\n\t\t\t\t\tindex++;\n\t\t\t} else {\n\t\t\t\tindex = typeof value === 'number' ? value : _default;\n\t\t\t}\n\t\t\treturn Math.min(index < 0 && closed\n\t\t\t\t\t? index % length\n\t\t\t\t\t: index < 0 ? index + length : index, length - 1);\n\t\t}\n\n\t\tvar loop = closed && opts.from === undefined && opts.to === undefined,\n\t\t\tfrom = getIndex(opts.from, 0),\n\t\t\tto = getIndex(opts.to, length - 1);\n\n\t\tif (from > to) {\n\t\t\tif (closed) {\n\t\t\t\tfrom -= length;\n\t\t\t} else {\n\t\t\t\tvar tmp = from;\n\t\t\t\tfrom = to;\n\t\t\t\tto = tmp;\n\t\t\t}\n\t\t}\n\t\tif (/^(?:asymmetric|continuous)$/.test(type)) {\n\t\t\tvar asymmetric = type === 'asymmetric',\n\t\t\t\tmin = Math.min,\n\t\t\t\tamount = to - from + 1,\n\t\t\t\tn = amount - 1,\n\t\t\t\tpadding = loop ? min(amount, 4) : 1,\n\t\t\t\tpaddingLeft = padding,\n\t\t\t\tpaddingRight = padding,\n\t\t\t\tknots = [];\n\t\t\tif (!closed) {\n\t\t\t\tpaddingLeft = min(1, from);\n\t\t\t\tpaddingRight = min(1, length - to - 1);\n\t\t\t}\n\t\t\tn += paddingLeft + paddingRight;\n\t\t\tif (n <= 1)\n\t\t\t\treturn;\n\t\t\tfor (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {\n\t\t\t\tknots[i] = segments[(j < 0 ? j + length : j) % length]._point;\n\t\t\t}\n\n\t\t\tvar x = knots[0]._x + 2 * knots[1]._x,\n\t\t\t\ty = knots[0]._y + 2 * knots[1]._y,\n\t\t\t\tf = 2,\n\t\t\t\tn_1 = n - 1,\n\t\t\t\trx = [x],\n\t\t\t\try = [y],\n\t\t\t\trf = [f],\n\t\t\t\tpx = [],\n\t\t\t\tpy = [];\n\t\t\tfor (var i = 1; i < n; i++) {\n\t\t\t\tvar internal = i < n_1,\n\t\t\t\t\ta = internal ? 1 : asymmetric ? 1 : 2,\n\t\t\t\t\tb = internal ? 4 : asymmetric ? 2 : 7,\n\t\t\t\t\tu = internal ? 4 : asymmetric ? 3 : 8,\n\t\t\t\t\tv = internal ? 2 : asymmetric ? 0 : 1,\n\t\t\t\t\tm = a / f;\n\t\t\t\tf = rf[i] = b - m;\n\t\t\t\tx = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;\n\t\t\t\ty = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;\n\t\t\t}\n\n\t\t\tpx[n_1] = rx[n_1] / rf[n_1];\n\t\t\tpy[n_1] = ry[n_1] / rf[n_1];\n\t\t\tfor (var i = n - 2; i >= 0; i--) {\n\t\t\t\tpx[i] = (rx[i] - px[i + 1]) / rf[i];\n\t\t\t\tpy[i] = (ry[i] - py[i + 1]) / rf[i];\n\t\t\t}\n\t\t\tpx[n] = (3 * knots[n]._x - px[n_1]) / 2;\n\t\t\tpy[n] = (3 * knots[n]._y - py[n_1]) / 2;\n\n\t\t\tfor (var i = paddingLeft, max = n - paddingRight, j = from;\n\t\t\t\t\ti <= max; i++, j++) {\n\t\t\t\tvar segment = segments[j < 0 ? j + length : j],\n\t\t\t\t\tpt = segment._point,\n\t\t\t\t\thx = px[i] - pt._x,\n\t\t\t\t\thy = py[i] - pt._y;\n\t\t\t\tif (loop || i < max)\n\t\t\t\t\tsegment.setHandleOut(hx, hy);\n\t\t\t\tif (loop || i > paddingLeft)\n\t\t\t\t\tsegment.setHandleIn(-hx, -hy);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (var i = from; i <= to; i++) {\n\t\t\t\tsegments[i < 0 ? i + length : i].smooth(opts,\n\t\t\t\t\t\t!loop && i === from, !loop && i === to);\n\t\t\t}\n\t\t}\n\t},\n\n\ttoShape: function(insert) {\n\t\tif (!this._closed)\n\t\t\treturn null;\n\n\t\tvar segments = this._segments,\n\t\t\ttype,\n\t\t\tsize,\n\t\t\tradius,\n\t\t\ttopCenter;\n\n\t\tfunction isCollinear(i, j) {\n\t\t\tvar seg1 = segments[i],\n\t\t\t\tseg2 = seg1.getNext(),\n\t\t\t\tseg3 = segments[j],\n\t\t\t\tseg4 = seg3.getNext();\n\t\t\treturn seg1._handleOut.isZero() && seg2._handleIn.isZero()\n\t\t\t\t\t&& seg3._handleOut.isZero() && seg4._handleIn.isZero()\n\t\t\t\t\t&& seg2._point.subtract(seg1._point).isCollinear(\n\t\t\t\t\t\tseg4._point.subtract(seg3._point));\n\t\t}\n\n\t\tfunction isOrthogonal(i) {\n\t\t\tvar seg2 = segments[i],\n\t\t\t\tseg1 = seg2.getPrevious(),\n\t\t\t\tseg3 = seg2.getNext();\n\t\t\treturn seg1._handleOut.isZero() && seg2._handleIn.isZero()\n\t\t\t\t\t&& seg2._handleOut.isZero() && seg3._handleIn.isZero()\n\t\t\t\t\t&& seg2._point.subtract(seg1._point).isOrthogonal(\n\t\t\t\t\t\tseg3._point.subtract(seg2._point));\n\t\t}\n\n\t\tfunction isArc(i) {\n\t\t\tvar seg1 = segments[i],\n\t\t\t\tseg2 = seg1.getNext(),\n\t\t\t\thandle1 = seg1._handleOut,\n\t\t\t\thandle2 = seg2._handleIn,\n\t\t\t\tkappa = 0.5522847498307936;\n\t\t\tif (handle1.isOrthogonal(handle2)) {\n\t\t\t\tvar pt1 = seg1._point,\n\t\t\t\t\tpt2 = seg2._point,\n\t\t\t\t\tcorner = new Line(pt1, handle1, true).intersect(\n\t\t\t\t\t\t\tnew Line(pt2, handle2, true), true);\n\t\t\t\treturn corner && Numerical.isZero(handle1.getLength() /\n\t\t\t\t\t\tcorner.subtract(pt1).getLength() - kappa)\n\t\t\t\t\t&& Numerical.isZero(handle2.getLength() /\n\t\t\t\t\t\tcorner.subtract(pt2).getLength() - kappa);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction getDistance(i, j) {\n\t\t\treturn segments[i]._point.getDistance(segments[j]._point);\n\t\t}\n\n\t\tif (!this.hasHandles() && segments.length === 4\n\t\t\t\t&& isCollinear(0, 2) && isCollinear(1, 3) && isOrthogonal(1)) {\n\t\t\ttype = Shape.Rectangle;\n\t\t\tsize = new Size(getDistance(0, 3), getDistance(0, 1));\n\t\t\ttopCenter = segments[1]._point.add(segments[2]._point).divide(2);\n\t\t} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)\n\t\t\t\t&& isArc(6) && isCollinear(1, 5) && isCollinear(3, 7)) {\n\t\t\ttype = Shape.Rectangle;\n\t\t\tsize = new Size(getDistance(1, 6), getDistance(0, 3));\n\t\t\tradius = size.subtract(new Size(getDistance(0, 7),\n\t\t\t\t\tgetDistance(1, 2))).divide(2);\n\t\t\ttopCenter = segments[3]._point.add(segments[4]._point).divide(2);\n\t\t} else if (segments.length === 4\n\t\t\t\t&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {\n\t\t\tif (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {\n\t\t\t\ttype = Shape.Circle;\n\t\t\t\tradius = getDistance(0, 2) / 2;\n\t\t\t} else {\n\t\t\t\ttype = Shape.Ellipse;\n\t\t\t\tradius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);\n\t\t\t}\n\t\t\ttopCenter = segments[1]._point;\n\t\t}\n\n\t\tif (type) {\n\t\t\tvar center = this.getPosition(true),\n\t\t\t\tshape = new type({\n\t\t\t\t\tcenter: center,\n\t\t\t\t\tsize: size,\n\t\t\t\t\tradius: radius,\n\t\t\t\t\tinsert: false\n\t\t\t\t});\n\t\t\tshape.copyAttributes(this, true);\n\t\t\tshape._matrix.prepend(this._matrix);\n\t\t\tshape.rotate(topCenter.subtract(center).getAngle() + 90);\n\t\t\tif (insert === undefined || insert)\n\t\t\t\tshape.insertAbove(this);\n\t\t\treturn shape;\n\t\t}\n\t\treturn null;\n\t},\n\n\ttoPath: '#clone',\n\n\tcompare: function compare(path) {\n\t\tif (!path || path instanceof CompoundPath)\n\t\t\treturn compare.base.call(this, path);\n\t\tvar curves1 = this.getCurves(),\n\t\t\tcurves2 = path.getCurves(),\n\t\t\tlength1 = curves1.length,\n\t\t\tlength2 = curves2.length;\n\t\tif (!length1 || !length2) {\n\t\t\treturn length1 == length2;\n\t\t}\n\t\tvar v1 = curves1[0].getValues(),\n\t\t\tvalues2 = [],\n\t\t\tpos1 = 0, pos2,\n\t\t\tend1 = 0, end2;\n\t\tfor (var i = 0; i < length2; i++) {\n\t\t\tvar v2 = curves2[i].getValues();\n\t\t\tvalues2.push(v2);\n\t\t\tvar overlaps = Curve.getOverlaps(v1, v2);\n\t\t\tif (overlaps) {\n\t\t\t\tpos2 = !i && overlaps[0][0] > 0 ? length2 - 1 : i;\n\t\t\t\tend2 = overlaps[0][1];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tvar abs = Math.abs,\n\t\t\tepsilon = 1e-8,\n\t\t\tv2 = values2[pos2],\n\t\t\tstart2;\n\t\twhile (v1 && v2) {\n\t\t\tvar overlaps = Curve.getOverlaps(v1, v2);\n\t\t\tif (overlaps) {\n\t\t\t\tvar t1 = overlaps[0][0];\n\t\t\t\tif (abs(t1 - end1) < epsilon) {\n\t\t\t\t\tend1 = overlaps[1][0];\n\t\t\t\t\tif (end1 === 1) {\n\t\t\t\t\t\tv1 = ++pos1 < length1 ? curves1[pos1].getValues() : null;\n\t\t\t\t\t\tend1 = 0;\n\t\t\t\t\t}\n\t\t\t\t\tvar t2 = overlaps[0][1];\n\t\t\t\t\tif (abs(t2 - end2) < epsilon) {\n\t\t\t\t\t\tif (!start2)\n\t\t\t\t\t\t\tstart2 = [pos2, t2];\n\t\t\t\t\t\tend2 = overlaps[1][1];\n\t\t\t\t\t\tif (end2 === 1) {\n\t\t\t\t\t\t\tif (++pos2 >= length2)\n\t\t\t\t\t\t\t\tpos2 = 0;\n\t\t\t\t\t\t\tv2 = values2[pos2] || curves2[pos2].getValues();\n\t\t\t\t\t\t\tend2 = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!v1) {\n\t\t\t\t\t\t\treturn start2[0] === pos2 && start2[1] === end2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn false;\n\t},\n\n\t_hitTestSelf: function(point, options, viewMatrix, strokeMatrix) {\n\t\tvar that = this,\n\t\t\tstyle = this.getStyle(),\n\t\t\tsegments = this._segments,\n\t\t\tnumSegments = segments.length,\n\t\t\tclosed = this._closed,\n\t\t\ttolerancePadding = options._tolerancePadding,\n\t\t\tstrokePadding = tolerancePadding,\n\t\t\tjoin, cap, miterLimit,\n\t\t\tarea, loc, res,\n\t\t\thitStroke = options.stroke && (style.hasStroke() || options.hitUnstrokedPaths),\n\t\t\thitFill = options.fill && (style.hasFill() || options.hitUnfilledPaths),\n\t\t\thitCurves = options.curves,\n\t\t\tstrokeRadius = hitStroke\n\t\t\t\t\t? style.getStrokeWidth() / 2\n\t\t\t\t\t: hitFill && options.tolerance > 0 || hitCurves\n\t\t\t\t\t\t? 0 : null;\n\t\tif (strokeRadius !== null) {\n\t\t\tif (strokeRadius > 0) {\n\t\t\t\tjoin = style.getStrokeJoin();\n\t\t\t\tcap = style.getStrokeCap();\n\t\t\t\tmiterLimit = style.getMiterLimit();\n\t\t\t\tstrokePadding = strokePadding.add(\n\t\t\t\t\tPath._getStrokePadding(strokeRadius, strokeMatrix));\n\t\t\t} else {\n\t\t\t\tjoin = cap = 'round';\n\t\t\t}\n\t\t}\n\n\t\tfunction isCloseEnough(pt, padding) {\n\t\t\treturn point.subtract(pt).divide(padding).length <= 1;\n\t\t}\n\n\t\tfunction checkSegmentPoint(seg, pt, name) {\n\t\t\tif (!options.selected || pt.isSelected()) {\n\t\t\t\tvar anchor = seg._point;\n\t\t\t\tif (pt !== anchor)\n\t\t\t\t\tpt = pt.add(anchor);\n\t\t\t\tif (isCloseEnough(pt, strokePadding)) {\n\t\t\t\t\treturn new HitResult(name, that, {\n\t\t\t\t\t\tsegment: seg,\n\t\t\t\t\t\tpoint: pt\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction checkSegmentPoints(seg, ends) {\n\t\t\treturn (ends || options.segments)\n\t\t\t\t&& checkSegmentPoint(seg, seg._point, 'segment')\n\t\t\t\t|| (!ends && options.handles) && (\n\t\t\t\t\tcheckSegmentPoint(seg, seg._handleIn, 'handle-in') ||\n\t\t\t\t\tcheckSegmentPoint(seg, seg._handleOut, 'handle-out'));\n\t\t}\n\n\t\tfunction addToArea(point) {\n\t\t\tarea.add(point);\n\t\t}\n\n\t\tfunction checkSegmentStroke(segment) {\n\t\t\tvar isJoin = closed || segment._index > 0\n\t\t\t\t\t&& segment._index < numSegments - 1;\n\t\t\tif ((isJoin ? join : cap) === 'round') {\n\t\t\t\treturn isCloseEnough(segment._point, strokePadding);\n\t\t\t} else {\n\t\t\t\tarea = new Path({ internal: true, closed: true });\n\t\t\t\tif (isJoin) {\n\t\t\t\t\tif (!segment.isSmooth()) {\n\t\t\t\t\t\tPath._addBevelJoin(segment, join, strokeRadius,\n\t\t\t\t\t\t\t miterLimit, null, strokeMatrix, addToArea, true);\n\t\t\t\t\t}\n\t\t\t\t} else if (cap === 'square') {\n\t\t\t\t\tPath._addSquareCap(segment, cap, strokeRadius, null,\n\t\t\t\t\t\t\tstrokeMatrix, addToArea, true);\n\t\t\t\t}\n\t\t\t\tif (!area.isEmpty()) {\n\t\t\t\t\tvar loc;\n\t\t\t\t\treturn area.contains(point)\n\t\t\t\t\t\t|| (loc = area.getNearestLocation(point))\n\t\t\t\t\t\t\t&& isCloseEnough(loc.getPoint(), tolerancePadding);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (options.ends && !options.segments && !closed) {\n\t\t\tif (res = checkSegmentPoints(segments[0], true)\n\t\t\t\t\t|| checkSegmentPoints(segments[numSegments - 1], true))\n\t\t\t\treturn res;\n\t\t} else if (options.segments || options.handles) {\n\t\t\tfor (var i = 0; i < numSegments; i++)\n\t\t\t\tif (res = checkSegmentPoints(segments[i]))\n\t\t\t\t\treturn res;\n\t\t}\n\t\tif (strokeRadius !== null) {\n\t\t\tloc = this.getNearestLocation(point);\n\t\t\tif (loc) {\n\t\t\t\tvar time = loc.getTime();\n\t\t\t\tif (time === 0 || time === 1 && numSegments > 1) {\n\t\t\t\t\tif (!checkSegmentStroke(loc.getSegment()))\n\t\t\t\t\t\tloc = null;\n\t\t\t\t} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {\n\t\t\t\t\tloc = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!loc && join === 'miter' && numSegments > 1) {\n\t\t\t\tfor (var i = 0; i < numSegments; i++) {\n\t\t\t\t\tvar segment = segments[i];\n\t\t\t\t\tif (point.getDistance(segment._point)\n\t\t\t\t\t\t\t<= miterLimit * strokeRadius\n\t\t\t\t\t\t\t&& checkSegmentStroke(segment)) {\n\t\t\t\t\t\tloc = segment.getLocation();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn !loc && hitFill && this._contains(point)\n\t\t\t\t|| loc && !hitStroke && !hitCurves\n\t\t\t\t\t? new HitResult('fill', this)\n\t\t\t\t\t: loc\n\t\t\t\t\t\t? new HitResult(hitStroke ? 'stroke' : 'curve', this, {\n\t\t\t\t\t\t\tlocation: loc,\n\t\t\t\t\t\t\tpoint: loc.getPoint()\n\t\t\t\t\t\t})\n\t\t\t\t\t\t: null;\n\t}\n\n}, Base.each(Curve._evaluateMethods,\n\tfunction(name) {\n\t\tthis[name + 'At'] = function(offset) {\n\t\t\tvar loc = this.getLocationAt(offset);\n\t\t\treturn loc && loc[name]();\n\t\t};\n\t},\n{\n\tbeans: false,\n\n\tgetLocationOf: function() {\n\t\tvar point = Point.read(arguments),\n\t\t\tcurves = this.getCurves();\n\t\tfor (var i = 0, l = curves.length; i < l; i++) {\n\t\t\tvar loc = curves[i].getLocationOf(point);\n\t\t\tif (loc)\n\t\t\t\treturn loc;\n\t\t}\n\t\treturn null;\n\t},\n\n\tgetOffsetOf: function() {\n\t\tvar loc = this.getLocationOf.apply(this, arguments);\n\t\treturn loc ? loc.getOffset() : null;\n\t},\n\n\tgetLocationAt: function(offset) {\n\t\tif (typeof offset === 'number') {\n\t\t\tvar curves = this.getCurves(),\n\t\t\t\tlength = 0;\n\t\t\tfor (var i = 0, l = curves.length; i < l; i++) {\n\t\t\t\tvar start = length,\n\t\t\t\t\tcurve = curves[i];\n\t\t\t\tlength += curve.getLength();\n\t\t\t\tif (length > offset) {\n\t\t\t\t\treturn curve.getLocationAt(offset - start);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (curves.length > 0 && offset <= this.getLength()) {\n\t\t\t\treturn new CurveLocation(curves[curves.length - 1], 1);\n\t\t\t}\n\t\t} else if (offset && offset.getPath && offset.getPath() === this) {\n\t\t\treturn offset;\n\t\t}\n\t\treturn null;\n\t},\n\n\tgetOffsetsWithTangent: function() {\n\t\tvar tangent = Point.read(arguments);\n\t\tif (tangent.isZero()) {\n\t\t\treturn [];\n\t\t}\n\n\t\tvar offsets = [];\n\t\tvar curveStart = 0;\n\t\tvar curves = this.getCurves();\n\t\tfor (var i = 0, l = curves.length; i < l; i++) {\n\t\t\tvar curve = curves[i];\n\t\t\tvar curveTimes = curve.getTimesWithTangent(tangent);\n\t\t\tfor (var j = 0, m = curveTimes.length; j < m; j++) {\n\t\t\t\tvar offset = curveStart + curve.getOffsetAtTime(curveTimes[j]);\n\t\t\t\tif (offsets.indexOf(offset) < 0) {\n\t\t\t\t\toffsets.push(offset);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurveStart += curve.length;\n\t\t}\n\t\treturn offsets;\n\t}\n}),\nnew function() {\n\n\tfunction drawHandles(ctx, segments, matrix, size, isFullySelected) {\n\t\tif (size <= 0) return;\n\n\t\tvar half = size / 2,\n\t\t\tcoords = new Array(6),\n\t\t\tpX, pY;\n\n\t\tfunction drawHandle(index) {\n\t\t\tvar hX = coords[index],\n\t\t\t\thY = coords[index + 1];\n\t\t\tif (pX != hX || pY != hY) {\n\t\t\t\tctx.beginPath();\n\t\t\t\tctx.moveTo(pX, pY);\n\t\t\t\tctx.lineTo(hX, hY);\n\t\t\t\tctx.moveTo(hX - half, hY);\n\t\t\t\tctx.lineTo(hX, hY + half);\n\t\t\t\tctx.lineTo(hX + half, hY);\n\t\t\t\tctx.lineTo(hX, hY - half);\n\t\t\t\tctx.closePath();\n\t\t\t\tctx.stroke();\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\tvar segment = segments[i],\n\t\t\t\tselection = segment._selection;\n\t\t\tsegment._transformCoordinates(matrix, coords);\n\t\t\tpX = coords[0];\n\t\t\tpY = coords[1];\n\t\t\tif (selection & 2 && !isFullySelected)\n\t\t\t\tdrawHandle(2);\n\t\t\tif (selection & 4 && !isFullySelected)\n\t\t\t\tdrawHandle(4);\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(pX, pY, half, 0, Math.PI * 2, true);\n\t\t\tctx.stroke();\n\t\t\tvar fillStyle = ctx.fillStyle;\n\t\t\tif (!(selection & 1)) {\n\t\t\t\tctx.fillStyle = 'rgba(255, 255, 255, 0.5)';\n\t\t\t}\n\t\t\tctx.fill();\n\t\t\tctx.fillStyle = fillStyle;\n\t\t}\n\t}\n\n\tfunction drawSegments(ctx, path, matrix) {\n\t\tvar segments = path._segments,\n\t\t\tlength = segments.length,\n\t\t\tcoords = new Array(6),\n\t\t\tfirst = true,\n\t\t\tcurX, curY,\n\t\t\tprevX, prevY,\n\t\t\tinX, inY,\n\t\t\toutX, outY;\n\n\t\tfunction drawSegment(segment) {\n\t\t\tif (matrix) {\n\t\t\t\tsegment._transformCoordinates(matrix, coords);\n\t\t\t\tcurX = coords[0];\n\t\t\t\tcurY = coords[1];\n\t\t\t} else {\n\t\t\t\tvar point = segment._point;\n\t\t\t\tcurX = point._x;\n\t\t\t\tcurY = point._y;\n\t\t\t}\n\t\t\tif (first) {\n\t\t\t\tctx.moveTo(curX, curY);\n\t\t\t\tfirst = false;\n\t\t\t} else {\n\t\t\t\tif (matrix) {\n\t\t\t\t\tinX = coords[2];\n\t\t\t\t\tinY = coords[3];\n\t\t\t\t} else {\n\t\t\t\t\tvar handle = segment._handleIn;\n\t\t\t\t\tinX = curX + handle._x;\n\t\t\t\t\tinY = curY + handle._y;\n\t\t\t\t}\n\t\t\t\tif (inX === curX && inY === curY\n\t\t\t\t\t\t&& outX === prevX && outY === prevY) {\n\t\t\t\t\tctx.lineTo(curX, curY);\n\t\t\t\t} else {\n\t\t\t\t\tctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);\n\t\t\t\t}\n\t\t\t}\n\t\t\tprevX = curX;\n\t\t\tprevY = curY;\n\t\t\tif (matrix) {\n\t\t\t\toutX = coords[4];\n\t\t\t\toutY = coords[5];\n\t\t\t} else {\n\t\t\t\tvar handle = segment._handleOut;\n\t\t\t\toutX = prevX + handle._x;\n\t\t\t\toutY = prevY + handle._y;\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i < length; i++)\n\t\t\tdrawSegment(segments[i]);\n\t\tif (path._closed && length > 0)\n\t\t\tdrawSegment(segments[0]);\n\t}\n\n\treturn {\n\t\t_draw: function(ctx, param, viewMatrix, strokeMatrix) {\n\t\t\tvar dontStart = param.dontStart,\n\t\t\t\tdontPaint = param.dontFinish || param.clip,\n\t\t\t\tstyle = this.getStyle(),\n\t\t\t\thasFill = style.hasFill(),\n\t\t\t\thasStroke = style.hasStroke(),\n\t\t\t\tdashArray = style.getDashArray(),\n\t\t\t\tdashLength = !paper.support.nativeDash && hasStroke\n\t\t\t\t\t\t&& dashArray && dashArray.length;\n\n\t\t\tif (!dontStart)\n\t\t\t\tctx.beginPath();\n\n\t\t\tif (hasFill || hasStroke && !dashLength || dontPaint) {\n\t\t\t\tdrawSegments(ctx, this, strokeMatrix);\n\t\t\t\tif (this._closed)\n\t\t\t\t\tctx.closePath();\n\t\t\t}\n\n\t\t\tfunction getOffset(i) {\n\t\t\t\treturn dashArray[((i % dashLength) + dashLength) % dashLength];\n\t\t\t}\n\n\t\t\tif (!dontPaint && (hasFill || hasStroke)) {\n\t\t\t\tthis._setStyles(ctx, param, viewMatrix, strokeMatrix);\n\t\t\t\tif (hasFill) {\n\t\t\t\t\tctx.fill(style.getFillRule());\n\t\t\t\t\tctx.shadowColor = 'rgba(0,0,0,0)';\n\t\t\t\t}\n\t\t\t\tif (hasStroke) {\n\t\t\t\t\tif (dashLength) {\n\t\t\t\t\t\tif (!dontStart)\n\t\t\t\t\t\t\tctx.beginPath();\n\t\t\t\t\t\tvar flattener = new PathFlattener(this, 0.25, 32, false,\n\t\t\t\t\t\t\t\tstrokeMatrix),\n\t\t\t\t\t\t\tlength = flattener.length,\n\t\t\t\t\t\t\tfrom = -style.getDashOffset(), to,\n\t\t\t\t\t\t\ti = 0;\n\t\t\t\t\t\tfrom = from % length;\n\t\t\t\t\t\twhile (from > 0) {\n\t\t\t\t\t\t\tfrom -= getOffset(i--) + getOffset(i--);\n\t\t\t\t\t\t}\n\t\t\t\t\t\twhile (from < length) {\n\t\t\t\t\t\t\tto = from + getOffset(i++);\n\t\t\t\t\t\t\tif (from > 0 || to > 0)\n\t\t\t\t\t\t\t\tflattener.drawPart(ctx,\n\t\t\t\t\t\t\t\t\t\tMath.max(from, 0), Math.max(to, 0));\n\t\t\t\t\t\t\tfrom = to + getOffset(i++);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tctx.stroke();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t_drawSelected: function(ctx, matrix) {\n\t\t\tctx.beginPath();\n\t\t\tdrawSegments(ctx, this, matrix);\n\t\t\tctx.stroke();\n\t\t\tdrawHandles(ctx, this._segments, matrix, paper.settings.handleSize,\n\t\t\t\tthis.isFullySelected());\n\t\t}\n\t};\n},\nnew function() {\n\tfunction getCurrentSegment(that) {\n\t\tvar segments = that._segments;\n\t\tif (!segments.length)\n\t\t\tthrow new Error('Use a moveTo() command first');\n\t\treturn segments[segments.length - 1];\n\t}\n\n\treturn {\n\t\tmoveTo: function() {\n\t\t\tvar segments = this._segments;\n\t\t\tif (segments.length === 1)\n\t\t\t\tthis.removeSegment(0);\n\t\t\tif (!segments.length)\n\t\t\t\tthis._add([ new Segment(Point.read(arguments)) ]);\n\t\t},\n\n\t\tmoveBy: function() {\n\t\t\tthrow new Error('moveBy() is unsupported on Path items.');\n\t\t},\n\n\t\tlineTo: function() {\n\t\t\tthis._add([ new Segment(Point.read(arguments)) ]);\n\t\t},\n\n\t\tcubicCurveTo: function() {\n\t\t\tvar args = arguments,\n\t\t\t\thandle1 = Point.read(args),\n\t\t\t\thandle2 = Point.read(args),\n\t\t\t\tto = Point.read(args),\n\t\t\t\tcurrent = getCurrentSegment(this);\n\t\t\tcurrent.setHandleOut(handle1.subtract(current._point));\n\t\t\tthis._add([ new Segment(to, handle2.subtract(to)) ]);\n\t\t},\n\n\t\tquadraticCurveTo: function() {\n\t\t\tvar args = arguments,\n\t\t\t\thandle = Point.read(args),\n\t\t\t\tto = Point.read(args),\n\t\t\t\tcurrent = getCurrentSegment(this)._point;\n\t\t\tthis.cubicCurveTo(\n\t\t\t\thandle.add(current.subtract(handle).multiply(1 / 3)),\n\t\t\t\thandle.add(to.subtract(handle).multiply(1 / 3)),\n\t\t\t\tto\n\t\t\t);\n\t\t},\n\n\t\tcurveTo: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tthrough = Point.read(args),\n\t\t\t\tto = Point.read(args),\n\t\t\t\tt = Base.pick(Base.read(args), 0.5),\n\t\t\t\tt1 = 1 - t,\n\t\t\t\tcurrent = getCurrentSegment(this)._point,\n\t\t\t\thandle = through.subtract(current.multiply(t1 * t1))\n\t\t\t\t\t.subtract(to.multiply(t * t)).divide(2 * t * t1);\n\t\t\tif (handle.isNaN())\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Cannot put a curve through points with parameter = ' + t);\n\t\t\tthis.quadraticCurveTo(handle, to);\n\t\t},\n\n\t\tarcTo: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tabs = Math.abs,\n\t\t\t\tsqrt = Math.sqrt,\n\t\t\t\tcurrent = getCurrentSegment(this),\n\t\t\t\tfrom = current._point,\n\t\t\t\tto = Point.read(args),\n\t\t\t\tthrough,\n\t\t\t\tpeek = Base.peek(args),\n\t\t\t\tclockwise = Base.pick(peek, true),\n\t\t\t\tcenter, extent, vector, matrix;\n\t\t\tif (typeof clockwise === 'boolean') {\n\t\t\t\tvar middle = from.add(to).divide(2),\n\t\t\t\tthrough = middle.add(middle.subtract(from).rotate(\n\t\t\t\t\t\tclockwise ? -90 : 90));\n\t\t\t} else if (Base.remain(args) <= 2) {\n\t\t\t\tthrough = to;\n\t\t\t\tto = Point.read(args);\n\t\t\t} else if (!from.equals(to)) {\n\t\t\t\tvar radius = Size.read(args),\n\t\t\t\t\tisZero = Numerical.isZero;\n\t\t\t\tif (isZero(radius.width) || isZero(radius.height))\n\t\t\t\t\treturn this.lineTo(to);\n\t\t\t\tvar rotation = Base.read(args),\n\t\t\t\t\tclockwise = !!Base.read(args),\n\t\t\t\t\tlarge = !!Base.read(args),\n\t\t\t\t\tmiddle = from.add(to).divide(2),\n\t\t\t\t\tpt = from.subtract(middle).rotate(-rotation),\n\t\t\t\t\tx = pt.x,\n\t\t\t\t\ty = pt.y,\n\t\t\t\t\trx = abs(radius.width),\n\t\t\t\t\try = abs(radius.height),\n\t\t\t\t\trxSq = rx * rx,\n\t\t\t\t\trySq = ry * ry,\n\t\t\t\t\txSq = x * x,\n\t\t\t\t\tySq = y * y;\n\t\t\t\tvar factor = sqrt(xSq / rxSq + ySq / rySq);\n\t\t\t\tif (factor > 1) {\n\t\t\t\t\trx *= factor;\n\t\t\t\t\try *= factor;\n\t\t\t\t\trxSq = rx * rx;\n\t\t\t\t\trySq = ry * ry;\n\t\t\t\t}\n\t\t\t\tfactor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /\n\t\t\t\t\t\t(rxSq * ySq + rySq * xSq);\n\t\t\t\tif (abs(factor) < 1e-12)\n\t\t\t\t\tfactor = 0;\n\t\t\t\tif (factor < 0)\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t'Cannot create an arc with the given arguments');\n\t\t\t\tcenter = new Point(rx * y / ry, -ry * x / rx)\n\t\t\t\t\t\t.multiply((large === clockwise ? -1 : 1) * sqrt(factor))\n\t\t\t\t\t\t.rotate(rotation).add(middle);\n\t\t\t\tmatrix = new Matrix().translate(center).rotate(rotation)\n\t\t\t\t\t\t.scale(rx, ry);\n\t\t\t\tvector = matrix._inverseTransform(from);\n\t\t\t\textent = vector.getDirectedAngle(matrix._inverseTransform(to));\n\t\t\t\tif (!clockwise && extent > 0)\n\t\t\t\t\textent -= 360;\n\t\t\t\telse if (clockwise && extent < 0)\n\t\t\t\t\textent += 360;\n\t\t\t}\n\t\t\tif (through) {\n\t\t\t\tvar l1 = new Line(from.add(through).divide(2),\n\t\t\t\t\t\t\tthrough.subtract(from).rotate(90), true),\n\t\t\t\t\tl2 = new Line(through.add(to).divide(2),\n\t\t\t\t\t\t\tto.subtract(through).rotate(90), true),\n\t\t\t\t\tline = new Line(from, to),\n\t\t\t\t\tthroughSide = line.getSide(through);\n\t\t\t\tcenter = l1.intersect(l2, true);\n\t\t\t\tif (!center) {\n\t\t\t\t\tif (!throughSide)\n\t\t\t\t\t\treturn this.lineTo(to);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t'Cannot create an arc with the given arguments');\n\t\t\t\t}\n\t\t\t\tvector = from.subtract(center);\n\t\t\t\textent = vector.getDirectedAngle(to.subtract(center));\n\t\t\t\tvar centerSide = line.getSide(center, true);\n\t\t\t\tif (centerSide === 0) {\n\t\t\t\t\textent = throughSide * abs(extent);\n\t\t\t\t} else if (throughSide === centerSide) {\n\t\t\t\t\textent += extent < 0 ? 360 : -360;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (extent) {\n\t\t\t\tvar epsilon = 1e-7,\n\t\t\t\t\text = abs(extent),\n\t\t\t\t\tcount = ext >= 360 ? 4 : Math.ceil((ext - epsilon) / 90),\n\t\t\t\t\tinc = extent / count,\n\t\t\t\t\thalf = inc * Math.PI / 360,\n\t\t\t\t\tz = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),\n\t\t\t\t\tsegments = [];\n\t\t\t\tfor (var i = 0; i <= count; i++) {\n\t\t\t\t\tvar pt = to,\n\t\t\t\t\t\tout = null;\n\t\t\t\t\tif (i < count) {\n\t\t\t\t\t\tout = vector.rotate(90).multiply(z);\n\t\t\t\t\t\tif (matrix) {\n\t\t\t\t\t\t\tpt = matrix._transformPoint(vector);\n\t\t\t\t\t\t\tout = matrix._transformPoint(vector.add(out))\n\t\t\t\t\t\t\t\t\t.subtract(pt);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpt = center.add(vector);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!i) {\n\t\t\t\t\t\tcurrent.setHandleOut(out);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar _in = vector.rotate(-90).multiply(z);\n\t\t\t\t\t\tif (matrix) {\n\t\t\t\t\t\t\t_in = matrix._transformPoint(vector.add(_in))\n\t\t\t\t\t\t\t\t\t.subtract(pt);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsegments.push(new Segment(pt, _in, out));\n\t\t\t\t\t}\n\t\t\t\t\tvector = vector.rotate(inc);\n\t\t\t\t}\n\t\t\t\tthis._add(segments);\n\t\t\t}\n\t\t},\n\n\t\tlineBy: function() {\n\t\t\tvar to = Point.read(arguments),\n\t\t\t\tcurrent = getCurrentSegment(this)._point;\n\t\t\tthis.lineTo(current.add(to));\n\t\t},\n\n\t\tcurveBy: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tthrough = Point.read(args),\n\t\t\t\tto = Point.read(args),\n\t\t\t\tparameter = Base.read(args),\n\t\t\t\tcurrent = getCurrentSegment(this)._point;\n\t\t\tthis.curveTo(current.add(through), current.add(to), parameter);\n\t\t},\n\n\t\tcubicCurveBy: function() {\n\t\t\tvar args = arguments,\n\t\t\t\thandle1 = Point.read(args),\n\t\t\t\thandle2 = Point.read(args),\n\t\t\t\tto = Point.read(args),\n\t\t\t\tcurrent = getCurrentSegment(this)._point;\n\t\t\tthis.cubicCurveTo(current.add(handle1), current.add(handle2),\n\t\t\t\t\tcurrent.add(to));\n\t\t},\n\n\t\tquadraticCurveBy: function() {\n\t\t\tvar args = arguments,\n\t\t\t\thandle = Point.read(args),\n\t\t\t\tto = Point.read(args),\n\t\t\t\tcurrent = getCurrentSegment(this)._point;\n\t\t\tthis.quadraticCurveTo(current.add(handle), current.add(to));\n\t\t},\n\n\t\tarcBy: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tcurrent = getCurrentSegment(this)._point,\n\t\t\t\tpoint = current.add(Point.read(args)),\n\t\t\t\tclockwise = Base.pick(Base.peek(args), true);\n\t\t\tif (typeof clockwise === 'boolean') {\n\t\t\t\tthis.arcTo(point, clockwise);\n\t\t\t} else {\n\t\t\t\tthis.arcTo(point, current.add(Point.read(args)));\n\t\t\t}\n\t\t},\n\n\t\tclosePath: function(tolerance) {\n\t\t\tthis.setClosed(true);\n\t\t\tthis.join(this, tolerance);\n\t\t}\n\t};\n}, {\n\n\t_getBounds: function(matrix, options) {\n\t\tvar method = options.handle\n\t\t\t\t? 'getHandleBounds'\n\t\t\t\t: options.stroke\n\t\t\t\t? 'getStrokeBounds'\n\t\t\t\t: 'getBounds';\n\t\treturn Path[method](this._segments, this._closed, this, matrix, options);\n\t},\n\nstatics: {\n\tgetBounds: function(segments, closed, path, matrix, options, strokePadding) {\n\t\tvar first = segments[0];\n\t\tif (!first)\n\t\t\treturn new Rectangle();\n\t\tvar coords = new Array(6),\n\t\t\tprevCoords = first._transformCoordinates(matrix, new Array(6)),\n\t\t\tmin = prevCoords.slice(0, 2),\n\t\t\tmax = min.slice(),\n\t\t\troots = new Array(2);\n\n\t\tfunction processSegment(segment) {\n\t\t\tsegment._transformCoordinates(matrix, coords);\n\t\t\tfor (var i = 0; i < 2; i++) {\n\t\t\t\tCurve._addBounds(\n\t\t\t\t\tprevCoords[i],\n\t\t\t\t\tprevCoords[i + 4],\n\t\t\t\t\tcoords[i + 2],\n\t\t\t\t\tcoords[i],\n\t\t\t\t\ti, strokePadding ? strokePadding[i] : 0, min, max, roots);\n\t\t\t}\n\t\t\tvar tmp = prevCoords;\n\t\t\tprevCoords = coords;\n\t\t\tcoords = tmp;\n\t\t}\n\n\t\tfor (var i = 1, l = segments.length; i < l; i++)\n\t\t\tprocessSegment(segments[i]);\n\t\tif (closed)\n\t\t\tprocessSegment(first);\n\t\treturn new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);\n\t},\n\n\tgetStrokeBounds: function(segments, closed, path, matrix, options) {\n\t\tvar style = path.getStyle(),\n\t\t\tstroke = style.hasStroke(),\n\t\t\tstrokeWidth = style.getStrokeWidth(),\n\t\t\tstrokeMatrix = stroke && path._getStrokeMatrix(matrix, options),\n\t\t\tstrokePadding = stroke && Path._getStrokePadding(strokeWidth,\n\t\t\t\tstrokeMatrix),\n\t\t\tbounds = Path.getBounds(segments, closed, path, matrix, options,\n\t\t\t\tstrokePadding);\n\t\tif (!stroke)\n\t\t\treturn bounds;\n\t\tvar strokeRadius = strokeWidth / 2,\n\t\t\tjoin = style.getStrokeJoin(),\n\t\t\tcap = style.getStrokeCap(),\n\t\t\tmiterLimit = style.getMiterLimit(),\n\t\t\tjoinBounds = new Rectangle(new Size(strokePadding));\n\n\t\tfunction addPoint(point) {\n\t\t\tbounds = bounds.include(point);\n\t\t}\n\n\t\tfunction addRound(segment) {\n\t\t\tbounds = bounds.unite(\n\t\t\t\t\tjoinBounds.setCenter(segment._point.transform(matrix)));\n\t\t}\n\n\t\tfunction addJoin(segment, join) {\n\t\t\tif (join === 'round' || segment.isSmooth()) {\n\t\t\t\taddRound(segment);\n\t\t\t} else {\n\t\t\t\tPath._addBevelJoin(segment, join, strokeRadius, miterLimit,\n\t\t\t\t\t\tmatrix, strokeMatrix, addPoint);\n\t\t\t}\n\t\t}\n\n\t\tfunction addCap(segment, cap) {\n\t\t\tif (cap === 'round') {\n\t\t\t\taddRound(segment);\n\t\t\t} else {\n\t\t\t\tPath._addSquareCap(segment, cap, strokeRadius, matrix,\n\t\t\t\t\t\tstrokeMatrix, addPoint);\n\t\t\t}\n\t\t}\n\n\t\tvar length = segments.length - (closed ? 0 : 1);\n\t\tif (length > 0) {\n\t\t\tfor (var i = 1; i < length; i++) {\n\t\t\t\taddJoin(segments[i], join);\n\t\t\t}\n\t\t\tif (closed) {\n\t\t\t\taddJoin(segments[0], join);\n\t\t\t} else {\n\t\t\t\taddCap(segments[0], cap);\n\t\t\t\taddCap(segments[segments.length - 1], cap);\n\t\t\t}\n\t\t}\n\t\treturn bounds;\n\t},\n\n\t_getStrokePadding: function(radius, matrix) {\n\t\tif (!matrix)\n\t\t\treturn [radius, radius];\n\t\tvar hor = new Point(radius, 0).transform(matrix),\n\t\t\tver = new Point(0, radius).transform(matrix),\n\t\t\tphi = hor.getAngleInRadians(),\n\t\t\ta = hor.getLength(),\n\t\t\tb = ver.getLength();\n\t\tvar sin = Math.sin(phi),\n\t\t\tcos = Math.cos(phi),\n\t\t\ttan = Math.tan(phi),\n\t\t\ttx = Math.atan2(b * tan, a),\n\t\t\tty = Math.atan2(b, tan * a);\n\t\treturn [Math.abs(a * Math.cos(tx) * cos + b * Math.sin(tx) * sin),\n\t\t\t\tMath.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];\n\t},\n\n\t_addBevelJoin: function(segment, join, radius, miterLimit, matrix,\n\t\t\tstrokeMatrix, addPoint, isArea) {\n\t\tvar curve2 = segment.getCurve(),\n\t\t\tcurve1 = curve2.getPrevious(),\n\t\t\tpoint = curve2.getPoint1().transform(matrix),\n\t\t\tnormal1 = curve1.getNormalAtTime(1).multiply(radius)\n\t\t\t\t.transform(strokeMatrix),\n\t\t\tnormal2 = curve2.getNormalAtTime(0).multiply(radius)\n\t\t\t\t.transform(strokeMatrix),\n\t\t\t\tangle = normal1.getDirectedAngle(normal2);\n\t\tif (angle < 0 || angle >= 180) {\n\t\t\tnormal1 = normal1.negate();\n\t\t\tnormal2 = normal2.negate();\n\t\t}\n\t\tif (isArea)\n\t\t\taddPoint(point);\n\t\taddPoint(point.add(normal1));\n\t\tif (join === 'miter') {\n\t\t\tvar corner = new Line(point.add(normal1),\n\t\t\t\t\tnew Point(-normal1.y, normal1.x), true\n\t\t\t\t).intersect(new Line(point.add(normal2),\n\t\t\t\t\tnew Point(-normal2.y, normal2.x), true\n\t\t\t\t), true);\n\t\t\tif (corner && point.getDistance(corner) <= miterLimit * radius) {\n\t\t\t\taddPoint(corner);\n\t\t\t}\n\t\t}\n\t\taddPoint(point.add(normal2));\n\t},\n\n\t_addSquareCap: function(segment, cap, radius, matrix, strokeMatrix,\n\t\t\taddPoint, isArea) {\n\t\tvar point = segment._point.transform(matrix),\n\t\t\tloc = segment.getLocation(),\n\t\t\tnormal = loc.getNormal()\n\t\t\t\t\t.multiply(loc.getTime() === 0 ? radius : -radius)\n\t\t\t\t\t.transform(strokeMatrix);\n\t\tif (cap === 'square') {\n\t\t\tif (isArea) {\n\t\t\t\taddPoint(point.subtract(normal));\n\t\t\t\taddPoint(point.add(normal));\n\t\t\t}\n\t\t\tpoint = point.add(normal.rotate(-90));\n\t\t}\n\t\taddPoint(point.add(normal));\n\t\taddPoint(point.subtract(normal));\n\t},\n\n\tgetHandleBounds: function(segments, closed, path, matrix, options) {\n\t\tvar style = path.getStyle(),\n\t\t\tstroke = options.stroke && style.hasStroke(),\n\t\t\tstrokePadding,\n\t\t\tjoinPadding;\n\t\tif (stroke) {\n\t\t\tvar strokeMatrix = path._getStrokeMatrix(matrix, options),\n\t\t\t\tstrokeRadius = style.getStrokeWidth() / 2,\n\t\t\t\tjoinRadius = strokeRadius;\n\t\t\tif (style.getStrokeJoin() === 'miter')\n\t\t\t\tjoinRadius = strokeRadius * style.getMiterLimit();\n\t\t\tif (style.getStrokeCap() === 'square')\n\t\t\t\tjoinRadius = Math.max(joinRadius, strokeRadius * Math.SQRT2);\n\t\t\tstrokePadding = Path._getStrokePadding(strokeRadius, strokeMatrix);\n\t\t\tjoinPadding = Path._getStrokePadding(joinRadius, strokeMatrix);\n\t\t}\n\t\tvar coords = new Array(6),\n\t\t\tx1 = Infinity,\n\t\t\tx2 = -x1,\n\t\t\ty1 = x1,\n\t\t\ty2 = x2;\n\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\tvar segment = segments[i];\n\t\t\tsegment._transformCoordinates(matrix, coords);\n\t\t\tfor (var j = 0; j < 6; j += 2) {\n\t\t\t\tvar padding = !j ? joinPadding : strokePadding,\n\t\t\t\t\tpaddingX = padding ? padding[0] : 0,\n\t\t\t\t\tpaddingY = padding ? padding[1] : 0,\n\t\t\t\t\tx = coords[j],\n\t\t\t\t\ty = coords[j + 1],\n\t\t\t\t\txn = x - paddingX,\n\t\t\t\t\txx = x + paddingX,\n\t\t\t\t\tyn = y - paddingY,\n\t\t\t\t\tyx = y + paddingY;\n\t\t\t\tif (xn < x1) x1 = xn;\n\t\t\t\tif (xx > x2) x2 = xx;\n\t\t\t\tif (yn < y1) y1 = yn;\n\t\t\t\tif (yx > y2) y2 = yx;\n\t\t\t}\n\t\t}\n\t\treturn new Rectangle(x1, y1, x2 - x1, y2 - y1);\n\t}\n}});\n\nPath.inject({ statics: new function() {\n\n\tvar kappa = 0.5522847498307936,\n\t\tellipseSegments = [\n\t\t\tnew Segment([-1, 0], [0, kappa ], [0, -kappa]),\n\t\t\tnew Segment([0, -1], [-kappa, 0], [kappa, 0 ]),\n\t\t\tnew Segment([1, 0], [0, -kappa], [0, kappa ]),\n\t\t\tnew Segment([0, 1], [kappa, 0 ], [-kappa, 0])\n\t\t];\n\n\tfunction createPath(segments, closed, args) {\n\t\tvar props = Base.getNamed(args),\n\t\t\tpath = new Path(props && props.insert == false && Item.NO_INSERT);\n\t\tpath._add(segments);\n\t\tpath._closed = closed;\n\t\treturn path.set(props, { insert: true });\n\t}\n\n\tfunction createEllipse(center, radius, args) {\n\t\tvar segments = new Array(4);\n\t\tfor (var i = 0; i < 4; i++) {\n\t\t\tvar segment = ellipseSegments[i];\n\t\t\tsegments[i] = new Segment(\n\t\t\t\tsegment._point.multiply(radius).add(center),\n\t\t\t\tsegment._handleIn.multiply(radius),\n\t\t\t\tsegment._handleOut.multiply(radius)\n\t\t\t);\n\t\t}\n\t\treturn createPath(segments, true, args);\n\t}\n\n\treturn {\n\t\tLine: function() {\n\t\t\tvar args = arguments;\n\t\t\treturn createPath([\n\t\t\t\tnew Segment(Point.readNamed(args, 'from')),\n\t\t\t\tnew Segment(Point.readNamed(args, 'to'))\n\t\t\t], false, args);\n\t\t},\n\n\t\tCircle: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tcenter = Point.readNamed(args, 'center'),\n\t\t\t\tradius = Base.readNamed(args, 'radius');\n\t\t\treturn createEllipse(center, new Size(radius), args);\n\t\t},\n\n\t\tRectangle: function() {\n\t\t\tvar args = arguments,\n\t\t\t\trect = Rectangle.readNamed(args, 'rectangle'),\n\t\t\t\tradius = Size.readNamed(args, 'radius', 0,\n\t\t\t\t\t\t{ readNull: true }),\n\t\t\t\tbl = rect.getBottomLeft(true),\n\t\t\t\ttl = rect.getTopLeft(true),\n\t\t\t\ttr = rect.getTopRight(true),\n\t\t\t\tbr = rect.getBottomRight(true),\n\t\t\t\tsegments;\n\t\t\tif (!radius || radius.isZero()) {\n\t\t\t\tsegments = [\n\t\t\t\t\tnew Segment(bl),\n\t\t\t\t\tnew Segment(tl),\n\t\t\t\t\tnew Segment(tr),\n\t\t\t\t\tnew Segment(br)\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\tradius = Size.min(radius, rect.getSize(true).divide(2));\n\t\t\t\tvar rx = radius.width,\n\t\t\t\t\try = radius.height,\n\t\t\t\t\thx = rx * kappa,\n\t\t\t\t\thy = ry * kappa;\n\t\t\t\tsegments = [\n\t\t\t\t\tnew Segment(bl.add(rx, 0), null, [-hx, 0]),\n\t\t\t\t\tnew Segment(bl.subtract(0, ry), [0, hy]),\n\t\t\t\t\tnew Segment(tl.add(0, ry), null, [0, -hy]),\n\t\t\t\t\tnew Segment(tl.add(rx, 0), [-hx, 0], null),\n\t\t\t\t\tnew Segment(tr.subtract(rx, 0), null, [hx, 0]),\n\t\t\t\t\tnew Segment(tr.add(0, ry), [0, -hy], null),\n\t\t\t\t\tnew Segment(br.subtract(0, ry), null, [0, hy]),\n\t\t\t\t\tnew Segment(br.subtract(rx, 0), [hx, 0])\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn createPath(segments, true, args);\n\t\t},\n\n\t\tRoundRectangle: '#Rectangle',\n\n\t\tEllipse: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tellipse = Shape._readEllipse(args);\n\t\t\treturn createEllipse(ellipse.center, ellipse.radius, args);\n\t\t},\n\n\t\tOval: '#Ellipse',\n\n\t\tArc: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tfrom = Point.readNamed(args, 'from'),\n\t\t\t\tthrough = Point.readNamed(args, 'through'),\n\t\t\t\tto = Point.readNamed(args, 'to'),\n\t\t\t\tprops = Base.getNamed(args),\n\t\t\t\tpath = new Path(props && props.insert == false\n\t\t\t\t\t\t&& Item.NO_INSERT);\n\t\t\tpath.moveTo(from);\n\t\t\tpath.arcTo(through, to);\n\t\t\treturn path.set(props);\n\t\t},\n\n\t\tRegularPolygon: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tcenter = Point.readNamed(args, 'center'),\n\t\t\t\tsides = Base.readNamed(args, 'sides'),\n\t\t\t\tradius = Base.readNamed(args, 'radius'),\n\t\t\t\tstep = 360 / sides,\n\t\t\t\tthree = sides % 3 === 0,\n\t\t\t\tvector = new Point(0, three ? -radius : radius),\n\t\t\t\toffset = three ? -1 : 0.5,\n\t\t\t\tsegments = new Array(sides);\n\t\t\tfor (var i = 0; i < sides; i++)\n\t\t\t\tsegments[i] = new Segment(center.add(\n\t\t\t\t\tvector.rotate((i + offset) * step)));\n\t\t\treturn createPath(segments, true, args);\n\t\t},\n\n\t\tStar: function() {\n\t\t\tvar args = arguments,\n\t\t\t\tcenter = Point.readNamed(args, 'center'),\n\t\t\t\tpoints = Base.readNamed(args, 'points') * 2,\n\t\t\t\tradius1 = Base.readNamed(args, 'radius1'),\n\t\t\t\tradius2 = Base.readNamed(args, 'radius2'),\n\t\t\t\tstep = 360 / points,\n\t\t\t\tvector = new Point(0, -1),\n\t\t\t\tsegments = new Array(points);\n\t\t\tfor (var i = 0; i < points; i++)\n\t\t\t\tsegments[i] = new Segment(center.add(vector.rotate(step * i)\n\t\t\t\t\t\t.multiply(i % 2 ? radius2 : radius1)));\n\t\t\treturn createPath(segments, true, args);\n\t\t}\n\t};\n}});\n\nvar CompoundPath = PathItem.extend({\n\t_class: 'CompoundPath',\n\t_serializeFields: {\n\t\tchildren: []\n\t},\n\tbeans: true,\n\n\tinitialize: function CompoundPath(arg) {\n\t\tthis._children = [];\n\t\tthis._namedChildren = {};\n\t\tif (!this._initialize(arg)) {\n\t\t\tif (typeof arg === 'string') {\n\t\t\t\tthis.setPathData(arg);\n\t\t\t} else {\n\t\t\t\tthis.addChildren(Array.isArray(arg) ? arg : arguments);\n\t\t\t}\n\t\t}\n\t},\n\n\tinsertChildren: function insertChildren(index, items) {\n\t\tvar list = items,\n\t\t\tfirst = list[0];\n\t\tif (first && typeof first[0] === 'number')\n\t\t\tlist = [list];\n\t\tfor (var i = items.length - 1; i >= 0; i--) {\n\t\t\tvar item = list[i];\n\t\t\tif (list === items && !(item instanceof Path))\n\t\t\t\tlist = Base.slice(list);\n\t\t\tif (Array.isArray(item)) {\n\t\t\t\tlist[i] = new Path({ segments: item, insert: false });\n\t\t\t} else if (item instanceof CompoundPath) {\n\t\t\t\tlist.splice.apply(list, [i, 1].concat(item.removeChildren()));\n\t\t\t\titem.remove();\n\t\t\t}\n\t\t}\n\t\treturn insertChildren.base.call(this, index, list);\n\t},\n\n\treduce: function reduce(options) {\n\t\tvar children = this._children;\n\t\tfor (var i = children.length - 1; i >= 0; i--) {\n\t\t\tvar path = children[i].reduce(options);\n\t\t\tif (path.isEmpty())\n\t\t\t\tpath.remove();\n\t\t}\n\t\tif (!children.length) {\n\t\t\tvar path = new Path(Item.NO_INSERT);\n\t\t\tpath.copyAttributes(this);\n\t\t\tpath.insertAbove(this);\n\t\t\tthis.remove();\n\t\t\treturn path;\n\t\t}\n\t\treturn reduce.base.call(this);\n\t},\n\n\tisClosed: function() {\n\t\tvar children = this._children;\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tif (!children[i]._closed)\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\tsetClosed: function(closed) {\n\t\tvar children = this._children;\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tchildren[i].setClosed(closed);\n\t\t}\n\t},\n\n\tgetFirstSegment: function() {\n\t\tvar first = this.getFirstChild();\n\t\treturn first && first.getFirstSegment();\n\t},\n\n\tgetLastSegment: function() {\n\t\tvar last = this.getLastChild();\n\t\treturn last && last.getLastSegment();\n\t},\n\n\tgetCurves: function() {\n\t\tvar children = this._children,\n\t\t\tcurves = [];\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tBase.push(curves, children[i].getCurves());\n\t\t}\n\t\treturn curves;\n\t},\n\n\tgetFirstCurve: function() {\n\t\tvar first = this.getFirstChild();\n\t\treturn first && first.getFirstCurve();\n\t},\n\n\tgetLastCurve: function() {\n\t\tvar last = this.getLastChild();\n\t\treturn last && last.getLastCurve();\n\t},\n\n\tgetArea: function() {\n\t\tvar children = this._children,\n\t\t\tarea = 0;\n\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\tarea += children[i].getArea();\n\t\treturn area;\n\t},\n\n\tgetLength: function() {\n\t\tvar children = this._children,\n\t\t\tlength = 0;\n\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\tlength += children[i].getLength();\n\t\treturn length;\n\t},\n\n\tgetPathData: function(_matrix, _precision) {\n\t\tvar children = this._children,\n\t\t\tpaths = [];\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tvar child = children[i],\n\t\t\t\tmx = child._matrix;\n\t\t\tpaths.push(child.getPathData(_matrix && !mx.isIdentity()\n\t\t\t\t\t? _matrix.appended(mx) : _matrix, _precision));\n\t\t}\n\t\treturn paths.join('');\n\t},\n\n\t_hitTestChildren: function _hitTestChildren(point, options, viewMatrix) {\n\t\treturn _hitTestChildren.base.call(this, point,\n\t\t\t\toptions.class === Path || options.type === 'path' || options.hitUnfilledPaths ? options\n\t\t\t\t\t: Base.set({}, options, { fill: false }),\n\t\t\t\tviewMatrix);\n\t},\n\n\t_draw: function(ctx, param, viewMatrix, strokeMatrix) {\n\t\tvar children = this._children;\n\t\tif (!children.length)\n\t\t\treturn;\n\n\t\tparam = param.extend({ dontStart: true, dontFinish: true });\n\t\tctx.beginPath();\n\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\tchildren[i].draw(ctx, param, strokeMatrix);\n\n\t\tif (!param.clip) {\n\t\t\tthis._setStyles(ctx, param, viewMatrix, strokeMatrix);\n\t\t\tvar style = this._style;\n\t\t\tif (style.hasFill()) {\n\t\t\t\tctx.fill(style.getFillRule());\n\t\t\t\tctx.shadowColor = 'rgba(0,0,0,0)';\n\t\t\t}\n\t\t\tif (style.hasStroke())\n\t\t\t\tctx.stroke();\n\t\t}\n\t},\n\n\t_drawSelected: function(ctx, matrix, selectionItems) {\n\t\tvar children = this._children;\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tvar child = children[i],\n\t\t\t\tmx = child._matrix;\n\t\t\tif (!selectionItems[child._id]) {\n\t\t\t\tchild._drawSelected(ctx, mx.isIdentity() ? matrix\n\t\t\t\t\t\t: matrix.appended(mx));\n\t\t\t}\n\t\t}\n\t}\n},\nnew function() {\n\tfunction getCurrentPath(that, check) {\n\t\tvar children = that._children;\n\t\tif (check && !children.length)\n\t\t\tthrow new Error('Use a moveTo() command first');\n\t\treturn children[children.length - 1];\n\t}\n\n\treturn Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',\n\t\t\t'arcTo', 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy',\n\t\t\t'arcBy'],\n\t\tfunction(key) {\n\t\t\tthis[key] = function() {\n\t\t\t\tvar path = getCurrentPath(this, true);\n\t\t\t\tpath[key].apply(path, arguments);\n\t\t\t};\n\t\t}, {\n\t\t\tmoveTo: function() {\n\t\t\t\tvar current = getCurrentPath(this),\n\t\t\t\t\tpath = current && current.isEmpty() ? current\n\t\t\t\t\t\t\t: new Path(Item.NO_INSERT);\n\t\t\t\tif (path !== current)\n\t\t\t\t\tthis.addChild(path);\n\t\t\t\tpath.moveTo.apply(path, arguments);\n\t\t\t},\n\n\t\t\tmoveBy: function() {\n\t\t\t\tvar current = getCurrentPath(this, true),\n\t\t\t\t\tlast = current && current.getLastSegment(),\n\t\t\t\t\tpoint = Point.read(arguments);\n\t\t\t\tthis.moveTo(last ? point.add(last._point) : point);\n\t\t\t},\n\n\t\t\tclosePath: function(tolerance) {\n\t\t\t\tgetCurrentPath(this, true).closePath(tolerance);\n\t\t\t}\n\t\t}\n\t);\n}, Base.each(['reverse', 'flatten', 'simplify', 'smooth'], function(key) {\n\tthis[key] = function(param) {\n\t\tvar children = this._children,\n\t\t\tres;\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tres = children[i][key](param) || res;\n\t\t}\n\t\treturn res;\n\t};\n}, {}));\n\nPathItem.inject(new function() {\n\tvar min = Math.min,\n\t\tmax = Math.max,\n\t\tabs = Math.abs,\n\t\toperators = {\n\t\t\tunite: { '1': true, '2': true },\n\t\t\tintersect: { '2': true },\n\t\t\tsubtract: { '1': true },\n\t\t\texclude: { '1': true, '-1': true }\n\t\t};\n\n\tfunction getPaths(path) {\n\t\treturn path._children || [path];\n\t}\n\n\tfunction preparePath(path, resolve) {\n\t\tvar res = path\n\t\t\t.clone(false)\n\t\t\t.reduce({ simplify: true })\n\t\t\t.transform(null, true, true);\n\t\tif (resolve) {\n\t\t\tvar paths = getPaths(res);\n\t\t\tfor (var i = 0, l = paths.length; i < l; i++) {\n\t\t\t\tvar path = paths[i];\n\t\t\t\tif (!path._closed && !path.isEmpty()) {\n\t\t\t\t\tpath.closePath(1e-12);\n\t\t\t\t\tpath.getFirstSegment().setHandleIn(0, 0);\n\t\t\t\t\tpath.getLastSegment().setHandleOut(0, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tres = res\n\t\t\t\t.resolveCrossings()\n\t\t\t\t.reorient(res.getFillRule() === 'nonzero', true);\n\t\t}\n\t\treturn res;\n\t}\n\n\tfunction createResult(paths, simplify, path1, path2, options) {\n\t\tvar result = new CompoundPath(Item.NO_INSERT);\n\t\tresult.addChildren(paths, true);\n\t\tresult = result.reduce({ simplify: simplify });\n\t\tif (!(options && options.insert == false)) {\n\t\t\tresult.insertAbove(path2 && path1.isSibling(path2)\n\t\t\t\t\t&& path1.getIndex() < path2.getIndex() ? path2 : path1);\n\t\t}\n\t\tresult.copyAttributes(path1, true);\n\t\treturn result;\n\t}\n\n\tfunction filterIntersection(inter) {\n\t\treturn inter.hasOverlap() || inter.isCrossing();\n\t}\n\n\tfunction traceBoolean(path1, path2, operation, options) {\n\t\tif (options && (options.trace == false || options.stroke) &&\n\t\t\t\t/^(subtract|intersect)$/.test(operation))\n\t\t\treturn splitBoolean(path1, path2, operation);\n\t\tvar _path1 = preparePath(path1, true),\n\t\t\t_path2 = path2 && path1 !== path2 && preparePath(path2, true),\n\t\t\toperator = operators[operation];\n\t\toperator[operation] = true;\n\t\tif (_path2 && (operator.subtract || operator.exclude)\n\t\t\t\t^ (_path2.isClockwise() ^ _path1.isClockwise()))\n\t\t\t_path2.reverse();\n\t\tvar crossings = divideLocations(CurveLocation.expand(\n\t\t\t\t_path1.getIntersections(_path2, filterIntersection))),\n\t\t\tpaths1 = getPaths(_path1),\n\t\t\tpaths2 = _path2 && getPaths(_path2),\n\t\t\tsegments = [],\n\t\t\tcurves = [],\n\t\t\tpaths;\n\n\t\tfunction collectPaths(paths) {\n\t\t\tfor (var i = 0, l = paths.length; i < l; i++) {\n\t\t\t\tvar path = paths[i];\n\t\t\t\tBase.push(segments, path._segments);\n\t\t\t\tBase.push(curves, path.getCurves());\n\t\t\t\tpath._overlapsOnly = true;\n\t\t\t}\n\t\t}\n\n\t\tfunction getCurves(indices) {\n\t\t\tvar list = [];\n\t\t\tfor (var i = 0, l = indices && indices.length; i < l; i++) {\n\t\t\t\tlist.push(curves[indices[i]]);\n\t\t\t}\n\t\t\treturn list;\n\t\t}\n\n\t\tif (crossings.length) {\n\t\t\tcollectPaths(paths1);\n\t\t\tif (paths2)\n\t\t\t\tcollectPaths(paths2);\n\n\t\t\tvar curvesValues = new Array(curves.length);\n\t\t\tfor (var i = 0, l = curves.length; i < l; i++) {\n\t\t\t\tcurvesValues[i] = curves[i].getValues();\n\t\t\t}\n\t\t\tvar curveCollisions = CollisionDetection.findCurveBoundsCollisions(\n\t\t\t\t\tcurvesValues, curvesValues, 0, true);\n\t\t\tvar curveCollisionsMap = {};\n\t\t\tfor (var i = 0; i < curves.length; i++) {\n\t\t\t\tvar curve = curves[i],\n\t\t\t\t\tid = curve._path._id,\n\t\t\t\t\tmap = curveCollisionsMap[id] = curveCollisionsMap[id] || {};\n\t\t\t\tmap[curve.getIndex()] = {\n\t\t\t\t\thor: getCurves(curveCollisions[i].hor),\n\t\t\t\t\tver: getCurves(curveCollisions[i].ver)\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tfor (var i = 0, l = crossings.length; i < l; i++) {\n\t\t\t\tpropagateWinding(crossings[i]._segment, _path1, _path2,\n\t\t\t\t\t\tcurveCollisionsMap, operator);\n\t\t\t}\n\t\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\t\tvar segment = segments[i],\n\t\t\t\t\tinter = segment._intersection;\n\t\t\t\tif (!segment._winding) {\n\t\t\t\t\tpropagateWinding(segment, _path1, _path2,\n\t\t\t\t\t\t\tcurveCollisionsMap, operator);\n\t\t\t\t}\n\t\t\t\tif (!(inter && inter._overlap))\n\t\t\t\t\tsegment._path._overlapsOnly = false;\n\t\t\t}\n\t\t\tpaths = tracePaths(segments, operator);\n\t\t} else {\n\t\t\tpaths = reorientPaths(\n\t\t\t\t\tpaths2 ? paths1.concat(paths2) : paths1.slice(),\n\t\t\t\t\tfunction(w) {\n\t\t\t\t\t\treturn !!operator[w];\n\t\t\t\t\t});\n\t\t}\n\t\treturn createResult(paths, true, path1, path2, options);\n\t}\n\n\tfunction splitBoolean(path1, path2, operation) {\n\t\tvar _path1 = preparePath(path1),\n\t\t\t_path2 = preparePath(path2),\n\t\t\tcrossings = _path1.getIntersections(_path2, filterIntersection),\n\t\t\tsubtract = operation === 'subtract',\n\t\t\tdivide = operation === 'divide',\n\t\t\tadded = {},\n\t\t\tpaths = [];\n\n\t\tfunction addPath(path) {\n\t\t\tif (!added[path._id] && (divide ||\n\t\t\t\t\t_path2.contains(path.getPointAt(path.getLength() / 2))\n\t\t\t\t\t\t^ subtract)) {\n\t\t\t\tpaths.unshift(path);\n\t\t\t\treturn added[path._id] = true;\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = crossings.length - 1; i >= 0; i--) {\n\t\t\tvar path = crossings[i].split();\n\t\t\tif (path) {\n\t\t\t\tif (addPath(path))\n\t\t\t\t\tpath.getFirstSegment().setHandleIn(0, 0);\n\t\t\t\t_path1.getLastSegment().setHandleOut(0, 0);\n\t\t\t}\n\t\t}\n\t\taddPath(_path1);\n\t\treturn createResult(paths, false, path1, path2);\n\t}\n\n\tfunction linkIntersections(from, to) {\n\t\tvar prev = from;\n\t\twhile (prev) {\n\t\t\tif (prev === to)\n\t\t\t\treturn;\n\t\t\tprev = prev._previous;\n\t\t}\n\t\twhile (from._next && from._next !== to)\n\t\t\tfrom = from._next;\n\t\tif (!from._next) {\n\t\t\twhile (to._previous)\n\t\t\t\tto = to._previous;\n\t\t\tfrom._next = to;\n\t\t\tto._previous = from;\n\t\t}\n\t}\n\n\tfunction clearCurveHandles(curves) {\n\t\tfor (var i = curves.length - 1; i >= 0; i--)\n\t\t\tcurves[i].clearHandles();\n\t}\n\n\tfunction reorientPaths(paths, isInside, clockwise) {\n\t\tvar length = paths && paths.length;\n\t\tif (length) {\n\t\t\tvar lookup = Base.each(paths, function (path, i) {\n\t\t\t\t\tthis[path._id] = {\n\t\t\t\t\t\tcontainer: null,\n\t\t\t\t\t\twinding: path.isClockwise() ? 1 : -1,\n\t\t\t\t\t\tindex: i\n\t\t\t\t\t};\n\t\t\t\t}, {}),\n\t\t\t\tsorted = paths.slice().sort(function (a, b) {\n\t\t\t\t\treturn abs(b.getArea()) - abs(a.getArea());\n\t\t\t\t}),\n\t\t\t\tfirst = sorted[0];\n\t\t\tvar collisions = CollisionDetection.findItemBoundsCollisions(sorted,\n\t\t\t\t\tnull, Numerical.GEOMETRIC_EPSILON);\n\t\t\tif (clockwise == null)\n\t\t\t\tclockwise = first.isClockwise();\n\t\t\tfor (var i = 0; i < length; i++) {\n\t\t\t\tvar path1 = sorted[i],\n\t\t\t\t\tentry1 = lookup[path1._id],\n\t\t\t\t\tcontainerWinding = 0,\n\t\t\t\t\tindices = collisions[i];\n\t\t\t\tif (indices) {\n\t\t\t\t\tvar point = null;\n\t\t\t\t\tfor (var j = indices.length - 1; j >= 0; j--) {\n\t\t\t\t\t\tif (indices[j] < i) {\n\t\t\t\t\t\t\tpoint = point || path1.getInteriorPoint();\n\t\t\t\t\t\t\tvar path2 = sorted[indices[j]];\n\t\t\t\t\t\t\tif (path2.contains(point)) {\n\t\t\t\t\t\t\t\tvar entry2 = lookup[path2._id];\n\t\t\t\t\t\t\t\tcontainerWinding = entry2.winding;\n\t\t\t\t\t\t\t\tentry1.winding += containerWinding;\n\t\t\t\t\t\t\t\tentry1.container = entry2.exclude\n\t\t\t\t\t\t\t\t\t? entry2.container : path2;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isInside(entry1.winding) === isInside(containerWinding)) {\n\t\t\t\t\tentry1.exclude = true;\n\t\t\t\t\tpaths[entry1.index] = null;\n\t\t\t\t} else {\n\t\t\t\t\tvar container = entry1.container;\n\t\t\t\t\tpath1.setClockwise(\n\t\t\t\t\t\t\tcontainer ? !container.isClockwise() : clockwise);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\tfunction divideLocations(locations, include, clearLater) {\n\t\tvar results = include && [],\n\t\t\ttMin = 1e-8,\n\t\t\ttMax = 1 - tMin,\n\t\t\tclearHandles = false,\n\t\t\tclearCurves = clearLater || [],\n\t\t\tclearLookup = clearLater && {},\n\t\t\trenormalizeLocs,\n\t\t\tprevCurve,\n\t\t\tprevTime;\n\n\t\tfunction getId(curve) {\n\t\t\treturn curve._path._id + '.' + curve._segment1._index;\n\t\t}\n\n\t\tfor (var i = (clearLater && clearLater.length) - 1; i >= 0; i--) {\n\t\t\tvar curve = clearLater[i];\n\t\t\tif (curve._path)\n\t\t\t\tclearLookup[getId(curve)] = true;\n\t\t}\n\n\t\tfor (var i = locations.length - 1; i >= 0; i--) {\n\t\t\tvar loc = locations[i],\n\t\t\t\ttime = loc._time,\n\t\t\t\torigTime = time,\n\t\t\t\texclude = include && !include(loc),\n\t\t\t\tcurve = loc._curve,\n\t\t\t\tsegment;\n\t\t\tif (curve) {\n\t\t\t\tif (curve !== prevCurve) {\n\t\t\t\t\tclearHandles = !curve.hasHandles()\n\t\t\t\t\t\t\t|| clearLookup && clearLookup[getId(curve)];\n\t\t\t\t\trenormalizeLocs = [];\n\t\t\t\t\tprevTime = null;\n\t\t\t\t\tprevCurve = curve;\n\t\t\t\t} else if (prevTime >= tMin) {\n\t\t\t\t\ttime /= prevTime;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (exclude) {\n\t\t\t\tif (renormalizeLocs)\n\t\t\t\t\trenormalizeLocs.push(loc);\n\t\t\t\tcontinue;\n\t\t\t} else if (include) {\n\t\t\t\tresults.unshift(loc);\n\t\t\t}\n\t\t\tprevTime = origTime;\n\t\t\tif (time < tMin) {\n\t\t\t\tsegment = curve._segment1;\n\t\t\t} else if (time > tMax) {\n\t\t\t\tsegment = curve._segment2;\n\t\t\t} else {\n\t\t\t\tvar newCurve = curve.divideAtTime(time, true);\n\t\t\t\tif (clearHandles)\n\t\t\t\t\tclearCurves.push(curve, newCurve);\n\t\t\t\tsegment = newCurve._segment1;\n\t\t\t\tfor (var j = renormalizeLocs.length - 1; j >= 0; j--) {\n\t\t\t\t\tvar l = renormalizeLocs[j];\n\t\t\t\t\tl._time = (l._time - time) / (1 - time);\n\t\t\t\t}\n\t\t\t}\n\t\t\tloc._setSegment(segment);\n\t\t\tvar inter = segment._intersection,\n\t\t\t\tdest = loc._intersection;\n\t\t\tif (inter) {\n\t\t\t\tlinkIntersections(inter, dest);\n\t\t\t\tvar other = inter;\n\t\t\t\twhile (other) {\n\t\t\t\t\tlinkIntersections(other._intersection, inter);\n\t\t\t\t\tother = other._next;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsegment._intersection = dest;\n\t\t\t}\n\t\t}\n\t\tif (!clearLater)\n\t\t\tclearCurveHandles(clearCurves);\n\t\treturn results || locations;\n\t}\n\n\tfunction getWinding(point, curves, dir, closed, dontFlip) {\n\t\tvar curvesList = Array.isArray(curves)\n\t\t\t? curves\n\t\t\t: curves[dir ? 'hor' : 'ver'];\n\t\tvar ia = dir ? 1 : 0,\n\t\t\tio = ia ^ 1,\n\t\t\tpv = [point.x, point.y],\n\t\t\tpa = pv[ia],\n\t\t\tpo = pv[io],\n\t\t\twindingEpsilon = 1e-9,\n\t\t\tqualityEpsilon = 1e-6,\n\t\t\tpaL = pa - windingEpsilon,\n\t\t\tpaR = pa + windingEpsilon,\n\t\t\twindingL = 0,\n\t\t\twindingR = 0,\n\t\t\tpathWindingL = 0,\n\t\t\tpathWindingR = 0,\n\t\t\tonPath = false,\n\t\t\tonAnyPath = false,\n\t\t\tquality = 1,\n\t\t\troots = [],\n\t\t\tvPrev,\n\t\t\tvClose;\n\n\t\tfunction addWinding(v) {\n\t\t\tvar o0 = v[io + 0],\n\t\t\t\to3 = v[io + 6];\n\t\t\tif (po < min(o0, o3) || po > max(o0, o3)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar a0 = v[ia + 0],\n\t\t\t\ta1 = v[ia + 2],\n\t\t\t\ta2 = v[ia + 4],\n\t\t\t\ta3 = v[ia + 6];\n\t\t\tif (o0 === o3) {\n\t\t\t\tif (a0 < paR && a3 > paL || a3 < paR && a0 > paL) {\n\t\t\t\t\tonPath = true;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar t = po === o0 ? 0\n\t\t\t\t\t: po === o3 ? 1\n\t\t\t\t\t: paL > max(a0, a1, a2, a3) || paR < min(a0, a1, a2, a3)\n\t\t\t\t\t? 1\n\t\t\t\t\t: Curve.solveCubic(v, io, po, roots, 0, 1) > 0\n\t\t\t\t\t\t? roots[0]\n\t\t\t\t\t\t: 1,\n\t\t\t\ta = t === 0 ? a0\n\t\t\t\t\t: t === 1 ? a3\n\t\t\t\t\t: Curve.getPoint(v, t)[dir ? 'y' : 'x'],\n\t\t\t\twinding = o0 > o3 ? 1 : -1,\n\t\t\t\twindingPrev = vPrev[io] > vPrev[io + 6] ? 1 : -1,\n\t\t\t\ta3Prev = vPrev[ia + 6];\n\t\t\tif (po !== o0) {\n\t\t\t\tif (a < paL) {\n\t\t\t\t\tpathWindingL += winding;\n\t\t\t\t} else if (a > paR) {\n\t\t\t\t\tpathWindingR += winding;\n\t\t\t\t} else {\n\t\t\t\t\tonPath = true;\n\t\t\t\t}\n\t\t\t\tif (a > pa - qualityEpsilon && a < pa + qualityEpsilon)\n\t\t\t\t\tquality /= 2;\n\t\t\t} else {\n\t\t\t\tif (winding !== windingPrev) {\n\t\t\t\t\tif (a0 < paL) {\n\t\t\t\t\t\tpathWindingL += winding;\n\t\t\t\t\t} else if (a0 > paR) {\n\t\t\t\t\t\tpathWindingR += winding;\n\t\t\t\t\t}\n\t\t\t\t} else if (a0 != a3Prev) {\n\t\t\t\t\tif (a3Prev < paR && a > paR) {\n\t\t\t\t\t\tpathWindingR += winding;\n\t\t\t\t\t\tonPath = true;\n\t\t\t\t\t} else if (a3Prev > paL && a < paL) {\n\t\t\t\t\t\tpathWindingL += winding;\n\t\t\t\t\t\tonPath = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tquality /= 4;\n\t\t\t}\n\t\t\tvPrev = v;\n\t\t\treturn !dontFlip && a > paL && a < paR\n\t\t\t\t\t&& Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0\n\t\t\t\t\t&& getWinding(point, curves, !dir, closed, true);\n\t\t}\n\n\t\tfunction handleCurve(v) {\n\t\t\tvar o0 = v[io + 0],\n\t\t\t\to1 = v[io + 2],\n\t\t\t\to2 = v[io + 4],\n\t\t\t\to3 = v[io + 6];\n\t\t\tif (po <= max(o0, o1, o2, o3) && po >= min(o0, o1, o2, o3)) {\n\t\t\t\tvar a0 = v[ia + 0],\n\t\t\t\t\ta1 = v[ia + 2],\n\t\t\t\t\ta2 = v[ia + 4],\n\t\t\t\t\ta3 = v[ia + 6],\n\t\t\t\t\tmonoCurves = paL > max(a0, a1, a2, a3) ||\n\t\t\t\t\t\t\t\t paR < min(a0, a1, a2, a3)\n\t\t\t\t\t\t\t? [v] : Curve.getMonoCurves(v, dir),\n\t\t\t\t\tres;\n\t\t\t\tfor (var i = 0, l = monoCurves.length; i < l; i++) {\n\t\t\t\t\tif (res = addWinding(monoCurves[i]))\n\t\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0, l = curvesList.length; i < l; i++) {\n\t\t\tvar curve = curvesList[i],\n\t\t\t\tpath = curve._path,\n\t\t\t\tv = curve.getValues(),\n\t\t\t\tres;\n\t\t\tif (!i || curvesList[i - 1]._path !== path) {\n\t\t\t\tvPrev = null;\n\t\t\t\tif (!path._closed) {\n\t\t\t\t\tvClose = Curve.getValues(\n\t\t\t\t\t\t\tpath.getLastCurve().getSegment2(),\n\t\t\t\t\t\t\tcurve.getSegment1(),\n\t\t\t\t\t\t\tnull, !closed);\n\t\t\t\t\tif (vClose[io] !== vClose[io + 6]) {\n\t\t\t\t\t\tvPrev = vClose;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!vPrev) {\n\t\t\t\t\tvPrev = v;\n\t\t\t\t\tvar prev = path.getLastCurve();\n\t\t\t\t\twhile (prev && prev !== curve) {\n\t\t\t\t\t\tvar v2 = prev.getValues();\n\t\t\t\t\t\tif (v2[io] !== v2[io + 6]) {\n\t\t\t\t\t\t\tvPrev = v2;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprev = prev.getPrevious();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (res = handleCurve(v))\n\t\t\t\treturn res;\n\n\t\t\tif (i + 1 === l || curvesList[i + 1]._path !== path) {\n\t\t\t\tif (vClose && (res = handleCurve(vClose)))\n\t\t\t\t\treturn res;\n\t\t\t\tif (onPath && !pathWindingL && !pathWindingR) {\n\t\t\t\t\tpathWindingL = pathWindingR = path.isClockwise(closed) ^ dir\n\t\t\t\t\t\t\t? 1 : -1;\n\t\t\t\t}\n\t\t\t\twindingL += pathWindingL;\n\t\t\t\twindingR += pathWindingR;\n\t\t\t\tpathWindingL = pathWindingR = 0;\n\t\t\t\tif (onPath) {\n\t\t\t\t\tonAnyPath = true;\n\t\t\t\t\tonPath = false;\n\t\t\t\t}\n\t\t\t\tvClose = null;\n\t\t\t}\n\t\t}\n\t\twindingL = abs(windingL);\n\t\twindingR = abs(windingR);\n\t\treturn {\n\t\t\twinding: max(windingL, windingR),\n\t\t\twindingL: windingL,\n\t\t\twindingR: windingR,\n\t\t\tquality: quality,\n\t\t\tonPath: onAnyPath\n\t\t};\n\t}\n\n\tfunction propagateWinding(segment, path1, path2, curveCollisionsMap,\n\t\t\toperator) {\n\t\tvar chain = [],\n\t\t\tstart = segment,\n\t\t\ttotalLength = 0,\n\t\t\twinding;\n\t\tdo {\n\t\t\tvar curve = segment.getCurve();\n\t\t\tif (curve) {\n\t\t\t\tvar length = curve.getLength();\n\t\t\t\tchain.push({ segment: segment, curve: curve, length: length });\n\t\t\t\ttotalLength += length;\n\t\t\t}\n\t\t\tsegment = segment.getNext();\n\t\t} while (segment && !segment._intersection && segment !== start);\n\t\tvar offsets = [0.5, 0.25, 0.75],\n\t\t\twinding = { winding: 0, quality: -1 },\n\t\t\ttMin = 1e-3,\n\t\t\ttMax = 1 - tMin;\n\t\tfor (var i = 0; i < offsets.length && winding.quality < 0.5; i++) {\n\t\t\tvar length = totalLength * offsets[i];\n\t\t\tfor (var j = 0, l = chain.length; j < l; j++) {\n\t\t\t\tvar entry = chain[j],\n\t\t\t\t\tcurveLength = entry.length;\n\t\t\t\tif (length <= curveLength) {\n\t\t\t\t\tvar curve = entry.curve,\n\t\t\t\t\t\tpath = curve._path,\n\t\t\t\t\t\tparent = path._parent,\n\t\t\t\t\t\toperand = parent instanceof CompoundPath ? parent : path,\n\t\t\t\t\t\tt = Numerical.clamp(curve.getTimeAt(length), tMin, tMax),\n\t\t\t\t\t\tpt = curve.getPointAtTime(t),\n\t\t\t\t\t\tdir = abs(curve.getTangentAtTime(t).y) < Math.SQRT1_2;\n\t\t\t\t\tvar wind = null;\n\t\t\t\t\tif (operator.subtract && path2) {\n\t\t\t\t\t\tvar otherPath = operand === path1 ? path2 : path1,\n\t\t\t\t\t\t\tpathWinding = otherPath._getWinding(pt, dir, true);\n\t\t\t\t\t\tif (operand === path1 && pathWinding.winding ||\n\t\t\t\t\t\t\toperand === path2 && !pathWinding.winding) {\n\t\t\t\t\t\t\tif (pathWinding.quality < 1) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\twind = { winding: 0, quality: 1 };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\twind = wind || getWinding(\n\t\t\t\t\t\t\tpt, curveCollisionsMap[path._id][curve.getIndex()],\n\t\t\t\t\t\t\tdir, true);\n\t\t\t\t\tif (wind.quality > winding.quality)\n\t\t\t\t\t\twinding = wind;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlength -= curveLength;\n\t\t\t}\n\t\t}\n\t\tfor (var j = chain.length - 1; j >= 0; j--) {\n\t\t\tchain[j].segment._winding = winding;\n\t\t}\n\t}\n\n\tfunction tracePaths(segments, operator) {\n\t\tvar paths = [],\n\t\t\tstarts;\n\n\t\tfunction isValid(seg) {\n\t\t\tvar winding;\n\t\t\treturn !!(seg && !seg._visited && (!operator\n\t\t\t\t\t|| operator[(winding = seg._winding || {}).winding]\n\t\t\t\t\t\t&& !(operator.unite && winding.winding === 2\n\t\t\t\t\t\t\t&& winding.windingL && winding.windingR)));\n\t\t}\n\n\t\tfunction isStart(seg) {\n\t\t\tif (seg) {\n\t\t\t\tfor (var i = 0, l = starts.length; i < l; i++) {\n\t\t\t\t\tif (seg === starts[i])\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction visitPath(path) {\n\t\t\tvar segments = path._segments;\n\t\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\t\tsegments[i]._visited = true;\n\t\t\t}\n\t\t}\n\n\t\tfunction getCrossingSegments(segment, collectStarts) {\n\t\t\tvar inter = segment._intersection,\n\t\t\t\tstart = inter,\n\t\t\t\tcrossings = [];\n\t\t\tif (collectStarts)\n\t\t\t\tstarts = [segment];\n\n\t\t\tfunction collect(inter, end) {\n\t\t\t\twhile (inter && inter !== end) {\n\t\t\t\t\tvar other = inter._segment,\n\t\t\t\t\t\tpath = other && other._path;\n\t\t\t\t\tif (path) {\n\t\t\t\t\t\tvar next = other.getNext() || path.getFirstSegment(),\n\t\t\t\t\t\t\tnextInter = next._intersection;\n\t\t\t\t\t\tif (other !== segment && (isStart(other)\n\t\t\t\t\t\t\t|| isStart(next)\n\t\t\t\t\t\t\t|| next && (isValid(other) && (isValid(next)\n\t\t\t\t\t\t\t\t|| nextInter && isValid(nextInter._segment))))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tcrossings.push(other);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (collectStarts)\n\t\t\t\t\t\t\tstarts.push(other);\n\t\t\t\t\t}\n\t\t\t\t\tinter = inter._next;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inter) {\n\t\t\t\tcollect(inter);\n\t\t\t\twhile (inter && inter._previous)\n\t\t\t\t\tinter = inter._previous;\n\t\t\t\tcollect(inter, start);\n\t\t\t}\n\t\t\treturn crossings;\n\t\t}\n\n\t\tsegments.sort(function(seg1, seg2) {\n\t\t\tvar inter1 = seg1._intersection,\n\t\t\t\tinter2 = seg2._intersection,\n\t\t\t\tover1 = !!(inter1 && inter1._overlap),\n\t\t\t\tover2 = !!(inter2 && inter2._overlap),\n\t\t\t\tpath1 = seg1._path,\n\t\t\t\tpath2 = seg2._path;\n\t\t\treturn over1 ^ over2\n\t\t\t\t\t? over1 ? 1 : -1\n\t\t\t\t\t: !inter1 ^ !inter2\n\t\t\t\t\t\t? inter1 ? 1 : -1\n\t\t\t\t\t\t: path1 !== path2\n\t\t\t\t\t\t\t? path1._id - path2._id\n\t\t\t\t\t\t\t: seg1._index - seg2._index;\n\t\t});\n\n\t\tfor (var i = 0, l = segments.length; i < l; i++) {\n\t\t\tvar seg = segments[i],\n\t\t\t\tvalid = isValid(seg),\n\t\t\t\tpath = null,\n\t\t\t\tfinished = false,\n\t\t\t\tclosed = true,\n\t\t\t\tbranches = [],\n\t\t\t\tbranch,\n\t\t\t\tvisited,\n\t\t\t\thandleIn;\n\t\t\tif (valid && seg._path._overlapsOnly) {\n\t\t\t\tvar path1 = seg._path,\n\t\t\t\t\tpath2 = seg._intersection._segment._path;\n\t\t\t\tif (path1.compare(path2)) {\n\t\t\t\t\tif (path1.getArea())\n\t\t\t\t\t\tpaths.push(path1.clone(false));\n\t\t\t\t\tvisitPath(path1);\n\t\t\t\t\tvisitPath(path2);\n\t\t\t\t\tvalid = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (valid) {\n\t\t\t\tvar first = !path,\n\t\t\t\t\tcrossings = getCrossingSegments(seg, first),\n\t\t\t\t\tother = crossings.shift(),\n\t\t\t\t\tfinished = !first && (isStart(seg) || isStart(other)),\n\t\t\t\t\tcross = !finished && other;\n\t\t\t\tif (first) {\n\t\t\t\t\tpath = new Path(Item.NO_INSERT);\n\t\t\t\t\tbranch = null;\n\t\t\t\t}\n\t\t\t\tif (finished) {\n\t\t\t\t\tif (seg.isFirst() || seg.isLast())\n\t\t\t\t\t\tclosed = seg._path._closed;\n\t\t\t\t\tseg._visited = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (cross && branch) {\n\t\t\t\t\tbranches.push(branch);\n\t\t\t\t\tbranch = null;\n\t\t\t\t}\n\t\t\t\tif (!branch) {\n\t\t\t\t\tif (cross)\n\t\t\t\t\t\tcrossings.push(seg);\n\t\t\t\t\tbranch = {\n\t\t\t\t\t\tstart: path._segments.length,\n\t\t\t\t\t\tcrossings: crossings,\n\t\t\t\t\t\tvisited: visited = [],\n\t\t\t\t\t\thandleIn: handleIn\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tif (cross)\n\t\t\t\t\tseg = other;\n\t\t\t\tif (!isValid(seg)) {\n\t\t\t\t\tpath.removeSegments(branch.start);\n\t\t\t\t\tfor (var j = 0, k = visited.length; j < k; j++) {\n\t\t\t\t\t\tvisited[j]._visited = false;\n\t\t\t\t\t}\n\t\t\t\t\tvisited.length = 0;\n\t\t\t\t\tdo {\n\t\t\t\t\t\tseg = branch && branch.crossings.shift();\n\t\t\t\t\t\tif (!seg || !seg._path) {\n\t\t\t\t\t\t\tseg = null;\n\t\t\t\t\t\t\tbranch = branches.pop();\n\t\t\t\t\t\t\tif (branch) {\n\t\t\t\t\t\t\t\tvisited = branch.visited;\n\t\t\t\t\t\t\t\thandleIn = branch.handleIn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (branch && !isValid(seg));\n\t\t\t\t\tif (!seg)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tvar next = seg.getNext();\n\t\t\t\tpath.add(new Segment(seg._point, handleIn,\n\t\t\t\t\t\tnext && seg._handleOut));\n\t\t\t\tseg._visited = true;\n\t\t\t\tvisited.push(seg);\n\t\t\t\tseg = next || seg._path.getFirstSegment();\n\t\t\t\thandleIn = next && next._handleIn;\n\t\t\t}\n\t\t\tif (finished) {\n\t\t\t\tif (closed) {\n\t\t\t\t\tpath.getFirstSegment().setHandleIn(handleIn);\n\t\t\t\t\tpath.setClosed(closed);\n\t\t\t\t}\n\t\t\t\tif (path.getArea() !== 0) {\n\t\t\t\t\tpaths.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\treturn {\n\t\t_getWinding: function(point, dir, closed) {\n\t\t\treturn getWinding(point, this.getCurves(), dir, closed);\n\t\t},\n\n\t\tunite: function(path, options) {\n\t\t\treturn traceBoolean(this, path, 'unite', options);\n\t\t},\n\n\t\tintersect: function(path, options) {\n\t\t\treturn traceBoolean(this, path, 'intersect', options);\n\t\t},\n\n\t\tsubtract: function(path, options) {\n\t\t\treturn traceBoolean(this, path, 'subtract', options);\n\t\t},\n\n\t\texclude: function(path, options) {\n\t\t\treturn traceBoolean(this, path, 'exclude', options);\n\t\t},\n\n\t\tdivide: function(path, options) {\n\t\t\treturn options && (options.trace == false || options.stroke)\n\t\t\t\t\t? splitBoolean(this, path, 'divide')\n\t\t\t\t\t: createResult([\n\t\t\t\t\t\tthis.subtract(path, options),\n\t\t\t\t\t\tthis.intersect(path, options)\n\t\t\t\t\t], true, this, path, options);\n\t\t},\n\n\t\tresolveCrossings: function() {\n\t\t\tvar children = this._children,\n\t\t\t\tpaths = children || [this];\n\n\t\t\tfunction hasOverlap(seg, path) {\n\t\t\t\tvar inter = seg && seg._intersection;\n\t\t\t\treturn inter && inter._overlap && inter._path === path;\n\t\t\t}\n\n\t\t\tvar hasOverlaps = false,\n\t\t\t\thasCrossings = false,\n\t\t\t\tintersections = this.getIntersections(null, function(inter) {\n\t\t\t\t\treturn inter.hasOverlap() && (hasOverlaps = true) ||\n\t\t\t\t\t\t\tinter.isCrossing() && (hasCrossings = true);\n\t\t\t\t}),\n\t\t\t\tclearCurves = hasOverlaps && hasCrossings && [];\n\t\t\tintersections = CurveLocation.expand(intersections);\n\t\t\tif (hasOverlaps) {\n\t\t\t\tvar overlaps = divideLocations(intersections, function(inter) {\n\t\t\t\t\treturn inter.hasOverlap();\n\t\t\t\t}, clearCurves);\n\t\t\t\tfor (var i = overlaps.length - 1; i >= 0; i--) {\n\t\t\t\t\tvar overlap = overlaps[i],\n\t\t\t\t\t\tpath = overlap._path,\n\t\t\t\t\t\tseg = overlap._segment,\n\t\t\t\t\t\tprev = seg.getPrevious(),\n\t\t\t\t\t\tnext = seg.getNext();\n\t\t\t\t\tif (hasOverlap(prev, path) && hasOverlap(next, path)) {\n\t\t\t\t\t\tseg.remove();\n\t\t\t\t\t\tprev._handleOut._set(0, 0);\n\t\t\t\t\t\tnext._handleIn._set(0, 0);\n\t\t\t\t\t\tif (prev !== seg && !prev.getCurve().hasLength()) {\n\t\t\t\t\t\t\tnext._handleIn.set(prev._handleIn);\n\t\t\t\t\t\t\tprev.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasCrossings) {\n\t\t\t\tdivideLocations(intersections, hasOverlaps && function(inter) {\n\t\t\t\t\tvar curve1 = inter.getCurve(),\n\t\t\t\t\t\tseg1 = inter.getSegment(),\n\t\t\t\t\t\tother = inter._intersection,\n\t\t\t\t\t\tcurve2 = other._curve,\n\t\t\t\t\t\tseg2 = other._segment;\n\t\t\t\t\tif (curve1 && curve2 && curve1._path && curve2._path)\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tif (seg1)\n\t\t\t\t\t\tseg1._intersection = null;\n\t\t\t\t\tif (seg2)\n\t\t\t\t\t\tseg2._intersection = null;\n\t\t\t\t}, clearCurves);\n\t\t\t\tif (clearCurves)\n\t\t\t\t\tclearCurveHandles(clearCurves);\n\t\t\t\tpaths = tracePaths(Base.each(paths, function(path) {\n\t\t\t\t\tBase.push(this, path._segments);\n\t\t\t\t}, []));\n\t\t\t}\n\t\t\tvar length = paths.length,\n\t\t\t\titem;\n\t\t\tif (length > 1 && children) {\n\t\t\t\tif (paths !== children)\n\t\t\t\t\tthis.setChildren(paths);\n\t\t\t\titem = this;\n\t\t\t} else if (length === 1 && !children) {\n\t\t\t\tif (paths[0] !== this)\n\t\t\t\t\tthis.setSegments(paths[0].removeSegments());\n\t\t\t\titem = this;\n\t\t\t}\n\t\t\tif (!item) {\n\t\t\t\titem = new CompoundPath(Item.NO_INSERT);\n\t\t\t\titem.addChildren(paths);\n\t\t\t\titem = item.reduce();\n\t\t\t\titem.copyAttributes(this);\n\t\t\t\tthis.replaceWith(item);\n\t\t\t}\n\t\t\treturn item;\n\t\t},\n\n\t\treorient: function(nonZero, clockwise) {\n\t\t\tvar children = this._children;\n\t\t\tif (children && children.length) {\n\t\t\t\tthis.setChildren(reorientPaths(this.removeChildren(),\n\t\t\t\t\t\tfunction(w) {\n\t\t\t\t\t\t\treturn !!(nonZero ? w : w & 1);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tclockwise));\n\t\t\t} else if (clockwise !== undefined) {\n\t\t\t\tthis.setClockwise(clockwise);\n\t\t\t}\n\t\t\treturn this;\n\t\t},\n\n\t\tgetInteriorPoint: function() {\n\t\t\tvar bounds = this.getBounds(),\n\t\t\t\tpoint = bounds.getCenter(true);\n\t\t\tif (!this.contains(point)) {\n\t\t\t\tvar curves = this.getCurves(),\n\t\t\t\t\ty = point.y,\n\t\t\t\t\tintercepts = [],\n\t\t\t\t\troots = [];\n\t\t\t\tfor (var i = 0, l = curves.length; i < l; i++) {\n\t\t\t\t\tvar v = curves[i].getValues(),\n\t\t\t\t\t\to0 = v[1],\n\t\t\t\t\t\to1 = v[3],\n\t\t\t\t\t\to2 = v[5],\n\t\t\t\t\t\to3 = v[7];\n\t\t\t\t\tif (y >= min(o0, o1, o2, o3) && y <= max(o0, o1, o2, o3)) {\n\t\t\t\t\t\tvar monoCurves = Curve.getMonoCurves(v);\n\t\t\t\t\t\tfor (var j = 0, m = monoCurves.length; j < m; j++) {\n\t\t\t\t\t\t\tvar mv = monoCurves[j],\n\t\t\t\t\t\t\t\tmo0 = mv[1],\n\t\t\t\t\t\t\t\tmo3 = mv[7];\n\t\t\t\t\t\t\tif ((mo0 !== mo3) &&\n\t\t\t\t\t\t\t\t(y >= mo0 && y <= mo3 || y >= mo3 && y <= mo0)){\n\t\t\t\t\t\t\t\tvar x = y === mo0 ? mv[0]\n\t\t\t\t\t\t\t\t\t: y === mo3 ? mv[6]\n\t\t\t\t\t\t\t\t\t: Curve.solveCubic(mv, 1, y, roots, 0, 1)\n\t\t\t\t\t\t\t\t\t\t=== 1\n\t\t\t\t\t\t\t\t\t\t? Curve.getPoint(mv, roots[0]).x\n\t\t\t\t\t\t\t\t\t\t: (mv[0] + mv[6]) / 2;\n\t\t\t\t\t\t\t\tintercepts.push(x);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (intercepts.length > 1) {\n\t\t\t\t\tintercepts.sort(function(a, b) { return a - b; });\n\t\t\t\t\tpoint.x = (intercepts[0] + intercepts[1]) / 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn point;\n\t\t}\n\t};\n});\n\nvar PathFlattener = Base.extend({\n\t_class: 'PathFlattener',\n\n\tinitialize: function(path, flatness, maxRecursion, ignoreStraight, matrix) {\n\t\tvar curves = [],\n\t\t\tparts = [],\n\t\t\tlength = 0,\n\t\t\tminSpan = 1 / (maxRecursion || 32),\n\t\t\tsegments = path._segments,\n\t\t\tsegment1 = segments[0],\n\t\t\tsegment2;\n\n\t\tfunction addCurve(segment1, segment2) {\n\t\t\tvar curve = Curve.getValues(segment1, segment2, matrix);\n\t\t\tcurves.push(curve);\n\t\t\tcomputeParts(curve, segment1._index, 0, 1);\n\t\t}\n\n\t\tfunction computeParts(curve, index, t1, t2) {\n\t\t\tif ((t2 - t1) > minSpan\n\t\t\t\t\t&& !(ignoreStraight && Curve.isStraight(curve))\n\t\t\t\t\t&& !Curve.isFlatEnough(curve, flatness || 0.25)) {\n\t\t\t\tvar halves = Curve.subdivide(curve, 0.5),\n\t\t\t\t\ttMid = (t1 + t2) / 2;\n\t\t\t\tcomputeParts(halves[0], index, t1, tMid);\n\t\t\t\tcomputeParts(halves[1], index, tMid, t2);\n\t\t\t} else {\n\t\t\t\tvar dx = curve[6] - curve[0],\n\t\t\t\t\tdy = curve[7] - curve[1],\n\t\t\t\t\tdist = Math.sqrt(dx * dx + dy * dy);\n\t\t\t\tif (dist > 0) {\n\t\t\t\t\tlength += dist;\n\t\t\t\t\tparts.push({\n\t\t\t\t\t\toffset: length,\n\t\t\t\t\t\tcurve: curve,\n\t\t\t\t\t\tindex: index,\n\t\t\t\t\t\ttime: t2,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 1, l = segments.length; i < l; i++) {\n\t\t\tsegment2 = segments[i];\n\t\t\taddCurve(segment1, segment2);\n\t\t\tsegment1 = segment2;\n\t\t}\n\t\tif (path._closed)\n\t\t\taddCurve(segment2 || segment1, segments[0]);\n\t\tthis.curves = curves;\n\t\tthis.parts = parts;\n\t\tthis.length = length;\n\t\tthis.index = 0;\n\t},\n\n\t_get: function(offset) {\n\t\tvar parts = this.parts,\n\t\t\tlength = parts.length,\n\t\t\tstart,\n\t\t\ti, j = this.index;\n\t\tfor (;;) {\n\t\t\ti = j;\n\t\t\tif (!j || parts[--j].offset < offset)\n\t\t\t\tbreak;\n\t\t}\n\t\tfor (; i < length; i++) {\n\t\t\tvar part = parts[i];\n\t\t\tif (part.offset >= offset) {\n\t\t\t\tthis.index = i;\n\t\t\t\tvar prev = parts[i - 1],\n\t\t\t\t\tprevTime = prev && prev.index === part.index ? prev.time : 0,\n\t\t\t\t\tprevOffset = prev ? prev.offset : 0;\n\t\t\t\treturn {\n\t\t\t\t\tindex: part.index,\n\t\t\t\t\ttime: prevTime + (part.time - prevTime)\n\t\t\t\t\t\t* (offset - prevOffset) / (part.offset - prevOffset)\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tindex: parts[length - 1].index,\n\t\t\ttime: 1\n\t\t};\n\t},\n\n\tdrawPart: function(ctx, from, to) {\n\t\tvar start = this._get(from),\n\t\t\tend = this._get(to);\n\t\tfor (var i = start.index, l = end.index; i <= l; i++) {\n\t\t\tvar curve = Curve.getPart(this.curves[i],\n\t\t\t\t\ti === start.index ? start.time : 0,\n\t\t\t\t\ti === end.index ? end.time : 1);\n\t\t\tif (i === start.index)\n\t\t\t\tctx.moveTo(curve[0], curve[1]);\n\t\t\tctx.bezierCurveTo.apply(ctx, curve.slice(2));\n\t\t}\n\t}\n}, Base.each(Curve._evaluateMethods,\n\tfunction(name) {\n\t\tthis[name + 'At'] = function(offset) {\n\t\t\tvar param = this._get(offset);\n\t\t\treturn Curve[name](this.curves[param.index], param.time);\n\t\t};\n\t}, {})\n);\n\nvar PathFitter = Base.extend({\n\tinitialize: function(path) {\n\t\tvar points = this.points = [],\n\t\t\tsegments = path._segments,\n\t\t\tclosed = path._closed;\n\t\tfor (var i = 0, prev, l = segments.length; i < l; i++) {\n\t\t\tvar point = segments[i].point;\n\t\t\tif (!prev || !prev.equals(point)) {\n\t\t\t\tpoints.push(prev = point.clone());\n\t\t\t}\n\t\t}\n\t\tif (closed) {\n\t\t\tpoints.unshift(points[points.length - 1]);\n\t\t\tpoints.push(points[1]);\n\t\t}\n\t\tthis.closed = closed;\n\t},\n\n\tfit: function(error) {\n\t\tvar points = this.points,\n\t\t\tlength = points.length,\n\t\t\tsegments = null;\n\t\tif (length > 0) {\n\t\t\tsegments = [new Segment(points[0])];\n\t\t\tif (length > 1) {\n\t\t\t\tthis.fitCubic(segments, error, 0, length - 1,\n\t\t\t\t\t\tpoints[1].subtract(points[0]),\n\t\t\t\t\t\tpoints[length - 2].subtract(points[length - 1]));\n\t\t\t\tif (this.closed) {\n\t\t\t\t\tsegments.shift();\n\t\t\t\t\tsegments.pop();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn segments;\n\t},\n\n\tfitCubic: function(segments, error, first, last, tan1, tan2) {\n\t\tvar points = this.points;\n\t\tif (last - first === 1) {\n\t\t\tvar pt1 = points[first],\n\t\t\t\tpt2 = points[last],\n\t\t\t\tdist = pt1.getDistance(pt2) / 3;\n\t\t\tthis.addCurve(segments, [pt1, pt1.add(tan1.normalize(dist)),\n\t\t\t\t\tpt2.add(tan2.normalize(dist)), pt2]);\n\t\t\treturn;\n\t\t}\n\t\tvar uPrime = this.chordLengthParameterize(first, last),\n\t\t\tmaxError = Math.max(error, error * error),\n\t\t\tsplit,\n\t\t\tparametersInOrder = true;\n\t\tfor (var i = 0; i <= 4; i++) {\n\t\t\tvar curve = this.generateBezier(first, last, uPrime, tan1, tan2);\n\t\t\tvar max = this.findMaxError(first, last, curve, uPrime);\n\t\t\tif (max.error < error && parametersInOrder) {\n\t\t\t\tthis.addCurve(segments, curve);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsplit = max.index;\n\t\t\tif (max.error >= maxError)\n\t\t\t\tbreak;\n\t\t\tparametersInOrder = this.reparameterize(first, last, uPrime, curve);\n\t\t\tmaxError = max.error;\n\t\t}\n\t\tvar tanCenter = points[split - 1].subtract(points[split + 1]);\n\t\tthis.fitCubic(segments, error, first, split, tan1, tanCenter);\n\t\tthis.fitCubic(segments, error, split, last, tanCenter.negate(), tan2);\n\t},\n\n\taddCurve: function(segments, curve) {\n\t\tvar prev = segments[segments.length - 1];\n\t\tprev.setHandleOut(curve[1].subtract(curve[0]));\n\t\tsegments.push(new Segment(curve[3], curve[2].subtract(curve[3])));\n\t},\n\n\tgenerateBezier: function(first, last, uPrime, tan1, tan2) {\n\t\tvar epsilon = 1e-12,\n\t\t\tabs = Math.abs,\n\t\t\tpoints = this.points,\n\t\t\tpt1 = points[first],\n\t\t\tpt2 = points[last],\n\t\t\tC = [[0, 0], [0, 0]],\n\t\t\tX = [0, 0];\n\n\t\tfor (var i = 0, l = last - first + 1; i < l; i++) {\n\t\t\tvar u = uPrime[i],\n\t\t\t\tt = 1 - u,\n\t\t\t\tb = 3 * u * t,\n\t\t\t\tb0 = t * t * t,\n\t\t\t\tb1 = b * t,\n\t\t\t\tb2 = b * u,\n\t\t\t\tb3 = u * u * u,\n\t\t\t\ta1 = tan1.normalize(b1),\n\t\t\t\ta2 = tan2.normalize(b2),\n\t\t\t\ttmp = points[first + i]\n\t\t\t\t\t.subtract(pt1.multiply(b0 + b1))\n\t\t\t\t\t.subtract(pt2.multiply(b2 + b3));\n\t\t\tC[0][0] += a1.dot(a1);\n\t\t\tC[0][1] += a1.dot(a2);\n\t\t\tC[1][0] = C[0][1];\n\t\t\tC[1][1] += a2.dot(a2);\n\t\t\tX[0] += a1.dot(tmp);\n\t\t\tX[1] += a2.dot(tmp);\n\t\t}\n\n\t\tvar detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],\n\t\t\talpha1,\n\t\t\talpha2;\n\t\tif (abs(detC0C1) > epsilon) {\n\t\t\tvar detC0X = C[0][0] * X[1] - C[1][0] * X[0],\n\t\t\t\tdetXC1 = X[0] * C[1][1] - X[1] * C[0][1];\n\t\t\talpha1 = detXC1 / detC0C1;\n\t\t\talpha2 = detC0X / detC0C1;\n\t\t} else {\n\t\t\tvar c0 = C[0][0] + C[0][1],\n\t\t\t\tc1 = C[1][0] + C[1][1];\n\t\t\talpha1 = alpha2 = abs(c0) > epsilon ? X[0] / c0\n\t\t\t\t\t\t\t: abs(c1) > epsilon ? X[1] / c1\n\t\t\t\t\t\t\t: 0;\n\t\t}\n\n\t\tvar segLength = pt2.getDistance(pt1),\n\t\t\teps = epsilon * segLength,\n\t\t\thandle1,\n\t\t\thandle2;\n\t\tif (alpha1 < eps || alpha2 < eps) {\n\t\t\talpha1 = alpha2 = segLength / 3;\n\t\t} else {\n\t\t\tvar line = pt2.subtract(pt1);\n\t\t\thandle1 = tan1.normalize(alpha1);\n\t\t\thandle2 = tan2.normalize(alpha2);\n\t\t\tif (handle1.dot(line) - handle2.dot(line) > segLength * segLength) {\n\t\t\t\talpha1 = alpha2 = segLength / 3;\n\t\t\t\thandle1 = handle2 = null;\n\t\t\t}\n\t\t}\n\n\t\treturn [pt1,\n\t\t\t\tpt1.add(handle1 || tan1.normalize(alpha1)),\n\t\t\t\tpt2.add(handle2 || tan2.normalize(alpha2)),\n\t\t\t\tpt2];\n\t},\n\n\treparameterize: function(first, last, u, curve) {\n\t\tfor (var i = first; i <= last; i++) {\n\t\t\tu[i - first] = this.findRoot(curve, this.points[i], u[i - first]);\n\t\t}\n\t\tfor (var i = 1, l = u.length; i < l; i++) {\n\t\t\tif (u[i] <= u[i - 1])\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\tfindRoot: function(curve, point, u) {\n\t\tvar curve1 = [],\n\t\t\tcurve2 = [];\n\t\tfor (var i = 0; i <= 2; i++) {\n\t\t\tcurve1[i] = curve[i + 1].subtract(curve[i]).multiply(3);\n\t\t}\n\t\tfor (var i = 0; i <= 1; i++) {\n\t\t\tcurve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2);\n\t\t}\n\t\tvar pt = this.evaluate(3, curve, u),\n\t\t\tpt1 = this.evaluate(2, curve1, u),\n\t\t\tpt2 = this.evaluate(1, curve2, u),\n\t\t\tdiff = pt.subtract(point),\n\t\t\tdf = pt1.dot(pt1) + diff.dot(pt2);\n\t\treturn Numerical.isMachineZero(df) ? u : u - diff.dot(pt1) / df;\n\t},\n\n\tevaluate: function(degree, curve, t) {\n\t\tvar tmp = curve.slice();\n\t\tfor (var i = 1; i <= degree; i++) {\n\t\t\tfor (var j = 0; j <= degree - i; j++) {\n\t\t\t\ttmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t));\n\t\t\t}\n\t\t}\n\t\treturn tmp[0];\n\t},\n\n\tchordLengthParameterize: function(first, last) {\n\t\tvar u = [0];\n\t\tfor (var i = first + 1; i <= last; i++) {\n\t\t\tu[i - first] = u[i - first - 1]\n\t\t\t\t\t+ this.points[i].getDistance(this.points[i - 1]);\n\t\t}\n\t\tfor (var i = 1, m = last - first; i <= m; i++) {\n\t\t\tu[i] /= u[m];\n\t\t}\n\t\treturn u;\n\t},\n\n\tfindMaxError: function(first, last, curve, u) {\n\t\tvar index = Math.floor((last - first + 1) / 2),\n\t\t\tmaxDist = 0;\n\t\tfor (var i = first + 1; i < last; i++) {\n\t\t\tvar P = this.evaluate(3, curve, u[i - first]);\n\t\t\tvar v = P.subtract(this.points[i]);\n\t\t\tvar dist = v.x * v.x + v.y * v.y;\n\t\t\tif (dist >= maxDist) {\n\t\t\t\tmaxDist = dist;\n\t\t\t\tindex = i;\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\terror: maxDist,\n\t\t\tindex: index\n\t\t};\n\t}\n});\n\nvar TextItem = Item.extend({\n\t_class: 'TextItem',\n\t_applyMatrix: false,\n\t_canApplyMatrix: false,\n\t_serializeFields: {\n\t\tcontent: null\n\t},\n\t_boundsOptions: { stroke: false, handle: false },\n\n\tinitialize: function TextItem(arg) {\n\t\tthis._content = '';\n\t\tthis._lines = [];\n\t\tvar hasProps = arg && Base.isPlainObject(arg)\n\t\t\t\t&& arg.x === undefined && arg.y === undefined;\n\t\tthis._initialize(hasProps && arg, !hasProps && Point.read(arguments));\n\t},\n\n\t_equals: function(item) {\n\t\treturn this._content === item._content;\n\t},\n\n\tcopyContent: function(source) {\n\t\tthis.setContent(source._content);\n\t},\n\n\tgetContent: function() {\n\t\treturn this._content;\n\t},\n\n\tsetContent: function(content) {\n\t\tthis._content = '' + content;\n\t\tthis._lines = this._content.split(/\\r\\n|\\n|\\r/mg);\n\t\tthis._changed(521);\n\t},\n\n\tisEmpty: function() {\n\t\treturn !this._content;\n\t},\n\n\tgetCharacterStyle: '#getStyle',\n\tsetCharacterStyle: '#setStyle',\n\n\tgetParagraphStyle: '#getStyle',\n\tsetParagraphStyle: '#setStyle'\n});\n\nvar PointText = TextItem.extend({\n\t_class: 'PointText',\n\n\tinitialize: function PointText() {\n\t\tTextItem.apply(this, arguments);\n\t},\n\n\tgetPoint: function() {\n\t\tvar point = this._matrix.getTranslation();\n\t\treturn new LinkedPoint(point.x, point.y, this, 'setPoint');\n\t},\n\n\tsetPoint: function() {\n\t\tvar point = Point.read(arguments);\n\t\tthis.translate(point.subtract(this._matrix.getTranslation()));\n\t},\n\n\t_draw: function(ctx, param, viewMatrix) {\n\t\tif (!this._content)\n\t\t\treturn;\n\t\tthis._setStyles(ctx, param, viewMatrix);\n\t\tvar lines = this._lines,\n\t\t\tstyle = this._style,\n\t\t\thasFill = style.hasFill(),\n\t\t\thasStroke = style.hasStroke(),\n\t\t\tleading = style.getLeading(),\n\t\t\tshadowColor = ctx.shadowColor;\n\t\tctx.font = style.getFontStyle();\n\t\tctx.textAlign = style.getJustification();\n\t\tfor (var i = 0, l = lines.length; i < l; i++) {\n\t\t\tctx.shadowColor = shadowColor;\n\t\t\tvar line = lines[i];\n\t\t\tif (hasFill) {\n\t\t\t\tctx.fillText(line, 0, 0);\n\t\t\t\tctx.shadowColor = 'rgba(0,0,0,0)';\n\t\t\t}\n\t\t\tif (hasStroke)\n\t\t\t\tctx.strokeText(line, 0, 0);\n\t\t\tctx.translate(0, leading);\n\t\t}\n\t},\n\n\t_getBounds: function(matrix, options) {\n\t\tvar rect = options.drawnTextBounds ? this._getDrawnTextSize() : this._getMeasuredTextSize();\n\t\treturn matrix ? matrix._transformBounds(rect, rect) : rect;\n\t},\n\n\t_getMeasuredTextSize: function() {\n\t\tvar style = this._style,\n\t\t\tlines = this._lines,\n\t\t\tnumLines = lines.length,\n\t\t\tjustification = style.getJustification(),\n\t\t\tleading = style.getLeading(),\n\t\t\twidth = this.getView().getTextWidth(style.getFontStyle(), lines),\n\t\t\tx = 0;\n\t\tif (justification !== 'left')\n\t\t\tx -= width / (justification === 'center' ? 2: 1);\n\t\treturn new Rectangle(x,\n\t\t\t\t\tnumLines ? - 0.75 * leading : 0,\n\t\t\t\t\twidth, numLines * leading);\n\t},\n\n\t_getDrawnTextSize: function() {\n\t\tvar style = this._style;\n\t\tvar lines = this._lines;\n\t\tvar numLines = lines.length;\n\t\tvar leading = style.getLeading();\n\t\tvar justification = style.getJustification();\n\n\t\tvar svg = SvgElement.create('svg', {\n\t\t\t\t\tversion: '1.1',\n\t\t\t\t\txmlns: SvgElement.svg\n\t\t\t\t});\n\t\tvar node = SvgElement.create('text');\n\t\tnode.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');\n\t\tsvg.appendChild(node);\n\t\tfor (var i = 0; i < numLines; i++) {\n\t\t\tvar tspanNode = SvgElement.create('tspan', {\n\t\t\t\tx: '0',\n\t\t\t\tdy: i === 0 ? '0' : leading + 'px'\n\t\t\t});\n\t\t\ttspanNode.textContent = this._lines[i];\n\t\t\tnode.appendChild(tspanNode);\n\t\t}\n\n\t\tvar element = document.createElement('span');\n\t\telement.style.visibility = ('hidden');\n\t\telement.style.whiteSpace = 'pre';\n\t\telement.style.fontSize = this.fontSize + 'px';\n\t\telement.style.fontFamily = this.font;\n\t\telement.style.lineHeight = this.leading / this.fontSize;\n\n\t\tvar bbox;\n\t\ttry {\n\t\t\telement.appendChild(svg);\n\t\t\tdocument.body.appendChild(element);\n\t\t\tbbox = svg.getBBox();\n\t\t} finally {\n\t\t\tdocument.body.removeChild(element);\n\t\t}\n\n\t\tvar halfStrokeWidth = this.strokeWidth / 2;\n\t\tvar width = bbox.width + (halfStrokeWidth * 2);\n\t\tvar height = bbox.height + (halfStrokeWidth * 2);\n\t\tvar x = bbox.x - halfStrokeWidth;\n\t\tvar y = bbox.y - halfStrokeWidth;\n\n\t\tif (justification !== 'left') {\n\t\t\tvar eltWidth = this.getView().getTextWidth(style.getFontStyle(), lines);\n\t\t\tx -= eltWidth / (justification === 'center' ? 2: 1);\n\t\t}\n\n\t\treturn new Rectangle(x, y, width + 1, Math.max(height, numLines * leading));\n\t},\n\n\t_hitTestSelf: function(point, options) {\n\t\tif (options.fill && (this.hasFill() || options.hitUnfilledPaths) && this._contains(point))\n\t\t\treturn new HitResult('fill', this);\n\t}\n});\n\nvar Color = Base.extend(new function() {\n\tvar types = {\n\t\tgray: ['gray'],\n\t\trgb: ['red', 'green', 'blue'],\n\t\thsb: ['hue', 'saturation', 'brightness'],\n\t\thsl: ['hue', 'saturation', 'lightness'],\n\t\tgradient: ['gradient', 'origin', 'destination', 'highlight']\n\t};\n\n\tvar componentParsers = {},\n\t\tnamedColors = {\n\t\t\ttransparent: [0, 0, 0, 0]\n\t\t},\n\t\tcolorCtx;\n\n\tfunction fromCSS(string) {\n\t\tvar match = string.match(\n\t\t\t\t/^#([\\da-f]{2})([\\da-f]{2})([\\da-f]{2})([\\da-f]{2})?$/i\n\t\t\t) || string.match(\n\t\t\t\t/^#([\\da-f])([\\da-f])([\\da-f])([\\da-f])?$/i\n\t\t\t),\n\t\t\ttype = 'rgb',\n\t\t\tcomponents;\n\t\tif (match) {\n\t\t\tvar amount = match[4] ? 4 : 3;\n\t\t\tcomponents = new Array(amount);\n\t\t\tfor (var i = 0; i < amount; i++) {\n\t\t\t\tvar value = match[i + 1];\n\t\t\t\tcomponents[i] = parseInt(value.length == 1\n\t\t\t\t\t\t? value + value : value, 16) / 255;\n\t\t\t}\n\t\t} else if (match = string.match(/^(rgb|hsl)a?\\((.*)\\)$/)) {\n\t\t\ttype = match[1];\n\t\t\tcomponents = match[2].trim().split(/[,\\s]+/g);\n\t\t\tvar isHSL = type === 'hsl';\n\t\t\tfor (var i = 0, l = Math.min(components.length, 4); i < l; i++) {\n\t\t\t\tvar component = components[i];\n\t\t\t\tvar value = parseFloat(component);\n\t\t\t\tif (isHSL) {\n\t\t\t\t\tif (i === 0) {\n\t\t\t\t\t\tvar unit = component.match(/([a-z]*)$/)[1];\n\t\t\t\t\t\tvalue *= ({\n\t\t\t\t\t\t\tturn: 360,\n\t\t\t\t\t\t\trad: 180 / Math.PI,\n\t\t\t\t\t\t\tgrad: 0.9\n\t\t\t\t\t\t}[unit] || 1);\n\t\t\t\t\t} else if (i < 3) {\n\t\t\t\t\t\tvalue /= 100;\n\t\t\t\t\t}\n\t\t\t\t} else if (i < 3) {\n\t\t\t\t\tvalue /= /%$/.test(component) ? 100 : 255;\n\t\t\t\t}\n\t\t\t\tcomponents[i] = value;\n\t\t\t}\n\t\t} else {\n\t\t\tvar color = namedColors[string];\n\t\t\tif (!color) {\n\t\t\t\tif (window) {\n\t\t\t\t\tif (!colorCtx) {\n\t\t\t\t\t\tcolorCtx = CanvasProvider.getContext(1, 1);\n\t\t\t\t\t\tcolorCtx.globalCompositeOperation = 'copy';\n\t\t\t\t\t}\n\t\t\t\t\tcolorCtx.fillStyle = 'rgba(0,0,0,0)';\n\t\t\t\t\tcolorCtx.fillStyle = string;\n\t\t\t\t\tcolorCtx.fillRect(0, 0, 1, 1);\n\t\t\t\t\tvar data = colorCtx.getImageData(0, 0, 1, 1).data;\n\t\t\t\t\tcolor = namedColors[string] = [\n\t\t\t\t\t\tdata[0] / 255,\n\t\t\t\t\t\tdata[1] / 255,\n\t\t\t\t\t\tdata[2] / 255\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\tcolor = [0, 0, 0];\n\t\t\t\t}\n\t\t\t}\n\t\t\tcomponents = color.slice();\n\t\t}\n\t\treturn [type, components];\n\t}\n\n\tvar hsbIndices = [\n\t\t[0, 3, 1],\n\t\t[2, 0, 1],\n\t\t[1, 0, 3],\n\t\t[1, 2, 0],\n\t\t[3, 1, 0],\n\t\t[0, 1, 2]\n\t];\n\n\tvar converters = {\n\t\t'rgb-hsb': function(r, g, b) {\n\t\t\tvar max = Math.max(r, g, b),\n\t\t\t\tmin = Math.min(r, g, b),\n\t\t\t\tdelta = max - min,\n\t\t\t\th = delta === 0 ? 0\n\t\t\t\t\t: ( max == r ? (g - b) / delta + (g < b ? 6 : 0)\n\t\t\t\t\t\t: max == g ? (b - r) / delta + 2\n\t\t\t\t\t\t: (r - g) / delta + 4) * 60;\n\t\t\treturn [h, max === 0 ? 0 : delta / max, max];\n\t\t},\n\n\t\t'hsb-rgb': function(h, s, b) {\n\t\t\th = (((h / 60) % 6) + 6) % 6;\n\t\t\tvar i = Math.floor(h),\n\t\t\t\tf = h - i,\n\t\t\t\ti = hsbIndices[i],\n\t\t\t\tv = [\n\t\t\t\t\tb,\n\t\t\t\t\tb * (1 - s),\n\t\t\t\t\tb * (1 - s * f),\n\t\t\t\t\tb * (1 - s * (1 - f))\n\t\t\t\t];\n\t\t\treturn [v[i[0]], v[i[1]], v[i[2]]];\n\t\t},\n\n\t\t'rgb-hsl': function(r, g, b) {\n\t\t\tvar max = Math.max(r, g, b),\n\t\t\t\tmin = Math.min(r, g, b),\n\t\t\t\tdelta = max - min,\n\t\t\t\tachromatic = delta === 0,\n\t\t\t\th = achromatic ? 0\n\t\t\t\t\t: ( max == r ? (g - b) / delta + (g < b ? 6 : 0)\n\t\t\t\t\t\t: max == g ? (b - r) / delta + 2\n\t\t\t\t\t\t: (r - g) / delta + 4) * 60,\n\t\t\t\tl = (max + min) / 2,\n\t\t\t\ts = achromatic ? 0 : l < 0.5\n\t\t\t\t\t\t? delta / (max + min)\n\t\t\t\t\t\t: delta / (2 - max - min);\n\t\t\treturn [h, s, l];\n\t\t},\n\n\t\t'hsl-rgb': function(h, s, l) {\n\t\t\th = (((h / 360) % 1) + 1) % 1;\n\t\t\tif (s === 0)\n\t\t\t\treturn [l, l, l];\n\t\t\tvar t3s = [ h + 1 / 3, h, h - 1 / 3 ],\n\t\t\t\tt2 = l < 0.5 ? l * (1 + s) : l + s - l * s,\n\t\t\t\tt1 = 2 * l - t2,\n\t\t\t\tc = [];\n\t\t\tfor (var i = 0; i < 3; i++) {\n\t\t\t\tvar t3 = t3s[i];\n\t\t\t\tif (t3 < 0) t3 += 1;\n\t\t\t\tif (t3 > 1) t3 -= 1;\n\t\t\t\tc[i] = 6 * t3 < 1\n\t\t\t\t\t? t1 + (t2 - t1) * 6 * t3\n\t\t\t\t\t: 2 * t3 < 1\n\t\t\t\t\t\t? t2\n\t\t\t\t\t\t: 3 * t3 < 2\n\t\t\t\t\t\t\t? t1 + (t2 - t1) * ((2 / 3) - t3) * 6\n\t\t\t\t\t\t\t: t1;\n\t\t\t}\n\t\t\treturn c;\n\t\t},\n\n\t\t'rgb-gray': function(r, g, b) {\n\t\t\treturn [r * 0.2989 + g * 0.587 + b * 0.114];\n\t\t},\n\n\t\t'gray-rgb': function(g) {\n\t\t\treturn [g, g, g];\n\t\t},\n\n\t\t'gray-hsb': function(g) {\n\t\t\treturn [0, 0, g];\n\t\t},\n\n\t\t'gray-hsl': function(g) {\n\t\t\treturn [0, 0, g];\n\t\t},\n\n\t\t'gradient-rgb': function() {\n\t\t\treturn [];\n\t\t},\n\n\t\t'rgb-gradient': function() {\n\t\t\treturn [];\n\t\t}\n\n\t};\n\n\treturn Base.each(types, function(properties, type) {\n\t\tcomponentParsers[type] = [];\n\t\tBase.each(properties, function(name, index) {\n\t\t\tvar part = Base.capitalize(name),\n\t\t\t\thasOverlap = /^(hue|saturation)$/.test(name),\n\t\t\t\tparser = componentParsers[type][index] = type === 'gradient'\n\t\t\t\t\t? name === 'gradient'\n\t\t\t\t\t\t? function(value) {\n\t\t\t\t\t\t\tvar current = this._components[0];\n\t\t\t\t\t\t\tvalue = Gradient.read(\n\t\t\t\t\t\t\t\tArray.isArray(value)\n\t\t\t\t\t\t\t\t\t? value\n\t\t\t\t\t\t\t\t\t: arguments, 0, { readNull: true }\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (current !== value) {\n\t\t\t\t\t\t\t\tif (current)\n\t\t\t\t\t\t\t\t\tcurrent._removeOwner(this);\n\t\t\t\t\t\t\t\tif (value)\n\t\t\t\t\t\t\t\t\tvalue._addOwner(this);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: function() {\n\t\t\t\t\t\t\treturn Point.read(arguments, 0, {\n\t\t\t\t\t\t\t\t\treadNull: name === 'highlight',\n\t\t\t\t\t\t\t\t\tclone: true\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t: function(value) {\n\t\t\t\t\t\treturn value == null || isNaN(value) ? 0 : +value;\n\t\t\t\t\t};\n\t\t\tthis['get' + part] = function() {\n\t\t\t\treturn this._type === type\n\t\t\t\t\t|| hasOverlap && /^hs[bl]$/.test(this._type)\n\t\t\t\t\t\t? this._components[index]\n\t\t\t\t\t\t: this._convert(type)[index];\n\t\t\t};\n\n\t\t\tthis['set' + part] = function(value) {\n\t\t\t\tif (this._type !== type\n\t\t\t\t\t\t&& !(hasOverlap && /^hs[bl]$/.test(this._type))) {\n\t\t\t\t\tthis._components = this._convert(type);\n\t\t\t\t\tthis._properties = types[type];\n\t\t\t\t\tthis._type = type;\n\t\t\t\t}\n\t\t\t\tthis._components[index] = parser.call(this, value);\n\t\t\t\tthis._changed();\n\t\t\t};\n\t\t}, this);\n\t}, {\n\t\t_class: 'Color',\n\t\t_readIndex: true,\n\n\t\tinitialize: function Color(arg) {\n\t\t\tvar args = arguments,\n\t\t\t\treading = this.__read,\n\t\t\t\tread = 0,\n\t\t\t\ttype,\n\t\t\t\tcomponents,\n\t\t\t\talpha,\n\t\t\t\tvalues;\n\t\t\tif (Array.isArray(arg)) {\n\t\t\t\targs = arg;\n\t\t\t\targ = args[0];\n\t\t\t}\n\t\t\tvar argType = arg != null && typeof arg;\n\t\t\tif (argType === 'string' && arg in types) {\n\t\t\t\ttype = arg;\n\t\t\t\targ = args[1];\n\t\t\t\tif (Array.isArray(arg)) {\n\t\t\t\t\tcomponents = arg;\n\t\t\t\t\talpha = args[2];\n\t\t\t\t} else {\n\t\t\t\t\tif (reading)\n\t\t\t\t\t\tread = 1;\n\t\t\t\t\targs = Base.slice(args, 1);\n\t\t\t\t\targType = typeof arg;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!components) {\n\t\t\t\tvalues = argType === 'number'\n\t\t\t\t\t\t? args\n\t\t\t\t\t\t: argType === 'object' && arg.length != null\n\t\t\t\t\t\t\t? arg\n\t\t\t\t\t\t\t: null;\n\t\t\t\tif (values) {\n\t\t\t\t\tif (!type)\n\t\t\t\t\t\ttype = values.length >= 3\n\t\t\t\t\t\t\t\t? 'rgb'\n\t\t\t\t\t\t\t\t: 'gray';\n\t\t\t\t\tvar length = types[type].length;\n\t\t\t\t\talpha = values[length];\n\t\t\t\t\tif (reading) {\n\t\t\t\t\t\tread += values === arguments\n\t\t\t\t\t\t\t? length + (alpha != null ? 1 : 0)\n\t\t\t\t\t\t\t: 1;\n\t\t\t\t\t}\n\t\t\t\t\tif (values.length > length)\n\t\t\t\t\t\tvalues = Base.slice(values, 0, length);\n\t\t\t\t} else if (argType === 'string') {\n\t\t\t\t\tvar converted = fromCSS(arg);\n\t\t\t\t\ttype = converted[0];\n\t\t\t\t\tcomponents = converted[1];\n\t\t\t\t\tif (components.length === 4) {\n\t\t\t\t\t\talpha = components[3];\n\t\t\t\t\t\tcomponents.length--;\n\t\t\t\t\t}\n\t\t\t\t} else if (argType === 'object') {\n\t\t\t\t\tif (arg.constructor === Color) {\n\t\t\t\t\t\ttype = arg._type;\n\t\t\t\t\t\tcomponents = arg._components.slice();\n\t\t\t\t\t\talpha = arg._alpha;\n\t\t\t\t\t\tif (type === 'gradient') {\n\t\t\t\t\t\t\tfor (var i = 1, l = components.length; i < l; i++) {\n\t\t\t\t\t\t\t\tvar point = components[i];\n\t\t\t\t\t\t\t\tif (point)\n\t\t\t\t\t\t\t\t\tcomponents[i] = point.clone();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (arg.constructor === Gradient) {\n\t\t\t\t\t\ttype = 'gradient';\n\t\t\t\t\t\tvalues = args;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttype = 'hue' in arg\n\t\t\t\t\t\t\t? 'lightness' in arg\n\t\t\t\t\t\t\t\t? 'hsl'\n\t\t\t\t\t\t\t\t: 'hsb'\n\t\t\t\t\t\t\t: 'gradient' in arg || 'stops' in arg\n\t\t\t\t\t\t\t\t\t|| 'radial' in arg\n\t\t\t\t\t\t\t\t? 'gradient'\n\t\t\t\t\t\t\t\t: 'gray' in arg\n\t\t\t\t\t\t\t\t\t? 'gray'\n\t\t\t\t\t\t\t\t\t: 'rgb';\n\t\t\t\t\t\tvar properties = types[type],\n\t\t\t\t\t\t\tparsers = componentParsers[type];\n\t\t\t\t\t\tthis._components = components = [];\n\t\t\t\t\t\tfor (var i = 0, l = properties.length; i < l; i++) {\n\t\t\t\t\t\t\tvar value = arg[properties[i]];\n\t\t\t\t\t\t\tif (value == null && !i && type === 'gradient'\n\t\t\t\t\t\t\t\t\t&& 'stops' in arg) {\n\t\t\t\t\t\t\t\tvalue = {\n\t\t\t\t\t\t\t\t\tstops: arg.stops,\n\t\t\t\t\t\t\t\t\tradial: arg.radial\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvalue = parsers[i].call(this, value);\n\t\t\t\t\t\t\tif (value != null)\n\t\t\t\t\t\t\t\tcomponents[i] = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t\talpha = arg.alpha;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (reading && type)\n\t\t\t\t\tread = 1;\n\t\t\t}\n\t\t\tthis._type = type || 'rgb';\n\t\t\tif (!components) {\n\t\t\t\tthis._components = components = [];\n\t\t\t\tvar parsers = componentParsers[this._type];\n\t\t\t\tfor (var i = 0, l = parsers.length; i < l; i++) {\n\t\t\t\t\tvar value = parsers[i].call(this, values && values[i]);\n\t\t\t\t\tif (value != null)\n\t\t\t\t\t\tcomponents[i] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._components = components;\n\t\t\tthis._properties = types[this._type];\n\t\t\tthis._alpha = alpha;\n\t\t\tif (reading)\n\t\t\t\tthis.__read = read;\n\t\t\treturn this;\n\t\t},\n\n\t\tset: '#initialize',\n\n\t\t_serialize: function(options, dictionary) {\n\t\t\tvar components = this.getComponents();\n\t\t\treturn Base.serialize(\n\t\t\t\t\t/^(gray|rgb)$/.test(this._type)\n\t\t\t\t\t\t? components\n\t\t\t\t\t\t: [this._type].concat(components),\n\t\t\t\t\toptions, true, dictionary);\n\t\t},\n\n\t\t_changed: function() {\n\t\t\tthis._canvasStyle = null;\n\t\t\tif (this._owner) {\n\t\t\t\tif (this._setter) {\n\t\t\t\t\tthis._owner[this._setter](this);\n\t\t\t\t} else {\n\t\t\t\t\tthis._owner._changed(129);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t_convert: function(type) {\n\t\t\tvar converter;\n\t\t\treturn this._type === type\n\t\t\t\t\t? this._components.slice()\n\t\t\t\t\t: (converter = converters[this._type + '-' + type])\n\t\t\t\t\t\t? converter.apply(this, this._components)\n\t\t\t\t\t\t: converters['rgb-' + type].apply(this,\n\t\t\t\t\t\t\tconverters[this._type + '-rgb'].apply(this,\n\t\t\t\t\t\t\t\tthis._components));\n\t\t},\n\n\t\tconvert: function(type) {\n\t\t\treturn new Color(type, this._convert(type), this._alpha);\n\t\t},\n\n\t\tgetType: function() {\n\t\t\treturn this._type;\n\t\t},\n\n\t\tsetType: function(type) {\n\t\t\tthis._components = this._convert(type);\n\t\t\tthis._properties = types[type];\n\t\t\tthis._type = type;\n\t\t},\n\n\t\tgetComponents: function() {\n\t\t\tvar components = this._components.slice();\n\t\t\tif (this._alpha != null)\n\t\t\t\tcomponents.push(this._alpha);\n\t\t\treturn components;\n\t\t},\n\n\t\tgetAlpha: function() {\n\t\t\treturn this._alpha != null ? this._alpha : 1;\n\t\t},\n\n\t\tsetAlpha: function(alpha) {\n\t\t\tthis._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1);\n\t\t\tthis._changed();\n\t\t},\n\n\t\thasAlpha: function() {\n\t\t\treturn this._alpha != null;\n\t\t},\n\n\t\tequals: function(color) {\n\t\t\tvar col = Base.isPlainValue(color, true)\n\t\t\t\t\t? Color.read(arguments)\n\t\t\t\t\t: color;\n\t\t\treturn col === this || col && this._class === col._class\n\t\t\t\t\t&& this._type === col._type\n\t\t\t\t\t&& this.getAlpha() === col.getAlpha()\n\t\t\t\t\t&& Base.equals(this._components, col._components)\n\t\t\t\t\t|| false;\n\t\t},\n\n\t\ttoString: function() {\n\t\t\tvar properties = this._properties,\n\t\t\t\tparts = [],\n\t\t\t\tisGradient = this._type === 'gradient',\n\t\t\t\tf = Formatter.instance;\n\t\t\tfor (var i = 0, l = properties.length; i < l; i++) {\n\t\t\t\tvar value = this._components[i];\n\t\t\t\tif (value != null)\n\t\t\t\t\tparts.push(properties[i] + ': '\n\t\t\t\t\t\t\t+ (isGradient ? value : f.number(value)));\n\t\t\t}\n\t\t\tif (this._alpha != null)\n\t\t\t\tparts.push('alpha: ' + f.number(this._alpha));\n\t\t\treturn '{ ' + parts.join(', ') + ' }';\n\t\t},\n\n\t\ttoCSS: function(hex) {\n\t\t\tvar components = this._convert('rgb'),\n\t\t\t\talpha = hex || this._alpha == null ? 1 : this._alpha;\n\t\t\tfunction convert(val) {\n\t\t\t\treturn Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);\n\t\t\t}\n\t\t\tcomponents = [\n\t\t\t\tconvert(components[0]),\n\t\t\t\tconvert(components[1]),\n\t\t\t\tconvert(components[2])\n\t\t\t];\n\t\t\tif (alpha < 1)\n\t\t\t\tcomponents.push(alpha < 0 ? 0 : alpha);\n\t\t\treturn hex\n\t\t\t\t\t? '#' + ((1 << 24) + (components[0] << 16)\n\t\t\t\t\t\t+ (components[1] << 8)\n\t\t\t\t\t\t+ components[2]).toString(16).slice(1)\n\t\t\t\t\t: (components.length == 4 ? 'rgba(' : 'rgb(')\n\t\t\t\t\t\t+ components.join(',') + ')';\n\t\t},\n\n\t\ttoCanvasStyle: function(ctx, matrix, strokeMatrix) {\n\t\t\tvar strokeMayChange = this._type === 'gradient' && strokeMatrix;\n\t\t\tif (this._canvasStyle && !strokeMayChange)\n\t\t\t\treturn this._canvasStyle;\n\t\t\tif (this._type !== 'gradient')\n\t\t\t\treturn this._canvasStyle = this.toCSS();\n\t\t\tvar components = this._components,\n\t\t\t\tgradient = components[0],\n\t\t\t\tstops = gradient._stops,\n\t\t\t\torigin = components[1],\n\t\t\t\tdestination = components[2],\n\t\t\t\thighlight = components[3],\n\t\t\t\tinverse = matrix && matrix.inverted(),\n\t\t\t\tcanvasGradient;\n\t\t\tif (inverse) {\n\t\t\t\torigin = inverse._transformPoint(origin);\n\t\t\t\tdestination = inverse._transformPoint(destination);\n\t\t\t\tif (highlight)\n\t\t\t\t\thighlight = inverse._transformPoint(highlight);\n\t\t\t}\n\t\t\tif (strokeMatrix) {\n\t\t\t\torigin = strokeMatrix._transformPoint(origin);\n\t\t\t\tdestination = strokeMatrix._transformPoint(destination);\n\t\t\t\tif (highlight)\n\t\t\t\t\thighlight = strokeMatrix._transformPoint(highlight);\n\t\t\t}\n\t\t\tif (gradient._radial) {\n\t\t\t\tvar radius = destination.getDistance(origin);\n\t\t\t\tif (highlight) {\n\t\t\t\t\tvar vector = highlight.subtract(origin);\n\t\t\t\t\tif (vector.getLength() > radius)\n\t\t\t\t\t\thighlight = origin.add(vector.normalize(radius - 0.1));\n\t\t\t\t}\n\t\t\t\tvar start = highlight || origin;\n\t\t\t\tcanvasGradient = ctx.createRadialGradient(start.x, start.y,\n\t\t\t\t\t\t0, origin.x, origin.y, radius);\n\t\t\t} else {\n\t\t\t\tcanvasGradient = ctx.createLinearGradient(origin.x, origin.y,\n\t\t\t\t\t\tdestination.x, destination.y);\n\t\t\t}\n\t\t\tfor (var i = 0, l = stops.length; i < l; i++) {\n\t\t\t\tvar stop = stops[i],\n\t\t\t\t\toffset = stop._offset;\n\t\t\t\tcanvasGradient.addColorStop(\n\t\t\t\t\t\toffset == null ? i / (l - 1) : offset,\n\t\t\t\t\t\tstop._color.toCanvasStyle());\n\t\t\t}\n\t\t\tif (!strokeMayChange) this._canvasStyle = canvasGradient;\n\t\t\treturn canvasGradient;\n\t\t},\n\n\t\ttransform: function(matrix) {\n\t\t\tif (this._type === 'gradient') {\n\t\t\t\tvar components = this._components;\n\t\t\t\tfor (var i = 1, l = components.length; i < l; i++) {\n\t\t\t\t\tvar point = components[i];\n\t\t\t\t\tmatrix._transformPoint(point, point, true);\n\t\t\t\t}\n\t\t\t\tthis._changed();\n\t\t\t}\n\t\t},\n\n\t\tstatics: {\n\t\t\t_types: types,\n\n\t\t\trandom: function() {\n\t\t\t\tvar random = Math.random;\n\t\t\t\treturn new Color(random(), random(), random());\n\t\t\t},\n\n\t\t\t_setOwner: function(color, owner, setter) {\n\t\t\t\tif (color) {\n\t\t\t\t\tif (color._owner && owner && color._owner !== owner) {\n\t\t\t\t\t\tcolor = color.clone();\n\t\t\t\t\t}\n\t\t\t\t\tif (!color._owner ^ !owner) {\n\t\t\t\t\t\tcolor._owner = owner || null;\n\t\t\t\t\t\tcolor._setter = setter || null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn color;\n\t\t\t}\n\t\t}\n\t});\n},\nnew function() {\n\tvar operators = {\n\t\tadd: function(a, b) {\n\t\t\treturn a + b;\n\t\t},\n\n\t\tsubtract: function(a, b) {\n\t\t\treturn a - b;\n\t\t},\n\n\t\tmultiply: function(a, b) {\n\t\t\treturn a * b;\n\t\t},\n\n\t\tdivide: function(a, b) {\n\t\t\treturn a / b;\n\t\t}\n\t};\n\n\treturn Base.each(operators, function(operator, name) {\n\t\tthis[name] = function(color) {\n\t\t\tcolor = Color.read(arguments);\n\t\t\tvar type = this._type,\n\t\t\t\tcomponents1 = this._components,\n\t\t\t\tcomponents2 = color._convert(type);\n\t\t\tfor (var i = 0, l = components1.length; i < l; i++)\n\t\t\t\tcomponents2[i] = operator(components1[i], components2[i]);\n\t\t\treturn new Color(type, components2,\n\t\t\t\t\tthis._alpha != null\n\t\t\t\t\t\t\t? operator(this._alpha, color.getAlpha())\n\t\t\t\t\t\t\t: null);\n\t\t};\n\t}, {\n\t});\n});\n\nvar Gradient = Base.extend({\n\t_class: 'Gradient',\n\n\tinitialize: function Gradient(stops, radial) {\n\t\tthis._id = UID.get();\n\t\tif (stops && Base.isPlainObject(stops)) {\n\t\t\tthis.set(stops);\n\t\t\tstops = radial = null;\n\t\t}\n\t\tif (this._stops == null) {\n\t\t\tthis.setStops(stops || ['white', 'black']);\n\t\t}\n\t\tif (this._radial == null) {\n\t\t\tthis.setRadial(typeof radial === 'string' && radial === 'radial'\n\t\t\t\t\t|| radial || false);\n\t\t}\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\treturn dictionary.add(this, function() {\n\t\t\treturn Base.serialize([this._stops, this._radial],\n\t\t\t\t\toptions, true, dictionary);\n\t\t});\n\t},\n\n\t_changed: function() {\n\t\tfor (var i = 0, l = this._owners && this._owners.length; i < l; i++) {\n\t\t\tthis._owners[i]._changed();\n\t\t}\n\t},\n\n\t_addOwner: function(color) {\n\t\tif (!this._owners)\n\t\t\tthis._owners = [];\n\t\tthis._owners.push(color);\n\t},\n\n\t_removeOwner: function(color) {\n\t\tvar index = this._owners ? this._owners.indexOf(color) : -1;\n\t\tif (index != -1) {\n\t\t\tthis._owners.splice(index, 1);\n\t\t\tif (!this._owners.length)\n\t\t\t\tthis._owners = undefined;\n\t\t}\n\t},\n\n\tclone: function() {\n\t\tvar stops = [];\n\t\tfor (var i = 0, l = this._stops.length; i < l; i++) {\n\t\t\tstops[i] = this._stops[i].clone();\n\t\t}\n\t\treturn new Gradient(stops, this._radial);\n\t},\n\n\tgetStops: function() {\n\t\treturn this._stops;\n\t},\n\n\tsetStops: function(stops) {\n\t\tif (stops.length < 2) {\n\t\t\tthrow new Error(\n\t\t\t\t\t'Gradient stop list needs to contain at least two stops.');\n\t\t}\n\t\tvar _stops = this._stops;\n\t\tif (_stops) {\n\t\t\tfor (var i = 0, l = _stops.length; i < l; i++)\n\t\t\t\t_stops[i]._owner = undefined;\n\t\t}\n\t\t_stops = this._stops = GradientStop.readList(stops, 0, { clone: true });\n\t\tfor (var i = 0, l = _stops.length; i < l; i++)\n\t\t\t_stops[i]._owner = this;\n\t\tthis._changed();\n\t},\n\n\tgetRadial: function() {\n\t\treturn this._radial;\n\t},\n\n\tsetRadial: function(radial) {\n\t\tthis._radial = radial;\n\t\tthis._changed();\n\t},\n\n\tequals: function(gradient) {\n\t\tif (gradient === this)\n\t\t\treturn true;\n\t\tif (gradient && this._class === gradient._class) {\n\t\t\tvar stops1 = this._stops,\n\t\t\t\tstops2 = gradient._stops,\n\t\t\t\tlength = stops1.length;\n\t\t\tif (length === stops2.length) {\n\t\t\t\tfor (var i = 0; i < length; i++) {\n\t\t\t\t\tif (!stops1[i].equals(stops2[i]))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n});\n\nvar GradientStop = Base.extend({\n\t_class: 'GradientStop',\n\n\tinitialize: function GradientStop(arg0, arg1) {\n\t\tvar color = arg0,\n\t\t\toffset = arg1;\n\t\tif (typeof arg0 === 'object' && arg1 === undefined) {\n\t\t\tif (Array.isArray(arg0) && typeof arg0[0] !== 'number') {\n\t\t\t\tcolor = arg0[0];\n\t\t\t\toffset = arg0[1];\n\t\t\t} else if ('color' in arg0 || 'offset' in arg0\n\t\t\t\t\t|| 'rampPoint' in arg0) {\n\t\t\t\tcolor = arg0.color;\n\t\t\t\toffset = arg0.offset || arg0.rampPoint || 0;\n\t\t\t}\n\t\t}\n\t\tthis.setColor(color);\n\t\tthis.setOffset(offset);\n\t},\n\n\tclone: function() {\n\t\treturn new GradientStop(this._color.clone(), this._offset);\n\t},\n\n\t_serialize: function(options, dictionary) {\n\t\tvar color = this._color,\n\t\t\toffset = this._offset;\n\t\treturn Base.serialize(offset == null ? [color] : [color, offset],\n\t\t\t\toptions, true, dictionary);\n\t},\n\n\t_changed: function() {\n\t\tif (this._owner)\n\t\t\tthis._owner._changed(129);\n\t},\n\n\tgetOffset: function() {\n\t\treturn this._offset;\n\t},\n\n\tsetOffset: function(offset) {\n\t\tthis._offset = offset;\n\t\tthis._changed();\n\t},\n\n\tgetRampPoint: '#getOffset',\n\tsetRampPoint: '#setOffset',\n\n\tgetColor: function() {\n\t\treturn this._color;\n\t},\n\n\tsetColor: function() {\n\t\tColor._setOwner(this._color, null);\n\t\tthis._color = Color._setOwner(Color.read(arguments, 0), this,\n\t\t\t\t'setColor');\n\t\tthis._changed();\n\t},\n\n\tequals: function(stop) {\n\t\treturn stop === this || stop && this._class === stop._class\n\t\t\t\t&& this._color.equals(stop._color)\n\t\t\t\t&& this._offset == stop._offset\n\t\t\t\t|| false;\n\t}\n});\n\nvar Style = Base.extend(new function() {\n\tvar itemDefaults = {\n\t\tfillColor: null,\n\t\tfillRule: 'nonzero',\n\t\tstrokeColor: null,\n\t\tstrokeWidth: 1,\n\t\tstrokeCap: 'butt',\n\t\tstrokeJoin: 'miter',\n\t\tstrokeScaling: true,\n\t\tmiterLimit: 10,\n\t\tdashOffset: 0,\n\t\tdashArray: [],\n\t\tshadowColor: null,\n\t\tshadowBlur: 0,\n\t\tshadowOffset: new Point(),\n\t\tselectedColor: null\n\t},\n\tgroupDefaults = Base.set({}, itemDefaults, {\n\t\tfontFamily: 'sans-serif',\n\t\tfontWeight: 'normal',\n\t\tfontSize: 12,\n\t\tleading: null,\n\t\tjustification: 'left'\n\t}),\n\ttextDefaults = Base.set({}, groupDefaults, {\n\t\tfillColor: new Color()\n\t}),\n\tflags = {\n\t\tstrokeWidth: 193,\n\t\tstrokeCap: 193,\n\t\tstrokeJoin: 193,\n\t\tstrokeScaling: 201,\n\t\tmiterLimit: 193,\n\t\tfontFamily: 9,\n\t\tfontWeight: 9,\n\t\tfontSize: 9,\n\t\tfont: 9,\n\t\tleading: 9,\n\t\tjustification: 9\n\t},\n\titem = {\n\t\tbeans: true\n\t},\n\tfields = {\n\t\t_class: 'Style',\n\t\tbeans: true,\n\n\t\tinitialize: function Style(style, _owner, _project) {\n\t\t\tthis._values = {};\n\t\t\tthis._owner = _owner;\n\t\t\tthis._project = _owner && _owner._project || _project\n\t\t\t\t\t|| paper.project;\n\t\t\tthis._defaults = !_owner || _owner instanceof Group ? groupDefaults\n\t\t\t\t\t: _owner instanceof TextItem ? textDefaults\n\t\t\t\t\t: itemDefaults;\n\t\t\tif (style)\n\t\t\t\tthis.set(style);\n\t\t}\n\t};\n\n\tBase.each(groupDefaults, function(value, key) {\n\t\tvar isColor = /Color$/.test(key),\n\t\t\tisPoint = key === 'shadowOffset',\n\t\t\tpart = Base.capitalize(key),\n\t\t\tflag = flags[key],\n\t\t\tset = 'set' + part,\n\t\t\tget = 'get' + part;\n\n\t\tfields[set] = function(value) {\n\t\t\tvar owner = this._owner,\n\t\t\t\tchildren = owner && owner._children,\n\t\t\t\tapplyToChildren = children && children.length > 0\n\t\t\t\t\t&& !(owner instanceof CompoundPath);\n\t\t\tif (applyToChildren) {\n\t\t\t\tfor (var i = 0, l = children.length; i < l; i++)\n\t\t\t\t\tchildren[i]._style[set](value);\n\t\t\t}\n\t\t\tif ((key === 'selectedColor' || !applyToChildren)\n\t\t\t\t\t&& key in this._defaults) {\n\t\t\t\tvar old = this._values[key];\n\t\t\t\tif (old !== value) {\n\t\t\t\t\tif (isColor) {\n\t\t\t\t\t\tif (old) {\n\t\t\t\t\t\t\tColor._setOwner(old, null);\n\t\t\t\t\t\t\told._canvasStyle = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (value && value.constructor === Color) {\n\t\t\t\t\t\t\tvalue = Color._setOwner(value, owner,\n\t\t\t\t\t\t\t\t\tapplyToChildren && set);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis._values[key] = value;\n\t\t\t\t\tif (owner)\n\t\t\t\t\t\towner._changed(flag || 129);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tfields[get] = function(_dontMerge) {\n\t\t\tvar owner = this._owner,\n\t\t\t\tchildren = owner && owner._children,\n\t\t\t\tapplyToChildren = children && children.length > 0\n\t\t\t\t\t&& !(owner instanceof CompoundPath),\n\t\t\t\tvalue;\n\t\t\tif (applyToChildren && !_dontMerge) {\n\t\t\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\t\t\tvar childValue = children[i]._style[get]();\n\t\t\t\t\tif (!i) {\n\t\t\t\t\t\tvalue = childValue;\n\t\t\t\t\t} else if (!Base.equals(value, childValue)) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (key in this._defaults) {\n\t\t\t\tvar value = this._values[key];\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tvalue = this._defaults[key];\n\t\t\t\t\tif (value && value.clone) {\n\t\t\t\t\t\tvalue = value.clone();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvar ctor = isColor ? Color : isPoint ? Point : null;\n\t\t\t\t\tif (ctor && !(value && value.constructor === ctor)) {\n\t\t\t\t\t\tthis._values[key] = value = ctor.read([value], 0,\n\t\t\t\t\t\t\t\t{ readNull: true, clone: true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (value && isColor) {\n\t\t\t\tvalue = Color._setOwner(value, owner, applyToChildren && set);\n\t\t\t}\n\t\t\treturn value;\n\t\t};\n\n\t\titem[get] = function(_dontMerge) {\n\t\t\treturn this._style[get](_dontMerge);\n\t\t};\n\n\t\titem[set] = function(value) {\n\t\t\tthis._style[set](value);\n\t\t};\n\t});\n\n\tBase.each({\n\t\tFont: 'FontFamily',\n\t\tWindingRule: 'FillRule'\n\t}, function(value, key) {\n\t\tvar get = 'get' + key,\n\t\t\tset = 'set' + key;\n\t\tfields[get] = item[get] = '#get' + value;\n\t\tfields[set] = item[set] = '#set' + value;\n\t});\n\n\tItem.inject(item);\n\treturn fields;\n}, {\n\tset: function(style) {\n\t\tvar isStyle = style instanceof Style,\n\t\t\tvalues = isStyle ? style._values : style;\n\t\tif (values) {\n\t\t\tfor (var key in values) {\n\t\t\t\tif (key in this._defaults) {\n\t\t\t\t\tvar value = values[key];\n\t\t\t\t\tthis[key] = value && isStyle && value.clone\n\t\t\t\t\t\t\t? value.clone() : value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tequals: function(style) {\n\t\tfunction compare(style1, style2, secondary) {\n\t\t\tvar values1 = style1._values,\n\t\t\t\tvalues2 = style2._values,\n\t\t\t\tdefaults2 = style2._defaults;\n\t\t\tfor (var key in values1) {\n\t\t\t\tvar value1 = values1[key],\n\t\t\t\t\tvalue2 = values2[key];\n\t\t\t\tif (!(secondary && key in values2) && !Base.equals(value1,\n\t\t\t\t\t\tvalue2 === undefined ? defaults2[key] : value2))\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\treturn style === this || style && this._class === style._class\n\t\t\t\t&& compare(this, style)\n\t\t\t\t&& compare(style, this, true)\n\t\t\t\t|| false;\n\t},\n\n\t_dispose: function() {\n\t\tvar color;\n\t\tcolor = this.getFillColor();\n\t\tif (color) color._canvasStyle = null;\n\t\tcolor = this.getStrokeColor();\n\t\tif (color) color._canvasStyle = null;\n\t\tcolor = this.getShadowColor();\n\t\tif (color) color._canvasStyle = null;\n\t},\n\n\thasFill: function() {\n\t\tvar color = this.getFillColor();\n\t\treturn !!color && color.alpha > 0;\n\t},\n\n\thasStroke: function() {\n\t\tvar color = this.getStrokeColor();\n\t\treturn !!color && color.alpha > 0 && this.getStrokeWidth() > 0;\n\t},\n\n\thasShadow: function() {\n\t\tvar color = this.getShadowColor();\n\t\treturn !!color && color.alpha > 0 && (this.getShadowBlur() > 0\n\t\t\t\t|| !this.getShadowOffset().isZero());\n\t},\n\n\tgetView: function() {\n\t\treturn this._project._view;\n\t},\n\n\tgetFontStyle: function() {\n\t\tvar fontSize = this.getFontSize();\n\t\treturn this.getFontWeight()\n\t\t\t\t+ ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')\n\t\t\t\t+ this.getFontFamily();\n\t},\n\n\tgetFont: '#getFontFamily',\n\tsetFont: '#setFontFamily',\n\n\tgetLeading: function getLeading() {\n\t\tvar leading = getLeading.base.call(this),\n\t\t\tfontSize = this.getFontSize();\n\t\tif (/pt|em|%|px/.test(fontSize))\n\t\t\tfontSize = this.getView().getPixelSize(fontSize);\n\t\treturn leading != null ? leading : fontSize * 1.2;\n\t}\n\n});\n\nvar DomElement = new function() {\n\tfunction handlePrefix(el, name, set, value) {\n\t\tvar prefixes = ['', 'webkit', 'moz', 'Moz', 'ms', 'o'],\n\t\t\tsuffix = name[0].toUpperCase() + name.substring(1);\n\t\tfor (var i = 0; i < 6; i++) {\n\t\t\tvar prefix = prefixes[i],\n\t\t\t\tkey = prefix ? prefix + suffix : name;\n\t\t\tif (key in el) {\n\t\t\t\tif (set) {\n\t\t\t\t\tel[key] = value;\n\t\t\t\t} else {\n\t\t\t\t\treturn el[key];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tgetStyles: function(el) {\n\t\t\tvar doc = el && el.nodeType !== 9 ? el.ownerDocument : el,\n\t\t\t\tview = doc && doc.defaultView;\n\t\t\treturn view && view.getComputedStyle(el, '');\n\t\t},\n\n\t\tgetBounds: function(el, viewport) {\n\t\t\tvar doc = el.ownerDocument,\n\t\t\t\tbody = doc.body,\n\t\t\t\thtml = doc.documentElement,\n\t\t\t\trect;\n\t\t\ttry {\n\t\t\t\trect = el.getBoundingClientRect();\n\t\t\t} catch (e) {\n\t\t\t\trect = { left: 0, top: 0, width: 0, height: 0 };\n\t\t\t}\n\t\t\tvar x = rect.left - (html.clientLeft || body.clientLeft || 0),\n\t\t\t\ty = rect.top - (html.clientTop || body.clientTop || 0);\n\t\t\tif (!viewport) {\n\t\t\t\tvar view = doc.defaultView;\n\t\t\t\tx += view.pageXOffset || html.scrollLeft || body.scrollLeft;\n\t\t\t\ty += view.pageYOffset || html.scrollTop || body.scrollTop;\n\t\t\t}\n\t\t\treturn new Rectangle(x, y, rect.width, rect.height);\n\t\t},\n\n\t\tgetViewportBounds: function(el) {\n\t\t\tvar doc = el.ownerDocument,\n\t\t\t\tview = doc.defaultView,\n\t\t\t\thtml = doc.documentElement;\n\t\t\treturn new Rectangle(0, 0,\n\t\t\t\tview.innerWidth || html.clientWidth,\n\t\t\t\tview.innerHeight || html.clientHeight\n\t\t\t);\n\t\t},\n\n\t\tgetOffset: function(el, viewport) {\n\t\t\treturn DomElement.getBounds(el, viewport).getPoint();\n\t\t},\n\n\t\tgetSize: function(el) {\n\t\t\treturn DomElement.getBounds(el, true).getSize();\n\t\t},\n\n\t\tisInvisible: function(el) {\n\t\t\treturn DomElement.getSize(el).equals(new Size(0, 0));\n\t\t},\n\n\t\tisInView: function(el) {\n\t\t\treturn !DomElement.isInvisible(el)\n\t\t\t\t\t&& DomElement.getViewportBounds(el).intersects(\n\t\t\t\t\t\tDomElement.getBounds(el, true));\n\t\t},\n\n\t\tisInserted: function(el) {\n\t\t\treturn document.body.contains(el);\n\t\t},\n\n\t\tgetPrefixed: function(el, name) {\n\t\t\treturn el && handlePrefix(el, name);\n\t\t},\n\n\t\tsetPrefixed: function(el, name, value) {\n\t\t\tif (typeof name === 'object') {\n\t\t\t\tfor (var key in name)\n\t\t\t\t\thandlePrefix(el, key, true, name[key]);\n\t\t\t} else {\n\t\t\t\thandlePrefix(el, name, true, value);\n\t\t\t}\n\t\t}\n\t};\n};\n\nvar DomEvent = {\n\tadd: function(el, events) {\n\t\tif (el) {\n\t\t\tfor (var type in events) {\n\t\t\t\tvar func = events[type],\n\t\t\t\t\tparts = type.split(/[\\s,]+/g);\n\t\t\t\tfor (var i = 0, l = parts.length; i < l; i++) {\n\t\t\t\t\tvar name = parts[i];\n\t\t\t\t\tvar options = (\n\t\t\t\t\t\tel === document\n\t\t\t\t\t\t&& (name === 'touchstart' || name === 'touchmove')\n\t\t\t\t\t) ? { passive: false } : false;\n\t\t\t\t\tel.addEventListener(name, func, options);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tremove: function(el, events) {\n\t\tif (el) {\n\t\t\tfor (var type in events) {\n\t\t\t\tvar func = events[type],\n\t\t\t\t\tparts = type.split(/[\\s,]+/g);\n\t\t\t\tfor (var i = 0, l = parts.length; i < l; i++)\n\t\t\t\t\tel.removeEventListener(parts[i], func, false);\n\t\t\t}\n\t\t}\n\t},\n\n\tgetPoint: function(event) {\n\t\tvar pos = event.targetTouches\n\t\t\t\t? event.targetTouches.length\n\t\t\t\t\t? event.targetTouches[0]\n\t\t\t\t\t: event.changedTouches[0]\n\t\t\t\t: event;\n\t\treturn new Point(\n\t\t\tpos.pageX || pos.clientX + document.documentElement.scrollLeft,\n\t\t\tpos.pageY || pos.clientY + document.documentElement.scrollTop\n\t\t);\n\t},\n\n\tgetTarget: function(event) {\n\t\treturn event.target || event.srcElement;\n\t},\n\n\tgetRelatedTarget: function(event) {\n\t\treturn event.relatedTarget || event.toElement;\n\t},\n\n\tgetOffset: function(event, target) {\n\t\treturn DomEvent.getPoint(event).subtract(DomElement.getOffset(\n\t\t\t\ttarget || DomEvent.getTarget(event)));\n\t}\n};\n\nDomEvent.requestAnimationFrame = new function() {\n\tvar nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),\n\t\trequested = false,\n\t\tcallbacks = [],\n\t\ttimer;\n\n\tfunction handleCallbacks() {\n\t\tvar functions = callbacks;\n\t\tcallbacks = [];\n\t\tfor (var i = 0, l = functions.length; i < l; i++)\n\t\t\tfunctions[i]();\n\t\trequested = nativeRequest && callbacks.length;\n\t\tif (requested)\n\t\t\tnativeRequest(handleCallbacks);\n\t}\n\n\treturn function(callback) {\n\t\tcallbacks.push(callback);\n\t\tif (nativeRequest) {\n\t\t\tif (!requested) {\n\t\t\t\tnativeRequest(handleCallbacks);\n\t\t\t\trequested = true;\n\t\t\t}\n\t\t} else if (!timer) {\n\t\t\ttimer = setInterval(handleCallbacks, 1000 / 60);\n\t\t}\n\t};\n};\n\nvar View = Base.extend(Emitter, {\n\t_class: 'View',\n\n\tinitialize: function View(project, element) {\n\n\t\tfunction getSize(name) {\n\t\t\treturn element[name] || parseInt(element.getAttribute(name), 10);\n\t\t}\n\n\t\tfunction getCanvasSize() {\n\t\t\tvar size = DomElement.getSize(element);\n\t\t\treturn size.isNaN() || size.isZero()\n\t\t\t\t\t? new Size(getSize('width'), getSize('height'))\n\t\t\t\t\t: size;\n\t\t}\n\n\t\tvar size;\n\t\tif (window && element) {\n\t\t\tthis._id = element.getAttribute('id');\n\t\t\tif (this._id == null)\n\t\t\t\telement.setAttribute('id', this._id = 'paper-view-' + View._id++);\n\t\t\tDomEvent.add(element, this._viewEvents);\n\t\t\tvar none = 'none';\n\t\t\tDomElement.setPrefixed(element.style, {\n\t\t\t\tuserDrag: none,\n\t\t\t\tuserSelect: none,\n\t\t\t\ttouchCallout: none,\n\t\t\t\tcontentZooming: none,\n\t\t\t\ttapHighlightColor: 'rgba(0,0,0,0)'\n\t\t\t});\n\n\t\t\tif (PaperScope.hasAttribute(element, 'resize')) {\n\t\t\t\tvar that = this;\n\t\t\t\tDomEvent.add(window, this._windowEvents = {\n\t\t\t\t\tresize: function() {\n\t\t\t\t\t\tthat.setViewSize(getCanvasSize());\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tsize = getCanvasSize();\n\n\t\t\tif (PaperScope.hasAttribute(element, 'stats')\n\t\t\t\t\t&& typeof Stats !== 'undefined') {\n\t\t\t\tthis._stats = new Stats();\n\t\t\t\tvar stats = this._stats.domElement,\n\t\t\t\t\tstyle = stats.style,\n\t\t\t\t\toffset = DomElement.getOffset(element);\n\t\t\t\tstyle.position = 'absolute';\n\t\t\t\tstyle.left = offset.x + 'px';\n\t\t\t\tstyle.top = offset.y + 'px';\n\t\t\t\tdocument.body.appendChild(stats);\n\t\t\t}\n\t\t} else {\n\t\t\tsize = new Size(element);\n\t\t\telement = null;\n\t\t}\n\t\tthis._project = project;\n\t\tthis._scope = project._scope;\n\t\tthis._element = element;\n\t\tif (!this._pixelRatio)\n\t\t\tthis._pixelRatio = window && window.devicePixelRatio || 1;\n\t\tthis._setElementSize(size.width, size.height);\n\t\tthis._viewSize = size;\n\t\tView._views.push(this);\n\t\tView._viewsById[this._id] = this;\n\t\t(this._matrix = new Matrix())._owner = this;\n\t\tif (!View._focused)\n\t\t\tView._focused = this;\n\t\tthis._frameItems = {};\n\t\tthis._frameItemCount = 0;\n\t\tthis._itemEvents = { native: {}, virtual: {} };\n\t\tthis._autoUpdate = !paper.agent.node;\n\t\tthis._needsUpdate = false;\n\t},\n\n\tremove: function() {\n\t\tif (!this._project)\n\t\t\treturn false;\n\t\tif (View._focused === this)\n\t\t\tView._focused = null;\n\t\tView._views.splice(View._views.indexOf(this), 1);\n\t\tdelete View._viewsById[this._id];\n\t\tvar project = this._project;\n\t\tif (project._view === this)\n\t\t\tproject._view = null;\n\t\tDomEvent.remove(this._element, this._viewEvents);\n\t\tDomEvent.remove(window, this._windowEvents);\n\t\tthis._element = this._project = null;\n\t\tthis.off('frame');\n\t\tthis._animate = false;\n\t\tthis._frameItems = {};\n\t\treturn true;\n\t},\n\n\t_events: Base.each(\n\t\tItem._itemHandlers.concat(['onResize', 'onKeyDown', 'onKeyUp']),\n\t\tfunction(name) {\n\t\t\tthis[name] = {};\n\t\t}, {\n\t\t\tonFrame: {\n\t\t\t\tinstall: function() {\n\t\t\t\t\tthis.play();\n\t\t\t\t},\n\n\t\t\t\tuninstall: function() {\n\t\t\t\t\tthis.pause();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t),\n\n\t_animate: false,\n\t_time: 0,\n\t_count: 0,\n\n\tgetAutoUpdate: function() {\n\t\treturn this._autoUpdate;\n\t},\n\n\tsetAutoUpdate: function(autoUpdate) {\n\t\tthis._autoUpdate = autoUpdate;\n\t\tif (autoUpdate)\n\t\t\tthis.requestUpdate();\n\t},\n\n\tupdate: function() {\n\t},\n\n\tdraw: function() {\n\t\tthis.update();\n\t},\n\n\trequestUpdate: function() {\n\t\tif (!this._requested) {\n\t\t\tvar that = this;\n\t\t\tDomEvent.requestAnimationFrame(function() {\n\t\t\t\tthat._requested = false;\n\t\t\t\tif (that._animate) {\n\t\t\t\t\tthat.requestUpdate();\n\t\t\t\t\tvar element = that._element;\n\t\t\t\t\tif ((!DomElement.getPrefixed(document, 'hidden')\n\t\t\t\t\t\t\t|| PaperScope.getAttribute(element, 'keepalive')\n\t\t\t\t\t\t\t\t=== 'true') && DomElement.isInView(element)) {\n\t\t\t\t\t\tthat._handleFrame();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (that._autoUpdate)\n\t\t\t\t\tthat.update();\n\t\t\t});\n\t\t\tthis._requested = true;\n\t\t}\n\t},\n\n\tplay: function() {\n\t\tthis._animate = true;\n\t\tthis.requestUpdate();\n\t},\n\n\tpause: function() {\n\t\tthis._animate = false;\n\t},\n\n\t_handleFrame: function() {\n\t\tpaper = this._scope;\n\t\tvar now = Date.now() / 1000,\n\t\t\tdelta = this._last ? now - this._last : 0;\n\t\tthis._last = now;\n\t\tthis.emit('frame', new Base({\n\t\t\tdelta: delta,\n\t\t\ttime: this._time += delta,\n\t\t\tcount: this._count++\n\t\t}));\n\t\tif (this._stats)\n\t\t\tthis._stats.update();\n\t},\n\n\t_animateItem: function(item, animate) {\n\t\tvar items = this._frameItems;\n\t\tif (animate) {\n\t\t\titems[item._id] = {\n\t\t\t\titem: item,\n\t\t\t\ttime: 0,\n\t\t\t\tcount: 0\n\t\t\t};\n\t\t\tif (++this._frameItemCount === 1)\n\t\t\t\tthis.on('frame', this._handleFrameItems);\n\t\t} else {\n\t\t\tdelete items[item._id];\n\t\t\tif (--this._frameItemCount === 0) {\n\t\t\t\tthis.off('frame', this._handleFrameItems);\n\t\t\t}\n\t\t}\n\t},\n\n\t_handleFrameItems: function(event) {\n\t\tfor (var i in this._frameItems) {\n\t\t\tvar entry = this._frameItems[i];\n\t\t\tentry.item.emit('frame', new Base(event, {\n\t\t\t\ttime: entry.time += event.delta,\n\t\t\t\tcount: entry.count++\n\t\t\t}));\n\t\t}\n\t},\n\n\t_changed: function() {\n\t\tthis._project._changed(4097);\n\t\tthis._bounds = this._decomposed = undefined;\n\t},\n\n\tgetElement: function() {\n\t\treturn this._element;\n\t},\n\n\tgetPixelRatio: function() {\n\t\treturn this._pixelRatio;\n\t},\n\n\tgetResolution: function() {\n\t\treturn this._pixelRatio * 72;\n\t},\n\n\tgetViewSize: function() {\n\t\tvar size = this._viewSize;\n\t\treturn new LinkedSize(size.width, size.height, this, 'setViewSize');\n\t},\n\n\tsetViewSize: function() {\n\t\tvar size = Size.read(arguments),\n\t\t\tdelta = size.subtract(this._viewSize);\n\t\tif (delta.isZero())\n\t\t\treturn;\n\t\tthis._setElementSize(size.width, size.height);\n\t\tthis._viewSize.set(size);\n\t\tthis._changed();\n\t\tthis.emit('resize', { size: size, delta: delta });\n\t\tif (this._autoUpdate) {\n\t\t\tthis.update();\n\t\t}\n\t},\n\n\t_setElementSize: function(width, height) {\n\t\tvar element = this._element;\n\t\tif (element) {\n\t\t\tif (element.width !== width)\n\t\t\t\telement.width = width;\n\t\t\tif (element.height !== height)\n\t\t\t\telement.height = height;\n\t\t}\n\t},\n\n\tgetBounds: function() {\n\t\tif (!this._bounds)\n\t\t\tthis._bounds = this._matrix.inverted()._transformBounds(\n\t\t\t\t\tnew Rectangle(new Point(), this._viewSize));\n\t\treturn this._bounds;\n\t},\n\n\tgetSize: function() {\n\t\treturn this.getBounds().getSize();\n\t},\n\n\tisVisible: function() {\n\t\treturn DomElement.isInView(this._element);\n\t},\n\n\tisInserted: function() {\n\t\treturn DomElement.isInserted(this._element);\n\t},\n\n\tgetPixelSize: function(size) {\n\t\tvar element = this._element,\n\t\t\tpixels;\n\t\tif (element) {\n\t\t\tvar parent = element.parentNode,\n\t\t\t\ttemp = document.createElement('div');\n\t\t\ttemp.style.fontSize = size;\n\t\t\tparent.appendChild(temp);\n\t\t\tpixels = parseFloat(DomElement.getStyles(temp).fontSize);\n\t\t\tparent.removeChild(temp);\n\t\t} else {\n\t\t\tpixels = parseFloat(pixels);\n\t\t}\n\t\treturn pixels;\n\t},\n\n\tgetTextWidth: function(font, lines) {\n\t\treturn 0;\n\t}\n}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {\n\tvar rotate = key === 'rotate';\n\tthis[key] = function() {\n\t\tvar args = arguments,\n\t\t\tvalue = (rotate ? Base : Point).read(args),\n\t\t\tcenter = Point.read(args, 0, { readNull: true });\n\t\treturn this.transform(new Matrix()[key](value,\n\t\t\t\tcenter || this.getCenter(true)));\n\t};\n}, {\n\t_decompose: function() {\n\t\treturn this._decomposed || (this._decomposed = this._matrix.decompose());\n\t},\n\n\ttranslate: function() {\n\t\tvar mx = new Matrix();\n\t\treturn this.transform(mx.translate.apply(mx, arguments));\n\t},\n\n\tgetCenter: function() {\n\t\treturn this.getBounds().getCenter();\n\t},\n\n\tsetCenter: function() {\n\t\tvar center = Point.read(arguments);\n\t\tthis.translate(this.getCenter().subtract(center));\n\t},\n\n\tgetZoom: function() {\n\t\tvar scaling = this._decompose().scaling;\n\t\treturn (scaling.x + scaling.y) / 2;\n\t},\n\n\tsetZoom: function(zoom) {\n\t\tthis.transform(new Matrix().scale(zoom / this.getZoom(),\n\t\t\tthis.getCenter()));\n\t},\n\n\tgetRotation: function() {\n\t\treturn this._decompose().rotation;\n\t},\n\n\tsetRotation: function(rotation) {\n\t\tvar current = this.getRotation();\n\t\tif (current != null && rotation != null) {\n\t\t\tthis.rotate(rotation - current);\n\t\t}\n\t},\n\n\tgetScaling: function() {\n\t\tvar scaling = this._decompose().scaling;\n\t\treturn new LinkedPoint(scaling.x, scaling.y, this, 'setScaling');\n\t},\n\n\tsetScaling: function() {\n\t\tvar current = this.getScaling(),\n\t\t\tscaling = Point.read(arguments, 0, { clone: true, readNull: true });\n\t\tif (current && scaling) {\n\t\t\tthis.scale(scaling.x / current.x, scaling.y / current.y);\n\t\t}\n\t},\n\n\tgetMatrix: function() {\n\t\treturn this._matrix;\n\t},\n\n\tsetMatrix: function() {\n\t\tvar matrix = this._matrix;\n\t\tmatrix.initialize.apply(matrix, arguments);\n\t},\n\n\ttransform: function(matrix) {\n\t\tthis._matrix.append(matrix);\n\t},\n\n\tscrollBy: function() {\n\t\tthis.translate(Point.read(arguments).negate());\n\t}\n}), {\n\n\tprojectToView: function() {\n\t\treturn this._matrix._transformPoint(Point.read(arguments));\n\t},\n\n\tviewToProject: function() {\n\t\treturn this._matrix._inverseTransform(Point.read(arguments));\n\t},\n\n\tgetEventPoint: function(event) {\n\t\treturn this.viewToProject(DomEvent.getOffset(event, this._element));\n\t},\n\n}, {\n\tstatics: {\n\t\t_views: [],\n\t\t_viewsById: {},\n\t\t_id: 0,\n\n\t\tcreate: function(project, element) {\n\t\t\tif (document && typeof element === 'string')\n\t\t\t\telement = document.getElementById(element);\n\t\t\tvar ctor = window ? CanvasView : View;\n\t\t\treturn new ctor(project, element);\n\t\t}\n\t}\n},\nnew function() {\n\tif (!window)\n\t\treturn;\n\tvar prevFocus,\n\t\ttempFocus,\n\t\tdragging = false,\n\t\tmouseDown = false;\n\n\tfunction getView(event) {\n\t\tvar target = DomEvent.getTarget(event);\n\t\treturn target.getAttribute && View._viewsById[\n\t\t\t\ttarget.getAttribute('id')];\n\t}\n\n\tfunction updateFocus() {\n\t\tvar view = View._focused;\n\t\tif (!view || !view.isVisible()) {\n\t\t\tfor (var i = 0, l = View._views.length; i < l; i++) {\n\t\t\t\tif ((view = View._views[i]).isVisible()) {\n\t\t\t\t\tView._focused = tempFocus = view;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction handleMouseMove(view, event, point) {\n\t\tview._handleMouseEvent('mousemove', event, point);\n\t}\n\n\tvar navigator = window.navigator,\n\t\tmousedown, mousemove, mouseup;\n\tif (navigator.pointerEnabled || navigator.msPointerEnabled) {\n\t\tmousedown = 'pointerdown MSPointerDown';\n\t\tmousemove = 'pointermove MSPointerMove';\n\t\tmouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';\n\t} else {\n\t\tmousedown = 'touchstart';\n\t\tmousemove = 'touchmove';\n\t\tmouseup = 'touchend touchcancel';\n\t\tif (!('ontouchstart' in window && navigator.userAgent.match(\n\t\t\t\t/mobile|tablet|ip(ad|hone|od)|android|silk/i))) {\n\t\t\tmousedown += ' mousedown';\n\t\t\tmousemove += ' mousemove';\n\t\t\tmouseup += ' mouseup';\n\t\t}\n\t}\n\n\tvar viewEvents = {},\n\t\tdocEvents = {\n\t\t\tmouseout: function(event) {\n\t\t\t\tvar view = View._focused,\n\t\t\t\t\ttarget = DomEvent.getRelatedTarget(event);\n\t\t\t\tif (view && (!target || target.nodeName === 'HTML')) {\n\t\t\t\t\tvar offset = DomEvent.getOffset(event, view._element),\n\t\t\t\t\t\tx = offset.x,\n\t\t\t\t\t\tabs = Math.abs,\n\t\t\t\t\t\tax = abs(x),\n\t\t\t\t\t\tmax = 1 << 25,\n\t\t\t\t\t\tdiff = ax - max;\n\t\t\t\t\toffset.x = abs(diff) < ax ? diff * (x < 0 ? -1 : 1) : x;\n\t\t\t\t\thandleMouseMove(view, event, view.viewToProject(offset));\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tscroll: updateFocus\n\t\t};\n\n\tviewEvents[mousedown] = function(event) {\n\t\tvar view = View._focused = getView(event);\n\t\tif (!dragging) {\n\t\t\tdragging = true;\n\t\t\tview._handleMouseEvent('mousedown', event);\n\t\t}\n\t};\n\n\tdocEvents[mousemove] = function(event) {\n\t\tvar view = View._focused;\n\t\tif (!mouseDown) {\n\t\t\tvar target = getView(event);\n\t\t\tif (target) {\n\t\t\t\tif (view !== target) {\n\t\t\t\t\tif (view)\n\t\t\t\t\t\thandleMouseMove(view, event);\n\t\t\t\t\tif (!prevFocus)\n\t\t\t\t\t\tprevFocus = view;\n\t\t\t\t\tview = View._focused = tempFocus = target;\n\t\t\t\t}\n\t\t\t} else if (tempFocus && tempFocus === view) {\n\t\t\t\tif (prevFocus && !prevFocus.isInserted())\n\t\t\t\t\tprevFocus = null;\n\t\t\t\tview = View._focused = prevFocus;\n\t\t\t\tprevFocus = null;\n\t\t\t\tupdateFocus();\n\t\t\t}\n\t\t}\n\t\tif (view)\n\t\t\thandleMouseMove(view, event);\n\t};\n\n\tdocEvents[mousedown] = function() {\n\t\tmouseDown = true;\n\t};\n\n\tdocEvents[mouseup] = function(event) {\n\t\tvar view = View._focused;\n\t\tif (view && dragging)\n\t\t\tview._handleMouseEvent('mouseup', event);\n\t\tmouseDown = dragging = false;\n\t};\n\n\tDomEvent.add(document, docEvents);\n\n\tDomEvent.add(window, {\n\t\tload: updateFocus\n\t});\n\n\tvar called = false,\n\t\tprevented = false,\n\t\tfallbacks = {\n\t\t\tdoubleclick: 'click',\n\t\t\tmousedrag: 'mousemove'\n\t\t},\n\t\twasInView = false,\n\t\toverView,\n\t\tdownPoint,\n\t\tlastPoint,\n\t\tdownItem,\n\t\toverItem,\n\t\tdragItem,\n\t\tclickItem,\n\t\tclickTime,\n\t\tdblClick;\n\n\tfunction emitMouseEvent(obj, target, type, event, point, prevPoint,\n\t\t\tstopItem) {\n\t\tvar stopped = false,\n\t\t\tmouseEvent;\n\n\t\tfunction emit(obj, type) {\n\t\t\tif (obj.responds(type)) {\n\t\t\t\tif (!mouseEvent) {\n\t\t\t\t\tmouseEvent = new MouseEvent(type, event, point,\n\t\t\t\t\t\t\ttarget || obj,\n\t\t\t\t\t\t\tprevPoint ? point.subtract(prevPoint) : null);\n\t\t\t\t}\n\t\t\t\tif (obj.emit(type, mouseEvent)) {\n\t\t\t\t\tcalled = true;\n\t\t\t\t\tif (mouseEvent.prevented)\n\t\t\t\t\t\tprevented = true;\n\t\t\t\t\tif (mouseEvent.stopped)\n\t\t\t\t\t\treturn stopped = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvar fallback = fallbacks[type];\n\t\t\t\tif (fallback)\n\t\t\t\t\treturn emit(obj, fallback);\n\t\t\t}\n\t\t}\n\n\t\twhile (obj && obj !== stopItem) {\n\t\t\tif (emit(obj, type))\n\t\t\t\tbreak;\n\t\t\tobj = obj._parent;\n\t\t}\n\t\treturn stopped;\n\t}\n\n\tfunction emitMouseEvents(view, hitItem, type, event, point, prevPoint) {\n\t\tview._project.removeOn(type);\n\t\tprevented = called = false;\n\t\treturn (dragItem && emitMouseEvent(dragItem, null, type, event,\n\t\t\t\t\tpoint, prevPoint)\n\t\t\t|| hitItem && hitItem !== dragItem\n\t\t\t\t&& !hitItem.isDescendant(dragItem)\n\t\t\t\t&& emitMouseEvent(hitItem, null, type === 'mousedrag' ?\n\t\t\t\t\t'mousemove' : type, event, point, prevPoint, dragItem)\n\t\t\t|| emitMouseEvent(view, dragItem || hitItem || view, type, event,\n\t\t\t\t\tpoint, prevPoint));\n\t}\n\n\tvar itemEventsMap = {\n\t\tmousedown: {\n\t\t\tmousedown: 1,\n\t\t\tmousedrag: 1,\n\t\t\tclick: 1,\n\t\t\tdoubleclick: 1\n\t\t},\n\t\tmouseup: {\n\t\t\tmouseup: 1,\n\t\t\tmousedrag: 1,\n\t\t\tclick: 1,\n\t\t\tdoubleclick: 1\n\t\t},\n\t\tmousemove: {\n\t\t\tmousedrag: 1,\n\t\t\tmousemove: 1,\n\t\t\tmouseenter: 1,\n\t\t\tmouseleave: 1\n\t\t}\n\t};\n\n\treturn {\n\t\t_viewEvents: viewEvents,\n\n\t\t_handleMouseEvent: function(type, event, point) {\n\t\t\tvar itemEvents = this._itemEvents,\n\t\t\t\thitItems = itemEvents.native[type],\n\t\t\t\tnativeMove = type === 'mousemove',\n\t\t\t\ttool = this._scope.tool,\n\t\t\t\tview = this;\n\n\t\t\tfunction responds(type) {\n\t\t\t\treturn itemEvents.virtual[type] || view.responds(type)\n\t\t\t\t\t\t|| tool && tool.responds(type);\n\t\t\t}\n\n\t\t\tif (nativeMove && dragging && responds('mousedrag'))\n\t\t\t\ttype = 'mousedrag';\n\t\t\tif (!point)\n\t\t\t\tpoint = this.getEventPoint(event);\n\n\t\t\tvar inView = this.getBounds().contains(point),\n\t\t\t\thit = hitItems && inView && view._project.hitTest(point, {\n\t\t\t\t\ttolerance: 0,\n\t\t\t\t\tfill: true,\n\t\t\t\t\tstroke: true\n\t\t\t\t}),\n\t\t\t\thitItem = hit && hit.item || null,\n\t\t\t\thandle = false,\n\t\t\t\tmouse = {};\n\t\t\tmouse[type.substr(5)] = true;\n\n\t\t\tif (hitItems && hitItem !== overItem) {\n\t\t\t\tif (overItem) {\n\t\t\t\t\temitMouseEvent(overItem, null, 'mouseleave', event, point);\n\t\t\t\t}\n\t\t\t\tif (hitItem) {\n\t\t\t\t\temitMouseEvent(hitItem, null, 'mouseenter', event, point);\n\t\t\t\t}\n\t\t\t\toverItem = hitItem;\n\t\t\t}\n\t\t\tif (wasInView ^ inView) {\n\t\t\t\temitMouseEvent(this, null, inView ? 'mouseenter' : 'mouseleave',\n\t\t\t\t\t\tevent, point);\n\t\t\t\toverView = inView ? this : null;\n\t\t\t\thandle = true;\n\t\t\t}\n\t\t\tif ((inView || mouse.drag) && !point.equals(lastPoint)) {\n\t\t\t\temitMouseEvents(this, hitItem, nativeMove ? type : 'mousemove',\n\t\t\t\t\t\tevent, point, lastPoint);\n\t\t\t\thandle = true;\n\t\t\t}\n\t\t\twasInView = inView;\n\t\t\tif (mouse.down && inView || mouse.up && downPoint) {\n\t\t\t\temitMouseEvents(this, hitItem, type, event, point, downPoint);\n\t\t\t\tif (mouse.down) {\n\t\t\t\t\tdblClick = hitItem === clickItem\n\t\t\t\t\t\t&& (Date.now() - clickTime < 300);\n\t\t\t\t\tdownItem = clickItem = hitItem;\n\t\t\t\t\tif (!prevented && hitItem) {\n\t\t\t\t\t\tvar item = hitItem;\n\t\t\t\t\t\twhile (item && !item.responds('mousedrag'))\n\t\t\t\t\t\t\titem = item._parent;\n\t\t\t\t\t\tif (item)\n\t\t\t\t\t\t\tdragItem = hitItem;\n\t\t\t\t\t}\n\t\t\t\t\tdownPoint = point;\n\t\t\t\t} else if (mouse.up) {\n\t\t\t\t\tif (!prevented && hitItem === downItem) {\n\t\t\t\t\t\tclickTime = Date.now();\n\t\t\t\t\t\temitMouseEvents(this, hitItem, dblClick ? 'doubleclick'\n\t\t\t\t\t\t\t\t: 'click', event, point, downPoint);\n\t\t\t\t\t\tdblClick = false;\n\t\t\t\t\t}\n\t\t\t\t\tdownItem = dragItem = null;\n\t\t\t\t}\n\t\t\t\twasInView = false;\n\t\t\t\thandle = true;\n\t\t\t}\n\t\t\tlastPoint = point;\n\t\t\tif (handle && tool) {\n\t\t\t\tcalled = tool._handleMouseEvent(type, event, point, mouse)\n\t\t\t\t\t|| called;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tevent.cancelable !== false\n\t\t\t\t&& (called && !mouse.move || mouse.down && responds('mouseup'))\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\t\t},\n\n\t\t_handleKeyEvent: function(type, event, key, character) {\n\t\t\tvar scope = this._scope,\n\t\t\t\ttool = scope.tool,\n\t\t\t\tkeyEvent;\n\n\t\t\tfunction emit(obj) {\n\t\t\t\tif (obj.responds(type)) {\n\t\t\t\t\tpaper = scope;\n\t\t\t\t\tobj.emit(type, keyEvent = keyEvent\n\t\t\t\t\t\t\t|| new KeyEvent(type, event, key, character));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.isVisible()) {\n\t\t\t\temit(this);\n\t\t\t\tif (tool && tool.responds(type))\n\t\t\t\t\temit(tool);\n\t\t\t}\n\t\t},\n\n\t\t_countItemEvent: function(type, sign) {\n\t\t\tvar itemEvents = this._itemEvents,\n\t\t\t\tnative = itemEvents.native,\n\t\t\t\tvirtual = itemEvents.virtual;\n\t\t\tfor (var key in itemEventsMap) {\n\t\t\t\tnative[key] = (native[key] || 0)\n\t\t\t\t\t\t+ (itemEventsMap[key][type] || 0) * sign;\n\t\t\t}\n\t\t\tvirtual[type] = (virtual[type] || 0) + sign;\n\t\t},\n\n\t\tstatics: {\n\t\t\tupdateFocus: updateFocus,\n\n\t\t\t_resetState: function() {\n\t\t\t\tdragging = mouseDown = called = wasInView = false;\n\t\t\t\tprevFocus = tempFocus = overView = downPoint = lastPoint =\n\t\t\t\t\tdownItem = overItem = dragItem = clickItem = clickTime =\n\t\t\t\t\tdblClick = null;\n\t\t\t}\n\t\t}\n\t};\n});\n\nvar CanvasView = View.extend({\n\t_class: 'CanvasView',\n\n\tinitialize: function CanvasView(project, canvas) {\n\t\tif (!(canvas instanceof window.HTMLCanvasElement)) {\n\t\t\tvar size = Size.read(arguments, 1);\n\t\t\tif (size.isZero())\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Cannot create CanvasView with the provided argument: '\n\t\t\t\t\t\t+ Base.slice(arguments, 1));\n\t\t\tcanvas = CanvasProvider.getCanvas(size);\n\t\t}\n\t\tvar ctx = this._context = canvas.getContext('2d');\n\t\tctx.save();\n\t\tthis._pixelRatio = 1;\n\t\tif (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) {\n\t\t\tvar deviceRatio = window.devicePixelRatio || 1,\n\t\t\t\tbackingStoreRatio = DomElement.getPrefixed(ctx,\n\t\t\t\t\t\t'backingStorePixelRatio') || 1;\n\t\t\tthis._pixelRatio = deviceRatio / backingStoreRatio;\n\t\t}\n\t\tView.call(this, project, canvas);\n\t\tthis._needsUpdate = true;\n\t},\n\n\tremove: function remove() {\n\t\tthis._context.restore();\n\t\treturn remove.base.call(this);\n\t},\n\n\t_setElementSize: function _setElementSize(width, height) {\n\t\tvar pixelRatio = this._pixelRatio;\n\t\t_setElementSize.base.call(this, width * pixelRatio, height * pixelRatio);\n\t\tif (pixelRatio !== 1) {\n\t\t\tvar element = this._element,\n\t\t\t\tctx = this._context;\n\t\t\tif (!PaperScope.hasAttribute(element, 'resize')) {\n\t\t\t\tvar style = element.style;\n\t\t\t\tstyle.width = width + 'px';\n\t\t\t\tstyle.height = height + 'px';\n\t\t\t}\n\t\t\tctx.restore();\n\t\t\tctx.save();\n\t\t\tctx.scale(pixelRatio, pixelRatio);\n\t\t}\n\t},\n\n\tgetContext: function() {\n\t\treturn this._context;\n\t},\n\n\tgetPixelSize: function getPixelSize(size) {\n\t\tvar agent = paper.agent,\n\t\t\tpixels;\n\t\tif (agent && agent.firefox) {\n\t\t\tpixels = getPixelSize.base.call(this, size);\n\t\t} else {\n\t\t\tvar ctx = this._context,\n\t\t\t\tprevFont = ctx.font;\n\t\t\tctx.font = size + ' serif';\n\t\t\tpixels = parseFloat(ctx.font);\n\t\t\tctx.font = prevFont;\n\t\t}\n\t\treturn pixels;\n\t},\n\n\tgetTextWidth: function(font, lines) {\n\t\tvar ctx = this._context,\n\t\t\tprevFont = ctx.font,\n\t\t\twidth = 0;\n\t\tctx.font = font;\n\t\tfor (var i = 0, l = lines.length; i < l; i++)\n\t\t\twidth = Math.max(width, ctx.measureText(lines[i]).width);\n\t\tctx.font = prevFont;\n\t\treturn width;\n\t},\n\n\tupdate: function() {\n\t\tif (!this._needsUpdate)\n\t\t\treturn false;\n\t\tvar project = this._project,\n\t\t\tctx = this._context,\n\t\t\tsize = this._viewSize;\n\t\tctx.clearRect(0, 0, size.width + 1, size.height + 1);\n\t\tif (project)\n\t\t\tproject.draw(ctx, this._matrix, this._pixelRatio);\n\t\tthis._needsUpdate = false;\n\t\treturn true;\n\t}\n});\n\nvar Event = Base.extend({\n\t_class: 'Event',\n\n\tinitialize: function Event(event) {\n\t\tthis.event = event;\n\t\tthis.type = event && event.type;\n\t},\n\n\tprevented: false,\n\tstopped: false,\n\n\tpreventDefault: function() {\n\t\tthis.prevented = true;\n\t\tthis.event.preventDefault();\n\t},\n\n\tstopPropagation: function() {\n\t\tthis.stopped = true;\n\t\tthis.event.stopPropagation();\n\t},\n\n\tstop: function() {\n\t\tthis.stopPropagation();\n\t\tthis.preventDefault();\n\t},\n\n\tgetTimeStamp: function() {\n\t\treturn this.event.timeStamp;\n\t},\n\n\tgetModifiers: function() {\n\t\treturn Key.modifiers;\n\t}\n});\n\nvar KeyEvent = Event.extend({\n\t_class: 'KeyEvent',\n\n\tinitialize: function KeyEvent(type, event, key, character) {\n\t\tthis.type = type;\n\t\tthis.event = event;\n\t\tthis.key = key;\n\t\tthis.character = character;\n\t},\n\n\ttoString: function() {\n\t\treturn \"{ type: '\" + this.type\n\t\t\t\t+ \"', key: '\" + this.key\n\t\t\t\t+ \"', character: '\" + this.character\n\t\t\t\t+ \"', modifiers: \" + this.getModifiers()\n\t\t\t\t+ \" }\";\n\t}\n});\n\nvar Key = new function() {\n\tvar keyLookup = {\n\t\t\t'\\t': 'tab',\n\t\t\t' ': 'space',\n\t\t\t'\\b': 'backspace',\n\t\t\t'\\x7f': 'delete',\n\t\t\t'Spacebar': 'space',\n\t\t\t'Del': 'delete',\n\t\t\t'Win': 'meta',\n\t\t\t'Esc': 'escape'\n\t\t},\n\n\t\tcharLookup = {\n\t\t\t'tab': '\\t',\n\t\t\t'space': ' ',\n\t\t\t'enter': '\\r'\n\t\t},\n\n\t\tkeyMap = {},\n\t\tcharMap = {},\n\t\tmetaFixMap,\n\t\tdownKey,\n\n\t\tmodifiers = new Base({\n\t\t\tshift: false,\n\t\t\tcontrol: false,\n\t\t\talt: false,\n\t\t\tmeta: false,\n\t\t\tcapsLock: false,\n\t\t\tspace: false\n\t\t}).inject({\n\t\t\toption: {\n\t\t\t\tget: function() {\n\t\t\t\t\treturn this.alt;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tcommand: {\n\t\t\t\tget: function() {\n\t\t\t\t\tvar agent = paper && paper.agent;\n\t\t\t\t\treturn agent && agent.mac ? this.meta : this.control;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\tfunction getKey(event) {\n\t\tvar key = event.key || event.keyIdentifier;\n\t\tkey = /^U\\+/.test(key)\n\t\t\t\t? String.fromCharCode(parseInt(key.substr(2), 16))\n\t\t\t\t: /^Arrow[A-Z]/.test(key) ? key.substr(5)\n\t\t\t\t: key === 'Unidentified' || key === undefined\n\t\t\t\t\t? String.fromCharCode(event.keyCode)\n\t\t\t\t\t: key;\n\t\treturn keyLookup[key] ||\n\t\t\t\t(key.length > 1 ? Base.hyphenate(key) : key.toLowerCase());\n\t}\n\n\tfunction handleKey(down, key, character, event) {\n\t\tvar type = down ? 'keydown' : 'keyup',\n\t\t\tview = View._focused,\n\t\t\tname;\n\t\tkeyMap[key] = down;\n\t\tif (down) {\n\t\t\tcharMap[key] = character;\n\t\t} else {\n\t\t\tdelete charMap[key];\n\t\t}\n\t\tif (key.length > 1 && (name = Base.camelize(key)) in modifiers) {\n\t\t\tmodifiers[name] = down;\n\t\t\tvar agent = paper && paper.agent;\n\t\t\tif (name === 'meta' && agent && agent.mac) {\n\t\t\t\tif (down) {\n\t\t\t\t\tmetaFixMap = {};\n\t\t\t\t} else {\n\t\t\t\t\tfor (var k in metaFixMap) {\n\t\t\t\t\t\tif (k in charMap)\n\t\t\t\t\t\t\thandleKey(false, k, metaFixMap[k], event);\n\t\t\t\t\t}\n\t\t\t\t\tmetaFixMap = null;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (down && metaFixMap) {\n\t\t\tmetaFixMap[key] = character;\n\t\t}\n\t\tif (view) {\n\t\t\tview._handleKeyEvent(down ? 'keydown' : 'keyup', event, key,\n\t\t\t\t\tcharacter);\n\t\t}\n\t}\n\n\tDomEvent.add(document, {\n\t\tkeydown: function(event) {\n\t\t\tvar key = getKey(event),\n\t\t\t\tagent = paper && paper.agent;\n\t\t\tif (key.length > 1 || agent && (agent.chrome && (event.altKey\n\t\t\t\t\t\t|| agent.mac && event.metaKey\n\t\t\t\t\t\t|| !agent.mac && event.ctrlKey))) {\n\t\t\t\thandleKey(true, key,\n\t\t\t\t\t\tcharLookup[key] || (key.length > 1 ? '' : key), event);\n\t\t\t} else {\n\t\t\t\tdownKey = key;\n\t\t\t}\n\t\t},\n\n\t\tkeypress: function(event) {\n\t\t\tif (downKey) {\n\t\t\t\tvar key = getKey(event),\n\t\t\t\t\tcode = event.charCode,\n\t\t\t\t\tcharacter = code >= 32 ? String.fromCharCode(code)\n\t\t\t\t\t\t: key.length > 1 ? '' : key;\n\t\t\t\tif (key !== downKey) {\n\t\t\t\t\tkey = character.toLowerCase();\n\t\t\t\t}\n\t\t\t\thandleKey(true, key, character, event);\n\t\t\t\tdownKey = null;\n\t\t\t}\n\t\t},\n\n\t\tkeyup: function(event) {\n\t\t\tvar key = getKey(event);\n\t\t\tif (key in charMap)\n\t\t\t\thandleKey(false, key, charMap[key], event);\n\t\t}\n\t});\n\n\tDomEvent.add(window, {\n\t\tblur: function(event) {\n\t\t\tfor (var key in charMap)\n\t\t\t\thandleKey(false, key, charMap[key], event);\n\t\t}\n\t});\n\n\treturn {\n\t\tmodifiers: modifiers,\n\n\t\tisDown: function(key) {\n\t\t\treturn !!keyMap[key];\n\t\t}\n\t};\n};\n\nvar MouseEvent = Event.extend({\n\t_class: 'MouseEvent',\n\n\tinitialize: function MouseEvent(type, event, point, target, delta) {\n\t\tthis.type = type;\n\t\tthis.event = event;\n\t\tthis.point = point;\n\t\tthis.target = target;\n\t\tthis.delta = delta;\n\t},\n\n\ttoString: function() {\n\t\treturn \"{ type: '\" + this.type\n\t\t\t\t+ \"', point: \" + this.point\n\t\t\t\t+ ', target: ' + this.target\n\t\t\t\t+ (this.delta ? ', delta: ' + this.delta : '')\n\t\t\t\t+ ', modifiers: ' + this.getModifiers()\n\t\t\t\t+ ' }';\n\t}\n});\n\nvar ToolEvent = Event.extend({\n\t_class: 'ToolEvent',\n\t_item: null,\n\n\tinitialize: function ToolEvent(tool, type, event) {\n\t\tthis.tool = tool;\n\t\tthis.type = type;\n\t\tthis.event = event;\n\t},\n\n\t_choosePoint: function(point, toolPoint) {\n\t\treturn point ? point : toolPoint ? toolPoint.clone() : null;\n\t},\n\n\tgetPoint: function() {\n\t\treturn this._choosePoint(this._point, this.tool._point);\n\t},\n\n\tsetPoint: function(point) {\n\t\tthis._point = point;\n\t},\n\n\tgetLastPoint: function() {\n\t\treturn this._choosePoint(this._lastPoint, this.tool._lastPoint);\n\t},\n\n\tsetLastPoint: function(lastPoint) {\n\t\tthis._lastPoint = lastPoint;\n\t},\n\n\tgetDownPoint: function() {\n\t\treturn this._choosePoint(this._downPoint, this.tool._downPoint);\n\t},\n\n\tsetDownPoint: function(downPoint) {\n\t\tthis._downPoint = downPoint;\n\t},\n\n\tgetMiddlePoint: function() {\n\t\tif (!this._middlePoint && this.tool._lastPoint) {\n\t\t\treturn this.tool._point.add(this.tool._lastPoint).divide(2);\n\t\t}\n\t\treturn this._middlePoint;\n\t},\n\n\tsetMiddlePoint: function(middlePoint) {\n\t\tthis._middlePoint = middlePoint;\n\t},\n\n\tgetDelta: function() {\n\t\treturn !this._delta && this.tool._lastPoint\n\t\t\t\t? this.tool._point.subtract(this.tool._lastPoint)\n\t\t\t\t: this._delta;\n\t},\n\n\tsetDelta: function(delta) {\n\t\tthis._delta = delta;\n\t},\n\n\tgetCount: function() {\n\t\treturn this.tool[/^mouse(down|up)$/.test(this.type)\n\t\t\t\t? '_downCount' : '_moveCount'];\n\t},\n\n\tsetCount: function(count) {\n\t\tthis.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']\n\t\t\t= count;\n\t},\n\n\tgetItem: function() {\n\t\tif (!this._item) {\n\t\t\tvar result = this.tool._scope.project.hitTest(this.getPoint());\n\t\t\tif (result) {\n\t\t\t\tvar item = result.item,\n\t\t\t\t\tparent = item._parent;\n\t\t\t\twhile (/^(Group|CompoundPath)$/.test(parent._class)) {\n\t\t\t\t\titem = parent;\n\t\t\t\t\tparent = parent._parent;\n\t\t\t\t}\n\t\t\t\tthis._item = item;\n\t\t\t}\n\t\t}\n\t\treturn this._item;\n\t},\n\n\tsetItem: function(item) {\n\t\tthis._item = item;\n\t},\n\n\ttoString: function() {\n\t\treturn '{ type: ' + this.type\n\t\t\t\t+ ', point: ' + this.getPoint()\n\t\t\t\t+ ', count: ' + this.getCount()\n\t\t\t\t+ ', modifiers: ' + this.getModifiers()\n\t\t\t\t+ ' }';\n\t}\n});\n\nvar Tool = PaperScopeItem.extend({\n\t_class: 'Tool',\n\t_list: 'tools',\n\t_reference: 'tool',\n\t_events: ['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',\n\t\t\t'onActivate', 'onDeactivate', 'onEditOptions', 'onKeyDown',\n\t\t\t'onKeyUp'],\n\n\tinitialize: function Tool(props) {\n\t\tPaperScopeItem.call(this);\n\t\tthis._moveCount = -1;\n\t\tthis._downCount = -1;\n\t\tthis.set(props);\n\t},\n\n\tgetMinDistance: function() {\n\t\treturn this._minDistance;\n\t},\n\n\tsetMinDistance: function(minDistance) {\n\t\tthis._minDistance = minDistance;\n\t\tif (minDistance != null && this._maxDistance != null\n\t\t\t\t&& minDistance > this._maxDistance) {\n\t\t\tthis._maxDistance = minDistance;\n\t\t}\n\t},\n\n\tgetMaxDistance: function() {\n\t\treturn this._maxDistance;\n\t},\n\n\tsetMaxDistance: function(maxDistance) {\n\t\tthis._maxDistance = maxDistance;\n\t\tif (this._minDistance != null && maxDistance != null\n\t\t\t\t&& maxDistance < this._minDistance) {\n\t\t\tthis._minDistance = maxDistance;\n\t\t}\n\t},\n\n\tgetFixedDistance: function() {\n\t\treturn this._minDistance == this._maxDistance\n\t\t\t? this._minDistance : null;\n\t},\n\n\tsetFixedDistance: function(distance) {\n\t\tthis._minDistance = this._maxDistance = distance;\n\t},\n\n\t_handleMouseEvent: function(type, event, point, mouse) {\n\t\tpaper = this._scope;\n\t\tif (mouse.drag && !this.responds(type))\n\t\t\ttype = 'mousemove';\n\t\tvar move = mouse.move || mouse.drag,\n\t\t\tresponds = this.responds(type),\n\t\t\tminDistance = this.minDistance,\n\t\t\tmaxDistance = this.maxDistance,\n\t\t\tcalled = false,\n\t\t\ttool = this;\n\t\tfunction update(minDistance, maxDistance) {\n\t\t\tvar pt = point,\n\t\t\t\ttoolPoint = move ? tool._point : (tool._downPoint || pt);\n\t\t\tif (move) {\n\t\t\t\tif (tool._moveCount >= 0 && pt.equals(toolPoint)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (toolPoint && (minDistance != null || maxDistance != null)) {\n\t\t\t\t\tvar vector = pt.subtract(toolPoint),\n\t\t\t\t\t\tdistance = vector.getLength();\n\t\t\t\t\tif (distance < (minDistance || 0))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif (maxDistance) {\n\t\t\t\t\t\tpt = toolPoint.add(vector.normalize(\n\t\t\t\t\t\t\t\tMath.min(distance, maxDistance)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttool._moveCount++;\n\t\t\t}\n\t\t\ttool._point = pt;\n\t\t\ttool._lastPoint = toolPoint || pt;\n\t\t\tif (mouse.down) {\n\t\t\t\ttool._moveCount = -1;\n\t\t\t\ttool._downPoint = pt;\n\t\t\t\ttool._downCount++;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tfunction emit() {\n\t\t\tif (responds) {\n\t\t\t\tcalled = tool.emit(type, new ToolEvent(tool, type, event))\n\t\t\t\t\t\t|| called;\n\t\t\t}\n\t\t}\n\n\t\tif (mouse.down) {\n\t\t\tupdate();\n\t\t\temit();\n\t\t} else if (mouse.up) {\n\t\t\tupdate(null, maxDistance);\n\t\t\temit();\n\t\t} else if (responds) {\n\t\t\twhile (update(minDistance, maxDistance))\n\t\t\t\temit();\n\t\t}\n\t\treturn called;\n\t}\n\n});\n\nvar Tween = Base.extend(Emitter, {\n\t_class: 'Tween',\n\n\tstatics: {\n\t\teasings: {\n\t\t\tlinear: function(t) {\n\t\t\t\treturn t;\n\t\t\t},\n\n\t\t\teaseInQuad: function(t) {\n\t\t\t\treturn t * t;\n\t\t\t},\n\n\t\t\teaseOutQuad: function(t) {\n\t\t\t\treturn t * (2 - t);\n\t\t\t},\n\n\t\t\teaseInOutQuad: function(t) {\n\t\t\t\treturn t < 0.5\n\t\t\t\t\t? 2 * t * t\n\t\t\t\t\t: -1 + 2 * (2 - t) * t;\n\t\t\t},\n\n\t\t\teaseInCubic: function(t) {\n\t\t\t\treturn t * t * t;\n\t\t\t},\n\n\t\t\teaseOutCubic: function(t) {\n\t\t\t\treturn --t * t * t + 1;\n\t\t\t},\n\n\t\t\teaseInOutCubic: function(t) {\n\t\t\t\treturn t < 0.5\n\t\t\t\t\t? 4 * t * t * t\n\t\t\t\t\t: (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;\n\t\t\t},\n\n\t\t\teaseInQuart: function(t) {\n\t\t\t\treturn t * t * t * t;\n\t\t\t},\n\n\t\t\teaseOutQuart: function(t) {\n\t\t\t\treturn 1 - (--t) * t * t * t;\n\t\t\t},\n\n\t\t\teaseInOutQuart: function(t) {\n\t\t\t\treturn t < 0.5\n\t\t\t\t\t? 8 * t * t * t * t\n\t\t\t\t\t: 1 - 8 * (--t) * t * t * t;\n\t\t\t},\n\n\t\t\teaseInQuint: function(t) {\n\t\t\t\treturn t * t * t * t * t;\n\t\t\t},\n\n\t\t\teaseOutQuint: function(t) {\n\t\t\t\treturn 1 + --t * t * t * t * t;\n\t\t\t},\n\n\t\t\teaseInOutQuint: function(t) {\n\t\t\t\treturn t < 0.5\n\t\t\t\t\t? 16 * t * t * t * t * t\n\t\t\t\t\t: 1 + 16 * (--t) * t * t * t * t;\n\t\t\t}\n\t\t}\n\t},\n\n\tinitialize: function Tween(object, from, to, duration, easing, start) {\n\t\tthis.object = object;\n\t\tvar type = typeof easing;\n\t\tvar isFunction = type === 'function';\n\t\tthis.type = isFunction\n\t\t\t? type\n\t\t\t: type === 'string'\n\t\t\t\t? easing\n\t\t\t\t: 'linear';\n\t\tthis.easing = isFunction ? easing : Tween.easings[this.type];\n\t\tthis.duration = duration;\n\t\tthis.running = false;\n\n\t\tthis._then = null;\n\t\tthis._startTime = null;\n\t\tvar state = from || to;\n\t\tthis._keys = state ? Object.keys(state) : [];\n\t\tthis._parsedKeys = this._parseKeys(this._keys);\n\t\tthis._from = state && this._getState(from);\n\t\tthis._to = state && this._getState(to);\n\t\tif (start !== false) {\n\t\t\tthis.start();\n\t\t}\n\t},\n\n\tthen: function(then) {\n\t\tthis._then = then;\n\t\treturn this;\n\t},\n\n\tstart: function() {\n\t\tthis._startTime = null;\n\t\tthis.running = true;\n\t\treturn this;\n\t},\n\n\tstop: function() {\n\t\tthis.running = false;\n\t\treturn this;\n\t},\n\n\tupdate: function(progress) {\n\t\tif (this.running) {\n\t\t\tif (progress > 1) {\n\t\t\t\tprogress = 1;\n\t\t\t\tthis.running = false;\n\t\t\t}\n\n\t\t\tvar factor = this.easing(progress),\n\t\t\t\tkeys = this._keys,\n\t\t\t\tgetValue = function(value) {\n\t\t\t\t\treturn typeof value === 'function'\n\t\t\t\t\t\t? value(factor, progress)\n\t\t\t\t\t\t: value;\n\t\t\t\t};\n\t\t\tfor (var i = 0, l = keys && keys.length; i < l; i++) {\n\t\t\t\tvar key = keys[i],\n\t\t\t\t\tfrom = getValue(this._from[key]),\n\t\t\t\t\tto = getValue(this._to[key]),\n\t\t\t\t\tvalue = (from && to && from.__add && to.__add)\n\t\t\t\t\t\t? to.__subtract(from).__multiply(factor).__add(from)\n\t\t\t\t\t\t: ((to - from) * factor) + from;\n\t\t\t\tthis._setProperty(this._parsedKeys[key], value);\n\t\t\t}\n\n\t\t\tif (!this.running && this._then) {\n\t\t\t\tthis._then(this.object);\n\t\t\t}\n\t\t\tif (this.responds('update')) {\n\t\t\t\tthis.emit('update', new Base({\n\t\t\t\t\tprogress: progress,\n\t\t\t\t\tfactor: factor\n\t\t\t\t}));\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t},\n\n\t_events: {\n\t\tonUpdate: {}\n\t},\n\n\t_handleFrame: function(time) {\n\t\tvar startTime = this._startTime,\n\t\t\tprogress = startTime\n\t\t\t\t? (time - startTime) / this.duration\n\t\t\t\t: 0;\n\t\tif (!startTime) {\n\t\t\tthis._startTime = time;\n\t\t}\n\t\tthis.update(progress);\n\t},\n\n\t_getState: function(state) {\n\t\tvar keys = this._keys,\n\t\t\tresult = {};\n\t\tfor (var i = 0, l = keys.length; i < l; i++) {\n\t\t\tvar key = keys[i],\n\t\t\t\tpath = this._parsedKeys[key],\n\t\t\t\tcurrent = this._getProperty(path),\n\t\t\t\tvalue;\n\t\t\tif (state) {\n\t\t\t\tvar resolved = this._resolveValue(current, state[key]);\n\t\t\t\tthis._setProperty(path, resolved);\n\t\t\t\tvalue = this._getProperty(path);\n\t\t\t\tvalue = value && value.clone ? value.clone() : value;\n\t\t\t\tthis._setProperty(path, current);\n\t\t\t} else {\n\t\t\t\tvalue = current && current.clone ? current.clone() : current;\n\t\t\t}\n\t\t\tresult[key] = value;\n\t\t}\n\t\treturn result;\n\t},\n\n\t_resolveValue: function(current, value) {\n\t\tif (value) {\n\t\t\tif (Array.isArray(value) && value.length === 2) {\n\t\t\t\tvar operator = value[0];\n\t\t\t\treturn (\n\t\t\t\t\toperator &&\n\t\t\t\t\toperator.match &&\n\t\t\t\t\toperator.match(/^[+\\-\\*\\/]=/)\n\t\t\t\t)\n\t\t\t\t\t? this._calculate(current, operator[0], value[1])\n\t\t\t\t\t: value;\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tvar match = value.match(/^[+\\-*/]=(.*)/);\n\t\t\t\tif (match) {\n\t\t\t\t\tvar parsed = JSON.parse(match[1].replace(\n\t\t\t\t\t\t/(['\"])?([a-zA-Z0-9_]+)(['\"])?:/g,\n\t\t\t\t\t\t'\"$2\": '\n\t\t\t\t\t));\n\t\t\t\t\treturn this._calculate(current, value[0], parsed);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn value;\n\t},\n\n\t_calculate: function(left, operator, right) {\n\t\treturn paper.PaperScript.calculateBinary(left, operator, right);\n\t},\n\n\t_parseKeys: function(keys) {\n\t\tvar parsed = {};\n\t\tfor (var i = 0, l = keys.length; i < l; i++) {\n\t\t\tvar key = keys[i],\n\t\t\t\tpath = key\n\t\t\t\t\t.replace(/\\.([^.]*)/g, '/$1')\n\t\t\t\t\t.replace(/\\[['\"]?([^'\"\\]]*)['\"]?\\]/g, '/$1');\n\t\t\tparsed[key] = path.split('/');\n\t\t}\n\t\treturn parsed;\n\t},\n\n\t_getProperty: function(path, offset) {\n\t\tvar obj = this.object;\n\t\tfor (var i = 0, l = path.length - (offset || 0); i < l && obj; i++) {\n\t\t\tobj = obj[path[i]];\n\t\t}\n\t\treturn obj;\n\t},\n\n\t_setProperty: function(path, value) {\n\t\tvar dest = this._getProperty(path, 1);\n\t\tif (dest) {\n\t\t\tdest[path[path.length - 1]] = value;\n\t\t}\n\t}\n});\n\nvar Http = {\n\trequest: function(options) {\n\t\tvar xhr = new self.XMLHttpRequest();\n\t\txhr.open((options.method || 'get').toUpperCase(), options.url,\n\t\t\t\tBase.pick(options.async, true));\n\t\tif (options.mimeType)\n\t\t\txhr.overrideMimeType(options.mimeType);\n\t\txhr.onload = function() {\n\t\t\tvar status = xhr.status;\n\t\t\tif (status === 0 || status === 200) {\n\t\t\t\tif (options.onLoad) {\n\t\t\t\t\toptions.onLoad.call(xhr, xhr.responseText);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\txhr.onerror();\n\t\t\t}\n\t\t};\n\t\txhr.onerror = function() {\n\t\t\tvar status = xhr.status,\n\t\t\t\tmessage = 'Could not load \"' + options.url + '\" (Status: '\n\t\t\t\t\t\t+ status + ')';\n\t\t\tif (options.onError) {\n\t\t\t\toptions.onError(message, status);\n\t\t\t} else {\n\t\t\t\tthrow new Error(message);\n\t\t\t}\n\t\t};\n\t\treturn xhr.send(null);\n\t}\n};\n\nvar CanvasProvider = {\n\tcanvases: [],\n\n\tgetCanvas: function(width, height) {\n\t\tif (!window)\n\t\t\treturn null;\n\t\tvar canvas,\n\t\t\tclear = true;\n\t\tif (typeof width === 'object') {\n\t\t\theight = width.height;\n\t\t\twidth = width.width;\n\t\t}\n\t\tif (this.canvases.length) {\n\t\t\tcanvas = this.canvases.pop();\n\t\t} else {\n\t\t\tcanvas = document.createElement('canvas');\n\t\t\tclear = false;\n\t\t}\n\t\tvar ctx = canvas.getContext('2d');\n\t\tif (!ctx) {\n\t\t\tthrow new Error('Canvas ' + canvas +\n\t\t\t\t\t' is unable to provide a 2D context.');\n\t\t}\n\t\tif (canvas.width === width && canvas.height === height) {\n\t\t\tif (clear)\n\t\t\t\tctx.clearRect(0, 0, width + 1, height + 1);\n\t\t} else {\n\t\t\tcanvas.width = width;\n\t\t\tcanvas.height = height;\n\t\t}\n\t\tctx.save();\n\t\treturn canvas;\n\t},\n\n\tgetContext: function(width, height) {\n\t\tvar canvas = this.getCanvas(width, height);\n\t\treturn canvas ? canvas.getContext('2d') : null;\n\t},\n\n\trelease: function(obj) {\n\t\tvar canvas = obj && obj.canvas ? obj.canvas : obj;\n\t\tif (canvas && canvas.getContext) {\n\t\t\tcanvas.getContext('2d').restore();\n\t\t\tthis.canvases.push(canvas);\n\t\t}\n\t}\n};\n\nvar BlendMode = new function() {\n\tvar min = Math.min,\n\t\tmax = Math.max,\n\t\tabs = Math.abs,\n\t\tsr, sg, sb, sa,\n\t\tbr, bg, bb, ba,\n\t\tdr, dg, db;\n\n\tfunction getLum(r, g, b) {\n\t\treturn 0.2989 * r + 0.587 * g + 0.114 * b;\n\t}\n\n\tfunction setLum(r, g, b, l) {\n\t\tvar d = l - getLum(r, g, b);\n\t\tdr = r + d;\n\t\tdg = g + d;\n\t\tdb = b + d;\n\t\tvar l = getLum(dr, dg, db),\n\t\t\tmn = min(dr, dg, db),\n\t\t\tmx = max(dr, dg, db);\n\t\tif (mn < 0) {\n\t\t\tvar lmn = l - mn;\n\t\t\tdr = l + (dr - l) * l / lmn;\n\t\t\tdg = l + (dg - l) * l / lmn;\n\t\t\tdb = l + (db - l) * l / lmn;\n\t\t}\n\t\tif (mx > 255) {\n\t\t\tvar ln = 255 - l,\n\t\t\t\tmxl = mx - l;\n\t\t\tdr = l + (dr - l) * ln / mxl;\n\t\t\tdg = l + (dg - l) * ln / mxl;\n\t\t\tdb = l + (db - l) * ln / mxl;\n\t\t}\n\t}\n\n\tfunction getSat(r, g, b) {\n\t\treturn max(r, g, b) - min(r, g, b);\n\t}\n\n\tfunction setSat(r, g, b, s) {\n\t\tvar col = [r, g, b],\n\t\t\tmx = max(r, g, b),\n\t\t\tmn = min(r, g, b),\n\t\t\tmd;\n\t\tmn = mn === r ? 0 : mn === g ? 1 : 2;\n\t\tmx = mx === r ? 0 : mx === g ? 1 : 2;\n\t\tmd = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;\n\t\tif (col[mx] > col[mn]) {\n\t\t\tcol[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]);\n\t\t\tcol[mx] = s;\n\t\t} else {\n\t\t\tcol[md] = col[mx] = 0;\n\t\t}\n\t\tcol[mn] = 0;\n\t\tdr = col[0];\n\t\tdg = col[1];\n\t\tdb = col[2];\n\t}\n\n\tvar modes = {\n\t\tmultiply: function() {\n\t\t\tdr = br * sr / 255;\n\t\t\tdg = bg * sg / 255;\n\t\t\tdb = bb * sb / 255;\n\t\t},\n\n\t\tscreen: function() {\n\t\t\tdr = br + sr - (br * sr / 255);\n\t\t\tdg = bg + sg - (bg * sg / 255);\n\t\t\tdb = bb + sb - (bb * sb / 255);\n\t\t},\n\n\t\toverlay: function() {\n\t\t\tdr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255;\n\t\t\tdg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255;\n\t\t\tdb = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255;\n\t\t},\n\n\t\t'soft-light': function() {\n\t\t\tvar t = sr * br / 255;\n\t\t\tdr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255;\n\t\t\tt = sg * bg / 255;\n\t\t\tdg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255;\n\t\t\tt = sb * bb / 255;\n\t\t\tdb = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255;\n\t\t},\n\n\t\t'hard-light': function() {\n\t\t\tdr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255;\n\t\t\tdg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255;\n\t\t\tdb = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255;\n\t\t},\n\n\t\t'color-dodge': function() {\n\t\t\tdr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr));\n\t\t\tdg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg));\n\t\t\tdb = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb));\n\t\t},\n\n\t\t'color-burn': function() {\n\t\t\tdr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr);\n\t\t\tdg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg);\n\t\t\tdb = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb);\n\t\t},\n\n\t\tdarken: function() {\n\t\t\tdr = br < sr ? br : sr;\n\t\t\tdg = bg < sg ? bg : sg;\n\t\t\tdb = bb < sb ? bb : sb;\n\t\t},\n\n\t\tlighten: function() {\n\t\t\tdr = br > sr ? br : sr;\n\t\t\tdg = bg > sg ? bg : sg;\n\t\t\tdb = bb > sb ? bb : sb;\n\t\t},\n\n\t\tdifference: function() {\n\t\t\tdr = br - sr;\n\t\t\tif (dr < 0)\n\t\t\t\tdr = -dr;\n\t\t\tdg = bg - sg;\n\t\t\tif (dg < 0)\n\t\t\t\tdg = -dg;\n\t\t\tdb = bb - sb;\n\t\t\tif (db < 0)\n\t\t\t\tdb = -db;\n\t\t},\n\n\t\texclusion: function() {\n\t\t\tdr = br + sr * (255 - br - br) / 255;\n\t\t\tdg = bg + sg * (255 - bg - bg) / 255;\n\t\t\tdb = bb + sb * (255 - bb - bb) / 255;\n\t\t},\n\n\t\thue: function() {\n\t\t\tsetSat(sr, sg, sb, getSat(br, bg, bb));\n\t\t\tsetLum(dr, dg, db, getLum(br, bg, bb));\n\t\t},\n\n\t\tsaturation: function() {\n\t\t\tsetSat(br, bg, bb, getSat(sr, sg, sb));\n\t\t\tsetLum(dr, dg, db, getLum(br, bg, bb));\n\t\t},\n\n\t\tluminosity: function() {\n\t\t\tsetLum(br, bg, bb, getLum(sr, sg, sb));\n\t\t},\n\n\t\tcolor: function() {\n\t\t\tsetLum(sr, sg, sb, getLum(br, bg, bb));\n\t\t},\n\n\t\tadd: function() {\n\t\t\tdr = min(br + sr, 255);\n\t\t\tdg = min(bg + sg, 255);\n\t\t\tdb = min(bb + sb, 255);\n\t\t},\n\n\t\tsubtract: function() {\n\t\t\tdr = max(br - sr, 0);\n\t\t\tdg = max(bg - sg, 0);\n\t\t\tdb = max(bb - sb, 0);\n\t\t},\n\n\t\taverage: function() {\n\t\t\tdr = (br + sr) / 2;\n\t\t\tdg = (bg + sg) / 2;\n\t\t\tdb = (bb + sb) / 2;\n\t\t},\n\n\t\tnegation: function() {\n\t\t\tdr = 255 - abs(255 - sr - br);\n\t\t\tdg = 255 - abs(255 - sg - bg);\n\t\t\tdb = 255 - abs(255 - sb - bb);\n\t\t}\n\t};\n\n\tvar nativeModes = this.nativeModes = Base.each([\n\t\t'source-over', 'source-in', 'source-out', 'source-atop',\n\t\t'destination-over', 'destination-in', 'destination-out',\n\t\t'destination-atop', 'lighter', 'darker', 'copy', 'xor'\n\t], function(mode) {\n\t\tthis[mode] = true;\n\t}, {});\n\n\tvar ctx = CanvasProvider.getContext(1, 1);\n\tif (ctx) {\n\t\tBase.each(modes, function(func, mode) {\n\t\t\tvar darken = mode === 'darken',\n\t\t\t\tok = false;\n\t\t\tctx.save();\n\t\t\ttry {\n\t\t\t\tctx.fillStyle = darken ? '#300' : '#a00';\n\t\t\t\tctx.fillRect(0, 0, 1, 1);\n\t\t\t\tctx.globalCompositeOperation = mode;\n\t\t\t\tif (ctx.globalCompositeOperation === mode) {\n\t\t\t\t\tctx.fillStyle = darken ? '#a00' : '#300';\n\t\t\t\t\tctx.fillRect(0, 0, 1, 1);\n\t\t\t\t\tok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken\n\t\t\t\t\t\t\t? 170 : 51;\n\t\t\t\t}\n\t\t\t} catch (e) {}\n\t\t\tctx.restore();\n\t\t\tnativeModes[mode] = ok;\n\t\t});\n\t\tCanvasProvider.release(ctx);\n\t}\n\n\tthis.process = function(mode, srcContext, dstContext, alpha, offset) {\n\t\tvar srcCanvas = srcContext.canvas,\n\t\t\tnormal = mode === 'normal';\n\t\tif (normal || nativeModes[mode]) {\n\t\t\tdstContext.save();\n\t\t\tdstContext.setTransform(1, 0, 0, 1, 0, 0);\n\t\t\tdstContext.globalAlpha = alpha;\n\t\t\tif (!normal)\n\t\t\t\tdstContext.globalCompositeOperation = mode;\n\t\t\tdstContext.drawImage(srcCanvas, offset.x, offset.y);\n\t\t\tdstContext.restore();\n\t\t} else {\n\t\t\tvar process = modes[mode];\n\t\t\tif (!process)\n\t\t\t\treturn;\n\t\t\tvar dstData = dstContext.getImageData(offset.x, offset.y,\n\t\t\t\t\tsrcCanvas.width, srcCanvas.height),\n\t\t\t\tdst = dstData.data,\n\t\t\t\tsrc = srcContext.getImageData(0, 0,\n\t\t\t\t\tsrcCanvas.width, srcCanvas.height).data;\n\t\t\tfor (var i = 0, l = dst.length; i < l; i += 4) {\n\t\t\t\tsr = src[i];\n\t\t\t\tbr = dst[i];\n\t\t\t\tsg = src[i + 1];\n\t\t\t\tbg = dst[i + 1];\n\t\t\t\tsb = src[i + 2];\n\t\t\t\tbb = dst[i + 2];\n\t\t\t\tsa = src[i + 3];\n\t\t\t\tba = dst[i + 3];\n\t\t\t\tprocess();\n\t\t\t\tvar a1 = sa * alpha / 255,\n\t\t\t\t\ta2 = 1 - a1;\n\t\t\t\tdst[i] = a1 * dr + a2 * br;\n\t\t\t\tdst[i + 1] = a1 * dg + a2 * bg;\n\t\t\t\tdst[i + 2] = a1 * db + a2 * bb;\n\t\t\t\tdst[i + 3] = sa * alpha + a2 * ba;\n\t\t\t}\n\t\t\tdstContext.putImageData(dstData, offset.x, offset.y);\n\t\t}\n\t};\n};\n\nvar SvgElement = new function() {\n\tvar svg = 'http://www.w3.org/2000/svg',\n\t\txmlns = 'http://www.w3.org/2000/xmlns',\n\t\txlink = 'http://www.w3.org/1999/xlink',\n\t\tattributeNamespace = {\n\t\t\thref: xlink,\n\t\t\txlink: xmlns,\n\t\t\txmlns: xmlns + '/',\n\t\t\t'xmlns:xlink': xmlns + '/'\n\t\t};\n\n\tfunction create(tag, attributes, formatter) {\n\t\treturn set(document.createElementNS(svg, tag), attributes, formatter);\n\t}\n\n\tfunction get(node, name) {\n\t\tvar namespace = attributeNamespace[name],\n\t\t\tvalue = namespace\n\t\t\t\t? node.getAttributeNS(namespace, name)\n\t\t\t\t: node.getAttribute(name);\n\t\treturn value === 'null' ? null : value;\n\t}\n\n\tfunction set(node, attributes, formatter) {\n\t\tfor (var name in attributes) {\n\t\t\tvar value = attributes[name],\n\t\t\t\tnamespace = attributeNamespace[name];\n\t\t\tif (typeof value === 'number' && formatter)\n\t\t\t\tvalue = formatter.number(value);\n\t\t\tif (namespace) {\n\t\t\t\tnode.setAttributeNS(namespace, name, value);\n\t\t\t} else {\n\t\t\t\tnode.setAttribute(name, value);\n\t\t\t}\n\t\t}\n\t\treturn node;\n\t}\n\n\treturn {\n\t\tsvg: svg,\n\t\txmlns: xmlns,\n\t\txlink: xlink,\n\n\t\tcreate: create,\n\t\tget: get,\n\t\tset: set\n\t};\n};\n\nvar SvgStyles = Base.each({\n\tfillColor: ['fill', 'color'],\n\tfillRule: ['fill-rule', 'string'],\n\tstrokeColor: ['stroke', 'color'],\n\tstrokeWidth: ['stroke-width', 'number'],\n\tstrokeCap: ['stroke-linecap', 'string'],\n\tstrokeJoin: ['stroke-linejoin', 'string'],\n\tstrokeScaling: ['vector-effect', 'lookup', {\n\t\ttrue: 'none',\n\t\tfalse: 'non-scaling-stroke'\n\t}, function(item, value) {\n\t\treturn !value\n\t\t\t\t&& (item instanceof PathItem\n\t\t\t\t\t|| item instanceof Shape\n\t\t\t\t\t|| item instanceof TextItem);\n\t}],\n\tmiterLimit: ['stroke-miterlimit', 'number'],\n\tdashArray: ['stroke-dasharray', 'array'],\n\tdashOffset: ['stroke-dashoffset', 'number'],\n\tfontFamily: ['font-family', 'string'],\n\tfontWeight: ['font-weight', 'string'],\n\tfontSize: ['font-size', 'number'],\n\tjustification: ['text-anchor', 'lookup', {\n\t\tleft: 'start',\n\t\tcenter: 'middle',\n\t\tright: 'end'\n\t}],\n\topacity: ['opacity', 'number'],\n\tblendMode: ['mix-blend-mode', 'style']\n}, function(entry, key) {\n\tvar part = Base.capitalize(key),\n\t\tlookup = entry[2];\n\tthis[key] = {\n\t\ttype: entry[1],\n\t\tproperty: key,\n\t\tattribute: entry[0],\n\t\ttoSVG: lookup,\n\t\tfromSVG: lookup && Base.each(lookup, function(value, name) {\n\t\t\tthis[value] = name;\n\t\t}, {}),\n\t\texportFilter: entry[3],\n\t\tget: 'get' + part,\n\t\tset: 'set' + part\n\t};\n}, {});\n\nnew function() {\n\tvar formatter;\n\n\tfunction getTransform(matrix, coordinates, center) {\n\t\tvar attrs = new Base(),\n\t\t\ttrans = matrix.getTranslation();\n\t\tif (coordinates) {\n\t\t\tvar point;\n\t\t\tif (matrix.isInvertible()) {\n\t\t\t\tmatrix = matrix._shiftless();\n\t\t\t\tpoint = matrix._inverseTransform(trans);\n\t\t\t\ttrans = null;\n\t\t\t} else {\n\t\t\t\tpoint = new Point();\n\t\t\t}\n\t\t\tattrs[center ? 'cx' : 'x'] = point.x;\n\t\t\tattrs[center ? 'cy' : 'y'] = point.y;\n\t\t}\n\t\tif (!matrix.isIdentity()) {\n\t\t\tvar decomposed = matrix.decompose();\n\t\t\tif (decomposed) {\n\t\t\t\tvar parts = [],\n\t\t\t\t\tangle = decomposed.rotation,\n\t\t\t\t\tscale = decomposed.scaling,\n\t\t\t\t\tskew = decomposed.skewing;\n\t\t\t\tif (trans && !trans.isZero())\n\t\t\t\t\tparts.push('translate(' + formatter.point(trans) + ')');\n\t\t\t\tif (angle)\n\t\t\t\t\tparts.push('rotate(' + formatter.number(angle) + ')');\n\t\t\t\tif (!Numerical.isZero(scale.x - 1)\n\t\t\t\t\t\t|| !Numerical.isZero(scale.y - 1))\n\t\t\t\t\tparts.push('scale(' + formatter.point(scale) +')');\n\t\t\t\tif (skew.x)\n\t\t\t\t\tparts.push('skewX(' + formatter.number(skew.x) + ')');\n\t\t\t\tif (skew.y)\n\t\t\t\t\tparts.push('skewY(' + formatter.number(skew.y) + ')');\n\t\t\t\tattrs.transform = parts.join(' ');\n\t\t\t} else {\n\t\t\t\tattrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';\n\t\t\t}\n\t\t}\n\t\treturn attrs;\n\t}\n\n\tfunction exportGroup(item, options) {\n\t\tvar attrs = getTransform(item._matrix),\n\t\t\tchildren = item._children;\n\t\tvar node = SvgElement.create('g', attrs, formatter);\n\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\tvar child = children[i];\n\t\t\tvar childNode = exportSVG(child, options);\n\t\t\tif (childNode) {\n\t\t\t\tif (child.isClipMask()) {\n\t\t\t\t\tvar clip = SvgElement.create('clipPath');\n\t\t\t\t\tclip.appendChild(childNode);\n\t\t\t\t\tsetDefinition(child, clip, 'clip');\n\t\t\t\t\tSvgElement.set(node, {\n\t\t\t\t\t\t'clip-path': 'url(#' + clip.id + ')'\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tnode.appendChild(childNode);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn node;\n\t}\n\n\tfunction exportRaster(item, options) {\n\t\tvar attrs = getTransform(item._matrix, true),\n\t\t\tsize = item.getSize(),\n\t\t\timage = item.getImage();\n\t\tattrs.x -= size.width / 2;\n\t\tattrs.y -= size.height / 2;\n\t\tattrs.width = size.width;\n\t\tattrs.height = size.height;\n\t\tattrs.href = options.embedImages == false && image && image.src\n\t\t\t\t|| item.toDataURL();\n\t\treturn SvgElement.create('image', attrs, formatter);\n\t}\n\n\tfunction exportPath(item, options) {\n\t\tvar matchShapes = options.matchShapes;\n\t\tif (matchShapes) {\n\t\t\tvar shape = item.toShape(false);\n\t\t\tif (shape)\n\t\t\t\treturn exportShape(shape, options);\n\t\t}\n\t\tvar segments = item._segments,\n\t\t\tlength = segments.length,\n\t\t\ttype,\n\t\t\tattrs = getTransform(item._matrix);\n\t\tif (matchShapes && length >= 2 && !item.hasHandles()) {\n\t\t\tif (length > 2) {\n\t\t\t\ttype = item._closed ? 'polygon' : 'polyline';\n\t\t\t\tvar parts = [];\n\t\t\t\tfor (var i = 0; i < length; i++) {\n\t\t\t\t\tparts.push(formatter.point(segments[i]._point));\n\t\t\t\t}\n\t\t\t\tattrs.points = parts.join(' ');\n\t\t\t} else {\n\t\t\t\ttype = 'line';\n\t\t\t\tvar start = segments[0]._point,\n\t\t\t\t\tend = segments[1]._point;\n\t\t\t\tattrs.set({\n\t\t\t\t\tx1: start.x,\n\t\t\t\t\ty1: start.y,\n\t\t\t\t\tx2: end.x,\n\t\t\t\t\ty2: end.y\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\ttype = 'path';\n\t\t\tattrs.d = item.getPathData(null, options.precision);\n\t\t}\n\t\treturn SvgElement.create(type, attrs, formatter);\n\t}\n\n\tfunction exportShape(item) {\n\t\tvar type = item._type,\n\t\t\tradius = item._radius,\n\t\t\tattrs = getTransform(item._matrix, true, type !== 'rectangle');\n\t\tif (type === 'rectangle') {\n\t\t\ttype = 'rect';\n\t\t\tvar size = item._size,\n\t\t\t\twidth = size.width,\n\t\t\t\theight = size.height;\n\t\t\tattrs.x -= width / 2;\n\t\t\tattrs.y -= height / 2;\n\t\t\tattrs.width = width;\n\t\t\tattrs.height = height;\n\t\t\tif (radius.isZero())\n\t\t\t\tradius = null;\n\t\t}\n\t\tif (radius) {\n\t\t\tif (type === 'circle') {\n\t\t\t\tattrs.r = radius;\n\t\t\t} else {\n\t\t\t\tattrs.rx = radius.width;\n\t\t\t\tattrs.ry = radius.height;\n\t\t\t}\n\t\t}\n\t\treturn SvgElement.create(type, attrs, formatter);\n\t}\n\n\tfunction exportCompoundPath(item, options) {\n\t\tvar attrs = getTransform(item._matrix);\n\t\tvar data = item.getPathData(null, options.precision);\n\t\tif (data)\n\t\t\tattrs.d = data;\n\t\treturn SvgElement.create('path', attrs, formatter);\n\t}\n\n\tfunction exportSymbolItem(item, options) {\n\t\tvar attrs = getTransform(item._matrix, true),\n\t\t\tdefinition = item._definition,\n\t\t\tnode = getDefinition(definition, 'symbol'),\n\t\t\tdefinitionItem = definition._item,\n\t\t\tbounds = definitionItem.getStrokeBounds();\n\t\tif (!node) {\n\t\t\tnode = SvgElement.create('symbol', {\n\t\t\t\tviewBox: formatter.rectangle(bounds)\n\t\t\t});\n\t\t\tnode.appendChild(exportSVG(definitionItem, options));\n\t\t\tsetDefinition(definition, node, 'symbol');\n\t\t}\n\t\tattrs.href = '#' + node.id;\n\t\tattrs.x += bounds.x;\n\t\tattrs.y += bounds.y;\n\t\tattrs.width = bounds.width;\n\t\tattrs.height = bounds.height;\n\t\tattrs.overflow = 'visible';\n\t\treturn SvgElement.create('use', attrs, formatter);\n\t}\n\n\tfunction exportGradient(color, item) {\n\t\tvar gradientNode = getDefinition(color, 'color');\n\t\tif (!gradientNode) {\n\t\t\tvar gradient = color.getGradient(),\n\t\t\t\tradial = gradient._radial,\n\t\t\t\torigin = color.getOrigin(),\n\t\t\t\tdestination = color.getDestination(),\n\t\t\t\tattrs;\n\t\t\tif (radial) {\n\t\t\t\tattrs = {\n\t\t\t\t\tcx: origin.x,\n\t\t\t\t\tcy: origin.y,\n\t\t\t\t\tr: origin.getDistance(destination)\n\t\t\t\t};\n\t\t\t\tvar highlight = color.getHighlight();\n\t\t\t\tif (highlight) {\n\t\t\t\t\tattrs.fx = highlight.x;\n\t\t\t\t\tattrs.fy = highlight.y;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tattrs = {\n\t\t\t\t\tx1: origin.x,\n\t\t\t\t\ty1: origin.y,\n\t\t\t\t\tx2: destination.x,\n\t\t\t\t\ty2: destination.y\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (item instanceof paper.PointText) {\n\t\t\t\tattrs.gradientTransform = getTransform(\n\t\t\t\t\titem._matrix.clone().invert(), false, formatter).transform;\n\t\t\t}\n\n\t\t\tattrs.gradientUnits = 'userSpaceOnUse';\n\t\t\tgradientNode = SvgElement.create((radial ? 'radial' : 'linear')\n\t\t\t\t\t+ 'Gradient', attrs, formatter);\n\t\t\tvar stops = gradient._stops;\n\t\t\tfor (var i = 0, l = stops.length; i < l; i++) {\n\t\t\t\tvar stop = stops[i],\n\t\t\t\t\tstopColor = stop._color,\n\t\t\t\t\talpha = stopColor.getAlpha(),\n\t\t\t\t\toffset = stop._offset;\n\t\t\t\tattrs = {\n\t\t\t\t\toffset: offset == null ? i / (l - 1) : offset\n\t\t\t\t};\n\t\t\t\tif (stopColor)\n\t\t\t\t\tattrs['stop-color'] = stopColor.toCSS(true);\n\t\t\t\tif (alpha < 1)\n\t\t\t\t\tattrs['stop-opacity'] = alpha;\n\t\t\t\tgradientNode.appendChild(\n\t\t\t\t\t\tSvgElement.create('stop', attrs, formatter));\n\t\t\t}\n\t\t\tsetDefinition(color, gradientNode, 'color');\n\t\t}\n\t\treturn 'url(#' + gradientNode.id + ')';\n\t}\n\n\tfunction exportText(item) {\n\t\tvar node = SvgElement.create('text', getTransform(item._matrix, false),\n\t\t\t\tformatter);\n\t\tnode.setAttribute('font-size', item.fontSize);\n\t\tnode.setAttribute('xml:space', 'preserve');\n\t\tfor (var i = 0; i < item._lines.length; i++) {\n\t\t\tvar tspanNode = SvgElement.create('tspan', {\n\t\t\t\tx: '0',\n\t\t\t\tdy: i === 0 ? '0' : item.getLeading() + 'px'\n\t\t\t}, formatter);\n\t\t\ttspanNode.textContent = item._lines[i] ? item._lines[i] : ' ';\n\t\t\tnode.appendChild(tspanNode);\n\t\t}\n\t\treturn node;\n\t}\n\n\tvar exporters = {\n\t\tGroup: exportGroup,\n\t\tLayer: exportGroup,\n\t\tRaster: exportRaster,\n\t\tPath: exportPath,\n\t\tShape: exportShape,\n\t\tCompoundPath: exportCompoundPath,\n\t\tSymbolItem: exportSymbolItem,\n\t\tPointText: exportText\n\t};\n\n\tfunction applyStyle(item, node, isRoot) {\n\t\tvar attrs = {},\n\t\t\tparent = !isRoot && item.getParent(),\n\t\t\tstyle = [];\n\n\t\tif (item._name != null)\n\t\t\tattrs.id = item._name;\n\n\t\tBase.each(SvgStyles, function(entry) {\n\t\t\tvar get = entry.get,\n\t\t\t\ttype = entry.type,\n\t\t\t\tvalue = item[get]();\n\n\t\t\tif (value === undefined) return;\n\n\t\t\tif (entry.exportFilter\n\t\t\t\t\t? entry.exportFilter(item, value)\n\t\t\t\t\t: !parent || !Base.equals(parent[get](), value) ||\n\t\t\t\t\t item instanceof paper.PointText) {\n\t\t\t\tif (type === 'color' && value != null) {\n\t\t\t\t\tvar alpha = value.getAlpha();\n\t\t\t\t\tif (alpha < 1)\n\t\t\t\t\t\tattrs[entry.attribute + '-opacity'] = alpha;\n\t\t\t\t}\n\t\t\t\tif (type === 'style') {\n\t\t\t\t\tstyle.push(entry.attribute + ': ' + value);\n\t\t\t\t} else {\n\t\t\t\t\tattrs[entry.attribute] = value == null ? 'none'\n\t\t\t\t\t\t\t: type === 'color' ? value.gradient\n\t\t\t\t\t\t\t\t? exportGradient(value, item)\n\t\t\t\t\t\t\t\t: value.toCSS(true)\n\t\t\t\t\t\t\t: type === 'array' ? value.join(',')\n\t\t\t\t\t\t\t: type === 'lookup' ? entry.toSVG[value]\n\t\t\t\t\t\t\t: value;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (style.length)\n\t\t\tattrs.style = style.join(';');\n\n\t\tif (attrs.opacity === 1)\n\t\t\tdelete attrs.opacity;\n\n\t\tif (!item._visible)\n\t\t\tattrs.visibility = 'hidden';\n\n\t\treturn SvgElement.set(node, attrs, formatter);\n\t}\n\n\tvar definitions;\n\tfunction getDefinition(item, type) {\n\t\tif (!definitions)\n\t\t\tdefinitions = { ids: {}, svgs: {} };\n\t\treturn item && definitions.svgs[type + '-'\n\t\t\t\t+ (item._id || item.__id || (item.__id = UID.get('svg')))];\n\t}\n\n\tfunction setDefinition(item, node, type) {\n\t\tif (!definitions)\n\t\t\tgetDefinition();\n\t\tvar typeId = definitions.ids[type] = (definitions.ids[type] || 0) + 1;\n\t\tnode.id = type + '-' + typeId;\n\t\tdefinitions.svgs[type + '-' + (item._id || item.__id)] = node;\n\t}\n\n\tfunction exportDefinitions(node, options) {\n\t\tvar svg = node,\n\t\t\tdefs = null;\n\t\tif (definitions) {\n\t\t\tsvg = node.nodeName.toLowerCase() === 'svg' && node;\n\t\t\tfor (var i in definitions.svgs) {\n\t\t\t\tif (!defs) {\n\t\t\t\t\tif (!svg) {\n\t\t\t\t\t\tsvg = SvgElement.create('svg');\n\t\t\t\t\t\tsvg.appendChild(node);\n\t\t\t\t\t}\n\t\t\t\t\tdefs = svg.insertBefore(SvgElement.create('defs'),\n\t\t\t\t\t\t\tsvg.firstChild);\n\t\t\t\t}\n\t\t\t\tdefs.appendChild(definitions.svgs[i]);\n\t\t\t}\n\t\t\tdefinitions = null;\n\t\t}\n\t\treturn options.asString\n\t\t\t\t? new self.XMLSerializer().serializeToString(svg)\n\t\t\t\t: svg;\n\t}\n\n\tfunction exportSVG(item, options, isRoot) {\n\t\tvar exporter = exporters[item._class],\n\t\t\tnode = exporter && exporter(item, options);\n\t\tif (node) {\n\t\t\tvar onExport = options.onExport;\n\t\t\tif (onExport)\n\t\t\t\tnode = onExport(item, node, options) || node;\n\t\t\tvar data = JSON.stringify(item._data);\n\t\t\tif (data && data !== '{}' && data !== 'null')\n\t\t\t\tnode.setAttribute('data-paper-data', data);\n\t\t}\n\t\treturn node && applyStyle(item, node, isRoot);\n\t}\n\n\tfunction setOptions(options) {\n\t\tif (!options)\n\t\t\toptions = {};\n\t\tformatter = new Formatter(options.precision);\n\t\treturn options;\n\t}\n\n\tItem.inject({\n\t\texportSVG: function(options) {\n\t\t\toptions = setOptions(options);\n\t\t\treturn exportDefinitions(exportSVG(this, options, true), options);\n\t\t}\n\t});\n\n\tProject.inject({\n\t\texportSVG: function(options) {\n\t\t\toptions = setOptions(options);\n\t\t\tvar children = this._children,\n\t\t\t\tview = this.getView(),\n\t\t\t\tbounds = Base.pick(options.bounds, 'view'),\n\t\t\t\tmx = options.matrix || bounds === 'view' && view._matrix,\n\t\t\t\tmatrix = mx && Matrix.read([mx]),\n\t\t\t\trect = bounds === 'view'\n\t\t\t\t\t? new Rectangle([0, 0], view.getViewSize())\n\t\t\t\t\t: bounds === 'content'\n\t\t\t\t\t\t? Item._getBounds(children, matrix, { stroke: true, drawnTextBounds: true })\n\t\t\t\t\t\t\t.rect\n\t\t\t\t\t\t: Rectangle.read([bounds], 0, { readNull: true }),\n\t\t\t\tattrs = {\n\t\t\t\t\tversion: '1.1',\n\t\t\t\t\txmlns: SvgElement.svg,\n\t\t\t\t\t'xmlns:xlink': SvgElement.xlink,\n\t\t\t\t};\n\t\t\tif (rect) {\n\t\t\t\tattrs.width = rect.width;\n\t\t\t\tattrs.height = rect.height;\n\t\t\t\tif (rect.x || rect.x === 0 || rect.y || rect.y === 0)\n\t\t\t\t\tattrs.viewBox = formatter.rectangle(rect);\n\t\t\t}\n\t\t\tvar node = SvgElement.create('svg', attrs, formatter),\n\t\t\t\tparent = node;\n\t\t\tif (matrix && !matrix.isIdentity()) {\n\t\t\t\tparent = node.appendChild(SvgElement.create('g',\n\t\t\t\t\t\tgetTransform(matrix), formatter));\n\t\t\t}\n\t\t\tfor (var i = 0, l = children.length; i < l; i++) {\n\t\t\t\tparent.appendChild(exportSVG(children[i], options, true));\n\t\t\t}\n\t\t\treturn exportDefinitions(node, options);\n\t\t}\n\t});\n};\n\nnew function() {\n\n\tvar definitions = {},\n\t\trootSize;\n\n\tfunction getValue(node, name, isString, allowNull, allowPercent,\n\t\t\tdefaultValue) {\n\t\tvar value = SvgElement.get(node, name) || defaultValue,\n\t\t\tres = value == null\n\t\t\t\t? allowNull\n\t\t\t\t\t? null\n\t\t\t\t\t: isString ? '' : 0\n\t\t\t\t: isString\n\t\t\t\t\t? value\n\t\t\t\t\t: parseFloat(value);\n\t\treturn /%\\s*$/.test(value)\n\t\t\t? (res / 100) * (allowPercent ? 1\n\t\t\t\t: rootSize[/x|^width/.test(name) ? 'width' : 'height'])\n\t\t\t: res;\n\t}\n\n\tfunction getPoint(node, x, y, allowNull, allowPercent, defaultX, defaultY) {\n\t\tx = getValue(node, x || 'x', false, allowNull, allowPercent, defaultX);\n\t\ty = getValue(node, y || 'y', false, allowNull, allowPercent, defaultY);\n\t\treturn allowNull && (x == null || y == null) ? null\n\t\t\t\t: new Point(x, y);\n\t}\n\n\tfunction getSize(node, w, h, allowNull, allowPercent) {\n\t\tw = getValue(node, w || 'width', false, allowNull, allowPercent);\n\t\th = getValue(node, h || 'height', false, allowNull, allowPercent);\n\t\treturn allowNull && (w == null || h == null) ? null\n\t\t\t\t: new Size(w, h);\n\t}\n\n\tfunction convertValue(value, type, lookup) {\n\t\treturn value === 'none' ? null\n\t\t\t\t: type === 'number' ? parseFloat(value)\n\t\t\t\t: type === 'array' ?\n\t\t\t\t\tvalue ? value.split(/[\\s,]+/g).map(parseFloat) : []\n\t\t\t\t: type === 'color' ? getDefinition(value) || value\n\t\t\t\t: type === 'lookup' ? lookup[value]\n\t\t\t\t: value;\n\t}\n\n\tfunction importGroup(node, type, options, isRoot) {\n\t\tvar nodes = node.childNodes,\n\t\t\tisClip = type === 'clippath',\n\t\t\tisDefs = type === 'defs',\n\t\t\titem = new Group(),\n\t\t\tproject = item._project,\n\t\t\tcurrentStyle = project._currentStyle,\n\t\t\tchildren = [];\n\t\tif (!isClip && !isDefs) {\n\t\t\titem = applyAttributes(item, node, isRoot);\n\t\t\tproject._currentStyle = item._style.clone();\n\t\t}\n\t\tif (isRoot) {\n\t\t\tvar defs = node.querySelectorAll('defs');\n\t\t\tfor (var i = 0, l = defs.length; i < l; i++) {\n\t\t\t\timportNode(defs[i], options, false);\n\t\t\t}\n\t\t}\n\t\tfor (var i = 0, l = nodes.length; i < l; i++) {\n\t\t\tvar childNode = nodes[i],\n\t\t\t\tchild;\n\t\t\tif (childNode.nodeType === 1\n\t\t\t\t\t&& !/^defs$/i.test(childNode.nodeName)\n\t\t\t\t\t&& (child = importNode(childNode, options, false))\n\t\t\t\t\t&& !(child instanceof SymbolDefinition))\n\t\t\t\tchildren.push(child);\n\t\t}\n\t\titem.addChildren(children);\n\t\tif (isClip)\n\t\t\titem = applyAttributes(item.reduce(), node, isRoot);\n\t\tproject._currentStyle = currentStyle;\n\t\tif (isClip || isDefs) {\n\t\t\titem.remove();\n\t\t\titem = null;\n\t\t}\n\t\treturn item;\n\t}\n\n\tfunction importPoly(node, type) {\n\t\tvar coords = node.getAttribute('points').match(\n\t\t\t\t\t/[+-]?(?:\\d*\\.\\d+|\\d+\\.?)(?:[eE][+-]?\\d+)?/g),\n\t\t\tpoints = [];\n\t\tfor (var i = 0, l = coords.length; i < l; i += 2)\n\t\t\tpoints.push(new Point(\n\t\t\t\t\tparseFloat(coords[i]),\n\t\t\t\t\tparseFloat(coords[i + 1])));\n\t\tvar path = new Path(points);\n\t\tif (type === 'polygon')\n\t\t\tpath.closePath();\n\t\treturn path;\n\t}\n\n\tfunction importPath(node) {\n\t\treturn PathItem.create(node.getAttribute('d'));\n\t}\n\n\tfunction importGradient(node, type) {\n\t\tvar id = (getValue(node, 'href', true) || '').substring(1),\n\t\t\tradial = type === 'radialgradient',\n\t\t\tgradient;\n\t\tif (id) {\n\t\t\tgradient = definitions[id].getGradient();\n\t\t\tif (gradient._radial ^ radial) {\n\t\t\t\tgradient = gradient.clone();\n\t\t\t\tgradient._radial = radial;\n\t\t\t}\n\t\t} else {\n\t\t\tvar nodes = node.childNodes,\n\t\t\t\tstops = [];\n\t\t\tfor (var i = 0, l = nodes.length; i < l; i++) {\n\t\t\t\tvar child = nodes[i];\n\t\t\t\tif (child.nodeType === 1)\n\t\t\t\t\tstops.push(applyAttributes(new GradientStop(), child));\n\t\t\t}\n\t\t\tgradient = new Gradient(stops, radial);\n\t\t}\n\t\tvar origin, destination, highlight,\n\t\t\tscaleToBounds = getValue(node, 'gradientUnits', true) !==\n\t\t\t\t'userSpaceOnUse';\n\t\tif (radial) {\n\t\t\torigin = getPoint(node, 'cx', 'cy', false, scaleToBounds,\n\t\t\t\t'50%', '50%');\n\t\t\tdestination = origin.add(\n\t\t\t\tgetValue(node, 'r', false, false, scaleToBounds, '50%'), 0);\n\t\t\thighlight = getPoint(node, 'fx', 'fy', true, scaleToBounds);\n\t\t} else {\n\t\t\torigin = getPoint(node, 'x1', 'y1', false, scaleToBounds,\n\t\t\t\t'0%', '0%');\n\t\t\tdestination = getPoint(node, 'x2', 'y2', false, scaleToBounds,\n\t\t\t\t'100%', '0%');\n\t\t}\n\t\tvar color = applyAttributes(\n\t\t\t\tnew Color(gradient, origin, destination, highlight), node);\n\t\tcolor._scaleToBounds = scaleToBounds;\n\t\treturn null;\n\t}\n\n\tvar importers = {\n\t\t'#document': function (node, type, options, isRoot) {\n\t\t\tvar nodes = node.childNodes;\n\t\t\tfor (var i = 0, l = nodes.length; i < l; i++) {\n\t\t\t\tvar child = nodes[i];\n\t\t\t\tif (child.nodeType === 1)\n\t\t\t\t\treturn importNode(child, options, isRoot);\n\t\t\t}\n\t\t},\n\t\tg: importGroup,\n\t\tsvg: importGroup,\n\t\tclippath: importGroup,\n\t\tpolygon: importPoly,\n\t\tpolyline: importPoly,\n\t\tpath: importPath,\n\t\tlineargradient: importGradient,\n\t\tradialgradient: importGradient,\n\n\t\timage: function (node) {\n\t\t\tvar raster = new Raster(getValue(node, 'href', true));\n\t\t\traster.on('load', function() {\n\t\t\t\tvar size = getSize(node);\n\t\t\t\tthis.setSize(size);\n\t\t\t\tvar center = getPoint(node).add(size.divide(2));\n\t\t\t\tthis._matrix.append(new Matrix().translate(center));\n\t\t\t});\n\t\t\treturn raster;\n\t\t},\n\n\t\tsymbol: function(node, type, options, isRoot) {\n\t\t\treturn new SymbolDefinition(\n\t\t\t\t\timportGroup(node, type, options, isRoot), true);\n\t\t},\n\n\t\tdefs: importGroup,\n\n\t\tuse: function(node) {\n\t\t\tvar id = (getValue(node, 'href', true) || '').substring(1),\n\t\t\t\tdefinition = definitions[id],\n\t\t\t\tpoint = getPoint(node);\n\t\t\treturn definition\n\t\t\t\t\t? definition instanceof SymbolDefinition\n\t\t\t\t\t\t? definition.place(point)\n\t\t\t\t\t\t: definition.clone().translate(point)\n\t\t\t\t\t: null;\n\t\t},\n\n\t\tcircle: function(node) {\n\t\t\treturn new Shape.Circle(\n\t\t\t\t\tgetPoint(node, 'cx', 'cy'),\n\t\t\t\t\tgetValue(node, 'r'));\n\t\t},\n\n\t\tellipse: function(node) {\n\t\t\treturn new Shape.Ellipse({\n\t\t\t\tcenter: getPoint(node, 'cx', 'cy'),\n\t\t\t\tradius: getSize(node, 'rx', 'ry')\n\t\t\t});\n\t\t},\n\n\t\trect: function(node) {\n\t\t\treturn new Shape.Rectangle(new Rectangle(\n\t\t\t\t\t\tgetPoint(node),\n\t\t\t\t\t\tgetSize(node)\n\t\t\t\t\t), getSize(node, 'rx', 'ry'));\n\t\t\t},\n\n\t\tline: function(node) {\n\t\t\treturn new Path.Line(\n\t\t\t\t\tgetPoint(node, 'x1', 'y1'),\n\t\t\t\t\tgetPoint(node, 'x2', 'y2'));\n\t\t},\n\n\t\ttext: function(node) {\n\n\t\t\tvar fontSize = parseFloat(node.getAttribute(\"font-size\"));\n\t\t\tvar alignmentBaseline = node.getAttribute(\"alignment-baseline\");\n\t\t\tif (node.childElementCount === 0) {\n\t\t\t\tvar text = new PointText();\n\t\t\t\ttext.setContent(node.textContent.trim() || '');\n\t\t\t\ttext.translate(0, text._style.getLeading());\n\t\t\t\tif (!isNaN(fontSize)) text.setFontSize(fontSize);\n\t\t\t\treturn text;\n\t\t\t} else {\n\t\t\t\tvar lines = [];\n\t\t\t\tvar spacing = 1.2;\n\t\t\t\tfor (var i = 0; i < node.childNodes.length; i++) {\n\t\t\t\t\tvar child = node.childNodes[i];\n\t\t\t\t\tif (!child.getAttribute) continue;\n\t\t\t\t\tlines.push(child.textContent);\n\t\t\t\t\tvar dyString = child.getAttribute('dy');\n\t\t\t\t\tif (dyString) {\n\t\t\t\t\t\tvar dy = parseFloat(dyString);\n\t\t\t\t\t\tif (!isNaN(dy)) {\n\t\t\t\t\t\t\tif (dyString.endsWith('em')) {\n\t\t\t\t\t\t\t\tspacing = dy;\n\t\t\t\t\t\t\t} else if (dyString.endsWith('px') && !isNaN(fontSize)) {\n\t\t\t\t\t\t\t\tspacing = dy / fontSize;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvar text = new PointText();\n\t\t\t\tif (!isNaN(fontSize)) text.setFontSize(fontSize);\n\t\t\t\ttext.setLeading(text.fontSize * spacing);\n\t\t\t\tif (alignmentBaseline === 'text-before-edge') {\n\t\t\t\t\ttext.setContent(' ');\n\t\t\t\t\ttext.translate(0, text.bounds.height);\n\t\t\t\t}\n\t\t\t\ttext.setContent(lines.join('\\n'));\n\t\t\t\treturn text;\n\t\t\t}\n\t\t},\n\n\t\tswitch: importGroup\n\t};\n\n\tfunction applyTransform(item, value, name, node) {\n\t\tif (item.transform) {\n\t\t\tvar transforms = (node.getAttribute(name) || '').split(/\\)\\s*/g),\n\t\t\t\tmatrix = new Matrix();\n\t\t\tfor (var i = 0, l = transforms.length; i < l; i++) {\n\t\t\t\tvar transform = transforms[i];\n\t\t\t\tif (!transform)\n\t\t\t\t\tbreak;\n\t\t\t\tvar parts = transform.split(/\\(\\s*/),\n\t\t\t\t\tcommand = parts[0],\n\t\t\t\t\tv = parts[1].split(/[\\s,]+/g);\n\t\t\t\tfor (var j = 0, m = v.length; j < m; j++)\n\t\t\t\t\tv[j] = parseFloat(v[j]);\n\t\t\t\tswitch (command) {\n\t\t\t\tcase 'matrix':\n\t\t\t\t\tmatrix.append(\n\t\t\t\t\t\t\tnew Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'rotate':\n\t\t\t\t\tmatrix.rotate(v[0], v[1] || 0, v[2] || 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'translate':\n\t\t\t\t\tmatrix.translate(v[0], v[1] || 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'scale':\n\t\t\t\t\tmatrix.scale(v);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'skewX':\n\t\t\t\t\tmatrix.skew(v[0], 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'skewY':\n\t\t\t\t\tmatrix.skew(0, v[0]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\titem.transform(matrix);\n\t\t}\n\t}\n\n\tfunction applyOpacity(item, value, name) {\n\t\tvar key = name === 'fill-opacity' ? 'getFillColor' : 'getStrokeColor',\n\t\t\tcolor = item[key] && item[key]();\n\t\tif (color)\n\t\t\tcolor.setAlpha(parseFloat(value));\n\t}\n\n\tvar attributes = Base.set(Base.each(SvgStyles, function(entry) {\n\t\tthis[entry.attribute] = function(item, value) {\n\t\t\tif (item[entry.set]) {\n\t\t\t\titem[entry.set](convertValue(value, entry.type, entry.fromSVG));\n\t\t\t\tif (entry.type === 'color') {\n\t\t\t\t\tvar color = item[entry.get]();\n\t\t\t\t\tif (color) {\n\t\t\t\t\t\tif (color._scaleToBounds) {\n\t\t\t\t\t\t\tvar bounds = item.getBounds();\n\t\t\t\t\t\t\tcolor.transform(new Matrix()\n\t\t\t\t\t\t\t\t.translate(bounds.getPoint())\n\t\t\t\t\t\t\t\t.scale(bounds.getSize()));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}, {}), {\n\t\tid: function(item, value) {\n\t\t\tdefinitions[value] = item;\n\t\t},\n\n\t\t'clip-path': function(item, value) {\n\t\t\tvar clip = getDefinition(value);\n\t\t\tif (clip) {\n\t\t\t\tclip = clip.clone();\n\t\t\t\tclip.setClipMask(true);\n\t\t\t\tif (item instanceof Group) {\n\t\t\t\t\titem.insertChild(0, clip);\n\t\t\t\t} else {\n\t\t\t\t\treturn new Group(clip, item);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tgradientTransform: applyTransform,\n\t\ttransform: applyTransform,\n\n\t\t'fill-opacity': applyOpacity,\n\t\t'stroke-opacity': applyOpacity,\n\n\t\tvisibility: function(item, value) {\n\t\t\tif (item.setVisible)\n\t\t\t\titem.setVisible(value === 'visible');\n\t\t},\n\n\t\tdisplay: function(item, value) {\n\t\t\tif (item.setVisible)\n\t\t\t\titem.setVisible(value !== null);\n\t\t},\n\n\t\t'stop-color': function(item, value) {\n\t\t\tif (item.setColor)\n\t\t\t\titem.setColor(value);\n\t\t},\n\n\t\t'stop-opacity': function(item, value) {\n\t\t\tif (item._color)\n\t\t\t\titem._color.setAlpha(parseFloat(value));\n\t\t},\n\n\t\toffset: function(item, value) {\n\t\t\tif (item.setOffset) {\n\t\t\t\tvar percent = value.match(/(.*)%$/);\n\t\t\t\titem.setOffset(percent ? percent[1] / 100 : parseFloat(value));\n\t\t\t}\n\t\t},\n\n\t\tviewBox: function(item, value, name, node, styles) {\n\t\t\tvar rect = new Rectangle(convertValue(value, 'array')),\n\t\t\t\tsize = getSize(node, null, null, true),\n\t\t\t\tgroup,\n\t\t\t\tmatrix;\n\t\t\tif (item instanceof Group) {\n\t\t\t\tvar scale = size ? size.divide(rect.getSize()) : 1,\n\t\t\t\tmatrix = new Matrix().scale(scale)\n\t\t\t\t\t\t.translate(rect.getPoint().negate());\n\t\t\t\tgroup = item;\n\t\t\t} else if (item instanceof SymbolDefinition) {\n\t\t\t\tif (size)\n\t\t\t\t\trect.setSize(size);\n\t\t\t\tgroup = item._item;\n\t\t\t}\n\t\t\tif (group) {\n\t\t\t\tif (getAttribute(node, 'overflow', styles) !== 'visible') {\n\t\t\t\t\tvar clip = new Shape.Rectangle(rect);\n\t\t\t\t\tclip.setClipMask(true);\n\t\t\t\t\tgroup.addChild(clip);\n\t\t\t\t}\n\t\t\t\tif (matrix)\n\t\t\t\t\tgroup.transform(matrix);\n\t\t\t}\n\t\t},\n\n\t\t'fill-rule': function(item, value) {\n\t\t\tif (value === 'evenodd' || value === 'nonzero') item.fillRule = value;\n\t\t}\n\t});\n\n\tfunction getAttribute(node, name, styles) {\n\t\tvar attr = node.attributes[name],\n\t\t\tvalue = attr && attr.value;\n\t\tif (!value && node.style) {\n\t\t\tvar style = Base.camelize(name);\n\t\t\tvalue = node.style[style];\n\t\t\tif (!value && styles.node[style] !== styles.parent[style])\n\t\t\t\tvalue = styles.node[style];\n\t\t}\n\t\treturn !value ? undefined\n\t\t\t\t: value === 'none' ? null\n\t\t\t\t: value;\n\t}\n\n\tfunction applyAttributes(item, node, isRoot) {\n\t\tvar parent = node.parentNode,\n\t\t\tstyles = {\n\t\t\t\tnode: DomElement.getStyles(node) || {},\n\t\t\t\tparent: !isRoot && !/^defs$/i.test(parent.tagName)\n\t\t\t\t\t\t&& DomElement.getStyles(parent) || {}\n\t\t\t};\n\t\tBase.each(attributes, function(apply, name) {\n\t\t\tvar value = getAttribute(node, name, styles);\n\t\t\titem = value !== undefined\n\t\t\t\t\t&& apply(item, value, name, node, styles) || item;\n\t\t});\n\t\treturn item;\n\t}\n\n\tfunction getDefinition(value) {\n\t\tvar match = value && value.match(/\\((?:[\"'#]*)([^\"')]+)/),\n\t\t\tname = match && match[1],\n\t\t\tres = name && definitions[window\n\t\t\t\t\t? name.replace(window.location.href.split('#')[0] + '#', '')\n\t\t\t\t\t: name];\n\t\tif (res && res._scaleToBounds) {\n\t\t\tres = res.clone();\n\t\t\tres._scaleToBounds = true;\n\t\t}\n\t\treturn res;\n\t}\n\n\tfunction importNode(node, options, isRoot) {\n\t\tvar type = node.nodeName.toLowerCase(),\n\t\t\tisElement = type !== '#document',\n\t\t\tbody = document.body,\n\t\t\tcontainer,\n\t\t\tparent,\n\t\t\tnext;\n\t\tif (isRoot && isElement) {\n\t\t\trootSize = paper.getView().getSize();\n\t\t\trootSize = getSize(node, null, null, true) || rootSize;\n\t\t\tcontainer = SvgElement.create('svg', {\n\t\t\t\tstyle: 'stroke-width: 1px; stroke-miterlimit: 10'\n\t\t\t});\n\t\t\tparent = node.parentNode;\n\t\t\tnext = node.nextSibling;\n\t\t\tcontainer.appendChild(node);\n\t\t\tbody.appendChild(container);\n\t\t}\n\t\tvar settings = paper.settings,\n\t\t\tapplyMatrix = settings.applyMatrix,\n\t\t\tinsertItems = settings.insertItems;\n\t\tsettings.applyMatrix = false;\n\t\tsettings.insertItems = false;\n\t\tvar importer = importers[type],\n\t\t\titem = importer && importer(node, type, options, isRoot) || null;\n\t\tsettings.insertItems = insertItems;\n\t\tsettings.applyMatrix = applyMatrix;\n\t\tif (item) {\n\t\t\tif (isElement && !(item instanceof Group))\n\t\t\t\titem = applyAttributes(item, node, isRoot);\n\t\t\tvar onImport = options.onImport,\n\t\t\t\tdata = isElement && node.getAttribute('data-paper-data');\n\t\t\tif (onImport)\n\t\t\t\titem = onImport(node, item, options) || item;\n\t\t\tif (options.expandShapes && item instanceof Shape) {\n\t\t\t\titem.remove();\n\t\t\t\titem = item.toPath();\n\t\t\t}\n\t\t\tif (data)\n\t\t\t\titem._data = JSON.parse(data);\n\t\t}\n\t\tif (container) {\n\t\t\tbody.removeChild(container);\n\t\t\tif (parent) {\n\t\t\t\tif (next) {\n\t\t\t\t\tparent.insertBefore(node, next);\n\t\t\t\t} else {\n\t\t\t\t\tparent.appendChild(node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (isRoot) {\n\t\t\tdefinitions = {};\n\t\t\tif (item && Base.pick(options.applyMatrix, applyMatrix))\n\t\t\t\titem.matrix.apply(true, true);\n\t\t}\n\t\treturn item;\n\t}\n\n\tfunction importSVG(source, options, owner) {\n\t\tif (!source)\n\t\t\treturn null;\n\t\toptions = typeof options === 'function' ? { onLoad: options }\n\t\t\t\t: options || {};\n\t\tvar scope = paper,\n\t\t\titem = null;\n\n\t\tfunction onLoad(svg) {\n\t\t\ttry {\n\t\t\t\tvar node = typeof svg === 'object'\n\t\t\t\t\t? svg\n\t\t\t\t\t: new self.DOMParser().parseFromString(\n\t\t\t\t\t\tsvg,\n\t\t\t\t\t\t'image/svg+xml'\n\t\t\t\t\t);\n\t\t\t\tif (!node.nodeName) {\n\t\t\t\t\tnode = null;\n\t\t\t\t\tthrow new Error('Unsupported SVG source: ' + source);\n\t\t\t\t}\n\t\t\t\tpaper = scope;\n\t\t\t\titem = importNode(node, options, true);\n\t\t\t\tif (!options || options.insert !== false) {\n\t\t\t\t\towner._insertItem(undefined, item);\n\t\t\t\t}\n\t\t\t\tvar onLoad = options.onLoad;\n\t\t\t\tif (onLoad)\n\t\t\t\t\tonLoad(item, svg);\n\t\t\t} catch (e) {\n\t\t\t\tonError(e);\n\t\t\t}\n\t\t}\n\n\t\tfunction onError(message, status) {\n\t\t\tvar onError = options.onError;\n\t\t\tif (onError) {\n\t\t\t\tonError(message, status);\n\t\t\t} else {\n\t\t\t\tthrow new Error(message);\n\t\t\t}\n\t\t}\n\n\t\tif (typeof source === 'string' && !/^[\\s\\S]*</.test(source)) {\n\t\t\tvar node = document.getElementById(source);\n\t\t\tif (node) {\n\t\t\t\tonLoad(node);\n\t\t\t} else {\n\t\t\t\tHttp.request({\n\t\t\t\t\turl: source,\n\t\t\t\t\tasync: true,\n\t\t\t\t\tonLoad: onLoad,\n\t\t\t\t\tonError: onError\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (typeof File !== 'undefined' && source instanceof File) {\n\t\t\tvar reader = new FileReader();\n\t\t\treader.onload = function() {\n\t\t\t\tonLoad(reader.result);\n\t\t\t};\n\t\t\treader.onerror = function() {\n\t\t\t\tonError(reader.error);\n\t\t\t};\n\t\t\treturn reader.readAsText(source);\n\t\t} else {\n\t\t\tonLoad(source);\n\t\t}\n\n\t\treturn item;\n\t}\n\n\tItem.inject({\n\t\timportSVG: function(node, options) {\n\t\t\treturn importSVG(node, options, this);\n\t\t}\n\t});\n\n\tProject.inject({\n\t\timportSVG: function(node, options) {\n\t\t\tthis.activate();\n\t\t\treturn importSVG(node, options, this);\n\t\t}\n\t});\n};\n\nBase.exports.PaperScript = function() {\n\tvar global = this,\n\t\tacorn = global.acorn;\n\tif (!acorn && typeof require !== 'undefined') {\n\t\ttry { acorn = require('acorn'); } catch(e) {}\n\t}\n\tif (!acorn) {\n\t\tvar exports, module;\n\t\tacorn = exports = module = {};\n\n(function(root, mod) {\n if (typeof exports == \"object\" && typeof module == \"object\") return mod(exports);\n if (typeof define == \"function\" && define.amd) return define([\"exports\"], mod);\n mod(root.acorn || (root.acorn = {}));\n})(this, function(exports) {\n \"use strict\";\n\n exports.version = \"0.5.0\";\n\n var options, input, inputLen, sourceFile;\n\n exports.parse = function(inpt, opts) {\n\tinput = String(inpt); inputLen = input.length;\n\tsetOptions(opts);\n\tinitTokenState();\n\treturn parseTopLevel(options.program);\n };\n\n var defaultOptions = exports.defaultOptions = {\n\tecmaVersion: 5,\n\tstrictSemicolons: false,\n\tallowTrailingCommas: true,\n\tforbidReserved: false,\n\tallowReturnOutsideFunction: false,\n\tlocations: false,\n\tonComment: null,\n\tranges: false,\n\tprogram: null,\n\tsourceFile: null,\n\tdirectSourceFile: null\n };\n\n function setOptions(opts) {\n\toptions = opts || {};\n\tfor (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt))\n\t options[opt] = defaultOptions[opt];\n\tsourceFile = options.sourceFile || null;\n }\n\n var getLineInfo = exports.getLineInfo = function(input, offset) {\n\tfor (var line = 1, cur = 0;;) {\n\t lineBreak.lastIndex = cur;\n\t var match = lineBreak.exec(input);\n\t if (match && match.index < offset) {\n\t\t++line;\n\t\tcur = match.index + match[0].length;\n\t } else break;\n\t}\n\treturn {line: line, column: offset - cur};\n };\n\n exports.tokenize = function(inpt, opts) {\n\tinput = String(inpt); inputLen = input.length;\n\tsetOptions(opts);\n\tinitTokenState();\n\n\tvar t = {};\n\tfunction getToken(forceRegexp) {\n\t lastEnd = tokEnd;\n\t readToken(forceRegexp);\n\t t.start = tokStart; t.end = tokEnd;\n\t t.startLoc = tokStartLoc; t.endLoc = tokEndLoc;\n\t t.type = tokType; t.value = tokVal;\n\t return t;\n\t}\n\tgetToken.jumpTo = function(pos, reAllowed) {\n\t tokPos = pos;\n\t if (options.locations) {\n\t\ttokCurLine = 1;\n\t\ttokLineStart = lineBreak.lastIndex = 0;\n\t\tvar match;\n\t\twhile ((match = lineBreak.exec(input)) && match.index < pos) {\n\t\t ++tokCurLine;\n\t\t tokLineStart = match.index + match[0].length;\n\t\t}\n\t }\n\t tokRegexpAllowed = reAllowed;\n\t skipSpace();\n\t};\n\treturn getToken;\n };\n\n var tokPos;\n\n var tokStart, tokEnd;\n\n var tokStartLoc, tokEndLoc;\n\n var tokType, tokVal;\n\n var tokRegexpAllowed;\n\n var tokCurLine, tokLineStart;\n\n var lastStart, lastEnd, lastEndLoc;\n\n var inFunction, labels, strict;\n\n function raise(pos, message) {\n\tvar loc = getLineInfo(input, pos);\n\tmessage += \" (\" + loc.line + \":\" + loc.column + \")\";\n\tvar err = new SyntaxError(message);\n\terr.pos = pos; err.loc = loc; err.raisedAt = tokPos;\n\tthrow err;\n }\n\n var empty = [];\n\n var _num = {type: \"num\"}, _regexp = {type: \"regexp\"}, _string = {type: \"string\"};\n var _name = {type: \"name\"}, _eof = {type: \"eof\"};\n\n var _break = {keyword: \"break\"}, _case = {keyword: \"case\", beforeExpr: true}, _catch = {keyword: \"catch\"};\n var _continue = {keyword: \"continue\"}, _debugger = {keyword: \"debugger\"}, _default = {keyword: \"default\"};\n var _do = {keyword: \"do\", isLoop: true}, _else = {keyword: \"else\", beforeExpr: true};\n var _finally = {keyword: \"finally\"}, _for = {keyword: \"for\", isLoop: true}, _function = {keyword: \"function\"};\n var _if = {keyword: \"if\"}, _return = {keyword: \"return\", beforeExpr: true}, _switch = {keyword: \"switch\"};\n var _throw = {keyword: \"throw\", beforeExpr: true}, _try = {keyword: \"try\"}, _var = {keyword: \"var\"};\n var _while = {keyword: \"while\", isLoop: true}, _with = {keyword: \"with\"}, _new = {keyword: \"new\", beforeExpr: true};\n var _this = {keyword: \"this\"};\n\n var _null = {keyword: \"null\", atomValue: null}, _true = {keyword: \"true\", atomValue: true};\n var _false = {keyword: \"false\", atomValue: false};\n\n var _in = {keyword: \"in\", binop: 7, beforeExpr: true};\n\n var keywordTypes = {\"break\": _break, \"case\": _case, \"catch\": _catch,\n\t\t\t\t\t \"continue\": _continue, \"debugger\": _debugger, \"default\": _default,\n\t\t\t\t\t \"do\": _do, \"else\": _else, \"finally\": _finally, \"for\": _for,\n\t\t\t\t\t \"function\": _function, \"if\": _if, \"return\": _return, \"switch\": _switch,\n\t\t\t\t\t \"throw\": _throw, \"try\": _try, \"var\": _var, \"while\": _while, \"with\": _with,\n\t\t\t\t\t \"null\": _null, \"true\": _true, \"false\": _false, \"new\": _new, \"in\": _in,\n\t\t\t\t\t \"instanceof\": {keyword: \"instanceof\", binop: 7, beforeExpr: true}, \"this\": _this,\n\t\t\t\t\t \"typeof\": {keyword: \"typeof\", prefix: true, beforeExpr: true},\n\t\t\t\t\t \"void\": {keyword: \"void\", prefix: true, beforeExpr: true},\n\t\t\t\t\t \"delete\": {keyword: \"delete\", prefix: true, beforeExpr: true}};\n\n var _bracketL = {type: \"[\", beforeExpr: true}, _bracketR = {type: \"]\"}, _braceL = {type: \"{\", beforeExpr: true};\n var _braceR = {type: \"}\"}, _parenL = {type: \"(\", beforeExpr: true}, _parenR = {type: \")\"};\n var _comma = {type: \",\", beforeExpr: true}, _semi = {type: \";\", beforeExpr: true};\n var _colon = {type: \":\", beforeExpr: true}, _dot = {type: \".\"}, _question = {type: \"?\", beforeExpr: true};\n\n var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true};\n var _assign = {isAssign: true, beforeExpr: true};\n var _incDec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true};\n var _logicalOR = {binop: 1, beforeExpr: true};\n var _logicalAND = {binop: 2, beforeExpr: true};\n var _bitwiseOR = {binop: 3, beforeExpr: true};\n var _bitwiseXOR = {binop: 4, beforeExpr: true};\n var _bitwiseAND = {binop: 5, beforeExpr: true};\n var _equality = {binop: 6, beforeExpr: true};\n var _relational = {binop: 7, beforeExpr: true};\n var _bitShift = {binop: 8, beforeExpr: true};\n var _plusMin = {binop: 9, prefix: true, beforeExpr: true};\n var _multiplyModulo = {binop: 10, beforeExpr: true};\n\n exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR,\n\t\t\t\t\t parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon,\n\t\t\t\t\t dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof,\n\t\t\t\t\t num: _num, regexp: _regexp, string: _string};\n for (var kw in keywordTypes) exports.tokTypes[\"_\" + kw] = keywordTypes[kw];\n\n function makePredicate(words) {\n\twords = words.split(\" \");\n\tvar f = \"\", cats = [];\n\tout: for (var i = 0; i < words.length; ++i) {\n\t for (var j = 0; j < cats.length; ++j)\n\t\tif (cats[j][0].length == words[i].length) {\n\t\t cats[j].push(words[i]);\n\t\t continue out;\n\t\t}\n\t cats.push([words[i]]);\n\t}\n\tfunction compareTo(arr) {\n\t if (arr.length == 1) return f += \"return str === \" + JSON.stringify(arr[0]) + \";\";\n\t f += \"switch(str){\";\n\t for (var i = 0; i < arr.length; ++i) f += \"case \" + JSON.stringify(arr[i]) + \":\";\n\t f += \"return true}return false;\";\n\t}\n\n\tif (cats.length > 3) {\n\t cats.sort(function(a, b) {return b.length - a.length;});\n\t f += \"switch(str.length){\";\n\t for (var i = 0; i < cats.length; ++i) {\n\t\tvar cat = cats[i];\n\t\tf += \"case \" + cat[0].length + \":\";\n\t\tcompareTo(cat);\n\t }\n\t f += \"}\";\n\n\t} else {\n\t compareTo(words);\n\t}\n\treturn new Function(\"str\", f);\n }\n\n var isReservedWord3 = makePredicate(\"abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile\");\n\n var isReservedWord5 = makePredicate(\"class enum extends super const export import\");\n\n var isStrictReservedWord = makePredicate(\"implements interface let package private protected public static yield\");\n\n var isStrictBadIdWord = makePredicate(\"eval arguments\");\n\n var isKeyword = makePredicate(\"break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this\");\n\n var nonASCIIwhitespace = /[\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]/;\n var nonASCIIidentifierStartChars = \"\\xaa\\xb5\\xba\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05d0-\\u05ea\\u05f0-\\u05f2\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u08a0\\u08a2-\\u08ac\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097f\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c33\\u0c35-\\u0c39\\u0c3d\\u0c58\\u0c59\\u0c60\\u0c61\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d05-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d60\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0e01-\\u0e30\\u0e32\\u0e33\\u0e40-\\u0e46\\u0e81\\u0e82\\u0e84\\u0e87\\u0e88\\u0e8a\\u0e8d\\u0e94-\\u0e97\\u0e99-\\u0e9f\\u0ea1-\\u0ea3\\u0ea5\\u0ea7\\u0eaa\\u0eab\\u0ead-\\u0eb0\\u0eb2\\u0eb3\\u0ebd\\u0ec0-\\u0ec4\\u0ec6\\u0edc-\\u0edf\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u1000-\\u102a\\u103f\\u1050-\\u1055\\u105a-\\u105d\\u1061\\u1065\\u1066\\u106e-\\u1070\\u1075-\\u1081\\u108e\\u10a0-\\u10c5\\u10c7\\u10cd\\u10d0-\\u10fa\\u10fc-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f4\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16ee-\\u16f0\\u1700-\\u170c\\u170e-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1780-\\u17b3\\u17d7\\u17dc\\u1820-\\u1877\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191c\\u1950-\\u196d\\u1970-\\u1974\\u1980-\\u19ab\\u19c1-\\u19c7\\u1a00-\\u1a16\\u1a20-\\u1a54\\u1aa7\\u1b05-\\u1b33\\u1b45-\\u1b4b\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bba-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1ce9-\\u1cec\\u1cee-\\u1cf1\\u1cf5\\u1cf6\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2119-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u212d\\u212f-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2160-\\u2188\\u2c00-\\u2c2e\\u2c30-\\u2c5e\\u2c60-\\u2ce4\\u2ceb-\\u2cee\\u2cf2\\u2cf3\\u2d00-\\u2d25\\u2d27\\u2d2d\\u2d30-\\u2d67\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u2e2f\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303c\\u3041-\\u3096\\u309d-\\u309f\\u30a1-\\u30fa\\u30fc-\\u30ff\\u3105-\\u312d\\u3131-\\u318e\\u31a0-\\u31ba\\u31f0-\\u31ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\ua000-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua697\\ua6a0-\\ua6ef\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua78e\\ua790-\\ua793\\ua7a0-\\ua7aa\\ua7f8-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uaa60-\\uaa76\\uaa7a\\uaa80-\\uaaaf\\uaab1\\uaab5\\uaab6\\uaab9-\\uaabd\\uaac0\\uaac2\\uaadb-\\uaadd\\uaae0-\\uaaea\\uaaf2-\\uaaf4\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uabc0-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\uf900-\\ufa6d\\ufa70-\\ufad9\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uff66-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc\";\n var nonASCIIidentifierChars = \"\\u0300-\\u036f\\u0483-\\u0487\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u0620-\\u0649\\u0672-\\u06d3\\u06e7-\\u06e8\\u06fb-\\u06fc\\u0730-\\u074a\\u0800-\\u0814\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0840-\\u0857\\u08e4-\\u08fe\\u0900-\\u0903\\u093a-\\u093c\\u093e-\\u094f\\u0951-\\u0957\\u0962-\\u0963\\u0966-\\u096f\\u0981-\\u0983\\u09bc\\u09be-\\u09c4\\u09c7\\u09c8\\u09d7\\u09df-\\u09e0\\u0a01-\\u0a03\\u0a3c\\u0a3e-\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a66-\\u0a71\\u0a75\\u0a81-\\u0a83\\u0abc\\u0abe-\\u0ac5\\u0ac7-\\u0ac9\\u0acb-\\u0acd\\u0ae2-\\u0ae3\\u0ae6-\\u0aef\\u0b01-\\u0b03\\u0b3c\\u0b3e-\\u0b44\\u0b47\\u0b48\\u0b4b-\\u0b4d\\u0b56\\u0b57\\u0b5f-\\u0b60\\u0b66-\\u0b6f\\u0b82\\u0bbe-\\u0bc2\\u0bc6-\\u0bc8\\u0bca-\\u0bcd\\u0bd7\\u0be6-\\u0bef\\u0c01-\\u0c03\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62-\\u0c63\\u0c66-\\u0c6f\\u0c82\\u0c83\\u0cbc\\u0cbe-\\u0cc4\\u0cc6-\\u0cc8\\u0cca-\\u0ccd\\u0cd5\\u0cd6\\u0ce2-\\u0ce3\\u0ce6-\\u0cef\\u0d02\\u0d03\\u0d46-\\u0d48\\u0d57\\u0d62-\\u0d63\\u0d66-\\u0d6f\\u0d82\\u0d83\\u0dca\\u0dcf-\\u0dd4\\u0dd6\\u0dd8-\\u0ddf\\u0df2\\u0df3\\u0e34-\\u0e3a\\u0e40-\\u0e45\\u0e50-\\u0e59\\u0eb4-\\u0eb9\\u0ec8-\\u0ecd\\u0ed0-\\u0ed9\\u0f18\\u0f19\\u0f20-\\u0f29\\u0f35\\u0f37\\u0f39\\u0f41-\\u0f47\\u0f71-\\u0f84\\u0f86-\\u0f87\\u0f8d-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u1000-\\u1029\\u1040-\\u1049\\u1067-\\u106d\\u1071-\\u1074\\u1082-\\u108d\\u108f-\\u109d\\u135d-\\u135f\\u170e-\\u1710\\u1720-\\u1730\\u1740-\\u1750\\u1772\\u1773\\u1780-\\u17b2\\u17dd\\u17e0-\\u17e9\\u180b-\\u180d\\u1810-\\u1819\\u1920-\\u192b\\u1930-\\u193b\\u1951-\\u196d\\u19b0-\\u19c0\\u19c8-\\u19c9\\u19d0-\\u19d9\\u1a00-\\u1a15\\u1a20-\\u1a53\\u1a60-\\u1a7c\\u1a7f-\\u1a89\\u1a90-\\u1a99\\u1b46-\\u1b4b\\u1b50-\\u1b59\\u1b6b-\\u1b73\\u1bb0-\\u1bb9\\u1be6-\\u1bf3\\u1c00-\\u1c22\\u1c40-\\u1c49\\u1c5b-\\u1c7d\\u1cd0-\\u1cd2\\u1d00-\\u1dbe\\u1e01-\\u1f15\\u200c\\u200d\\u203f\\u2040\\u2054\\u20d0-\\u20dc\\u20e1\\u20e5-\\u20f0\\u2d81-\\u2d96\\u2de0-\\u2dff\\u3021-\\u3028\\u3099\\u309a\\ua640-\\ua66d\\ua674-\\ua67d\\ua69f\\ua6f0-\\ua6f1\\ua7f8-\\ua800\\ua806\\ua80b\\ua823-\\ua827\\ua880-\\ua881\\ua8b4-\\ua8c4\\ua8d0-\\ua8d9\\ua8f3-\\ua8f7\\ua900-\\ua909\\ua926-\\ua92d\\ua930-\\ua945\\ua980-\\ua983\\ua9b3-\\ua9c0\\uaa00-\\uaa27\\uaa40-\\uaa41\\uaa4c-\\uaa4d\\uaa50-\\uaa59\\uaa7b\\uaae0-\\uaae9\\uaaf2-\\uaaf3\\uabc0-\\uabe1\\uabec\\uabed\\uabf0-\\uabf9\\ufb20-\\ufb28\\ufe00-\\ufe0f\\ufe20-\\ufe26\\ufe33\\ufe34\\ufe4d-\\ufe4f\\uff10-\\uff19\\uff3f\";\n var nonASCIIidentifierStart = new RegExp(\"[\" + nonASCIIidentifierStartChars + \"]\");\n var nonASCIIidentifier = new RegExp(\"[\" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + \"]\");\n\n var newline = /[\\n\\r\\u2028\\u2029]/;\n\n var lineBreak = /\\r\\n|[\\n\\r\\u2028\\u2029]/g;\n\n var isIdentifierStart = exports.isIdentifierStart = function(code) {\n\tif (code < 65) return code === 36;\n\tif (code < 91) return true;\n\tif (code < 97) return code === 95;\n\tif (code < 123)return true;\n\treturn code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));\n };\n\n var isIdentifierChar = exports.isIdentifierChar = function(code) {\n\tif (code < 48) return code === 36;\n\tif (code < 58) return true;\n\tif (code < 65) return false;\n\tif (code < 91) return true;\n\tif (code < 97) return code === 95;\n\tif (code < 123)return true;\n\treturn code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));\n };\n\n function line_loc_t() {\n\tthis.line = tokCurLine;\n\tthis.column = tokPos - tokLineStart;\n }\n\n function initTokenState() {\n\ttokCurLine = 1;\n\ttokPos = tokLineStart = 0;\n\ttokRegexpAllowed = true;\n\tskipSpace();\n }\n\n function finishToken(type, val) {\n\ttokEnd = tokPos;\n\tif (options.locations) tokEndLoc = new line_loc_t;\n\ttokType = type;\n\tskipSpace();\n\ttokVal = val;\n\ttokRegexpAllowed = type.beforeExpr;\n }\n\n function skipBlockComment() {\n\tvar startLoc = options.onComment && options.locations && new line_loc_t;\n\tvar start = tokPos, end = input.indexOf(\"*/\", tokPos += 2);\n\tif (end === -1) raise(tokPos - 2, \"Unterminated comment\");\n\ttokPos = end + 2;\n\tif (options.locations) {\n\t lineBreak.lastIndex = start;\n\t var match;\n\t while ((match = lineBreak.exec(input)) && match.index < tokPos) {\n\t\t++tokCurLine;\n\t\ttokLineStart = match.index + match[0].length;\n\t }\n\t}\n\tif (options.onComment)\n\t options.onComment(true, input.slice(start + 2, end), start, tokPos,\n\t\t\t\t\t\tstartLoc, options.locations && new line_loc_t);\n }\n\n function skipLineComment() {\n\tvar start = tokPos;\n\tvar startLoc = options.onComment && options.locations && new line_loc_t;\n\tvar ch = input.charCodeAt(tokPos+=2);\n\twhile (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {\n\t ++tokPos;\n\t ch = input.charCodeAt(tokPos);\n\t}\n\tif (options.onComment)\n\t options.onComment(false, input.slice(start + 2, tokPos), start, tokPos,\n\t\t\t\t\t\tstartLoc, options.locations && new line_loc_t);\n }\n\n function skipSpace() {\n\twhile (tokPos < inputLen) {\n\t var ch = input.charCodeAt(tokPos);\n\t if (ch === 32) {\n\t\t++tokPos;\n\t } else if (ch === 13) {\n\t\t++tokPos;\n\t\tvar next = input.charCodeAt(tokPos);\n\t\tif (next === 10) {\n\t\t ++tokPos;\n\t\t}\n\t\tif (options.locations) {\n\t\t ++tokCurLine;\n\t\t tokLineStart = tokPos;\n\t\t}\n\t } else if (ch === 10 || ch === 8232 || ch === 8233) {\n\t\t++tokPos;\n\t\tif (options.locations) {\n\t\t ++tokCurLine;\n\t\t tokLineStart = tokPos;\n\t\t}\n\t } else if (ch > 8 && ch < 14) {\n\t\t++tokPos;\n\t } else if (ch === 47) {\n\t\tvar next = input.charCodeAt(tokPos + 1);\n\t\tif (next === 42) {\n\t\t skipBlockComment();\n\t\t} else if (next === 47) {\n\t\t skipLineComment();\n\t\t} else break;\n\t } else if (ch === 160) {\n\t\t++tokPos;\n\t } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {\n\t\t++tokPos;\n\t } else {\n\t\tbreak;\n\t }\n\t}\n }\n\n function readToken_dot() {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (next >= 48 && next <= 57) return readNumber(true);\n\t++tokPos;\n\treturn finishToken(_dot);\n }\n\n function readToken_slash() {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (tokRegexpAllowed) {++tokPos; return readRegexp();}\n\tif (next === 61) return finishOp(_assign, 2);\n\treturn finishOp(_slash, 1);\n }\n\n function readToken_mult_modulo() {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (next === 61) return finishOp(_assign, 2);\n\treturn finishOp(_multiplyModulo, 1);\n }\n\n function readToken_pipe_amp(code) {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (next === code) return finishOp(code === 124 ? _logicalOR : _logicalAND, 2);\n\tif (next === 61) return finishOp(_assign, 2);\n\treturn finishOp(code === 124 ? _bitwiseOR : _bitwiseAND, 1);\n }\n\n function readToken_caret() {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (next === 61) return finishOp(_assign, 2);\n\treturn finishOp(_bitwiseXOR, 1);\n }\n\n function readToken_plus_min(code) {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (next === code) {\n\t if (next == 45 && input.charCodeAt(tokPos + 2) == 62 &&\n\t\t newline.test(input.slice(lastEnd, tokPos))) {\n\t\ttokPos += 3;\n\t\tskipLineComment();\n\t\tskipSpace();\n\t\treturn readToken();\n\t }\n\t return finishOp(_incDec, 2);\n\t}\n\tif (next === 61) return finishOp(_assign, 2);\n\treturn finishOp(_plusMin, 1);\n }\n\n function readToken_lt_gt(code) {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tvar size = 1;\n\tif (next === code) {\n\t size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2;\n\t if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1);\n\t return finishOp(_bitShift, size);\n\t}\n\tif (next == 33 && code == 60 && input.charCodeAt(tokPos + 2) == 45 &&\n\t\tinput.charCodeAt(tokPos + 3) == 45) {\n\t tokPos += 4;\n\t skipLineComment();\n\t skipSpace();\n\t return readToken();\n\t}\n\tif (next === 61)\n\t size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2;\n\treturn finishOp(_relational, size);\n }\n\n function readToken_eq_excl(code) {\n\tvar next = input.charCodeAt(tokPos + 1);\n\tif (next === 61) return finishOp(_equality, input.charCodeAt(tokPos + 2) === 61 ? 3 : 2);\n\treturn finishOp(code === 61 ? _eq : _prefix, 1);\n }\n\n function getTokenFromCode(code) {\n\tswitch(code) {\n\tcase 46:\n\t return readToken_dot();\n\n\tcase 40: ++tokPos; return finishToken(_parenL);\n\tcase 41: ++tokPos; return finishToken(_parenR);\n\tcase 59: ++tokPos; return finishToken(_semi);\n\tcase 44: ++tokPos; return finishToken(_comma);\n\tcase 91: ++tokPos; return finishToken(_bracketL);\n\tcase 93: ++tokPos; return finishToken(_bracketR);\n\tcase 123: ++tokPos; return finishToken(_braceL);\n\tcase 125: ++tokPos; return finishToken(_braceR);\n\tcase 58: ++tokPos; return finishToken(_colon);\n\tcase 63: ++tokPos; return finishToken(_question);\n\n\tcase 48:\n\t var next = input.charCodeAt(tokPos + 1);\n\t if (next === 120 || next === 88) return readHexNumber();\n\tcase 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57:\n\t return readNumber(false);\n\n\tcase 34: case 39:\n\t return readString(code);\n\n\tcase 47:\n\t return readToken_slash(code);\n\n\tcase 37: case 42:\n\t return readToken_mult_modulo();\n\n\tcase 124: case 38:\n\t return readToken_pipe_amp(code);\n\n\tcase 94:\n\t return readToken_caret();\n\n\tcase 43: case 45:\n\t return readToken_plus_min(code);\n\n\tcase 60: case 62:\n\t return readToken_lt_gt(code);\n\n\tcase 61: case 33:\n\t return readToken_eq_excl(code);\n\n\tcase 126:\n\t return finishOp(_prefix, 1);\n\t}\n\n\treturn false;\n }\n\n function readToken(forceRegexp) {\n\tif (!forceRegexp) tokStart = tokPos;\n\telse tokPos = tokStart + 1;\n\tif (options.locations) tokStartLoc = new line_loc_t;\n\tif (forceRegexp) return readRegexp();\n\tif (tokPos >= inputLen) return finishToken(_eof);\n\n\tvar code = input.charCodeAt(tokPos);\n\tif (isIdentifierStart(code) || code === 92 ) return readWord();\n\n\tvar tok = getTokenFromCode(code);\n\n\tif (tok === false) {\n\t var ch = String.fromCharCode(code);\n\t if (ch === \"\\\\\" || nonASCIIidentifierStart.test(ch)) return readWord();\n\t raise(tokPos, \"Unexpected character '\" + ch + \"'\");\n\t}\n\treturn tok;\n }\n\n function finishOp(type, size) {\n\tvar str = input.slice(tokPos, tokPos + size);\n\ttokPos += size;\n\tfinishToken(type, str);\n }\n\n function readRegexp() {\n\tvar content = \"\", escaped, inClass, start = tokPos;\n\tfor (;;) {\n\t if (tokPos >= inputLen) raise(start, \"Unterminated regular expression\");\n\t var ch = input.charAt(tokPos);\n\t if (newline.test(ch)) raise(start, \"Unterminated regular expression\");\n\t if (!escaped) {\n\t\tif (ch === \"[\") inClass = true;\n\t\telse if (ch === \"]\" && inClass) inClass = false;\n\t\telse if (ch === \"/\" && !inClass) break;\n\t\tescaped = ch === \"\\\\\";\n\t } else escaped = false;\n\t ++tokPos;\n\t}\n\tvar content = input.slice(start, tokPos);\n\t++tokPos;\n\tvar mods = readWord1();\n\tif (mods && !/^[gmsiy]*$/.test(mods)) raise(start, \"Invalid regexp flag\");\n\ttry {\n\t var value = new RegExp(content, mods);\n\t} catch (e) {\n\t if (e instanceof SyntaxError) raise(start, e.message);\n\t raise(e);\n\t}\n\treturn finishToken(_regexp, value);\n }\n\n function readInt(radix, len) {\n\tvar start = tokPos, total = 0;\n\tfor (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {\n\t var code = input.charCodeAt(tokPos), val;\n\t if (code >= 97) val = code - 97 + 10;\n\t else if (code >= 65) val = code - 65 + 10;\n\t else if (code >= 48 && code <= 57) val = code - 48;\n\t else val = Infinity;\n\t if (val >= radix) break;\n\t ++tokPos;\n\t total = total * radix + val;\n\t}\n\tif (tokPos === start || len != null && tokPos - start !== len) return null;\n\n\treturn total;\n }\n\n function readHexNumber() {\n\ttokPos += 2;\n\tvar val = readInt(16);\n\tif (val == null) raise(tokStart + 2, \"Expected hexadecimal number\");\n\tif (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, \"Identifier directly after number\");\n\treturn finishToken(_num, val);\n }\n\n function readNumber(startsWithDot) {\n\tvar start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48;\n\tif (!startsWithDot && readInt(10) === null) raise(start, \"Invalid number\");\n\tif (input.charCodeAt(tokPos) === 46) {\n\t ++tokPos;\n\t readInt(10);\n\t isFloat = true;\n\t}\n\tvar next = input.charCodeAt(tokPos);\n\tif (next === 69 || next === 101) {\n\t next = input.charCodeAt(++tokPos);\n\t if (next === 43 || next === 45) ++tokPos;\n\t if (readInt(10) === null) raise(start, \"Invalid number\");\n\t isFloat = true;\n\t}\n\tif (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, \"Identifier directly after number\");\n\n\tvar str = input.slice(start, tokPos), val;\n\tif (isFloat) val = parseFloat(str);\n\telse if (!octal || str.length === 1) val = parseInt(str, 10);\n\telse if (/[89]/.test(str) || strict) raise(start, \"Invalid number\");\n\telse val = parseInt(str, 8);\n\treturn finishToken(_num, val);\n }\n\n function readString(quote) {\n\ttokPos++;\n\tvar out = \"\";\n\tfor (;;) {\n\t if (tokPos >= inputLen) raise(tokStart, \"Unterminated string constant\");\n\t var ch = input.charCodeAt(tokPos);\n\t if (ch === quote) {\n\t\t++tokPos;\n\t\treturn finishToken(_string, out);\n\t }\n\t if (ch === 92) {\n\t\tch = input.charCodeAt(++tokPos);\n\t\tvar octal = /^[0-7]+/.exec(input.slice(tokPos, tokPos + 3));\n\t\tif (octal) octal = octal[0];\n\t\twhile (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1);\n\t\tif (octal === \"0\") octal = null;\n\t\t++tokPos;\n\t\tif (octal) {\n\t\t if (strict) raise(tokPos - 2, \"Octal literal in strict mode\");\n\t\t out += String.fromCharCode(parseInt(octal, 8));\n\t\t tokPos += octal.length - 1;\n\t\t} else {\n\t\t switch (ch) {\n\t\t case 110: out += \"\\n\"; break;\n\t\t case 114: out += \"\\r\"; break;\n\t\t case 120: out += String.fromCharCode(readHexChar(2)); break;\n\t\t case 117: out += String.fromCharCode(readHexChar(4)); break;\n\t\t case 85: out += String.fromCharCode(readHexChar(8)); break;\n\t\t case 116: out += \"\\t\"; break;\n\t\t case 98: out += \"\\b\"; break;\n\t\t case 118: out += \"\\u000b\"; break;\n\t\t case 102: out += \"\\f\"; break;\n\t\t case 48: out += \"\\0\"; break;\n\t\t case 13: if (input.charCodeAt(tokPos) === 10) ++tokPos;\n\t\t case 10:\n\t\t\tif (options.locations) { tokLineStart = tokPos; ++tokCurLine; }\n\t\t\tbreak;\n\t\t default: out += String.fromCharCode(ch); break;\n\t\t }\n\t\t}\n\t } else {\n\t\tif (ch === 13 || ch === 10 || ch === 8232 || ch === 8233) raise(tokStart, \"Unterminated string constant\");\n\t\tout += String.fromCharCode(ch);\n\t\t++tokPos;\n\t }\n\t}\n }\n\n function readHexChar(len) {\n\tvar n = readInt(16, len);\n\tif (n === null) raise(tokStart, \"Bad character escape sequence\");\n\treturn n;\n }\n\n var containsEsc;\n\n function readWord1() {\n\tcontainsEsc = false;\n\tvar word, first = true, start = tokPos;\n\tfor (;;) {\n\t var ch = input.charCodeAt(tokPos);\n\t if (isIdentifierChar(ch)) {\n\t\tif (containsEsc) word += input.charAt(tokPos);\n\t\t++tokPos;\n\t } else if (ch === 92) {\n\t\tif (!containsEsc) word = input.slice(start, tokPos);\n\t\tcontainsEsc = true;\n\t\tif (input.charCodeAt(++tokPos) != 117)\n\t\t raise(tokPos, \"Expecting Unicode escape sequence \\\\uXXXX\");\n\t\t++tokPos;\n\t\tvar esc = readHexChar(4);\n\t\tvar escStr = String.fromCharCode(esc);\n\t\tif (!escStr) raise(tokPos - 1, \"Invalid Unicode escape\");\n\t\tif (!(first ? isIdentifierStart(esc) : isIdentifierChar(esc)))\n\t\t raise(tokPos - 4, \"Invalid Unicode escape\");\n\t\tword += escStr;\n\t } else {\n\t\tbreak;\n\t }\n\t first = false;\n\t}\n\treturn containsEsc ? word : input.slice(start, tokPos);\n }\n\n function readWord() {\n\tvar word = readWord1();\n\tvar type = _name;\n\tif (!containsEsc && isKeyword(word))\n\t type = keywordTypes[word];\n\treturn finishToken(type, word);\n }\n\n function next() {\n\tlastStart = tokStart;\n\tlastEnd = tokEnd;\n\tlastEndLoc = tokEndLoc;\n\treadToken();\n }\n\n function setStrict(strct) {\n\tstrict = strct;\n\ttokPos = tokStart;\n\tif (options.locations) {\n\t while (tokPos < tokLineStart) {\n\t\ttokLineStart = input.lastIndexOf(\"\\n\", tokLineStart - 2) + 1;\n\t\t--tokCurLine;\n\t }\n\t}\n\tskipSpace();\n\treadToken();\n }\n\n function node_t() {\n\tthis.type = null;\n\tthis.start = tokStart;\n\tthis.end = null;\n }\n\n function node_loc_t() {\n\tthis.start = tokStartLoc;\n\tthis.end = null;\n\tif (sourceFile !== null) this.source = sourceFile;\n }\n\n function startNode() {\n\tvar node = new node_t();\n\tif (options.locations)\n\t node.loc = new node_loc_t();\n\tif (options.directSourceFile)\n\t node.sourceFile = options.directSourceFile;\n\tif (options.ranges)\n\t node.range = [tokStart, 0];\n\treturn node;\n }\n\n function startNodeFrom(other) {\n\tvar node = new node_t();\n\tnode.start = other.start;\n\tif (options.locations) {\n\t node.loc = new node_loc_t();\n\t node.loc.start = other.loc.start;\n\t}\n\tif (options.ranges)\n\t node.range = [other.range[0], 0];\n\n\treturn node;\n }\n\n function finishNode(node, type) {\n\tnode.type = type;\n\tnode.end = lastEnd;\n\tif (options.locations)\n\t node.loc.end = lastEndLoc;\n\tif (options.ranges)\n\t node.range[1] = lastEnd;\n\treturn node;\n }\n\n function isUseStrict(stmt) {\n\treturn options.ecmaVersion >= 5 && stmt.type === \"ExpressionStatement\" &&\n\t stmt.expression.type === \"Literal\" && stmt.expression.value === \"use strict\";\n }\n\n function eat(type) {\n\tif (tokType === type) {\n\t next();\n\t return true;\n\t}\n }\n\n function canInsertSemicolon() {\n\treturn !options.strictSemicolons &&\n\t (tokType === _eof || tokType === _braceR || newline.test(input.slice(lastEnd, tokStart)));\n }\n\n function semicolon() {\n\tif (!eat(_semi) && !canInsertSemicolon()) unexpected();\n }\n\n function expect(type) {\n\tif (tokType === type) next();\n\telse unexpected();\n }\n\n function unexpected() {\n\traise(tokStart, \"Unexpected token\");\n }\n\n function checkLVal(expr) {\n\tif (expr.type !== \"Identifier\" && expr.type !== \"MemberExpression\")\n\t raise(expr.start, \"Assigning to rvalue\");\n\tif (strict && expr.type === \"Identifier\" && isStrictBadIdWord(expr.name))\n\t raise(expr.start, \"Assigning to \" + expr.name + \" in strict mode\");\n }\n\n function parseTopLevel(program) {\n\tlastStart = lastEnd = tokPos;\n\tif (options.locations) lastEndLoc = new line_loc_t;\n\tinFunction = strict = null;\n\tlabels = [];\n\treadToken();\n\n\tvar node = program || startNode(), first = true;\n\tif (!program) node.body = [];\n\twhile (tokType !== _eof) {\n\t var stmt = parseStatement();\n\t node.body.push(stmt);\n\t if (first && isUseStrict(stmt)) setStrict(true);\n\t first = false;\n\t}\n\treturn finishNode(node, \"Program\");\n }\n\n var loopLabel = {kind: \"loop\"}, switchLabel = {kind: \"switch\"};\n\n function parseStatement() {\n\tif (tokType === _slash || tokType === _assign && tokVal == \"/=\")\n\t readToken(true);\n\n\tvar starttype = tokType, node = startNode();\n\n\tswitch (starttype) {\n\tcase _break: case _continue:\n\t next();\n\t var isBreak = starttype === _break;\n\t if (eat(_semi) || canInsertSemicolon()) node.label = null;\n\t else if (tokType !== _name) unexpected();\n\t else {\n\t\tnode.label = parseIdent();\n\t\tsemicolon();\n\t }\n\n\t for (var i = 0; i < labels.length; ++i) {\n\t\tvar lab = labels[i];\n\t\tif (node.label == null || lab.name === node.label.name) {\n\t\t if (lab.kind != null && (isBreak || lab.kind === \"loop\")) break;\n\t\t if (node.label && isBreak) break;\n\t\t}\n\t }\n\t if (i === labels.length) raise(node.start, \"Unsyntactic \" + starttype.keyword);\n\t return finishNode(node, isBreak ? \"BreakStatement\" : \"ContinueStatement\");\n\n\tcase _debugger:\n\t next();\n\t semicolon();\n\t return finishNode(node, \"DebuggerStatement\");\n\n\tcase _do:\n\t next();\n\t labels.push(loopLabel);\n\t node.body = parseStatement();\n\t labels.pop();\n\t expect(_while);\n\t node.test = parseParenExpression();\n\t semicolon();\n\t return finishNode(node, \"DoWhileStatement\");\n\n\tcase _for:\n\t next();\n\t labels.push(loopLabel);\n\t expect(_parenL);\n\t if (tokType === _semi) return parseFor(node, null);\n\t if (tokType === _var) {\n\t\tvar init = startNode();\n\t\tnext();\n\t\tparseVar(init, true);\n\t\tfinishNode(init, \"VariableDeclaration\");\n\t\tif (init.declarations.length === 1 && eat(_in))\n\t\t return parseForIn(node, init);\n\t\treturn parseFor(node, init);\n\t }\n\t var init = parseExpression(false, true);\n\t if (eat(_in)) {checkLVal(init); return parseForIn(node, init);}\n\t return parseFor(node, init);\n\n\tcase _function:\n\t next();\n\t return parseFunction(node, true);\n\n\tcase _if:\n\t next();\n\t node.test = parseParenExpression();\n\t node.consequent = parseStatement();\n\t node.alternate = eat(_else) ? parseStatement() : null;\n\t return finishNode(node, \"IfStatement\");\n\n\tcase _return:\n\t if (!inFunction && !options.allowReturnOutsideFunction)\n\t\traise(tokStart, \"'return' outside of function\");\n\t next();\n\n\t if (eat(_semi) || canInsertSemicolon()) node.argument = null;\n\t else { node.argument = parseExpression(); semicolon(); }\n\t return finishNode(node, \"ReturnStatement\");\n\n\tcase _switch:\n\t next();\n\t node.discriminant = parseParenExpression();\n\t node.cases = [];\n\t expect(_braceL);\n\t labels.push(switchLabel);\n\n\t for (var cur, sawDefault; tokType != _braceR;) {\n\t\tif (tokType === _case || tokType === _default) {\n\t\t var isCase = tokType === _case;\n\t\t if (cur) finishNode(cur, \"SwitchCase\");\n\t\t node.cases.push(cur = startNode());\n\t\t cur.consequent = [];\n\t\t next();\n\t\t if (isCase) cur.test = parseExpression();\n\t\t else {\n\t\t\tif (sawDefault) raise(lastStart, \"Multiple default clauses\"); sawDefault = true;\n\t\t\tcur.test = null;\n\t\t }\n\t\t expect(_colon);\n\t\t} else {\n\t\t if (!cur) unexpected();\n\t\t cur.consequent.push(parseStatement());\n\t\t}\n\t }\n\t if (cur) finishNode(cur, \"SwitchCase\");\n\t next();\n\t labels.pop();\n\t return finishNode(node, \"SwitchStatement\");\n\n\tcase _throw:\n\t next();\n\t if (newline.test(input.slice(lastEnd, tokStart)))\n\t\traise(lastEnd, \"Illegal newline after throw\");\n\t node.argument = parseExpression();\n\t semicolon();\n\t return finishNode(node, \"ThrowStatement\");\n\n\tcase _try:\n\t next();\n\t node.block = parseBlock();\n\t node.handler = null;\n\t if (tokType === _catch) {\n\t\tvar clause = startNode();\n\t\tnext();\n\t\texpect(_parenL);\n\t\tclause.param = parseIdent();\n\t\tif (strict && isStrictBadIdWord(clause.param.name))\n\t\t raise(clause.param.start, \"Binding \" + clause.param.name + \" in strict mode\");\n\t\texpect(_parenR);\n\t\tclause.guard = null;\n\t\tclause.body = parseBlock();\n\t\tnode.handler = finishNode(clause, \"CatchClause\");\n\t }\n\t node.guardedHandlers = empty;\n\t node.finalizer = eat(_finally) ? parseBlock() : null;\n\t if (!node.handler && !node.finalizer)\n\t\traise(node.start, \"Missing catch or finally clause\");\n\t return finishNode(node, \"TryStatement\");\n\n\tcase _var:\n\t next();\n\t parseVar(node);\n\t semicolon();\n\t return finishNode(node, \"VariableDeclaration\");\n\n\tcase _while:\n\t next();\n\t node.test = parseParenExpression();\n\t labels.push(loopLabel);\n\t node.body = parseStatement();\n\t labels.pop();\n\t return finishNode(node, \"WhileStatement\");\n\n\tcase _with:\n\t if (strict) raise(tokStart, \"'with' in strict mode\");\n\t next();\n\t node.object = parseParenExpression();\n\t node.body = parseStatement();\n\t return finishNode(node, \"WithStatement\");\n\n\tcase _braceL:\n\t return parseBlock();\n\n\tcase _semi:\n\t next();\n\t return finishNode(node, \"EmptyStatement\");\n\n\tdefault:\n\t var maybeName = tokVal, expr = parseExpression();\n\t if (starttype === _name && expr.type === \"Identifier\" && eat(_colon)) {\n\t\tfor (var i = 0; i < labels.length; ++i)\n\t\t if (labels[i].name === maybeName) raise(expr.start, \"Label '\" + maybeName + \"' is already declared\");\n\t\tvar kind = tokType.isLoop ? \"loop\" : tokType === _switch ? \"switch\" : null;\n\t\tlabels.push({name: maybeName, kind: kind});\n\t\tnode.body = parseStatement();\n\t\tlabels.pop();\n\t\tnode.label = expr;\n\t\treturn finishNode(node, \"LabeledStatement\");\n\t } else {\n\t\tnode.expression = expr;\n\t\tsemicolon();\n\t\treturn finishNode(node, \"ExpressionStatement\");\n\t }\n\t}\n }\n\n function parseParenExpression() {\n\texpect(_parenL);\n\tvar val = parseExpression();\n\texpect(_parenR);\n\treturn val;\n }\n\n function parseBlock(allowStrict) {\n\tvar node = startNode(), first = true, strict = false, oldStrict;\n\tnode.body = [];\n\texpect(_braceL);\n\twhile (!eat(_braceR)) {\n\t var stmt = parseStatement();\n\t node.body.push(stmt);\n\t if (first && allowStrict && isUseStrict(stmt)) {\n\t\toldStrict = strict;\n\t\tsetStrict(strict = true);\n\t }\n\t first = false;\n\t}\n\tif (strict && !oldStrict) setStrict(false);\n\treturn finishNode(node, \"BlockStatement\");\n }\n\n function parseFor(node, init) {\n\tnode.init = init;\n\texpect(_semi);\n\tnode.test = tokType === _semi ? null : parseExpression();\n\texpect(_semi);\n\tnode.update = tokType === _parenR ? null : parseExpression();\n\texpect(_parenR);\n\tnode.body = parseStatement();\n\tlabels.pop();\n\treturn finishNode(node, \"ForStatement\");\n }\n\n function parseForIn(node, init) {\n\tnode.left = init;\n\tnode.right = parseExpression();\n\texpect(_parenR);\n\tnode.body = parseStatement();\n\tlabels.pop();\n\treturn finishNode(node, \"ForInStatement\");\n }\n\n function parseVar(node, noIn) {\n\tnode.declarations = [];\n\tnode.kind = \"var\";\n\tfor (;;) {\n\t var decl = startNode();\n\t decl.id = parseIdent();\n\t if (strict && isStrictBadIdWord(decl.id.name))\n\t\traise(decl.id.start, \"Binding \" + decl.id.name + \" in strict mode\");\n\t decl.init = eat(_eq) ? parseExpression(true, noIn) : null;\n\t node.declarations.push(finishNode(decl, \"VariableDeclarator\"));\n\t if (!eat(_comma)) break;\n\t}\n\treturn node;\n }\n\n function parseExpression(noComma, noIn) {\n\tvar expr = parseMaybeAssign(noIn);\n\tif (!noComma && tokType === _comma) {\n\t var node = startNodeFrom(expr);\n\t node.expressions = [expr];\n\t while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn));\n\t return finishNode(node, \"SequenceExpression\");\n\t}\n\treturn expr;\n }\n\n function parseMaybeAssign(noIn) {\n\tvar left = parseMaybeConditional(noIn);\n\tif (tokType.isAssign) {\n\t var node = startNodeFrom(left);\n\t node.operator = tokVal;\n\t node.left = left;\n\t next();\n\t node.right = parseMaybeAssign(noIn);\n\t checkLVal(left);\n\t return finishNode(node, \"AssignmentExpression\");\n\t}\n\treturn left;\n }\n\n function parseMaybeConditional(noIn) {\n\tvar expr = parseExprOps(noIn);\n\tif (eat(_question)) {\n\t var node = startNodeFrom(expr);\n\t node.test = expr;\n\t node.consequent = parseExpression(true);\n\t expect(_colon);\n\t node.alternate = parseExpression(true, noIn);\n\t return finishNode(node, \"ConditionalExpression\");\n\t}\n\treturn expr;\n }\n\n function parseExprOps(noIn) {\n\treturn parseExprOp(parseMaybeUnary(), -1, noIn);\n }\n\n function parseExprOp(left, minPrec, noIn) {\n\tvar prec = tokType.binop;\n\tif (prec != null && (!noIn || tokType !== _in)) {\n\t if (prec > minPrec) {\n\t\tvar node = startNodeFrom(left);\n\t\tnode.left = left;\n\t\tnode.operator = tokVal;\n\t\tvar op = tokType;\n\t\tnext();\n\t\tnode.right = parseExprOp(parseMaybeUnary(), prec, noIn);\n\t\tvar exprNode = finishNode(node, (op === _logicalOR || op === _logicalAND) ? \"LogicalExpression\" : \"BinaryExpression\");\n\t\treturn parseExprOp(exprNode, minPrec, noIn);\n\t }\n\t}\n\treturn left;\n }\n\n function parseMaybeUnary() {\n\tif (tokType.prefix) {\n\t var node = startNode(), update = tokType.isUpdate;\n\t node.operator = tokVal;\n\t node.prefix = true;\n\t tokRegexpAllowed = true;\n\t next();\n\t node.argument = parseMaybeUnary();\n\t if (update) checkLVal(node.argument);\n\t else if (strict && node.operator === \"delete\" &&\n\t\t\t node.argument.type === \"Identifier\")\n\t\traise(node.start, \"Deleting local variable in strict mode\");\n\t return finishNode(node, update ? \"UpdateExpression\" : \"UnaryExpression\");\n\t}\n\tvar expr = parseExprSubscripts();\n\twhile (tokType.postfix && !canInsertSemicolon()) {\n\t var node = startNodeFrom(expr);\n\t node.operator = tokVal;\n\t node.prefix = false;\n\t node.argument = expr;\n\t checkLVal(expr);\n\t next();\n\t expr = finishNode(node, \"UpdateExpression\");\n\t}\n\treturn expr;\n }\n\n function parseExprSubscripts() {\n\treturn parseSubscripts(parseExprAtom());\n }\n\n function parseSubscripts(base, noCalls) {\n\tif (eat(_dot)) {\n\t var node = startNodeFrom(base);\n\t node.object = base;\n\t node.property = parseIdent(true);\n\t node.computed = false;\n\t return parseSubscripts(finishNode(node, \"MemberExpression\"), noCalls);\n\t} else if (eat(_bracketL)) {\n\t var node = startNodeFrom(base);\n\t node.object = base;\n\t node.property = parseExpression();\n\t node.computed = true;\n\t expect(_bracketR);\n\t return parseSubscripts(finishNode(node, \"MemberExpression\"), noCalls);\n\t} else if (!noCalls && eat(_parenL)) {\n\t var node = startNodeFrom(base);\n\t node.callee = base;\n\t node.arguments = parseExprList(_parenR, false);\n\t return parseSubscripts(finishNode(node, \"CallExpression\"), noCalls);\n\t} else return base;\n }\n\n function parseExprAtom() {\n\tswitch (tokType) {\n\tcase _this:\n\t var node = startNode();\n\t next();\n\t return finishNode(node, \"ThisExpression\");\n\tcase _name:\n\t return parseIdent();\n\tcase _num: case _string: case _regexp:\n\t var node = startNode();\n\t node.value = tokVal;\n\t node.raw = input.slice(tokStart, tokEnd);\n\t next();\n\t return finishNode(node, \"Literal\");\n\n\tcase _null: case _true: case _false:\n\t var node = startNode();\n\t node.value = tokType.atomValue;\n\t node.raw = tokType.keyword;\n\t next();\n\t return finishNode(node, \"Literal\");\n\n\tcase _parenL:\n\t var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart;\n\t next();\n\t var val = parseExpression();\n\t val.start = tokStart1;\n\t val.end = tokEnd;\n\t if (options.locations) {\n\t\tval.loc.start = tokStartLoc1;\n\t\tval.loc.end = tokEndLoc;\n\t }\n\t if (options.ranges)\n\t\tval.range = [tokStart1, tokEnd];\n\t expect(_parenR);\n\t return val;\n\n\tcase _bracketL:\n\t var node = startNode();\n\t next();\n\t node.elements = parseExprList(_bracketR, true, true);\n\t return finishNode(node, \"ArrayExpression\");\n\n\tcase _braceL:\n\t return parseObj();\n\n\tcase _function:\n\t var node = startNode();\n\t next();\n\t return parseFunction(node, false);\n\n\tcase _new:\n\t return parseNew();\n\n\tdefault:\n\t unexpected();\n\t}\n }\n\n function parseNew() {\n\tvar node = startNode();\n\tnext();\n\tnode.callee = parseSubscripts(parseExprAtom(), true);\n\tif (eat(_parenL)) node.arguments = parseExprList(_parenR, false);\n\telse node.arguments = empty;\n\treturn finishNode(node, \"NewExpression\");\n }\n\n function parseObj() {\n\tvar node = startNode(), first = true, sawGetSet = false;\n\tnode.properties = [];\n\tnext();\n\twhile (!eat(_braceR)) {\n\t if (!first) {\n\t\texpect(_comma);\n\t\tif (options.allowTrailingCommas && eat(_braceR)) break;\n\t } else first = false;\n\n\t var prop = {key: parsePropertyName()}, isGetSet = false, kind;\n\t if (eat(_colon)) {\n\t\tprop.value = parseExpression(true);\n\t\tkind = prop.kind = \"init\";\n\t } else if (options.ecmaVersion >= 5 && prop.key.type === \"Identifier\" &&\n\t\t\t\t (prop.key.name === \"get\" || prop.key.name === \"set\")) {\n\t\tisGetSet = sawGetSet = true;\n\t\tkind = prop.kind = prop.key.name;\n\t\tprop.key = parsePropertyName();\n\t\tif (tokType !== _parenL) unexpected();\n\t\tprop.value = parseFunction(startNode(), false);\n\t } else unexpected();\n\n\t if (prop.key.type === \"Identifier\" && (strict || sawGetSet)) {\n\t\tfor (var i = 0; i < node.properties.length; ++i) {\n\t\t var other = node.properties[i];\n\t\t if (other.key.name === prop.key.name) {\n\t\t\tvar conflict = kind == other.kind || isGetSet && other.kind === \"init\" ||\n\t\t\t kind === \"init\" && (other.kind === \"get\" || other.kind === \"set\");\n\t\t\tif (conflict && !strict && kind === \"init\" && other.kind === \"init\") conflict = false;\n\t\t\tif (conflict) raise(prop.key.start, \"Redefinition of property\");\n\t\t }\n\t\t}\n\t }\n\t node.properties.push(prop);\n\t}\n\treturn finishNode(node, \"ObjectExpression\");\n }\n\n function parsePropertyName() {\n\tif (tokType === _num || tokType === _string) return parseExprAtom();\n\treturn parseIdent(true);\n }\n\n function parseFunction(node, isStatement) {\n\tif (tokType === _name) node.id = parseIdent();\n\telse if (isStatement) unexpected();\n\telse node.id = null;\n\tnode.params = [];\n\tvar first = true;\n\texpect(_parenL);\n\twhile (!eat(_parenR)) {\n\t if (!first) expect(_comma); else first = false;\n\t node.params.push(parseIdent());\n\t}\n\n\tvar oldInFunc = inFunction, oldLabels = labels;\n\tinFunction = true; labels = [];\n\tnode.body = parseBlock(true);\n\tinFunction = oldInFunc; labels = oldLabels;\n\n\tif (strict || node.body.body.length && isUseStrict(node.body.body[0])) {\n\t for (var i = node.id ? -1 : 0; i < node.params.length; ++i) {\n\t\tvar id = i < 0 ? node.id : node.params[i];\n\t\tif (isStrictReservedWord(id.name) || isStrictBadIdWord(id.name))\n\t\t raise(id.start, \"Defining '\" + id.name + \"' in strict mode\");\n\t\tif (i >= 0) for (var j = 0; j < i; ++j) if (id.name === node.params[j].name)\n\t\t raise(id.start, \"Argument name clash in strict mode\");\n\t }\n\t}\n\n\treturn finishNode(node, isStatement ? \"FunctionDeclaration\" : \"FunctionExpression\");\n }\n\n function parseExprList(close, allowTrailingComma, allowEmpty) {\n\tvar elts = [], first = true;\n\twhile (!eat(close)) {\n\t if (!first) {\n\t\texpect(_comma);\n\t\tif (allowTrailingComma && options.allowTrailingCommas && eat(close)) break;\n\t } else first = false;\n\n\t if (allowEmpty && tokType === _comma) elts.push(null);\n\t else elts.push(parseExpression(true));\n\t}\n\treturn elts;\n }\n\n function parseIdent(liberal) {\n\tvar node = startNode();\n\tif (liberal && options.forbidReserved == \"everywhere\") liberal = false;\n\tif (tokType === _name) {\n\t if (!liberal &&\n\t\t (options.forbidReserved &&\n\t\t (options.ecmaVersion === 3 ? isReservedWord3 : isReservedWord5)(tokVal) ||\n\t\t strict && isStrictReservedWord(tokVal)) &&\n\t\t input.slice(tokStart, tokEnd).indexOf(\"\\\\\") == -1)\n\t\traise(tokStart, \"The keyword '\" + tokVal + \"' is reserved\");\n\t node.name = tokVal;\n\t} else if (liberal && tokType.keyword) {\n\t node.name = tokType.keyword;\n\t} else {\n\t unexpected();\n\t}\n\ttokRegexpAllowed = false;\n\tnext();\n\treturn finishNode(node, \"Identifier\");\n }\n\n});\n\n\t\tif (!acorn.version)\n\t\t\tacorn = null;\n\t}\n\n\tfunction parse(code, options) {\n\t\treturn (global.acorn || acorn).parse(code, options);\n\t}\n\n\tvar binaryOperators = {\n\t\t'+': '__add',\n\t\t'-': '__subtract',\n\t\t'*': '__multiply',\n\t\t'/': '__divide',\n\t\t'%': '__modulo',\n\t\t'==': '__equals',\n\t\t'!=': '__equals'\n\t};\n\n\tvar unaryOperators = {\n\t\t'-': '__negate',\n\t\t'+': '__self'\n\t};\n\n\tvar fields = Base.each(\n\t\t['add', 'subtract', 'multiply', 'divide', 'modulo', 'equals', 'negate'],\n\t\tfunction(name) {\n\t\t\tthis['__' + name] = '#' + name;\n\t\t},\n\t\t{\n\t\t\t__self: function() {\n\t\t\t\treturn this;\n\t\t\t}\n\t\t}\n\t);\n\tPoint.inject(fields);\n\tSize.inject(fields);\n\tColor.inject(fields);\n\n\tfunction __$__(left, operator, right) {\n\t\tvar handler = binaryOperators[operator];\n\t\tif (left && left[handler]) {\n\t\t\tvar res = left[handler](right);\n\t\t\treturn operator === '!=' ? !res : res;\n\t\t}\n\t\tswitch (operator) {\n\t\tcase '+': return left + right;\n\t\tcase '-': return left - right;\n\t\tcase '*': return left * right;\n\t\tcase '/': return left / right;\n\t\tcase '%': return left % right;\n\t\tcase '==': return left == right;\n\t\tcase '!=': return left != right;\n\t\t}\n\t}\n\n\tfunction $__(operator, value) {\n\t\tvar handler = unaryOperators[operator];\n\t\tif (value && value[handler])\n\t\t\treturn value[handler]();\n\t\tswitch (operator) {\n\t\tcase '+': return +value;\n\t\tcase '-': return -value;\n\t\t}\n\t}\n\n\tfunction compile(code, options) {\n\t\tif (!code)\n\t\t\treturn '';\n\t\toptions = options || {};\n\n\t\tvar insertions = [];\n\n\t\tfunction getOffset(offset) {\n\t\t\tfor (var i = 0, l = insertions.length; i < l; i++) {\n\t\t\t\tvar insertion = insertions[i];\n\t\t\t\tif (insertion[0] >= offset)\n\t\t\t\t\tbreak;\n\t\t\t\toffset += insertion[1];\n\t\t\t}\n\t\t\treturn offset;\n\t\t}\n\n\t\tfunction getCode(node) {\n\t\t\treturn code.substring(getOffset(node.range[0]),\n\t\t\t\t\tgetOffset(node.range[1]));\n\t\t}\n\n\t\tfunction getBetween(left, right) {\n\t\t\treturn code.substring(getOffset(left.range[1]),\n\t\t\t\t\tgetOffset(right.range[0]));\n\t\t}\n\n\t\tfunction replaceCode(node, str) {\n\t\t\tvar start = getOffset(node.range[0]),\n\t\t\t\tend = getOffset(node.range[1]),\n\t\t\t\tinsert = 0;\n\t\t\tfor (var i = insertions.length - 1; i >= 0; i--) {\n\t\t\t\tif (start > insertions[i][0]) {\n\t\t\t\t\tinsert = i + 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tinsertions.splice(insert, 0, [start, str.length - end + start]);\n\t\t\tcode = code.substring(0, start) + str + code.substring(end);\n\t\t}\n\n\t\tfunction handleOverloading(node, parent) {\n\t\t\tswitch (node.type) {\n\t\t\tcase 'UnaryExpression':\n\t\t\t\tif (node.operator in unaryOperators\n\t\t\t\t\t\t&& node.argument.type !== 'Literal') {\n\t\t\t\t\tvar arg = getCode(node.argument);\n\t\t\t\t\treplaceCode(node, '$__(\"' + node.operator + '\", '\n\t\t\t\t\t\t\t+ arg + ')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'BinaryExpression':\n\t\t\t\tif (node.operator in binaryOperators\n\t\t\t\t\t\t&& node.left.type !== 'Literal') {\n\t\t\t\t\tvar left = getCode(node.left),\n\t\t\t\t\t\tright = getCode(node.right),\n\t\t\t\t\t\tbetween = getBetween(node.left, node.right),\n\t\t\t\t\t\toperator = node.operator;\n\t\t\t\t\treplaceCode(node, '__$__(' + left + ','\n\t\t\t\t\t\t\t+ between.replace(new RegExp('\\\\' + operator),\n\t\t\t\t\t\t\t\t'\"' + operator + '\"')\n\t\t\t\t\t\t\t+ ', ' + right + ')');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'UpdateExpression':\n\t\t\tcase 'AssignmentExpression':\n\t\t\t\tvar parentType = parent && parent.type;\n\t\t\t\tif (!(\n\t\t\t\t\t\tparentType === 'ForStatement'\n\t\t\t\t\t\t|| parentType === 'BinaryExpression'\n\t\t\t\t\t\t\t&& /^[=!<>]/.test(parent.operator)\n\t\t\t\t\t\t|| parentType === 'MemberExpression' && parent.computed\n\t\t\t\t)) {\n\t\t\t\t\tif (node.type === 'UpdateExpression') {\n\t\t\t\t\t\tvar arg = getCode(node.argument),\n\t\t\t\t\t\t\texp = '__$__(' + arg + ', \"' + node.operator[0]\n\t\t\t\t\t\t\t\t\t+ '\", 1)',\n\t\t\t\t\t\t\tstr = arg + ' = ' + exp;\n\t\t\t\t\t\tif (node.prefix) {\n\t\t\t\t\t\t\tstr = '(' + str + ')';\n\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\tparentType === 'AssignmentExpression' ||\n\t\t\t\t\t\t\tparentType === 'VariableDeclarator' ||\n\t\t\t\t\t\t\tparentType === 'BinaryExpression'\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tif (getCode(parent.left || parent.id) === arg)\n\t\t\t\t\t\t\t\tstr = exp;\n\t\t\t\t\t\t\tstr = arg + '; ' + str;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treplaceCode(node, str);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (/^.=$/.test(node.operator)\n\t\t\t\t\t\t\t\t&& node.left.type !== 'Literal') {\n\t\t\t\t\t\t\tvar left = getCode(node.left),\n\t\t\t\t\t\t\t\tright = getCode(node.right),\n\t\t\t\t\t\t\t\texp = left + ' = __$__(' + left + ', \"'\n\t\t\t\t\t\t\t\t\t+ node.operator[0] + '\", ' + right + ')';\n\t\t\t\t\t\t\treplaceCode(node, /^\\(.*\\)$/.test(getCode(node))\n\t\t\t\t\t\t\t\t\t? '(' + exp + ')' : exp);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfunction handleExports(node) {\n\t\t\tswitch (node.type) {\n\t\t\tcase 'ExportDefaultDeclaration':\n\t\t\t\treplaceCode({\n\t\t\t\t\trange: [node.start, node.declaration.start]\n\t\t\t\t}, 'module.exports = ');\n\t\t\t\tbreak;\n\t\t\tcase 'ExportNamedDeclaration':\n\t\t\t\tvar declaration = node.declaration;\n\t\t\t\tvar specifiers = node.specifiers;\n\t\t\t\tif (declaration) {\n\t\t\t\t\tvar declarations = declaration.declarations;\n\t\t\t\t\tif (declarations) {\n\t\t\t\t\t\tdeclarations.forEach(function(dec) {\n\t\t\t\t\t\t\treplaceCode(dec, 'module.exports.' + getCode(dec));\n\t\t\t\t\t\t});\n\t\t\t\t\t\treplaceCode({\n\t\t\t\t\t\t\trange: [\n\t\t\t\t\t\t\t\tnode.start,\n\t\t\t\t\t\t\t\tdeclaration.start + declaration.kind.length\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}, '');\n\t\t\t\t\t}\n\t\t\t\t} else if (specifiers) {\n\t\t\t\t\tvar exports = specifiers.map(function(specifier) {\n\t\t\t\t\t\tvar name = getCode(specifier);\n\t\t\t\t\t\treturn 'module.exports.' + name + ' = ' + name + '; ';\n\t\t\t\t\t}).join('');\n\t\t\t\t\tif (exports) {\n\t\t\t\t\t\treplaceCode(node, exports);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfunction walkAST(node, parent, paperFeatures) {\n\t\t\tif (node) {\n\t\t\t\tfor (var key in node) {\n\t\t\t\t\tif (key !== 'range' && key !== 'loc') {\n\t\t\t\t\t\tvar value = node[key];\n\t\t\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\t\t\tfor (var i = 0, l = value.length; i < l; i++) {\n\t\t\t\t\t\t\t\twalkAST(value[i], node, paperFeatures);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (value && typeof value === 'object') {\n\t\t\t\t\t\t\twalkAST(value, node, paperFeatures);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (paperFeatures.operatorOverloading !== false) {\n\t\t\t\t\thandleOverloading(node, parent);\n\t\t\t\t}\n\t\t\t\tif (paperFeatures.moduleExports !== false) {\n\t\t\t\t\thandleExports(node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction encodeVLQ(value) {\n\t\t\tvar res = '',\n\t\t\t\tbase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\t\t\tvalue = (Math.abs(value) << 1) + (value < 0 ? 1 : 0);\n\t\t\twhile (value || !res) {\n\t\t\t\tvar next = value & (32 - 1);\n\t\t\t\tvalue >>= 5;\n\t\t\t\tif (value)\n\t\t\t\t\tnext |= 32;\n\t\t\t\tres += base64[next];\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\n\t\tvar url = options.url || '',\n\t\t\tsourceMaps = options.sourceMaps,\n\t\t\tpaperFeatures = options.paperFeatures || {},\n\t\t\tsource = options.source || code,\n\t\t\toffset = options.offset || 0,\n\t\t\tagent = paper.agent,\n\t\t\tversion = agent.versionNumber,\n\t\t\toffsetCode = false,\n\t\t\tlineBreaks = /\\r\\n|\\n|\\r/mg,\n\t\t\tmap;\n\t\tif (sourceMaps && (agent.chrome && version >= 30\n\t\t\t\t|| agent.webkit && version >= 537.76\n\t\t\t\t|| agent.firefox && version >= 23\n\t\t\t\t|| agent.node)) {\n\t\t\tif (agent.node) {\n\t\t\t\toffset -= 2;\n\t\t\t} else if (window && url && !window.location.href.indexOf(url)) {\n\t\t\t\tvar html = document.getElementsByTagName('html')[0].innerHTML;\n\t\t\t\toffset = html.substr(0, html.indexOf(code) + 1).match(\n\t\t\t\t\t\tlineBreaks).length + 1;\n\t\t\t}\n\t\t\toffsetCode = offset > 0 && !(\n\t\t\t\t\tagent.chrome && version >= 36 ||\n\t\t\t\t\tagent.safari && version >= 600 ||\n\t\t\t\t\tagent.firefox && version >= 40 ||\n\t\t\t\t\tagent.node);\n\t\t\tvar mappings = ['AA' + encodeVLQ(offsetCode ? 0 : offset) + 'A'];\n\t\t\tmappings.length = (code.match(lineBreaks) || []).length + 1\n\t\t\t\t\t+ (offsetCode ? offset : 0);\n\t\t\tmap = {\n\t\t\t\tversion: 3,\n\t\t\t\tfile: url,\n\t\t\t\tnames:[],\n\t\t\t\tmappings: mappings.join(';AACA'),\n\t\t\t\tsourceRoot: '',\n\t\t\t\tsources: [url],\n\t\t\t\tsourcesContent: [source]\n\t\t\t};\n\t\t}\n\t\tif (\n\t\t\tpaperFeatures.operatorOverloading !== false ||\n\t\t\tpaperFeatures.moduleExports !== false\n\t\t) {\n\t\t\twalkAST(parse(code, {\n\t\t\t\tranges: true,\n\t\t\t\tpreserveParens: true,\n\t\t\t\tsourceType: 'module'\n\t\t\t}), null, paperFeatures);\n\t\t}\n\t\tif (map) {\n\t\t\tif (offsetCode) {\n\t\t\t\tcode = new Array(offset + 1).join('\\n') + code;\n\t\t\t}\n\t\t\tif (/^(inline|both)$/.test(sourceMaps)) {\n\t\t\t\tcode += \"\\n//# sourceMappingURL=data:application/json;base64,\"\n\t\t\t\t\t\t+ self.btoa(unescape(encodeURIComponent(\n\t\t\t\t\t\t\tJSON.stringify(map))));\n\t\t\t}\n\t\t\tcode += \"\\n//# sourceURL=\" + (url || 'paperscript');\n\t\t}\n\t\treturn {\n\t\t\turl: url,\n\t\t\tsource: source,\n\t\t\tcode: code,\n\t\t\tmap: map\n\t\t};\n\t}\n\n\tfunction execute(code, scope, options) {\n\t\tpaper = scope;\n\t\tvar view = scope.getView(),\n\t\t\ttool = /\\btool\\.\\w+|\\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\\b/\n\t\t\t\t\t.test(code) && !/\\bnew\\s+Tool\\b/.test(code)\n\t\t\t\t\t\t? new Tool() : null,\n\t\t\ttoolHandlers = tool ? tool._events : [],\n\t\t\thandlers = ['onFrame', 'onResize'].concat(toolHandlers),\n\t\t\tparams = [],\n\t\t\targs = [],\n\t\t\tfunc,\n\t\t\tcompiled = typeof code === 'object' ? code : compile(code, options);\n\t\tcode = compiled.code;\n\t\tfunction expose(scope, hidden) {\n\t\t\tfor (var key in scope) {\n\t\t\t\tif ((hidden || !/^_/.test(key)) && new RegExp('([\\\\b\\\\s\\\\W]|^)'\n\t\t\t\t\t\t+ key.replace(/\\$/g, '\\\\$') + '\\\\b').test(code)) {\n\t\t\t\t\tparams.push(key);\n\t\t\t\t\targs.push(scope[key]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\texpose({ __$__: __$__, $__: $__, paper: scope, tool: tool },\n\t\t\t\ttrue);\n\t\texpose(scope);\n\t\tcode = 'var module = { exports: {} }; ' + code;\n\t\tvar exports = Base.each(handlers, function(key) {\n\t\t\tif (new RegExp('\\\\s+' + key + '\\\\b').test(code)) {\n\t\t\t\tparams.push(key);\n\t\t\t\tthis.push('module.exports.' + key + ' = ' + key + ';');\n\t\t\t}\n\t\t}, []).join('\\n');\n\t\tif (exports) {\n\t\t\tcode += '\\n' + exports;\n\t\t}\n\t\tcode += '\\nreturn module.exports;';\n\t\tvar agent = paper.agent;\n\t\tif (document && (agent.chrome\n\t\t\t\t|| agent.firefox && agent.versionNumber < 40)) {\n\t\t\tvar script = document.createElement('script'),\n\t\t\t\thead = document.head || document.getElementsByTagName('head')[0];\n\t\t\tif (agent.firefox)\n\t\t\t\tcode = '\\n' + code;\n\t\t\tscript.appendChild(document.createTextNode(\n\t\t\t\t'document.__paperscript__ = function(' + params + ') {' +\n\t\t\t\t\tcode +\n\t\t\t\t'\\n}'\n\t\t\t));\n\t\t\thead.appendChild(script);\n\t\t\tfunc = document.__paperscript__;\n\t\t\tdelete document.__paperscript__;\n\t\t\thead.removeChild(script);\n\t\t} else {\n\t\t\tfunc = Function(params, code);\n\t\t}\n\t\tvar exports = func && func.apply(scope, args);\n\t\tvar obj = exports || {};\n\t\tBase.each(toolHandlers, function(key) {\n\t\t\tvar value = obj[key];\n\t\t\tif (value)\n\t\t\t\ttool[key] = value;\n\t\t});\n\t\tif (view) {\n\t\t\tif (obj.onResize)\n\t\t\t\tview.setOnResize(obj.onResize);\n\t\t\tview.emit('resize', {\n\t\t\t\tsize: view.size,\n\t\t\t\tdelta: new Point()\n\t\t\t});\n\t\t\tif (obj.onFrame)\n\t\t\t\tview.setOnFrame(obj.onFrame);\n\t\t\tview.requestUpdate();\n\t\t}\n\t\treturn exports;\n\t}\n\n\tfunction loadScript(script) {\n\t\tif (/^text\\/(?:x-|)paperscript$/.test(script.type)\n\t\t\t\t&& PaperScope.getAttribute(script, 'ignore') !== 'true') {\n\t\t\tvar canvasId = PaperScope.getAttribute(script, 'canvas'),\n\t\t\t\tcanvas = document.getElementById(canvasId),\n\t\t\t\tsrc = script.src || script.getAttribute('data-src'),\n\t\t\t\tasync = PaperScope.hasAttribute(script, 'async'),\n\t\t\t\tscopeAttribute = 'data-paper-scope';\n\t\t\tif (!canvas)\n\t\t\t\tthrow new Error('Unable to find canvas with id \"'\n\t\t\t\t\t\t+ canvasId + '\"');\n\t\t\tvar scope = PaperScope.get(canvas.getAttribute(scopeAttribute))\n\t\t\t\t\t\t|| new PaperScope().setup(canvas);\n\t\t\tcanvas.setAttribute(scopeAttribute, scope._id);\n\t\t\tif (src) {\n\t\t\t\tHttp.request({\n\t\t\t\t\turl: src,\n\t\t\t\t\tasync: async,\n\t\t\t\t\tmimeType: 'text/plain',\n\t\t\t\t\tonLoad: function(code) {\n\t\t\t\t\t\texecute(code, scope, src);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\texecute(script.innerHTML, scope, script.baseURI);\n\t\t\t}\n\t\t\tscript.setAttribute('data-paper-ignore', 'true');\n\t\t\treturn scope;\n\t\t}\n\t}\n\n\tfunction loadAll() {\n\t\tBase.each(document && document.getElementsByTagName('script'),\n\t\t\t\tloadScript);\n\t}\n\n\tfunction load(script) {\n\t\treturn script ? loadScript(script) : loadAll();\n\t}\n\n\tif (window) {\n\t\tif (document.readyState === 'complete') {\n\t\t\tsetTimeout(loadAll);\n\t\t} else {\n\t\t\tDomEvent.add(window, { load: loadAll });\n\t\t}\n\t}\n\n\treturn {\n\t\tcompile: compile,\n\t\texecute: execute,\n\t\tload: load,\n\t\tparse: parse,\n\t\tcalculateBinary: __$__,\n\t\tcalculateUnary: $__\n\t};\n\n}.call(this);\n\nvar paper = new (PaperScope.inject(Base.exports, {\n\tBase: Base,\n\tNumerical: Numerical,\n\tKey: Key,\n\tDomEvent: DomEvent,\n\tDomElement: DomElement,\n\tdocument: document,\n\twindow: window,\n\tSymbol: SymbolDefinition,\n\tPlacedSymbol: SymbolItem\n}))();\n\nif (paper.agent.node) {\n\trequire('./node/extend.js')(paper);\n}\n\nif (typeof define === 'function' && define.amd) {\n\tdefine('paper', paper);\n} else if (typeof module === 'object' && module) {\n\tmodule.exports = paper;\n}\n\nreturn paper;\n}.call(this, typeof self === 'object' ? self : null);\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.acorn = {}));\n})(this, (function (exports) { 'use strict';\n\n // This file was generated. Do not modify manually!\n var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 78, 5, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 199, 7, 137, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 55, 9, 266, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 233, 0, 3, 0, 8, 1, 6, 0, 475, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];\n\n // This file was generated. Do not modify manually!\n var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 7, 25, 39, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 5, 57, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 24, 43, 261, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 33, 24, 3, 24, 45, 74, 6, 0, 67, 12, 65, 1, 2, 0, 15, 4, 10, 7381, 42, 31, 98, 114, 8702, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 208, 30, 2, 2, 2, 1, 2, 6, 3, 4, 10, 1, 225, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4381, 3, 5773, 3, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 8489];\n\n // This file was generated. Do not modify manually!\n var nonASCIIidentifierChars = \"\\u200c\\u200d\\xb7\\u0300-\\u036f\\u0387\\u0483-\\u0487\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u0669\\u0670\\u06d6-\\u06dc\\u06df-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u06f0-\\u06f9\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07c0-\\u07c9\\u07eb-\\u07f3\\u07fd\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0859-\\u085b\\u0897-\\u089f\\u08ca-\\u08e1\\u08e3-\\u0903\\u093a-\\u093c\\u093e-\\u094f\\u0951-\\u0957\\u0962\\u0963\\u0966-\\u096f\\u0981-\\u0983\\u09bc\\u09be-\\u09c4\\u09c7\\u09c8\\u09cb-\\u09cd\\u09d7\\u09e2\\u09e3\\u09e6-\\u09ef\\u09fe\\u0a01-\\u0a03\\u0a3c\\u0a3e-\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a66-\\u0a71\\u0a75\\u0a81-\\u0a83\\u0abc\\u0abe-\\u0ac5\\u0ac7-\\u0ac9\\u0acb-\\u0acd\\u0ae2\\u0ae3\\u0ae6-\\u0aef\\u0afa-\\u0aff\\u0b01-\\u0b03\\u0b3c\\u0b3e-\\u0b44\\u0b47\\u0b48\\u0b4b-\\u0b4d\\u0b55-\\u0b57\\u0b62\\u0b63\\u0b66-\\u0b6f\\u0b82\\u0bbe-\\u0bc2\\u0bc6-\\u0bc8\\u0bca-\\u0bcd\\u0bd7\\u0be6-\\u0bef\\u0c00-\\u0c04\\u0c3c\\u0c3e-\\u0c44\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0c66-\\u0c6f\\u0c81-\\u0c83\\u0cbc\\u0cbe-\\u0cc4\\u0cc6-\\u0cc8\\u0cca-\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0ce6-\\u0cef\\u0cf3\\u0d00-\\u0d03\\u0d3b\\u0d3c\\u0d3e-\\u0d44\\u0d46-\\u0d48\\u0d4a-\\u0d4d\\u0d57\\u0d62\\u0d63\\u0d66-\\u0d6f\\u0d81-\\u0d83\\u0dca\\u0dcf-\\u0dd4\\u0dd6\\u0dd8-\\u0ddf\\u0de6-\\u0def\\u0df2\\u0df3\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0e50-\\u0e59\\u0eb1\\u0eb4-\\u0ebc\\u0ec8-\\u0ece\\u0ed0-\\u0ed9\\u0f18\\u0f19\\u0f20-\\u0f29\\u0f35\\u0f37\\u0f39\\u0f3e\\u0f3f\\u0f71-\\u0f84\\u0f86\\u0f87\\u0f8d-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102b-\\u103e\\u1040-\\u1049\\u1056-\\u1059\\u105e-\\u1060\\u1062-\\u1064\\u1067-\\u106d\\u1071-\\u1074\\u1082-\\u108d\\u108f-\\u109d\\u135d-\\u135f\\u1369-\\u1371\\u1712-\\u1715\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b4-\\u17d3\\u17dd\\u17e0-\\u17e9\\u180b-\\u180d\\u180f-\\u1819\\u18a9\\u1920-\\u192b\\u1930-\\u193b\\u1946-\\u194f\\u19d0-\\u19da\\u1a17-\\u1a1b\\u1a55-\\u1a5e\\u1a60-\\u1a7c\\u1a7f-\\u1a89\\u1a90-\\u1a99\\u1ab0-\\u1abd\\u1abf-\\u1add\\u1ae0-\\u1aeb\\u1b00-\\u1b04\\u1b34-\\u1b44\\u1b50-\\u1b59\\u1b6b-\\u1b73\\u1b80-\\u1b82\\u1ba1-\\u1bad\\u1bb0-\\u1bb9\\u1be6-\\u1bf3\\u1c24-\\u1c37\\u1c40-\\u1c49\\u1c50-\\u1c59\\u1cd0-\\u1cd2\\u1cd4-\\u1ce8\\u1ced\\u1cf4\\u1cf7-\\u1cf9\\u1dc0-\\u1dff\\u200c\\u200d\\u203f\\u2040\\u2054\\u20d0-\\u20dc\\u20e1\\u20e5-\\u20f0\\u2cef-\\u2cf1\\u2d7f\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\u30fb\\ua620-\\ua629\\ua66f\\ua674-\\ua67d\\ua69e\\ua69f\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua823-\\ua827\\ua82c\\ua880\\ua881\\ua8b4-\\ua8c5\\ua8d0-\\ua8d9\\ua8e0-\\ua8f1\\ua8ff-\\ua909\\ua926-\\ua92d\\ua947-\\ua953\\ua980-\\ua983\\ua9b3-\\ua9c0\\ua9d0-\\ua9d9\\ua9e5\\ua9f0-\\ua9f9\\uaa29-\\uaa36\\uaa43\\uaa4c\\uaa4d\\uaa50-\\uaa59\\uaa7b-\\uaa7d\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uaaeb-\\uaaef\\uaaf5\\uaaf6\\uabe3-\\uabea\\uabec\\uabed\\uabf0-\\uabf9\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe2f\\ufe33\\ufe34\\ufe4d-\\ufe4f\\uff10-\\uff19\\uff3f\\uff65\";\n\n // This file was generated. Do not modify manually!\n var nonASCIIidentifierStartChars = \"\\xaa\\xb5\\xba\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u037f\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u052f\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05d0-\\u05ea\\u05ef-\\u05f2\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086a\\u0870-\\u0887\\u0889-\\u088f\\u08a0-\\u08c9\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u09fc\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0af9\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c39\\u0c3d\\u0c58-\\u0c5a\\u0c5c\\u0c5d\\u0c60\\u0c61\\u0c80\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cdc-\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d04-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d54-\\u0d56\\u0d5f-\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0e01-\\u0e30\\u0e32\\u0e33\\u0e40-\\u0e46\\u0e81\\u0e82\\u0e84\\u0e86-\\u0e8a\\u0e8c-\\u0ea3\\u0ea5\\u0ea7-\\u0eb0\\u0eb2\\u0eb3\\u0ebd\\u0ec0-\\u0ec4\\u0ec6\\u0edc-\\u0edf\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u1000-\\u102a\\u103f\\u1050-\\u1055\\u105a-\\u105d\\u1061\\u1065\\u1066\\u106e-\\u1070\\u1075-\\u1081\\u108e\\u10a0-\\u10c5\\u10c7\\u10cd\\u10d0-\\u10fa\\u10fc-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f5\\u13f8-\\u13fd\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16ee-\\u16f8\\u1700-\\u1711\\u171f-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1780-\\u17b3\\u17d7\\u17dc\\u1820-\\u1878\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191e\\u1950-\\u196d\\u1970-\\u1974\\u1980-\\u19ab\\u19b0-\\u19c9\\u1a00-\\u1a16\\u1a20-\\u1a54\\u1aa7\\u1b05-\\u1b33\\u1b45-\\u1b4c\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bba-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1c80-\\u1c8a\\u1c90-\\u1cba\\u1cbd-\\u1cbf\\u1ce9-\\u1cec\\u1cee-\\u1cf3\\u1cf5\\u1cf6\\u1cfa\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2118-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2160-\\u2188\\u2c00-\\u2ce4\\u2ceb-\\u2cee\\u2cf2\\u2cf3\\u2d00-\\u2d25\\u2d27\\u2d2d\\u2d30-\\u2d67\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303c\\u3041-\\u3096\\u309b-\\u309f\\u30a1-\\u30fa\\u30fc-\\u30ff\\u3105-\\u312f\\u3131-\\u318e\\u31a0-\\u31bf\\u31f0-\\u31ff\\u3400-\\u4dbf\\u4e00-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua69d\\ua6a0-\\ua6ef\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua7dc\\ua7f1-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua8fd\\ua8fe\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\ua9e0-\\ua9e4\\ua9e6-\\ua9ef\\ua9fa-\\ua9fe\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uaa60-\\uaa76\\uaa7a\\uaa7e-\\uaaaf\\uaab1\\uaab5\\uaab6\\uaab9-\\uaabd\\uaac0\\uaac2\\uaadb-\\uaadd\\uaae0-\\uaaea\\uaaf2-\\uaaf4\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uab30-\\uab5a\\uab5c-\\uab69\\uab70-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\uf900-\\ufa6d\\ufa70-\\ufad9\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uff66-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc\";\n\n // These are a run-length and offset encoded representation of the\n // >0xffff code points that are a valid part of identifiers. The\n // offset starts at 0x10000, and each pair of numbers represents an\n // offset to the next range, and then a size of the range.\n\n // Reserved word lists for various dialects of the language\n\n var reservedWords = {\n 3: \"abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile\",\n 5: \"class enum extends super const export import\",\n 6: \"enum\",\n strict: \"implements interface let package private protected public static yield\",\n strictBind: \"eval arguments\"\n };\n\n // And the keywords\n\n var ecma5AndLessKeywords = \"break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this\";\n\n var keywords$1 = {\n 5: ecma5AndLessKeywords,\n \"5module\": ecma5AndLessKeywords + \" export import\",\n 6: ecma5AndLessKeywords + \" const class extends export import super\"\n };\n\n var keywordRelationalOperator = /^in(stanceof)?$/;\n\n // ## Character categories\n\n var nonASCIIidentifierStart = new RegExp(\"[\" + nonASCIIidentifierStartChars + \"]\");\n var nonASCIIidentifier = new RegExp(\"[\" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + \"]\");\n\n // This has a complexity linear to the value of the code. The\n // assumption is that looking up astral identifier characters is\n // rare.\n function isInAstralSet(code, set) {\n var pos = 0x10000;\n for (var i = 0; i < set.length; i += 2) {\n pos += set[i];\n if (pos > code) { return false }\n pos += set[i + 1];\n if (pos >= code) { return true }\n }\n return false\n }\n\n // Test whether a given character code starts an identifier.\n\n function isIdentifierStart(code, astral) {\n if (code < 65) { return code === 36 }\n if (code < 91) { return true }\n if (code < 97) { return code === 95 }\n if (code < 123) { return true }\n if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }\n if (astral === false) { return false }\n return isInAstralSet(code, astralIdentifierStartCodes)\n }\n\n // Test whether a given character is part of an identifier.\n\n function isIdentifierChar(code, astral) {\n if (code < 48) { return code === 36 }\n if (code < 58) { return true }\n if (code < 65) { return false }\n if (code < 91) { return true }\n if (code < 97) { return code === 95 }\n if (code < 123) { return true }\n if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }\n if (astral === false) { return false }\n return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)\n }\n\n // ## Token types\n\n // The assignment of fine-grained, information-carrying type objects\n // allows the tokenizer to store the information it has about a\n // token in a way that is very cheap for the parser to look up.\n\n // All token type variables start with an underscore, to make them\n // easy to recognize.\n\n // The `beforeExpr` property is used to disambiguate between regular\n // expressions and divisions. It is set on all token types that can\n // be followed by an expression (thus, a slash after them would be a\n // regular expression).\n //\n // The `startsExpr` property is used to check if the token ends a\n // `yield` expression. It is set on all token types that either can\n // directly start an expression (like a quotation mark) or can\n // continue an expression (like the body of a string).\n //\n // `isLoop` marks a keyword as starting a loop, which is important\n // to know when parsing a label, in order to allow or disallow\n // continue jumps to that label.\n\n var TokenType = function TokenType(label, conf) {\n if ( conf === void 0 ) conf = {};\n\n this.label = label;\n this.keyword = conf.keyword;\n this.beforeExpr = !!conf.beforeExpr;\n this.startsExpr = !!conf.startsExpr;\n this.isLoop = !!conf.isLoop;\n this.isAssign = !!conf.isAssign;\n this.prefix = !!conf.prefix;\n this.postfix = !!conf.postfix;\n this.binop = conf.binop || null;\n this.updateContext = null;\n };\n\n function binop(name, prec) {\n return new TokenType(name, {beforeExpr: true, binop: prec})\n }\n var beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true};\n\n // Map keyword names to token types.\n\n var keywords = {};\n\n // Succinct definitions of keyword token types\n function kw(name, options) {\n if ( options === void 0 ) options = {};\n\n options.keyword = name;\n return keywords[name] = new TokenType(name, options)\n }\n\n var types$1 = {\n num: new TokenType(\"num\", startsExpr),\n regexp: new TokenType(\"regexp\", startsExpr),\n string: new TokenType(\"string\", startsExpr),\n name: new TokenType(\"name\", startsExpr),\n privateId: new TokenType(\"privateId\", startsExpr),\n eof: new TokenType(\"eof\"),\n\n // Punctuation token types.\n bracketL: new TokenType(\"[\", {beforeExpr: true, startsExpr: true}),\n bracketR: new TokenType(\"]\"),\n braceL: new TokenType(\"{\", {beforeExpr: true, startsExpr: true}),\n braceR: new TokenType(\"}\"),\n parenL: new TokenType(\"(\", {beforeExpr: true, startsExpr: true}),\n parenR: new TokenType(\")\"),\n comma: new TokenType(\",\", beforeExpr),\n semi: new TokenType(\";\", beforeExpr),\n colon: new TokenType(\":\", beforeExpr),\n dot: new TokenType(\".\"),\n question: new TokenType(\"?\", beforeExpr),\n questionDot: new TokenType(\"?.\"),\n arrow: new TokenType(\"=>\", beforeExpr),\n template: new TokenType(\"template\"),\n invalidTemplate: new TokenType(\"invalidTemplate\"),\n ellipsis: new TokenType(\"...\", beforeExpr),\n backQuote: new TokenType(\"`\", startsExpr),\n dollarBraceL: new TokenType(\"${\", {beforeExpr: true, startsExpr: true}),\n\n // Operators. These carry several kinds of properties to help the\n // parser use them properly (the presence of these properties is\n // what categorizes them as operators).\n //\n // `binop`, when present, specifies that this operator is a binary\n // operator, and will refer to its precedence.\n //\n // `prefix` and `postfix` mark the operator as a prefix or postfix\n // unary operator.\n //\n // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as\n // binary operators with a very low precedence, that should result\n // in AssignmentExpression nodes.\n\n eq: new TokenType(\"=\", {beforeExpr: true, isAssign: true}),\n assign: new TokenType(\"_=\", {beforeExpr: true, isAssign: true}),\n incDec: new TokenType(\"++/--\", {prefix: true, postfix: true, startsExpr: true}),\n prefix: new TokenType(\"!/~\", {beforeExpr: true, prefix: true, startsExpr: true}),\n logicalOR: binop(\"||\", 1),\n logicalAND: binop(\"&&\", 2),\n bitwiseOR: binop(\"|\", 3),\n bitwiseXOR: binop(\"^\", 4),\n bitwiseAND: binop(\"&\", 5),\n equality: binop(\"==/!=/===/!==\", 6),\n relational: binop(\"</>/<=/>=\", 7),\n bitShift: binop(\"<</>>/>>>\", 8),\n plusMin: new TokenType(\"+/-\", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),\n modulo: binop(\"%\", 10),\n star: binop(\"*\", 10),\n slash: binop(\"/\", 10),\n starstar: new TokenType(\"**\", {beforeExpr: true}),\n coalesce: binop(\"??\", 1),\n\n // Keyword token types.\n _break: kw(\"break\"),\n _case: kw(\"case\", beforeExpr),\n _catch: kw(\"catch\"),\n _continue: kw(\"continue\"),\n _debugger: kw(\"debugger\"),\n _default: kw(\"default\", beforeExpr),\n _do: kw(\"do\", {isLoop: true, beforeExpr: true}),\n _else: kw(\"else\", beforeExpr),\n _finally: kw(\"finally\"),\n _for: kw(\"for\", {isLoop: true}),\n _function: kw(\"function\", startsExpr),\n _if: kw(\"if\"),\n _return: kw(\"return\", beforeExpr),\n _switch: kw(\"switch\"),\n _throw: kw(\"throw\", beforeExpr),\n _try: kw(\"try\"),\n _var: kw(\"var\"),\n _const: kw(\"const\"),\n _while: kw(\"while\", {isLoop: true}),\n _with: kw(\"with\"),\n _new: kw(\"new\", {beforeExpr: true, startsExpr: true}),\n _this: kw(\"this\", startsExpr),\n _super: kw(\"super\", startsExpr),\n _class: kw(\"class\", startsExpr),\n _extends: kw(\"extends\", beforeExpr),\n _export: kw(\"export\"),\n _import: kw(\"import\", startsExpr),\n _null: kw(\"null\", startsExpr),\n _true: kw(\"true\", startsExpr),\n _false: kw(\"false\", startsExpr),\n _in: kw(\"in\", {beforeExpr: true, binop: 7}),\n _instanceof: kw(\"instanceof\", {beforeExpr: true, binop: 7}),\n _typeof: kw(\"typeof\", {beforeExpr: true, prefix: true, startsExpr: true}),\n _void: kw(\"void\", {beforeExpr: true, prefix: true, startsExpr: true}),\n _delete: kw(\"delete\", {beforeExpr: true, prefix: true, startsExpr: true})\n };\n\n // Matches a whole line break (where CRLF is considered a single\n // line break). Used to count lines.\n\n var lineBreak = /\\r\\n?|\\n|\\u2028|\\u2029/;\n var lineBreakG = new RegExp(lineBreak.source, \"g\");\n\n function isNewLine(code) {\n return code === 10 || code === 13 || code === 0x2028 || code === 0x2029\n }\n\n function nextLineBreak(code, from, end) {\n if ( end === void 0 ) end = code.length;\n\n for (var i = from; i < end; i++) {\n var next = code.charCodeAt(i);\n if (isNewLine(next))\n { return i < end - 1 && next === 13 && code.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 }\n }\n return -1\n }\n\n var nonASCIIwhitespace = /[\\u1680\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]/;\n\n var skipWhiteSpace = /(?:\\s|\\/\\/.*|\\/\\*[^]*?\\*\\/)*/g;\n\n var ref = Object.prototype;\n var hasOwnProperty = ref.hasOwnProperty;\n var toString = ref.toString;\n\n var hasOwn = Object.hasOwn || (function (obj, propName) { return (\n hasOwnProperty.call(obj, propName)\n ); });\n\n var isArray = Array.isArray || (function (obj) { return (\n toString.call(obj) === \"[object Array]\"\n ); });\n\n var regexpCache = Object.create(null);\n\n function wordsRegexp(words) {\n return regexpCache[words] || (regexpCache[words] = new RegExp(\"^(?:\" + words.replace(/ /g, \"|\") + \")$\"))\n }\n\n function codePointToString(code) {\n // UTF-16 Decoding\n if (code <= 0xFFFF) { return String.fromCharCode(code) }\n code -= 0x10000;\n return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)\n }\n\n var loneSurrogate = /(?:[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])/;\n\n // These are used when `options.locations` is on, for the\n // `startLoc` and `endLoc` properties.\n\n var Position = function Position(line, col) {\n this.line = line;\n this.column = col;\n };\n\n Position.prototype.offset = function offset (n) {\n return new Position(this.line, this.column + n)\n };\n\n var SourceLocation = function SourceLocation(p, start, end) {\n this.start = start;\n this.end = end;\n if (p.sourceFile !== null) { this.source = p.sourceFile; }\n };\n\n // The `getLineInfo` function is mostly useful when the\n // `locations` option is off (for performance reasons) and you\n // want to find the line/column position for a given character\n // offset. `input` should be the code string that the offset refers\n // into.\n\n function getLineInfo(input, offset) {\n for (var line = 1, cur = 0;;) {\n var nextBreak = nextLineBreak(input, cur, offset);\n if (nextBreak < 0) { return new Position(line, offset - cur) }\n ++line;\n cur = nextBreak;\n }\n }\n\n // A second argument must be given to configure the parser process.\n // These options are recognized (only `ecmaVersion` is required):\n\n var defaultOptions = {\n // `ecmaVersion` indicates the ECMAScript version to parse. Must be\n // either 3, 5, 6 (or 2015), 7 (2016), 8 (2017), 9 (2018), 10\n // (2019), 11 (2020), 12 (2021), 13 (2022), 14 (2023), or `\"latest\"`\n // (the latest version the library supports). This influences\n // support for strict mode, the set of reserved words, and support\n // for new syntax features.\n ecmaVersion: null,\n // `sourceType` indicates the mode the code should be parsed in.\n // Can be either `\"script\"`, `\"module\"` or `\"commonjs\"`. This influences global\n // strict mode and parsing of `import` and `export` declarations.\n sourceType: \"script\",\n // `onInsertedSemicolon` can be a callback that will be called when\n // a semicolon is automatically inserted. It will be passed the\n // position of the inserted semicolon as an offset, and if\n // `locations` is enabled, it is given the location as a `{line,\n // column}` object as second argument.\n onInsertedSemicolon: null,\n // `onTrailingComma` is similar to `onInsertedSemicolon`, but for\n // trailing commas.\n onTrailingComma: null,\n // By default, reserved words are only enforced if ecmaVersion >= 5.\n // Set `allowReserved` to a boolean value to explicitly turn this on\n // an off. When this option has the value \"never\", reserved words\n // and keywords can also not be used as property names.\n allowReserved: null,\n // When enabled, a return at the top level is not considered an\n // error.\n allowReturnOutsideFunction: false,\n // When enabled, import/export statements are not constrained to\n // appearing at the top of the program, and an import.meta expression\n // in a script isn't considered an error.\n allowImportExportEverywhere: false,\n // By default, await identifiers are allowed to appear at the top-level scope only if ecmaVersion >= 2022.\n // When enabled, await identifiers are allowed to appear at the top-level scope,\n // but they are still not allowed in non-async functions.\n allowAwaitOutsideFunction: null,\n // When enabled, super identifiers are not constrained to\n // appearing in methods and do not raise an error when they appear elsewhere.\n allowSuperOutsideMethod: null,\n // When enabled, hashbang directive in the beginning of file is\n // allowed and treated as a line comment. Enabled by default when\n // `ecmaVersion` >= 2023.\n allowHashBang: false,\n // By default, the parser will verify that private properties are\n // only used in places where they are valid and have been declared.\n // Set this to false to turn such checks off.\n checkPrivateFields: true,\n // When `locations` is on, `loc` properties holding objects with\n // `start` and `end` properties in `{line, column}` form (with\n // line being 1-based and column 0-based) will be attached to the\n // nodes.\n locations: false,\n // A function can be passed as `onToken` option, which will\n // cause Acorn to call that function with object in the same\n // format as tokens returned from `tokenizer().getToken()`. Note\n // that you are not allowed to call the parser from the\n // callback—that will corrupt its internal state.\n onToken: null,\n // A function can be passed as `onComment` option, which will\n // cause Acorn to call that function with `(block, text, start,\n // end)` parameters whenever a comment is skipped. `block` is a\n // boolean indicating whether this is a block (`/* */`) comment,\n // `text` is the content of the comment, and `start` and `end` are\n // character offsets that denote the start and end of the comment.\n // When the `locations` option is on, two more parameters are\n // passed, the full `{line, column}` locations of the start and\n // end of the comments. Note that you are not allowed to call the\n // parser from the callback—that will corrupt its internal state.\n // When this option has an array as value, objects representing the\n // comments are pushed to it.\n onComment: null,\n // Nodes have their start and end characters offsets recorded in\n // `start` and `end` properties (directly on the node, rather than\n // the `loc` object, which holds line/column data. To also add a\n // [semi-standardized][range] `range` property holding a `[start,\n // end]` array with the same numbers, set the `ranges` option to\n // `true`.\n //\n // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678\n ranges: false,\n // It is possible to parse multiple files into a single AST by\n // passing the tree produced by parsing the first file as\n // `program` option in subsequent parses. This will add the\n // toplevel forms of the parsed file to the `Program` (top) node\n // of an existing parse tree.\n program: null,\n // When `locations` is on, you can pass this to record the source\n // file in every node's `loc` object.\n sourceFile: null,\n // This value, if given, is stored in every node, whether\n // `locations` is on or off.\n directSourceFile: null,\n // When enabled, parenthesized expressions are represented by\n // (non-standard) ParenthesizedExpression nodes\n preserveParens: false\n };\n\n // Interpret and default an options object\n\n var warnedAboutEcmaVersion = false;\n\n function getOptions(opts) {\n var options = {};\n\n for (var opt in defaultOptions)\n { options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt]; }\n\n if (options.ecmaVersion === \"latest\") {\n options.ecmaVersion = 1e8;\n } else if (options.ecmaVersion == null) {\n if (!warnedAboutEcmaVersion && typeof console === \"object\" && console.warn) {\n warnedAboutEcmaVersion = true;\n console.warn(\"Since Acorn 8.0.0, options.ecmaVersion is required.\\nDefaulting to 2020, but this will stop working in the future.\");\n }\n options.ecmaVersion = 11;\n } else if (options.ecmaVersion >= 2015) {\n options.ecmaVersion -= 2009;\n }\n\n if (options.allowReserved == null)\n { options.allowReserved = options.ecmaVersion < 5; }\n\n if (!opts || opts.allowHashBang == null)\n { options.allowHashBang = options.ecmaVersion >= 14; }\n\n if (isArray(options.onToken)) {\n var tokens = options.onToken;\n options.onToken = function (token) { return tokens.push(token); };\n }\n if (isArray(options.onComment))\n { options.onComment = pushComment(options, options.onComment); }\n\n if (options.sourceType === \"commonjs\" && options.allowAwaitOutsideFunction)\n { throw new Error(\"Cannot use allowAwaitOutsideFunction with sourceType: commonjs\") }\n\n return options\n }\n\n function pushComment(options, array) {\n return function(block, text, start, end, startLoc, endLoc) {\n var comment = {\n type: block ? \"Block\" : \"Line\",\n value: text,\n start: start,\n end: end\n };\n if (options.locations)\n { comment.loc = new SourceLocation(this, startLoc, endLoc); }\n if (options.ranges)\n { comment.range = [start, end]; }\n array.push(comment);\n }\n }\n\n // Each scope gets a bitset that may contain these flags\n var\n SCOPE_TOP = 1,\n SCOPE_FUNCTION = 2,\n SCOPE_ASYNC = 4,\n SCOPE_GENERATOR = 8,\n SCOPE_ARROW = 16,\n SCOPE_SIMPLE_CATCH = 32,\n SCOPE_SUPER = 64,\n SCOPE_DIRECT_SUPER = 128,\n SCOPE_CLASS_STATIC_BLOCK = 256,\n SCOPE_CLASS_FIELD_INIT = 512,\n SCOPE_SWITCH = 1024,\n SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK;\n\n function functionFlags(async, generator) {\n return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)\n }\n\n // Used in checkLVal* and declareName to determine the type of a binding\n var\n BIND_NONE = 0, // Not a binding\n BIND_VAR = 1, // Var-style binding\n BIND_LEXICAL = 2, // Let- or const-style binding\n BIND_FUNCTION = 3, // Function declaration\n BIND_SIMPLE_CATCH = 4, // Simple (identifier pattern) catch binding\n BIND_OUTSIDE = 5; // Special case for function names as bound inside the function\n\n var Parser = function Parser(options, input, startPos) {\n this.options = options = getOptions(options);\n this.sourceFile = options.sourceFile;\n this.keywords = wordsRegexp(keywords$1[options.ecmaVersion >= 6 ? 6 : options.sourceType === \"module\" ? \"5module\" : 5]);\n var reserved = \"\";\n if (options.allowReserved !== true) {\n reserved = reservedWords[options.ecmaVersion >= 6 ? 6 : options.ecmaVersion === 5 ? 5 : 3];\n if (options.sourceType === \"module\") { reserved += \" await\"; }\n }\n this.reservedWords = wordsRegexp(reserved);\n var reservedStrict = (reserved ? reserved + \" \" : \"\") + reservedWords.strict;\n this.reservedWordsStrict = wordsRegexp(reservedStrict);\n this.reservedWordsStrictBind = wordsRegexp(reservedStrict + \" \" + reservedWords.strictBind);\n this.input = String(input);\n\n // Used to signal to callers of `readWord1` whether the word\n // contained any escape sequences. This is needed because words with\n // escape sequences must not be interpreted as keywords.\n this.containsEsc = false;\n\n // Set up token state\n\n // The current position of the tokenizer in the input.\n if (startPos) {\n this.pos = startPos;\n this.lineStart = this.input.lastIndexOf(\"\\n\", startPos - 1) + 1;\n this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;\n } else {\n this.pos = this.lineStart = 0;\n this.curLine = 1;\n }\n\n // Properties of the current token:\n // Its type\n this.type = types$1.eof;\n // For tokens that include more information than their type, the value\n this.value = null;\n // Its start and end offset\n this.start = this.end = this.pos;\n // And, if locations are used, the {line, column} object\n // corresponding to those offsets\n this.startLoc = this.endLoc = this.curPosition();\n\n // Position information for the previous token\n this.lastTokEndLoc = this.lastTokStartLoc = null;\n this.lastTokStart = this.lastTokEnd = this.pos;\n\n // The context stack is used to superficially track syntactic\n // context to predict whether a regular expression is allowed in a\n // given position.\n this.context = this.initialContext();\n this.exprAllowed = true;\n\n // Figure out if it's a module code.\n this.inModule = options.sourceType === \"module\";\n this.strict = this.inModule || this.strictDirective(this.pos);\n\n // Used to signify the start of a potential arrow function\n this.potentialArrowAt = -1;\n this.potentialArrowInForAwait = false;\n\n // Positions to delayed-check that yield/await does not exist in default parameters.\n this.yieldPos = this.awaitPos = this.awaitIdentPos = 0;\n // Labels in scope.\n this.labels = [];\n // Thus-far undefined exports.\n this.undefinedExports = Object.create(null);\n\n // If enabled, skip leading hashbang line.\n if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === \"#!\")\n { this.skipLineComment(2); }\n\n // Scope tracking for duplicate variable names (see scope.js)\n this.scopeStack = [];\n this.enterScope(\n this.options.sourceType === \"commonjs\"\n // In commonjs, the top-level scope behaves like a function scope\n ? SCOPE_FUNCTION\n : SCOPE_TOP\n );\n\n // For RegExp validation\n this.regexpState = null;\n\n // The stack of private names.\n // Each element has two properties: 'declared' and 'used'.\n // When it exited from the outermost class definition, all used private names must be declared.\n this.privateNameStack = [];\n };\n\n var prototypeAccessors = { inFunction: { configurable: true },inGenerator: { configurable: true },inAsync: { configurable: true },canAwait: { configurable: true },allowReturn: { configurable: true },allowSuper: { configurable: true },allowDirectSuper: { configurable: true },treatFunctionsAsVar: { configurable: true },allowNewDotTarget: { configurable: true },allowUsing: { configurable: true },inClassStaticBlock: { configurable: true } };\n\n Parser.prototype.parse = function parse () {\n var node = this.options.program || this.startNode();\n this.nextToken();\n return this.parseTopLevel(node)\n };\n\n prototypeAccessors.inFunction.get = function () { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 };\n\n prototypeAccessors.inGenerator.get = function () { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 };\n\n prototypeAccessors.inAsync.get = function () { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 };\n\n prototypeAccessors.canAwait.get = function () {\n for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n var ref = this.scopeStack[i];\n var flags = ref.flags;\n if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) { return false }\n if (flags & SCOPE_FUNCTION) { return (flags & SCOPE_ASYNC) > 0 }\n }\n return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction\n };\n\n prototypeAccessors.allowReturn.get = function () {\n if (this.inFunction) { return true }\n if (this.options.allowReturnOutsideFunction && this.currentVarScope().flags & SCOPE_TOP) { return true }\n return false\n };\n\n prototypeAccessors.allowSuper.get = function () {\n var ref = this.currentThisScope();\n var flags = ref.flags;\n return (flags & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod\n };\n\n prototypeAccessors.allowDirectSuper.get = function () { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 };\n\n prototypeAccessors.treatFunctionsAsVar.get = function () { return this.treatFunctionsAsVarInScope(this.currentScope()) };\n\n prototypeAccessors.allowNewDotTarget.get = function () {\n for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n var ref = this.scopeStack[i];\n var flags = ref.flags;\n if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) ||\n ((flags & SCOPE_FUNCTION) && !(flags & SCOPE_ARROW))) { return true }\n }\n return false\n };\n\n prototypeAccessors.allowUsing.get = function () {\n var ref = this.currentScope();\n var flags = ref.flags;\n if (flags & SCOPE_SWITCH) { return false }\n if (!this.inModule && flags & SCOPE_TOP) { return false }\n return true\n };\n\n prototypeAccessors.inClassStaticBlock.get = function () {\n return (this.currentVarScope().flags & SCOPE_CLASS_STATIC_BLOCK) > 0\n };\n\n Parser.extend = function extend () {\n var plugins = [], len = arguments.length;\n while ( len-- ) plugins[ len ] = arguments[ len ];\n\n var cls = this;\n for (var i = 0; i < plugins.length; i++) { cls = plugins[i](cls); }\n return cls\n };\n\n Parser.parse = function parse (input, options) {\n return new this(options, input).parse()\n };\n\n Parser.parseExpressionAt = function parseExpressionAt (input, pos, options) {\n var parser = new this(options, input, pos);\n parser.nextToken();\n return parser.parseExpression()\n };\n\n Parser.tokenizer = function tokenizer (input, options) {\n return new this(options, input)\n };\n\n Object.defineProperties( Parser.prototype, prototypeAccessors );\n\n var pp$9 = Parser.prototype;\n\n // ## Parser utilities\n\n var literal = /^(?:'((?:\\\\[^]|[^'\\\\])*?)'|\"((?:\\\\[^]|[^\"\\\\])*?)\")/;\n pp$9.strictDirective = function(start) {\n if (this.options.ecmaVersion < 5) { return false }\n for (;;) {\n // Try to find string literal.\n skipWhiteSpace.lastIndex = start;\n start += skipWhiteSpace.exec(this.input)[0].length;\n var match = literal.exec(this.input.slice(start));\n if (!match) { return false }\n if ((match[1] || match[2]) === \"use strict\") {\n skipWhiteSpace.lastIndex = start + match[0].length;\n var spaceAfter = skipWhiteSpace.exec(this.input), end = spaceAfter.index + spaceAfter[0].length;\n var next = this.input.charAt(end);\n return next === \";\" || next === \"}\" ||\n (lineBreak.test(spaceAfter[0]) &&\n !(/[(`.[+\\-/*%<>=,?^&]/.test(next) || next === \"!\" && this.input.charAt(end + 1) === \"=\"))\n }\n start += match[0].length;\n\n // Skip semicolon, if any.\n skipWhiteSpace.lastIndex = start;\n start += skipWhiteSpace.exec(this.input)[0].length;\n if (this.input[start] === \";\")\n { start++; }\n }\n };\n\n // Predicate that tests whether the next token is of the given\n // type, and if yes, consumes it as a side effect.\n\n pp$9.eat = function(type) {\n if (this.type === type) {\n this.next();\n return true\n } else {\n return false\n }\n };\n\n // Tests whether parsed token is a contextual keyword.\n\n pp$9.isContextual = function(name) {\n return this.type === types$1.name && this.value === name && !this.containsEsc\n };\n\n // Consumes contextual keyword if possible.\n\n pp$9.eatContextual = function(name) {\n if (!this.isContextual(name)) { return false }\n this.next();\n return true\n };\n\n // Asserts that following token is given contextual keyword.\n\n pp$9.expectContextual = function(name) {\n if (!this.eatContextual(name)) { this.unexpected(); }\n };\n\n // Test whether a semicolon can be inserted at the current position.\n\n pp$9.canInsertSemicolon = function() {\n return this.type === types$1.eof ||\n this.type === types$1.braceR ||\n lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n };\n\n pp$9.insertSemicolon = function() {\n if (this.canInsertSemicolon()) {\n if (this.options.onInsertedSemicolon)\n { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }\n return true\n }\n };\n\n // Consume a semicolon, or, failing that, see if we are allowed to\n // pretend that there is a semicolon at this position.\n\n pp$9.semicolon = function() {\n if (!this.eat(types$1.semi) && !this.insertSemicolon()) { this.unexpected(); }\n };\n\n pp$9.afterTrailingComma = function(tokType, notNext) {\n if (this.type === tokType) {\n if (this.options.onTrailingComma)\n { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }\n if (!notNext)\n { this.next(); }\n return true\n }\n };\n\n // Expect a token of a given type. If found, consume it, otherwise,\n // raise an unexpected token error.\n\n pp$9.expect = function(type) {\n this.eat(type) || this.unexpected();\n };\n\n // Raise an unexpected token error.\n\n pp$9.unexpected = function(pos) {\n this.raise(pos != null ? pos : this.start, \"Unexpected token\");\n };\n\n var DestructuringErrors = function DestructuringErrors() {\n this.shorthandAssign =\n this.trailingComma =\n this.parenthesizedAssign =\n this.parenthesizedBind =\n this.doubleProto =\n -1;\n };\n\n pp$9.checkPatternErrors = function(refDestructuringErrors, isAssign) {\n if (!refDestructuringErrors) { return }\n if (refDestructuringErrors.trailingComma > -1)\n { this.raiseRecoverable(refDestructuringErrors.trailingComma, \"Comma is not permitted after the rest element\"); }\n var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;\n if (parens > -1) { this.raiseRecoverable(parens, isAssign ? \"Assigning to rvalue\" : \"Parenthesized pattern\"); }\n };\n\n pp$9.checkExpressionErrors = function(refDestructuringErrors, andThrow) {\n if (!refDestructuringErrors) { return false }\n var shorthandAssign = refDestructuringErrors.shorthandAssign;\n var doubleProto = refDestructuringErrors.doubleProto;\n if (!andThrow) { return shorthandAssign >= 0 || doubleProto >= 0 }\n if (shorthandAssign >= 0)\n { this.raise(shorthandAssign, \"Shorthand property assignments are valid only in destructuring patterns\"); }\n if (doubleProto >= 0)\n { this.raiseRecoverable(doubleProto, \"Redefinition of __proto__ property\"); }\n };\n\n pp$9.checkYieldAwaitInDefaultParams = function() {\n if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))\n { this.raise(this.yieldPos, \"Yield expression cannot be a default value\"); }\n if (this.awaitPos)\n { this.raise(this.awaitPos, \"Await expression cannot be a default value\"); }\n };\n\n pp$9.isSimpleAssignTarget = function(expr) {\n if (expr.type === \"ParenthesizedExpression\")\n { return this.isSimpleAssignTarget(expr.expression) }\n return expr.type === \"Identifier\" || expr.type === \"MemberExpression\"\n };\n\n var pp$8 = Parser.prototype;\n\n // ### Statement parsing\n\n // Parse a program. Initializes the parser, reads any number of\n // statements, and wraps them in a Program node. Optionally takes a\n // `program` argument. If present, the statements will be appended\n // to its body instead of creating a new node.\n\n pp$8.parseTopLevel = function(node) {\n var exports = Object.create(null);\n if (!node.body) { node.body = []; }\n while (this.type !== types$1.eof) {\n var stmt = this.parseStatement(null, true, exports);\n node.body.push(stmt);\n }\n if (this.inModule)\n { for (var i = 0, list = Object.keys(this.undefinedExports); i < list.length; i += 1)\n {\n var name = list[i];\n\n this.raiseRecoverable(this.undefinedExports[name].start, (\"Export '\" + name + \"' is not defined\"));\n } }\n this.adaptDirectivePrologue(node.body);\n this.next();\n node.sourceType = this.options.sourceType === \"commonjs\" ? \"script\" : this.options.sourceType;\n return this.finishNode(node, \"Program\")\n };\n\n var loopLabel = {kind: \"loop\"}, switchLabel = {kind: \"switch\"};\n\n pp$8.isLet = function(context) {\n if (this.options.ecmaVersion < 6 || !this.isContextual(\"let\")) { return false }\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, nextCh = this.fullCharCodeAt(next);\n // For ambiguous cases, determine if a LexicalDeclaration (or only a\n // Statement) is allowed here. If context is not empty then only a Statement\n // is allowed. However, `let [` is an explicit negative lookahead for\n // ExpressionStatement, so special-case it first.\n if (nextCh === 91 || nextCh === 92) { return true } // '[', '\\'\n if (context) { return false }\n\n if (nextCh === 123) { return true } // '{'\n if (isIdentifierStart(nextCh)) {\n var start = next;\n do { next += nextCh <= 0xffff ? 1 : 2; }\n while (isIdentifierChar(nextCh = this.fullCharCodeAt(next)))\n if (nextCh === 92) { return true }\n var ident = this.input.slice(start, next);\n if (!keywordRelationalOperator.test(ident)) { return true }\n }\n return false\n };\n\n // check 'async [no LineTerminator here] function'\n // - 'async /*foo*/ function' is OK.\n // - 'async /*\\n*/ function' is invalid.\n pp$8.isAsyncFunction = function() {\n if (this.options.ecmaVersion < 8 || !this.isContextual(\"async\"))\n { return false }\n\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, after;\n return !lineBreak.test(this.input.slice(this.pos, next)) &&\n this.input.slice(next, next + 8) === \"function\" &&\n (next + 8 === this.input.length ||\n !(isIdentifierChar(after = this.fullCharCodeAt(next + 8)) || after === 92 /* '\\' */))\n };\n\n pp$8.isUsingKeyword = function(isAwaitUsing, isFor) {\n if (this.options.ecmaVersion < 17 || !this.isContextual(isAwaitUsing ? \"await\" : \"using\"))\n { return false }\n\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length;\n\n if (lineBreak.test(this.input.slice(this.pos, next))) { return false }\n\n if (isAwaitUsing) {\n var usingEndPos = next + 5 /* using */, after;\n if (this.input.slice(next, usingEndPos) !== \"using\" ||\n usingEndPos === this.input.length ||\n isIdentifierChar(after = this.fullCharCodeAt(usingEndPos)) ||\n after === 92 /* '\\' */\n ) { return false }\n\n skipWhiteSpace.lastIndex = usingEndPos;\n var skipAfterUsing = skipWhiteSpace.exec(this.input);\n next = usingEndPos + skipAfterUsing[0].length;\n if (skipAfterUsing && lineBreak.test(this.input.slice(usingEndPos, next))) { return false }\n }\n\n var ch = this.fullCharCodeAt(next);\n if (!isIdentifierStart(ch) && ch !== 92 /* '\\' */) { return false }\n var idStart = next;\n do { next += ch <= 0xffff ? 1 : 2; }\n while (isIdentifierChar(ch = this.fullCharCodeAt(next)))\n if (ch === 92) { return true }\n var id = this.input.slice(idStart, next);\n if (keywordRelationalOperator.test(id) || isFor && id === \"of\") { return false }\n return true\n };\n\n pp$8.isAwaitUsing = function(isFor) {\n return this.isUsingKeyword(true, isFor)\n };\n\n pp$8.isUsing = function(isFor) {\n return this.isUsingKeyword(false, isFor)\n };\n\n // Parse a single statement.\n //\n // If expecting a statement and finding a slash operator, parse a\n // regular expression literal. This is to handle cases like\n // `if (foo) /blah/.exec(foo)`, where looking at the previous token\n // does not help.\n\n pp$8.parseStatement = function(context, topLevel, exports) {\n var starttype = this.type, node = this.startNode(), kind;\n\n if (this.isLet(context)) {\n starttype = types$1._var;\n kind = \"let\";\n }\n\n // Most types of statements are recognized by the keyword they\n // start with. Many are trivial to parse, some require a bit of\n // complexity.\n\n switch (starttype) {\n case types$1._break: case types$1._continue: return this.parseBreakContinueStatement(node, starttype.keyword)\n case types$1._debugger: return this.parseDebuggerStatement(node)\n case types$1._do: return this.parseDoStatement(node)\n case types$1._for: return this.parseForStatement(node)\n case types$1._function:\n // Function as sole body of either an if statement or a labeled statement\n // works, but not when it is part of a labeled statement that is the sole\n // body of an if statement.\n if ((context && (this.strict || context !== \"if\" && context !== \"label\")) && this.options.ecmaVersion >= 6) { this.unexpected(); }\n return this.parseFunctionStatement(node, false, !context)\n case types$1._class:\n if (context) { this.unexpected(); }\n return this.parseClass(node, true)\n case types$1._if: return this.parseIfStatement(node)\n case types$1._return: return this.parseReturnStatement(node)\n case types$1._switch: return this.parseSwitchStatement(node)\n case types$1._throw: return this.parseThrowStatement(node)\n case types$1._try: return this.parseTryStatement(node)\n case types$1._const: case types$1._var:\n kind = kind || this.value;\n if (context && kind !== \"var\") { this.unexpected(); }\n return this.parseVarStatement(node, kind)\n case types$1._while: return this.parseWhileStatement(node)\n case types$1._with: return this.parseWithStatement(node)\n case types$1.braceL: return this.parseBlock(true, node)\n case types$1.semi: return this.parseEmptyStatement(node)\n case types$1._export:\n case types$1._import:\n if (this.options.ecmaVersion > 10 && starttype === types$1._import) {\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);\n if (nextCh === 40 || nextCh === 46) // '(' or '.'\n { return this.parseExpressionStatement(node, this.parseExpression()) }\n }\n\n if (!this.options.allowImportExportEverywhere) {\n if (!topLevel)\n { this.raise(this.start, \"'import' and 'export' may only appear at the top level\"); }\n if (!this.inModule)\n { this.raise(this.start, \"'import' and 'export' may appear only with 'sourceType: module'\"); }\n }\n return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports)\n\n // If the statement does not start with a statement keyword or a\n // brace, it's an ExpressionStatement or LabeledStatement. We\n // simply start parsing an expression, and afterwards, if the\n // next token is a colon and the expression was a simple\n // Identifier node, we switch to interpreting it as a label.\n default:\n if (this.isAsyncFunction()) {\n if (context) { this.unexpected(); }\n this.next();\n return this.parseFunctionStatement(node, true, !context)\n }\n\n var usingKind = this.isAwaitUsing(false) ? \"await using\" : this.isUsing(false) ? \"using\" : null;\n if (usingKind) {\n if (!this.allowUsing) {\n this.raise(this.start, \"Using declaration cannot appear in the top level when source type is `script` or in the bare case statement\");\n }\n if (usingKind === \"await using\") {\n if (!this.canAwait) {\n this.raise(this.start, \"Await using cannot appear outside of async function\");\n }\n this.next();\n }\n this.next();\n this.parseVar(node, false, usingKind);\n this.semicolon();\n return this.finishNode(node, \"VariableDeclaration\")\n }\n\n var maybeName = this.value, expr = this.parseExpression();\n if (starttype === types$1.name && expr.type === \"Identifier\" && this.eat(types$1.colon))\n { return this.parseLabeledStatement(node, maybeName, expr, context) }\n else { return this.parseExpressionStatement(node, expr) }\n }\n };\n\n pp$8.parseBreakContinueStatement = function(node, keyword) {\n var isBreak = keyword === \"break\";\n this.next();\n if (this.eat(types$1.semi) || this.insertSemicolon()) { node.label = null; }\n else if (this.type !== types$1.name) { this.unexpected(); }\n else {\n node.label = this.parseIdent();\n this.semicolon();\n }\n\n // Verify that there is an actual destination to break or\n // continue to.\n var i = 0;\n for (; i < this.labels.length; ++i) {\n var lab = this.labels[i];\n if (node.label == null || lab.name === node.label.name) {\n if (lab.kind != null && (isBreak || lab.kind === \"loop\")) { break }\n if (node.label && isBreak) { break }\n }\n }\n if (i === this.labels.length) { this.raise(node.start, \"Unsyntactic \" + keyword); }\n return this.finishNode(node, isBreak ? \"BreakStatement\" : \"ContinueStatement\")\n };\n\n pp$8.parseDebuggerStatement = function(node) {\n this.next();\n this.semicolon();\n return this.finishNode(node, \"DebuggerStatement\")\n };\n\n pp$8.parseDoStatement = function(node) {\n this.next();\n this.labels.push(loopLabel);\n node.body = this.parseStatement(\"do\");\n this.labels.pop();\n this.expect(types$1._while);\n node.test = this.parseParenExpression();\n if (this.options.ecmaVersion >= 6)\n { this.eat(types$1.semi); }\n else\n { this.semicolon(); }\n return this.finishNode(node, \"DoWhileStatement\")\n };\n\n // Disambiguating between a `for` and a `for`/`in` or `for`/`of`\n // loop is non-trivial. Basically, we have to parse the init `var`\n // statement or expression, disallowing the `in` operator (see\n // the second parameter to `parseExpression`), and then check\n // whether the next token is `in` or `of`. When there is no init\n // part (semicolon immediately after the opening parenthesis), it\n // is a regular `for` loop.\n\n pp$8.parseForStatement = function(node) {\n this.next();\n var awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual(\"await\")) ? this.lastTokStart : -1;\n this.labels.push(loopLabel);\n this.enterScope(0);\n this.expect(types$1.parenL);\n if (this.type === types$1.semi) {\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, null)\n }\n var isLet = this.isLet();\n if (this.type === types$1._var || this.type === types$1._const || isLet) {\n var init$1 = this.startNode(), kind = isLet ? \"let\" : this.value;\n this.next();\n this.parseVar(init$1, true, kind);\n this.finishNode(init$1, \"VariableDeclaration\");\n return this.parseForAfterInit(node, init$1, awaitAt)\n }\n var startsWithLet = this.isContextual(\"let\"), isForOf = false;\n\n var usingKind = this.isUsing(true) ? \"using\" : this.isAwaitUsing(true) ? \"await using\" : null;\n if (usingKind) {\n var init$2 = this.startNode();\n this.next();\n if (usingKind === \"await using\") {\n if (!this.canAwait) {\n this.raise(this.start, \"Await using cannot appear outside of async function\");\n }\n this.next();\n }\n this.parseVar(init$2, true, usingKind);\n this.finishNode(init$2, \"VariableDeclaration\");\n return this.parseForAfterInit(node, init$2, awaitAt)\n }\n var containsEsc = this.containsEsc;\n var refDestructuringErrors = new DestructuringErrors;\n var initPos = this.start;\n var init = awaitAt > -1\n ? this.parseExprSubscripts(refDestructuringErrors, \"await\")\n : this.parseExpression(true, refDestructuringErrors);\n if (this.type === types$1._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) {\n if (awaitAt > -1) { // implies `ecmaVersion >= 9` (see declaration of awaitAt)\n if (this.type === types$1._in) { this.unexpected(awaitAt); }\n node.await = true;\n } else if (isForOf && this.options.ecmaVersion >= 8) {\n if (init.start === initPos && !containsEsc && init.type === \"Identifier\" && init.name === \"async\") { this.unexpected(); }\n else if (this.options.ecmaVersion >= 9) { node.await = false; }\n }\n if (startsWithLet && isForOf) { this.raise(init.start, \"The left-hand side of a for-of loop may not start with 'let'.\"); }\n this.toAssignable(init, false, refDestructuringErrors);\n this.checkLValPattern(init);\n return this.parseForIn(node, init)\n } else {\n this.checkExpressionErrors(refDestructuringErrors, true);\n }\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, init)\n };\n\n // Helper method to parse for loop after variable initialization\n pp$8.parseForAfterInit = function(node, init, awaitAt) {\n if ((this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) && init.declarations.length === 1) {\n if (this.options.ecmaVersion >= 9) {\n if (this.type === types$1._in) {\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n } else { node.await = awaitAt > -1; }\n }\n return this.parseForIn(node, init)\n }\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, init)\n };\n\n pp$8.parseFunctionStatement = function(node, isAsync, declarationPosition) {\n this.next();\n return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)\n };\n\n pp$8.parseIfStatement = function(node) {\n this.next();\n node.test = this.parseParenExpression();\n // allow function declarations in branches, but only in non-strict mode\n node.consequent = this.parseStatement(\"if\");\n node.alternate = this.eat(types$1._else) ? this.parseStatement(\"if\") : null;\n return this.finishNode(node, \"IfStatement\")\n };\n\n pp$8.parseReturnStatement = function(node) {\n if (!this.allowReturn)\n { this.raise(this.start, \"'return' outside of function\"); }\n this.next();\n\n // In `return` (and `break`/`continue`), the keywords with\n // optional arguments, we eagerly look for a semicolon or the\n // possibility to insert one.\n\n if (this.eat(types$1.semi) || this.insertSemicolon()) { node.argument = null; }\n else { node.argument = this.parseExpression(); this.semicolon(); }\n return this.finishNode(node, \"ReturnStatement\")\n };\n\n pp$8.parseSwitchStatement = function(node) {\n this.next();\n node.discriminant = this.parseParenExpression();\n node.cases = [];\n this.expect(types$1.braceL);\n this.labels.push(switchLabel);\n this.enterScope(SCOPE_SWITCH);\n\n // Statements under must be grouped (by label) in SwitchCase\n // nodes. `cur` is used to keep the node that we are currently\n // adding statements to.\n\n var cur;\n for (var sawDefault = false; this.type !== types$1.braceR;) {\n if (this.type === types$1._case || this.type === types$1._default) {\n var isCase = this.type === types$1._case;\n if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n node.cases.push(cur = this.startNode());\n cur.consequent = [];\n this.next();\n if (isCase) {\n cur.test = this.parseExpression();\n } else {\n if (sawDefault) { this.raiseRecoverable(this.lastTokStart, \"Multiple default clauses\"); }\n sawDefault = true;\n cur.test = null;\n }\n this.expect(types$1.colon);\n } else {\n if (!cur) { this.unexpected(); }\n cur.consequent.push(this.parseStatement(null));\n }\n }\n this.exitScope();\n if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n this.next(); // Closing brace\n this.labels.pop();\n return this.finishNode(node, \"SwitchStatement\")\n };\n\n pp$8.parseThrowStatement = function(node) {\n this.next();\n if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))\n { this.raise(this.lastTokEnd, \"Illegal newline after throw\"); }\n node.argument = this.parseExpression();\n this.semicolon();\n return this.finishNode(node, \"ThrowStatement\")\n };\n\n // Reused empty array added for node fields that are always empty.\n\n var empty$1 = [];\n\n pp$8.parseCatchClauseParam = function() {\n var param = this.parseBindingAtom();\n var simple = param.type === \"Identifier\";\n this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);\n this.checkLValPattern(param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);\n this.expect(types$1.parenR);\n\n return param\n };\n\n pp$8.parseTryStatement = function(node) {\n this.next();\n node.block = this.parseBlock();\n node.handler = null;\n if (this.type === types$1._catch) {\n var clause = this.startNode();\n this.next();\n if (this.eat(types$1.parenL)) {\n clause.param = this.parseCatchClauseParam();\n } else {\n if (this.options.ecmaVersion < 10) { this.unexpected(); }\n clause.param = null;\n this.enterScope(0);\n }\n clause.body = this.parseBlock(false);\n this.exitScope();\n node.handler = this.finishNode(clause, \"CatchClause\");\n }\n node.finalizer = this.eat(types$1._finally) ? this.parseBlock() : null;\n if (!node.handler && !node.finalizer)\n { this.raise(node.start, \"Missing catch or finally clause\"); }\n return this.finishNode(node, \"TryStatement\")\n };\n\n pp$8.parseVarStatement = function(node, kind, allowMissingInitializer) {\n this.next();\n this.parseVar(node, false, kind, allowMissingInitializer);\n this.semicolon();\n return this.finishNode(node, \"VariableDeclaration\")\n };\n\n pp$8.parseWhileStatement = function(node) {\n this.next();\n node.test = this.parseParenExpression();\n this.labels.push(loopLabel);\n node.body = this.parseStatement(\"while\");\n this.labels.pop();\n return this.finishNode(node, \"WhileStatement\")\n };\n\n pp$8.parseWithStatement = function(node) {\n if (this.strict) { this.raise(this.start, \"'with' in strict mode\"); }\n this.next();\n node.object = this.parseParenExpression();\n node.body = this.parseStatement(\"with\");\n return this.finishNode(node, \"WithStatement\")\n };\n\n pp$8.parseEmptyStatement = function(node) {\n this.next();\n return this.finishNode(node, \"EmptyStatement\")\n };\n\n pp$8.parseLabeledStatement = function(node, maybeName, expr, context) {\n for (var i$1 = 0, list = this.labels; i$1 < list.length; i$1 += 1)\n {\n var label = list[i$1];\n\n if (label.name === maybeName)\n { this.raise(expr.start, \"Label '\" + maybeName + \"' is already declared\");\n } }\n var kind = this.type.isLoop ? \"loop\" : this.type === types$1._switch ? \"switch\" : null;\n for (var i = this.labels.length - 1; i >= 0; i--) {\n var label$1 = this.labels[i];\n if (label$1.statementStart === node.start) {\n // Update information about previous labels on this node\n label$1.statementStart = this.start;\n label$1.kind = kind;\n } else { break }\n }\n this.labels.push({name: maybeName, kind: kind, statementStart: this.start});\n node.body = this.parseStatement(context ? context.indexOf(\"label\") === -1 ? context + \"label\" : context : \"label\");\n this.labels.pop();\n node.label = expr;\n return this.finishNode(node, \"LabeledStatement\")\n };\n\n pp$8.parseExpressionStatement = function(node, expr) {\n node.expression = expr;\n this.semicolon();\n return this.finishNode(node, \"ExpressionStatement\")\n };\n\n // Parse a semicolon-enclosed block of statements, handling `\"use\n // strict\"` declarations when `allowStrict` is true (used for\n // function bodies).\n\n pp$8.parseBlock = function(createNewLexicalScope, node, exitStrict) {\n if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;\n if ( node === void 0 ) node = this.startNode();\n\n node.body = [];\n this.expect(types$1.braceL);\n if (createNewLexicalScope) { this.enterScope(0); }\n while (this.type !== types$1.braceR) {\n var stmt = this.parseStatement(null);\n node.body.push(stmt);\n }\n if (exitStrict) { this.strict = false; }\n this.next();\n if (createNewLexicalScope) { this.exitScope(); }\n return this.finishNode(node, \"BlockStatement\")\n };\n\n // Parse a regular `for` loop. The disambiguation code in\n // `parseStatement` will already have parsed the init statement or\n // expression.\n\n pp$8.parseFor = function(node, init) {\n node.init = init;\n this.expect(types$1.semi);\n node.test = this.type === types$1.semi ? null : this.parseExpression();\n this.expect(types$1.semi);\n node.update = this.type === types$1.parenR ? null : this.parseExpression();\n this.expect(types$1.parenR);\n node.body = this.parseStatement(\"for\");\n this.exitScope();\n this.labels.pop();\n return this.finishNode(node, \"ForStatement\")\n };\n\n // Parse a `for`/`in` and `for`/`of` loop, which are almost\n // same from parser's perspective.\n\n pp$8.parseForIn = function(node, init) {\n var isForIn = this.type === types$1._in;\n this.next();\n\n if (\n init.type === \"VariableDeclaration\" &&\n init.declarations[0].init != null &&\n (\n !isForIn ||\n this.options.ecmaVersion < 8 ||\n this.strict ||\n init.kind !== \"var\" ||\n init.declarations[0].id.type !== \"Identifier\"\n )\n ) {\n this.raise(\n init.start,\n ((isForIn ? \"for-in\" : \"for-of\") + \" loop variable declaration may not have an initializer\")\n );\n }\n node.left = init;\n node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign();\n this.expect(types$1.parenR);\n node.body = this.parseStatement(\"for\");\n this.exitScope();\n this.labels.pop();\n return this.finishNode(node, isForIn ? \"ForInStatement\" : \"ForOfStatement\")\n };\n\n // Parse a list of variable declarations.\n\n pp$8.parseVar = function(node, isFor, kind, allowMissingInitializer) {\n node.declarations = [];\n node.kind = kind;\n for (;;) {\n var decl = this.startNode();\n this.parseVarId(decl, kind);\n if (this.eat(types$1.eq)) {\n decl.init = this.parseMaybeAssign(isFor);\n } else if (!allowMissingInitializer && kind === \"const\" && !(this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\")))) {\n this.unexpected();\n } else if (!allowMissingInitializer && (kind === \"using\" || kind === \"await using\") && this.options.ecmaVersion >= 17 && this.type !== types$1._in && !this.isContextual(\"of\")) {\n this.raise(this.lastTokEnd, (\"Missing initializer in \" + kind + \" declaration\"));\n } else if (!allowMissingInitializer && decl.id.type !== \"Identifier\" && !(isFor && (this.type === types$1._in || this.isContextual(\"of\")))) {\n this.raise(this.lastTokEnd, \"Complex binding patterns require an initialization value\");\n } else {\n decl.init = null;\n }\n node.declarations.push(this.finishNode(decl, \"VariableDeclarator\"));\n if (!this.eat(types$1.comma)) { break }\n }\n return node\n };\n\n pp$8.parseVarId = function(decl, kind) {\n decl.id = kind === \"using\" || kind === \"await using\"\n ? this.parseIdent()\n : this.parseBindingAtom();\n\n this.checkLValPattern(decl.id, kind === \"var\" ? BIND_VAR : BIND_LEXICAL, false);\n };\n\n var FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4;\n\n // Parse a function declaration or literal (depending on the\n // `statement & FUNC_STATEMENT`).\n\n // Remove `allowExpressionBody` for 7.0.0, as it is only called with false\n pp$8.parseFunction = function(node, statement, allowExpressionBody, isAsync, forInit) {\n this.initFunction(node);\n if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {\n if (this.type === types$1.star && (statement & FUNC_HANGING_STATEMENT))\n { this.unexpected(); }\n node.generator = this.eat(types$1.star);\n }\n if (this.options.ecmaVersion >= 8)\n { node.async = !!isAsync; }\n\n if (statement & FUNC_STATEMENT) {\n node.id = (statement & FUNC_NULLABLE_ID) && this.type !== types$1.name ? null : this.parseIdent();\n if (node.id && !(statement & FUNC_HANGING_STATEMENT))\n // If it is a regular function declaration in sloppy mode, then it is\n // subject to Annex B semantics (BIND_FUNCTION). Otherwise, the binding\n // mode depends on properties of the current scope (see\n // treatFunctionsAsVar).\n { this.checkLValSimple(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION); }\n }\n\n var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n this.enterScope(functionFlags(node.async, node.generator));\n\n if (!(statement & FUNC_STATEMENT))\n { node.id = this.type === types$1.name ? this.parseIdent() : null; }\n\n this.parseFunctionParams(node);\n this.parseFunctionBody(node, allowExpressionBody, false, forInit);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, (statement & FUNC_STATEMENT) ? \"FunctionDeclaration\" : \"FunctionExpression\")\n };\n\n pp$8.parseFunctionParams = function(node) {\n this.expect(types$1.parenL);\n node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n this.checkYieldAwaitInDefaultParams();\n };\n\n // Parse a class declaration or literal (depending on the\n // `isStatement` parameter).\n\n pp$8.parseClass = function(node, isStatement) {\n this.next();\n\n // ecma-262 14.6 Class Definitions\n // A class definition is always strict mode code.\n var oldStrict = this.strict;\n this.strict = true;\n\n this.parseClassId(node, isStatement);\n this.parseClassSuper(node);\n var privateNameMap = this.enterClassBody();\n var classBody = this.startNode();\n var hadConstructor = false;\n classBody.body = [];\n this.expect(types$1.braceL);\n while (this.type !== types$1.braceR) {\n var element = this.parseClassElement(node.superClass !== null);\n if (element) {\n classBody.body.push(element);\n if (element.type === \"MethodDefinition\" && element.kind === \"constructor\") {\n if (hadConstructor) { this.raiseRecoverable(element.start, \"Duplicate constructor in the same class\"); }\n hadConstructor = true;\n } else if (element.key && element.key.type === \"PrivateIdentifier\" && isPrivateNameConflicted(privateNameMap, element)) {\n this.raiseRecoverable(element.key.start, (\"Identifier '#\" + (element.key.name) + \"' has already been declared\"));\n }\n }\n }\n this.strict = oldStrict;\n this.next();\n node.body = this.finishNode(classBody, \"ClassBody\");\n this.exitClassBody();\n return this.finishNode(node, isStatement ? \"ClassDeclaration\" : \"ClassExpression\")\n };\n\n pp$8.parseClassElement = function(constructorAllowsSuper) {\n if (this.eat(types$1.semi)) { return null }\n\n var ecmaVersion = this.options.ecmaVersion;\n var node = this.startNode();\n var keyName = \"\";\n var isGenerator = false;\n var isAsync = false;\n var kind = \"method\";\n var isStatic = false;\n\n if (this.eatContextual(\"static\")) {\n // Parse static init block\n if (ecmaVersion >= 13 && this.eat(types$1.braceL)) {\n this.parseClassStaticBlock(node);\n return node\n }\n if (this.isClassElementNameStart() || this.type === types$1.star) {\n isStatic = true;\n } else {\n keyName = \"static\";\n }\n }\n node.static = isStatic;\n if (!keyName && ecmaVersion >= 8 && this.eatContextual(\"async\")) {\n if ((this.isClassElementNameStart() || this.type === types$1.star) && !this.canInsertSemicolon()) {\n isAsync = true;\n } else {\n keyName = \"async\";\n }\n }\n if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {\n isGenerator = true;\n }\n if (!keyName && !isAsync && !isGenerator) {\n var lastValue = this.value;\n if (this.eatContextual(\"get\") || this.eatContextual(\"set\")) {\n if (this.isClassElementNameStart()) {\n kind = lastValue;\n } else {\n keyName = lastValue;\n }\n }\n }\n\n // Parse element name\n if (keyName) {\n // 'async', 'get', 'set', or 'static' were not a keyword contextually.\n // The last token is any of those. Make it the element name.\n node.computed = false;\n node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);\n node.key.name = keyName;\n this.finishNode(node.key, \"Identifier\");\n } else {\n this.parseClassElementName(node);\n }\n\n // Parse element value\n if (ecmaVersion < 13 || this.type === types$1.parenL || kind !== \"method\" || isGenerator || isAsync) {\n var isConstructor = !node.static && checkKeyName(node, \"constructor\");\n var allowsDirectSuper = isConstructor && constructorAllowsSuper;\n // Couldn't move this check into the 'parseClassMethod' method for backward compatibility.\n if (isConstructor && kind !== \"method\") { this.raise(node.key.start, \"Constructor can't have get/set modifier\"); }\n node.kind = isConstructor ? \"constructor\" : kind;\n this.parseClassMethod(node, isGenerator, isAsync, allowsDirectSuper);\n } else {\n this.parseClassField(node);\n }\n\n return node\n };\n\n pp$8.isClassElementNameStart = function() {\n return (\n this.type === types$1.name ||\n this.type === types$1.privateId ||\n this.type === types$1.num ||\n this.type === types$1.string ||\n this.type === types$1.bracketL ||\n this.type.keyword\n )\n };\n\n pp$8.parseClassElementName = function(element) {\n if (this.type === types$1.privateId) {\n if (this.value === \"constructor\") {\n this.raise(this.start, \"Classes can't have an element named '#constructor'\");\n }\n element.computed = false;\n element.key = this.parsePrivateIdent();\n } else {\n this.parsePropertyName(element);\n }\n };\n\n pp$8.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {\n // Check key and flags\n var key = method.key;\n if (method.kind === \"constructor\") {\n if (isGenerator) { this.raise(key.start, \"Constructor can't be a generator\"); }\n if (isAsync) { this.raise(key.start, \"Constructor can't be an async method\"); }\n } else if (method.static && checkKeyName(method, \"prototype\")) {\n this.raise(key.start, \"Classes may not have a static property named prototype\");\n }\n\n // Parse value\n var value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper);\n\n // Check value\n if (method.kind === \"get\" && value.params.length !== 0)\n { this.raiseRecoverable(value.start, \"getter should have no params\"); }\n if (method.kind === \"set\" && value.params.length !== 1)\n { this.raiseRecoverable(value.start, \"setter should have exactly one param\"); }\n if (method.kind === \"set\" && value.params[0].type === \"RestElement\")\n { this.raiseRecoverable(value.params[0].start, \"Setter cannot use rest params\"); }\n\n return this.finishNode(method, \"MethodDefinition\")\n };\n\n pp$8.parseClassField = function(field) {\n if (checkKeyName(field, \"constructor\")) {\n this.raise(field.key.start, \"Classes can't have a field named 'constructor'\");\n } else if (field.static && checkKeyName(field, \"prototype\")) {\n this.raise(field.key.start, \"Classes can't have a static field named 'prototype'\");\n }\n\n if (this.eat(types$1.eq)) {\n // To raise SyntaxError if 'arguments' exists in the initializer.\n this.enterScope(SCOPE_CLASS_FIELD_INIT | SCOPE_SUPER);\n field.value = this.parseMaybeAssign();\n this.exitScope();\n } else {\n field.value = null;\n }\n this.semicolon();\n\n return this.finishNode(field, \"PropertyDefinition\")\n };\n\n pp$8.parseClassStaticBlock = function(node) {\n node.body = [];\n\n var oldLabels = this.labels;\n this.labels = [];\n this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER);\n while (this.type !== types$1.braceR) {\n var stmt = this.parseStatement(null);\n node.body.push(stmt);\n }\n this.next();\n this.exitScope();\n this.labels = oldLabels;\n\n return this.finishNode(node, \"StaticBlock\")\n };\n\n pp$8.parseClassId = function(node, isStatement) {\n if (this.type === types$1.name) {\n node.id = this.parseIdent();\n if (isStatement)\n { this.checkLValSimple(node.id, BIND_LEXICAL, false); }\n } else {\n if (isStatement === true)\n { this.unexpected(); }\n node.id = null;\n }\n };\n\n pp$8.parseClassSuper = function(node) {\n node.superClass = this.eat(types$1._extends) ? this.parseExprSubscripts(null, false) : null;\n };\n\n pp$8.enterClassBody = function() {\n var element = {declared: Object.create(null), used: []};\n this.privateNameStack.push(element);\n return element.declared\n };\n\n pp$8.exitClassBody = function() {\n var ref = this.privateNameStack.pop();\n var declared = ref.declared;\n var used = ref.used;\n if (!this.options.checkPrivateFields) { return }\n var len = this.privateNameStack.length;\n var parent = len === 0 ? null : this.privateNameStack[len - 1];\n for (var i = 0; i < used.length; ++i) {\n var id = used[i];\n if (!hasOwn(declared, id.name)) {\n if (parent) {\n parent.used.push(id);\n } else {\n this.raiseRecoverable(id.start, (\"Private field '#\" + (id.name) + \"' must be declared in an enclosing class\"));\n }\n }\n }\n };\n\n function isPrivateNameConflicted(privateNameMap, element) {\n var name = element.key.name;\n var curr = privateNameMap[name];\n\n var next = \"true\";\n if (element.type === \"MethodDefinition\" && (element.kind === \"get\" || element.kind === \"set\")) {\n next = (element.static ? \"s\" : \"i\") + element.kind;\n }\n\n // `class { get #a(){}; static set #a(_){} }` is also conflict.\n if (\n curr === \"iget\" && next === \"iset\" ||\n curr === \"iset\" && next === \"iget\" ||\n curr === \"sget\" && next === \"sset\" ||\n curr === \"sset\" && next === \"sget\"\n ) {\n privateNameMap[name] = \"true\";\n return false\n } else if (!curr) {\n privateNameMap[name] = next;\n return false\n } else {\n return true\n }\n }\n\n function checkKeyName(node, name) {\n var computed = node.computed;\n var key = node.key;\n return !computed && (\n key.type === \"Identifier\" && key.name === name ||\n key.type === \"Literal\" && key.value === name\n )\n }\n\n // Parses module export declaration.\n\n pp$8.parseExportAllDeclaration = function(node, exports) {\n if (this.options.ecmaVersion >= 11) {\n if (this.eatContextual(\"as\")) {\n node.exported = this.parseModuleExportName();\n this.checkExport(exports, node.exported, this.lastTokStart);\n } else {\n node.exported = null;\n }\n }\n this.expectContextual(\"from\");\n if (this.type !== types$1.string) { this.unexpected(); }\n node.source = this.parseExprAtom();\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n this.semicolon();\n return this.finishNode(node, \"ExportAllDeclaration\")\n };\n\n pp$8.parseExport = function(node, exports) {\n this.next();\n // export * from '...'\n if (this.eat(types$1.star)) {\n return this.parseExportAllDeclaration(node, exports)\n }\n if (this.eat(types$1._default)) { // export default ...\n this.checkExport(exports, \"default\", this.lastTokStart);\n node.declaration = this.parseExportDefaultDeclaration();\n return this.finishNode(node, \"ExportDefaultDeclaration\")\n }\n // export var|const|let|function|class ...\n if (this.shouldParseExportStatement()) {\n node.declaration = this.parseExportDeclaration(node);\n if (node.declaration.type === \"VariableDeclaration\")\n { this.checkVariableExport(exports, node.declaration.declarations); }\n else\n { this.checkExport(exports, node.declaration.id, node.declaration.id.start); }\n node.specifiers = [];\n node.source = null;\n if (this.options.ecmaVersion >= 16)\n { node.attributes = []; }\n } else { // export { x, y as z } [from '...']\n node.declaration = null;\n node.specifiers = this.parseExportSpecifiers(exports);\n if (this.eatContextual(\"from\")) {\n if (this.type !== types$1.string) { this.unexpected(); }\n node.source = this.parseExprAtom();\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n } else {\n for (var i = 0, list = node.specifiers; i < list.length; i += 1) {\n // check for keywords used as local names\n var spec = list[i];\n\n this.checkUnreserved(spec.local);\n // check if export is defined\n this.checkLocalExport(spec.local);\n\n if (spec.local.type === \"Literal\") {\n this.raise(spec.local.start, \"A string literal cannot be used as an exported binding without `from`.\");\n }\n }\n\n node.source = null;\n if (this.options.ecmaVersion >= 16)\n { node.attributes = []; }\n }\n this.semicolon();\n }\n return this.finishNode(node, \"ExportNamedDeclaration\")\n };\n\n pp$8.parseExportDeclaration = function(node) {\n return this.parseStatement(null)\n };\n\n pp$8.parseExportDefaultDeclaration = function() {\n var isAsync;\n if (this.type === types$1._function || (isAsync = this.isAsyncFunction())) {\n var fNode = this.startNode();\n this.next();\n if (isAsync) { this.next(); }\n return this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync)\n } else if (this.type === types$1._class) {\n var cNode = this.startNode();\n return this.parseClass(cNode, \"nullableID\")\n } else {\n var declaration = this.parseMaybeAssign();\n this.semicolon();\n return declaration\n }\n };\n\n pp$8.checkExport = function(exports, name, pos) {\n if (!exports) { return }\n if (typeof name !== \"string\")\n { name = name.type === \"Identifier\" ? name.name : name.value; }\n if (hasOwn(exports, name))\n { this.raiseRecoverable(pos, \"Duplicate export '\" + name + \"'\"); }\n exports[name] = true;\n };\n\n pp$8.checkPatternExport = function(exports, pat) {\n var type = pat.type;\n if (type === \"Identifier\")\n { this.checkExport(exports, pat, pat.start); }\n else if (type === \"ObjectPattern\")\n { for (var i = 0, list = pat.properties; i < list.length; i += 1)\n {\n var prop = list[i];\n\n this.checkPatternExport(exports, prop);\n } }\n else if (type === \"ArrayPattern\")\n { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {\n var elt = list$1[i$1];\n\n if (elt) { this.checkPatternExport(exports, elt); }\n } }\n else if (type === \"Property\")\n { this.checkPatternExport(exports, pat.value); }\n else if (type === \"AssignmentPattern\")\n { this.checkPatternExport(exports, pat.left); }\n else if (type === \"RestElement\")\n { this.checkPatternExport(exports, pat.argument); }\n };\n\n pp$8.checkVariableExport = function(exports, decls) {\n if (!exports) { return }\n for (var i = 0, list = decls; i < list.length; i += 1)\n {\n var decl = list[i];\n\n this.checkPatternExport(exports, decl.id);\n }\n };\n\n pp$8.shouldParseExportStatement = function() {\n return this.type.keyword === \"var\" ||\n this.type.keyword === \"const\" ||\n this.type.keyword === \"class\" ||\n this.type.keyword === \"function\" ||\n this.isLet() ||\n this.isAsyncFunction()\n };\n\n // Parses a comma-separated list of module exports.\n\n pp$8.parseExportSpecifier = function(exports) {\n var node = this.startNode();\n node.local = this.parseModuleExportName();\n\n node.exported = this.eatContextual(\"as\") ? this.parseModuleExportName() : node.local;\n this.checkExport(\n exports,\n node.exported,\n node.exported.start\n );\n\n return this.finishNode(node, \"ExportSpecifier\")\n };\n\n pp$8.parseExportSpecifiers = function(exports) {\n var nodes = [], first = true;\n // export { x, y as z } [from '...']\n this.expect(types$1.braceL);\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n nodes.push(this.parseExportSpecifier(exports));\n }\n return nodes\n };\n\n // Parses import declaration.\n\n pp$8.parseImport = function(node) {\n this.next();\n\n // import '...'\n if (this.type === types$1.string) {\n node.specifiers = empty$1;\n node.source = this.parseExprAtom();\n } else {\n node.specifiers = this.parseImportSpecifiers();\n this.expectContextual(\"from\");\n node.source = this.type === types$1.string ? this.parseExprAtom() : this.unexpected();\n }\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n this.semicolon();\n return this.finishNode(node, \"ImportDeclaration\")\n };\n\n // Parses a comma-separated list of module imports.\n\n pp$8.parseImportSpecifier = function() {\n var node = this.startNode();\n node.imported = this.parseModuleExportName();\n\n if (this.eatContextual(\"as\")) {\n node.local = this.parseIdent();\n } else {\n this.checkUnreserved(node.imported);\n node.local = node.imported;\n }\n this.checkLValSimple(node.local, BIND_LEXICAL);\n\n return this.finishNode(node, \"ImportSpecifier\")\n };\n\n pp$8.parseImportDefaultSpecifier = function() {\n // import defaultObj, { x, y as z } from '...'\n var node = this.startNode();\n node.local = this.parseIdent();\n this.checkLValSimple(node.local, BIND_LEXICAL);\n return this.finishNode(node, \"ImportDefaultSpecifier\")\n };\n\n pp$8.parseImportNamespaceSpecifier = function() {\n var node = this.startNode();\n this.next();\n this.expectContextual(\"as\");\n node.local = this.parseIdent();\n this.checkLValSimple(node.local, BIND_LEXICAL);\n return this.finishNode(node, \"ImportNamespaceSpecifier\")\n };\n\n pp$8.parseImportSpecifiers = function() {\n var nodes = [], first = true;\n if (this.type === types$1.name) {\n nodes.push(this.parseImportDefaultSpecifier());\n if (!this.eat(types$1.comma)) { return nodes }\n }\n if (this.type === types$1.star) {\n nodes.push(this.parseImportNamespaceSpecifier());\n return nodes\n }\n this.expect(types$1.braceL);\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n nodes.push(this.parseImportSpecifier());\n }\n return nodes\n };\n\n pp$8.parseWithClause = function() {\n var nodes = [];\n if (!this.eat(types$1._with)) {\n return nodes\n }\n this.expect(types$1.braceL);\n var attributeKeys = {};\n var first = true;\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n var attr = this.parseImportAttribute();\n var keyName = attr.key.type === \"Identifier\" ? attr.key.name : attr.key.value;\n if (hasOwn(attributeKeys, keyName))\n { this.raiseRecoverable(attr.key.start, \"Duplicate attribute key '\" + keyName + \"'\"); }\n attributeKeys[keyName] = true;\n nodes.push(attr);\n }\n return nodes\n };\n\n pp$8.parseImportAttribute = function() {\n var node = this.startNode();\n node.key = this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\");\n this.expect(types$1.colon);\n if (this.type !== types$1.string) {\n this.unexpected();\n }\n node.value = this.parseExprAtom();\n return this.finishNode(node, \"ImportAttribute\")\n };\n\n pp$8.parseModuleExportName = function() {\n if (this.options.ecmaVersion >= 13 && this.type === types$1.string) {\n var stringLiteral = this.parseLiteral(this.value);\n if (loneSurrogate.test(stringLiteral.value)) {\n this.raise(stringLiteral.start, \"An export name cannot include a lone surrogate.\");\n }\n return stringLiteral\n }\n return this.parseIdent(true)\n };\n\n // Set `ExpressionStatement#directive` property for directive prologues.\n pp$8.adaptDirectivePrologue = function(statements) {\n for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {\n statements[i].directive = statements[i].expression.raw.slice(1, -1);\n }\n };\n pp$8.isDirectiveCandidate = function(statement) {\n return (\n this.options.ecmaVersion >= 5 &&\n statement.type === \"ExpressionStatement\" &&\n statement.expression.type === \"Literal\" &&\n typeof statement.expression.value === \"string\" &&\n // Reject parenthesized strings.\n (this.input[statement.start] === \"\\\"\" || this.input[statement.start] === \"'\")\n )\n };\n\n var pp$7 = Parser.prototype;\n\n // Convert existing expression atom to assignable pattern\n // if possible.\n\n pp$7.toAssignable = function(node, isBinding, refDestructuringErrors) {\n if (this.options.ecmaVersion >= 6 && node) {\n switch (node.type) {\n case \"Identifier\":\n if (this.inAsync && node.name === \"await\")\n { this.raise(node.start, \"Cannot use 'await' as identifier inside an async function\"); }\n break\n\n case \"ObjectPattern\":\n case \"ArrayPattern\":\n case \"AssignmentPattern\":\n case \"RestElement\":\n break\n\n case \"ObjectExpression\":\n node.type = \"ObjectPattern\";\n if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n for (var i = 0, list = node.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n this.toAssignable(prop, isBinding);\n // Early error:\n // AssignmentRestProperty[Yield, Await] :\n // `...` DestructuringAssignmentTarget[Yield, Await]\n //\n // It is a Syntax Error if |DestructuringAssignmentTarget| is an |ArrayLiteral| or an |ObjectLiteral|.\n if (\n prop.type === \"RestElement\" &&\n (prop.argument.type === \"ArrayPattern\" || prop.argument.type === \"ObjectPattern\")\n ) {\n this.raise(prop.argument.start, \"Unexpected token\");\n }\n }\n break\n\n case \"Property\":\n // AssignmentProperty has type === \"Property\"\n if (node.kind !== \"init\") { this.raise(node.key.start, \"Object pattern can't contain getter or setter\"); }\n this.toAssignable(node.value, isBinding);\n break\n\n case \"ArrayExpression\":\n node.type = \"ArrayPattern\";\n if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n this.toAssignableList(node.elements, isBinding);\n break\n\n case \"SpreadElement\":\n node.type = \"RestElement\";\n this.toAssignable(node.argument, isBinding);\n if (node.argument.type === \"AssignmentPattern\")\n { this.raise(node.argument.start, \"Rest elements cannot have a default value\"); }\n break\n\n case \"AssignmentExpression\":\n if (node.operator !== \"=\") { this.raise(node.left.end, \"Only '=' operator can be used for specifying default value.\"); }\n node.type = \"AssignmentPattern\";\n delete node.operator;\n this.toAssignable(node.left, isBinding);\n break\n\n case \"ParenthesizedExpression\":\n this.toAssignable(node.expression, isBinding, refDestructuringErrors);\n break\n\n case \"ChainExpression\":\n this.raiseRecoverable(node.start, \"Optional chaining cannot appear in left-hand side\");\n break\n\n case \"MemberExpression\":\n if (!isBinding) { break }\n\n default:\n this.raise(node.start, \"Assigning to rvalue\");\n }\n } else if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n return node\n };\n\n // Convert list of expression atoms to binding list.\n\n pp$7.toAssignableList = function(exprList, isBinding) {\n var end = exprList.length;\n for (var i = 0; i < end; i++) {\n var elt = exprList[i];\n if (elt) { this.toAssignable(elt, isBinding); }\n }\n if (end) {\n var last = exprList[end - 1];\n if (this.options.ecmaVersion === 6 && isBinding && last && last.type === \"RestElement\" && last.argument.type !== \"Identifier\")\n { this.unexpected(last.argument.start); }\n }\n return exprList\n };\n\n // Parses spread element.\n\n pp$7.parseSpread = function(refDestructuringErrors) {\n var node = this.startNode();\n this.next();\n node.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n return this.finishNode(node, \"SpreadElement\")\n };\n\n pp$7.parseRestBinding = function() {\n var node = this.startNode();\n this.next();\n\n // RestElement inside of a function parameter must be an identifier\n if (this.options.ecmaVersion === 6 && this.type !== types$1.name)\n { this.unexpected(); }\n\n node.argument = this.parseBindingAtom();\n\n return this.finishNode(node, \"RestElement\")\n };\n\n // Parses lvalue (assignable) atom.\n\n pp$7.parseBindingAtom = function() {\n if (this.options.ecmaVersion >= 6) {\n switch (this.type) {\n case types$1.bracketL:\n var node = this.startNode();\n this.next();\n node.elements = this.parseBindingList(types$1.bracketR, true, true);\n return this.finishNode(node, \"ArrayPattern\")\n\n case types$1.braceL:\n return this.parseObj(true)\n }\n }\n return this.parseIdent()\n };\n\n pp$7.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowModifiers) {\n var elts = [], first = true;\n while (!this.eat(close)) {\n if (first) { first = false; }\n else { this.expect(types$1.comma); }\n if (allowEmpty && this.type === types$1.comma) {\n elts.push(null);\n } else if (allowTrailingComma && this.afterTrailingComma(close)) {\n break\n } else if (this.type === types$1.ellipsis) {\n var rest = this.parseRestBinding();\n this.parseBindingListItem(rest);\n elts.push(rest);\n if (this.type === types$1.comma) { this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\"); }\n this.expect(close);\n break\n } else {\n elts.push(this.parseAssignableListItem(allowModifiers));\n }\n }\n return elts\n };\n\n pp$7.parseAssignableListItem = function(allowModifiers) {\n var elem = this.parseMaybeDefault(this.start, this.startLoc);\n this.parseBindingListItem(elem);\n return elem\n };\n\n pp$7.parseBindingListItem = function(param) {\n return param\n };\n\n // Parses assignment pattern around given atom if possible.\n\n pp$7.parseMaybeDefault = function(startPos, startLoc, left) {\n left = left || this.parseBindingAtom();\n if (this.options.ecmaVersion < 6 || !this.eat(types$1.eq)) { return left }\n var node = this.startNodeAt(startPos, startLoc);\n node.left = left;\n node.right = this.parseMaybeAssign();\n return this.finishNode(node, \"AssignmentPattern\")\n };\n\n // The following three functions all verify that a node is an lvalue —\n // something that can be bound, or assigned to. In order to do so, they perform\n // a variety of checks:\n //\n // - Check that none of the bound/assigned-to identifiers are reserved words.\n // - Record name declarations for bindings in the appropriate scope.\n // - Check duplicate argument names, if checkClashes is set.\n //\n // If a complex binding pattern is encountered (e.g., object and array\n // destructuring), the entire pattern is recursively checked.\n //\n // There are three versions of checkLVal*() appropriate for different\n // circumstances:\n //\n // - checkLValSimple() shall be used if the syntactic construct supports\n // nothing other than identifiers and member expressions. Parenthesized\n // expressions are also correctly handled. This is generally appropriate for\n // constructs for which the spec says\n //\n // > It is a Syntax Error if AssignmentTargetType of [the production] is not\n // > simple.\n //\n // It is also appropriate for checking if an identifier is valid and not\n // defined elsewhere, like import declarations or function/class identifiers.\n //\n // Examples where this is used include:\n // a += …;\n // import a from '…';\n // where a is the node to be checked.\n //\n // - checkLValPattern() shall be used if the syntactic construct supports\n // anything checkLValSimple() supports, as well as object and array\n // destructuring patterns. This is generally appropriate for constructs for\n // which the spec says\n //\n // > It is a Syntax Error if [the production] is neither an ObjectLiteral nor\n // > an ArrayLiteral and AssignmentTargetType of [the production] is not\n // > simple.\n //\n // Examples where this is used include:\n // (a = …);\n // const a = …;\n // try { … } catch (a) { … }\n // where a is the node to be checked.\n //\n // - checkLValInnerPattern() shall be used if the syntactic construct supports\n // anything checkLValPattern() supports, as well as default assignment\n // patterns, rest elements, and other constructs that may appear within an\n // object or array destructuring pattern.\n //\n // As a special case, function parameters also use checkLValInnerPattern(),\n // as they also support defaults and rest constructs.\n //\n // These functions deliberately support both assignment and binding constructs,\n // as the logic for both is exceedingly similar. If the node is the target of\n // an assignment, then bindingType should be set to BIND_NONE. Otherwise, it\n // should be set to the appropriate BIND_* constant, like BIND_VAR or\n // BIND_LEXICAL.\n //\n // If the function is called with a non-BIND_NONE bindingType, then\n // additionally a checkClashes object may be specified to allow checking for\n // duplicate argument names. checkClashes is ignored if the provided construct\n // is an assignment (i.e., bindingType is BIND_NONE).\n\n pp$7.checkLValSimple = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n var isBind = bindingType !== BIND_NONE;\n\n switch (expr.type) {\n case \"Identifier\":\n if (this.strict && this.reservedWordsStrictBind.test(expr.name))\n { this.raiseRecoverable(expr.start, (isBind ? \"Binding \" : \"Assigning to \") + expr.name + \" in strict mode\"); }\n if (isBind) {\n if (bindingType === BIND_LEXICAL && expr.name === \"let\")\n { this.raiseRecoverable(expr.start, \"let is disallowed as a lexically bound name\"); }\n if (checkClashes) {\n if (hasOwn(checkClashes, expr.name))\n { this.raiseRecoverable(expr.start, \"Argument name clash\"); }\n checkClashes[expr.name] = true;\n }\n if (bindingType !== BIND_OUTSIDE) { this.declareName(expr.name, bindingType, expr.start); }\n }\n break\n\n case \"ChainExpression\":\n this.raiseRecoverable(expr.start, \"Optional chaining cannot appear in left-hand side\");\n break\n\n case \"MemberExpression\":\n if (isBind) { this.raiseRecoverable(expr.start, \"Binding member expression\"); }\n break\n\n case \"ParenthesizedExpression\":\n if (isBind) { this.raiseRecoverable(expr.start, \"Binding parenthesized expression\"); }\n return this.checkLValSimple(expr.expression, bindingType, checkClashes)\n\n default:\n this.raise(expr.start, (isBind ? \"Binding\" : \"Assigning to\") + \" rvalue\");\n }\n };\n\n pp$7.checkLValPattern = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n switch (expr.type) {\n case \"ObjectPattern\":\n for (var i = 0, list = expr.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n this.checkLValInnerPattern(prop, bindingType, checkClashes);\n }\n break\n\n case \"ArrayPattern\":\n for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {\n var elem = list$1[i$1];\n\n if (elem) { this.checkLValInnerPattern(elem, bindingType, checkClashes); }\n }\n break\n\n default:\n this.checkLValSimple(expr, bindingType, checkClashes);\n }\n };\n\n pp$7.checkLValInnerPattern = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n switch (expr.type) {\n case \"Property\":\n // AssignmentProperty has type === \"Property\"\n this.checkLValInnerPattern(expr.value, bindingType, checkClashes);\n break\n\n case \"AssignmentPattern\":\n this.checkLValPattern(expr.left, bindingType, checkClashes);\n break\n\n case \"RestElement\":\n this.checkLValPattern(expr.argument, bindingType, checkClashes);\n break\n\n default:\n this.checkLValPattern(expr, bindingType, checkClashes);\n }\n };\n\n // The algorithm used to determine whether a regexp can appear at a\n // given point in the program is loosely based on sweet.js' approach.\n // See https://github.com/mozilla/sweet.js/wiki/design\n\n\n var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {\n this.token = token;\n this.isExpr = !!isExpr;\n this.preserveSpace = !!preserveSpace;\n this.override = override;\n this.generator = !!generator;\n };\n\n var types = {\n b_stat: new TokContext(\"{\", false),\n b_expr: new TokContext(\"{\", true),\n b_tmpl: new TokContext(\"${\", false),\n p_stat: new TokContext(\"(\", false),\n p_expr: new TokContext(\"(\", true),\n q_tmpl: new TokContext(\"`\", true, true, function (p) { return p.tryReadTemplateToken(); }),\n f_stat: new TokContext(\"function\", false),\n f_expr: new TokContext(\"function\", true),\n f_expr_gen: new TokContext(\"function\", true, false, null, true),\n f_gen: new TokContext(\"function\", false, false, null, true)\n };\n\n var pp$6 = Parser.prototype;\n\n pp$6.initialContext = function() {\n return [types.b_stat]\n };\n\n pp$6.curContext = function() {\n return this.context[this.context.length - 1]\n };\n\n pp$6.braceIsBlock = function(prevType) {\n var parent = this.curContext();\n if (parent === types.f_expr || parent === types.f_stat)\n { return true }\n if (prevType === types$1.colon && (parent === types.b_stat || parent === types.b_expr))\n { return !parent.isExpr }\n\n // The check for `tt.name && exprAllowed` detects whether we are\n // after a `yield` or `of` construct. See the `updateContext` for\n // `tt.name`.\n if (prevType === types$1._return || prevType === types$1.name && this.exprAllowed)\n { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }\n if (prevType === types$1._else || prevType === types$1.semi || prevType === types$1.eof || prevType === types$1.parenR || prevType === types$1.arrow)\n { return true }\n if (prevType === types$1.braceL)\n { return parent === types.b_stat }\n if (prevType === types$1._var || prevType === types$1._const || prevType === types$1.name)\n { return false }\n return !this.exprAllowed\n };\n\n pp$6.inGeneratorContext = function() {\n for (var i = this.context.length - 1; i >= 1; i--) {\n var context = this.context[i];\n if (context.token === \"function\")\n { return context.generator }\n }\n return false\n };\n\n pp$6.updateContext = function(prevType) {\n var update, type = this.type;\n if (type.keyword && prevType === types$1.dot)\n { this.exprAllowed = false; }\n else if (update = type.updateContext)\n { update.call(this, prevType); }\n else\n { this.exprAllowed = type.beforeExpr; }\n };\n\n // Used to handle edge cases when token context could not be inferred correctly during tokenization phase\n\n pp$6.overrideContext = function(tokenCtx) {\n if (this.curContext() !== tokenCtx) {\n this.context[this.context.length - 1] = tokenCtx;\n }\n };\n\n // Token-specific context update code\n\n types$1.parenR.updateContext = types$1.braceR.updateContext = function() {\n if (this.context.length === 1) {\n this.exprAllowed = true;\n return\n }\n var out = this.context.pop();\n if (out === types.b_stat && this.curContext().token === \"function\") {\n out = this.context.pop();\n }\n this.exprAllowed = !out.isExpr;\n };\n\n types$1.braceL.updateContext = function(prevType) {\n this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);\n this.exprAllowed = true;\n };\n\n types$1.dollarBraceL.updateContext = function() {\n this.context.push(types.b_tmpl);\n this.exprAllowed = true;\n };\n\n types$1.parenL.updateContext = function(prevType) {\n var statementParens = prevType === types$1._if || prevType === types$1._for || prevType === types$1._with || prevType === types$1._while;\n this.context.push(statementParens ? types.p_stat : types.p_expr);\n this.exprAllowed = true;\n };\n\n types$1.incDec.updateContext = function() {\n // tokExprAllowed stays unchanged\n };\n\n types$1._function.updateContext = types$1._class.updateContext = function(prevType) {\n if (prevType.beforeExpr && prevType !== types$1._else &&\n !(prevType === types$1.semi && this.curContext() !== types.p_stat) &&\n !(prevType === types$1._return && lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) &&\n !((prevType === types$1.colon || prevType === types$1.braceL) && this.curContext() === types.b_stat))\n { this.context.push(types.f_expr); }\n else\n { this.context.push(types.f_stat); }\n this.exprAllowed = false;\n };\n\n types$1.colon.updateContext = function() {\n if (this.curContext().token === \"function\") { this.context.pop(); }\n this.exprAllowed = true;\n };\n\n types$1.backQuote.updateContext = function() {\n if (this.curContext() === types.q_tmpl)\n { this.context.pop(); }\n else\n { this.context.push(types.q_tmpl); }\n this.exprAllowed = false;\n };\n\n types$1.star.updateContext = function(prevType) {\n if (prevType === types$1._function) {\n var index = this.context.length - 1;\n if (this.context[index] === types.f_expr)\n { this.context[index] = types.f_expr_gen; }\n else\n { this.context[index] = types.f_gen; }\n }\n this.exprAllowed = true;\n };\n\n types$1.name.updateContext = function(prevType) {\n var allowed = false;\n if (this.options.ecmaVersion >= 6 && prevType !== types$1.dot) {\n if (this.value === \"of\" && !this.exprAllowed ||\n this.value === \"yield\" && this.inGeneratorContext())\n { allowed = true; }\n }\n this.exprAllowed = allowed;\n };\n\n // A recursive descent parser operates by defining functions for all\n // syntactic elements, and recursively calling those, each function\n // advancing the input stream and returning an AST node. Precedence\n // of constructs (for example, the fact that `!x[1]` means `!(x[1])`\n // instead of `(!x)[1]` is handled by the fact that the parser\n // function that parses unary prefix operators is called first, and\n // in turn calls the function that parses `[]` subscripts — that\n // way, it'll receive the node for `x[1]` already parsed, and wraps\n // *that* in the unary operator node.\n //\n // Acorn uses an [operator precedence parser][opp] to handle binary\n // operator precedence, because it is much more compact than using\n // the technique outlined above, which uses different, nesting\n // functions to specify precedence, for all of the ten binary\n // precedence levels that JavaScript defines.\n //\n // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser\n\n\n var pp$5 = Parser.prototype;\n\n // Check if property name clashes with already added.\n // Object/class getters and setters are not allowed to clash —\n // either with each other or with an init property — and in\n // strict mode, init properties are also not allowed to be repeated.\n\n pp$5.checkPropClash = function(prop, propHash, refDestructuringErrors) {\n if (this.options.ecmaVersion >= 9 && prop.type === \"SpreadElement\")\n { return }\n if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))\n { return }\n var key = prop.key;\n var name;\n switch (key.type) {\n case \"Identifier\": name = key.name; break\n case \"Literal\": name = String(key.value); break\n default: return\n }\n var kind = prop.kind;\n if (this.options.ecmaVersion >= 6) {\n if (name === \"__proto__\" && kind === \"init\") {\n if (propHash.proto) {\n if (refDestructuringErrors) {\n if (refDestructuringErrors.doubleProto < 0) {\n refDestructuringErrors.doubleProto = key.start;\n }\n } else {\n this.raiseRecoverable(key.start, \"Redefinition of __proto__ property\");\n }\n }\n propHash.proto = true;\n }\n return\n }\n name = \"$\" + name;\n var other = propHash[name];\n if (other) {\n var redefinition;\n if (kind === \"init\") {\n redefinition = this.strict && other.init || other.get || other.set;\n } else {\n redefinition = other.init || other[kind];\n }\n if (redefinition)\n { this.raiseRecoverable(key.start, \"Redefinition of property\"); }\n } else {\n other = propHash[name] = {\n init: false,\n get: false,\n set: false\n };\n }\n other[kind] = true;\n };\n\n // ### Expression parsing\n\n // These nest, from the most general expression type at the top to\n // 'atomic', nondivisible expression types at the bottom. Most of\n // the functions will simply let the function(s) below them parse,\n // and, *if* the syntactic construct they handle is present, wrap\n // the AST node that the inner parser gave them in another node.\n\n // Parse a full expression. The optional arguments are used to\n // forbid the `in` operator (in for loops initalization expressions)\n // and provide reference for storing '=' operator inside shorthand\n // property assignment in contexts where both object expression\n // and object pattern might appear (so it's possible to raise\n // delayed syntax error at correct position).\n\n pp$5.parseExpression = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseMaybeAssign(forInit, refDestructuringErrors);\n if (this.type === types$1.comma) {\n var node = this.startNodeAt(startPos, startLoc);\n node.expressions = [expr];\n while (this.eat(types$1.comma)) { node.expressions.push(this.parseMaybeAssign(forInit, refDestructuringErrors)); }\n return this.finishNode(node, \"SequenceExpression\")\n }\n return expr\n };\n\n // Parse an assignment expression. This includes applications of\n // operators like `+=`.\n\n pp$5.parseMaybeAssign = function(forInit, refDestructuringErrors, afterLeftParse) {\n if (this.isContextual(\"yield\")) {\n if (this.inGenerator) { return this.parseYield(forInit) }\n // The tokenizer will assume an expression is allowed after\n // `yield`, but this isn't that kind of yield\n else { this.exprAllowed = false; }\n }\n\n var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldDoubleProto = -1;\n if (refDestructuringErrors) {\n oldParenAssign = refDestructuringErrors.parenthesizedAssign;\n oldTrailingComma = refDestructuringErrors.trailingComma;\n oldDoubleProto = refDestructuringErrors.doubleProto;\n refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;\n } else {\n refDestructuringErrors = new DestructuringErrors;\n ownDestructuringErrors = true;\n }\n\n var startPos = this.start, startLoc = this.startLoc;\n if (this.type === types$1.parenL || this.type === types$1.name) {\n this.potentialArrowAt = this.start;\n this.potentialArrowInForAwait = forInit === \"await\";\n }\n var left = this.parseMaybeConditional(forInit, refDestructuringErrors);\n if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }\n if (this.type.isAssign) {\n var node = this.startNodeAt(startPos, startLoc);\n node.operator = this.value;\n if (this.type === types$1.eq)\n { left = this.toAssignable(left, false, refDestructuringErrors); }\n if (!ownDestructuringErrors) {\n refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1;\n }\n if (refDestructuringErrors.shorthandAssign >= left.start)\n { refDestructuringErrors.shorthandAssign = -1; } // reset because shorthand default was used correctly\n if (this.type === types$1.eq)\n { this.checkLValPattern(left); }\n else\n { this.checkLValSimple(left); }\n node.left = left;\n this.next();\n node.right = this.parseMaybeAssign(forInit);\n if (oldDoubleProto > -1) { refDestructuringErrors.doubleProto = oldDoubleProto; }\n return this.finishNode(node, \"AssignmentExpression\")\n } else {\n if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }\n }\n if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }\n if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }\n return left\n };\n\n // Parse a ternary conditional (`?:`) operator.\n\n pp$5.parseMaybeConditional = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseExprOps(forInit, refDestructuringErrors);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n if (this.eat(types$1.question)) {\n var node = this.startNodeAt(startPos, startLoc);\n node.test = expr;\n node.consequent = this.parseMaybeAssign();\n this.expect(types$1.colon);\n node.alternate = this.parseMaybeAssign(forInit);\n return this.finishNode(node, \"ConditionalExpression\")\n }\n return expr\n };\n\n // Start the precedence parser.\n\n pp$5.parseExprOps = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseMaybeUnary(refDestructuringErrors, false, false, forInit);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n return expr.start === startPos && expr.type === \"ArrowFunctionExpression\" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, forInit)\n };\n\n // Parse binary operators with the operator precedence parsing\n // algorithm. `left` is the left-hand side of the operator.\n // `minPrec` provides context that allows the function to stop and\n // defer further parser to one of its callers when it encounters an\n // operator that has a lower precedence than the set it is parsing.\n\n pp$5.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, forInit) {\n var prec = this.type.binop;\n if (prec != null && (!forInit || this.type !== types$1._in)) {\n if (prec > minPrec) {\n var logical = this.type === types$1.logicalOR || this.type === types$1.logicalAND;\n var coalesce = this.type === types$1.coalesce;\n if (coalesce) {\n // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions.\n // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error.\n prec = types$1.logicalAND.binop;\n }\n var op = this.value;\n this.next();\n var startPos = this.start, startLoc = this.startLoc;\n var right = this.parseExprOp(this.parseMaybeUnary(null, false, false, forInit), startPos, startLoc, prec, forInit);\n var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce);\n if ((logical && this.type === types$1.coalesce) || (coalesce && (this.type === types$1.logicalOR || this.type === types$1.logicalAND))) {\n this.raiseRecoverable(this.start, \"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses\");\n }\n return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, forInit)\n }\n }\n return left\n };\n\n pp$5.buildBinary = function(startPos, startLoc, left, right, op, logical) {\n if (right.type === \"PrivateIdentifier\") { this.raise(right.start, \"Private identifier can only be left side of binary expression\"); }\n var node = this.startNodeAt(startPos, startLoc);\n node.left = left;\n node.operator = op;\n node.right = right;\n return this.finishNode(node, logical ? \"LogicalExpression\" : \"BinaryExpression\")\n };\n\n // Parse unary operators, both prefix and postfix.\n\n pp$5.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forInit) {\n var startPos = this.start, startLoc = this.startLoc, expr;\n if (this.isContextual(\"await\") && this.canAwait) {\n expr = this.parseAwait(forInit);\n sawUnary = true;\n } else if (this.type.prefix) {\n var node = this.startNode(), update = this.type === types$1.incDec;\n node.operator = this.value;\n node.prefix = true;\n this.next();\n node.argument = this.parseMaybeUnary(null, true, update, forInit);\n this.checkExpressionErrors(refDestructuringErrors, true);\n if (update) { this.checkLValSimple(node.argument); }\n else if (this.strict && node.operator === \"delete\" && isLocalVariableAccess(node.argument))\n { this.raiseRecoverable(node.start, \"Deleting local variable in strict mode\"); }\n else if (node.operator === \"delete\" && isPrivateFieldAccess(node.argument))\n { this.raiseRecoverable(node.start, \"Private fields can not be deleted\"); }\n else { sawUnary = true; }\n expr = this.finishNode(node, update ? \"UpdateExpression\" : \"UnaryExpression\");\n } else if (!sawUnary && this.type === types$1.privateId) {\n if ((forInit || this.privateNameStack.length === 0) && this.options.checkPrivateFields) { this.unexpected(); }\n expr = this.parsePrivateIdent();\n // only could be private fields in 'in', such as #x in obj\n if (this.type !== types$1._in) { this.unexpected(); }\n } else {\n expr = this.parseExprSubscripts(refDestructuringErrors, forInit);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n while (this.type.postfix && !this.canInsertSemicolon()) {\n var node$1 = this.startNodeAt(startPos, startLoc);\n node$1.operator = this.value;\n node$1.prefix = false;\n node$1.argument = expr;\n this.checkLValSimple(expr);\n this.next();\n expr = this.finishNode(node$1, \"UpdateExpression\");\n }\n }\n\n if (!incDec && this.eat(types$1.starstar)) {\n if (sawUnary)\n { this.unexpected(this.lastTokStart); }\n else\n { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), \"**\", false) }\n } else {\n return expr\n }\n };\n\n function isLocalVariableAccess(node) {\n return (\n node.type === \"Identifier\" ||\n node.type === \"ParenthesizedExpression\" && isLocalVariableAccess(node.expression)\n )\n }\n\n function isPrivateFieldAccess(node) {\n return (\n node.type === \"MemberExpression\" && node.property.type === \"PrivateIdentifier\" ||\n node.type === \"ChainExpression\" && isPrivateFieldAccess(node.expression) ||\n node.type === \"ParenthesizedExpression\" && isPrivateFieldAccess(node.expression)\n )\n }\n\n // Parse call, dot, and `[]`-subscript expressions.\n\n pp$5.parseExprSubscripts = function(refDestructuringErrors, forInit) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseExprAtom(refDestructuringErrors, forInit);\n if (expr.type === \"ArrowFunctionExpression\" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== \")\")\n { return expr }\n var result = this.parseSubscripts(expr, startPos, startLoc, false, forInit);\n if (refDestructuringErrors && result.type === \"MemberExpression\") {\n if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }\n if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }\n if (refDestructuringErrors.trailingComma >= result.start) { refDestructuringErrors.trailingComma = -1; }\n }\n return result\n };\n\n pp$5.parseSubscripts = function(base, startPos, startLoc, noCalls, forInit) {\n var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === \"Identifier\" && base.name === \"async\" &&\n this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 &&\n this.potentialArrowAt === base.start;\n var optionalChained = false;\n\n while (true) {\n var element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit);\n\n if (element.optional) { optionalChained = true; }\n if (element === base || element.type === \"ArrowFunctionExpression\") {\n if (optionalChained) {\n var chainNode = this.startNodeAt(startPos, startLoc);\n chainNode.expression = element;\n element = this.finishNode(chainNode, \"ChainExpression\");\n }\n return element\n }\n\n base = element;\n }\n };\n\n pp$5.shouldParseAsyncArrow = function() {\n return !this.canInsertSemicolon() && this.eat(types$1.arrow)\n };\n\n pp$5.parseSubscriptAsyncArrow = function(startPos, startLoc, exprList, forInit) {\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true, forInit)\n };\n\n pp$5.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {\n var optionalSupported = this.options.ecmaVersion >= 11;\n var optional = optionalSupported && this.eat(types$1.questionDot);\n if (noCalls && optional) { this.raise(this.lastTokStart, \"Optional chaining cannot appear in the callee of new expressions\"); }\n\n var computed = this.eat(types$1.bracketL);\n if (computed || (optional && this.type !== types$1.parenL && this.type !== types$1.backQuote) || this.eat(types$1.dot)) {\n var node = this.startNodeAt(startPos, startLoc);\n node.object = base;\n if (computed) {\n node.property = this.parseExpression();\n this.expect(types$1.bracketR);\n } else if (this.type === types$1.privateId && base.type !== \"Super\") {\n node.property = this.parsePrivateIdent();\n } else {\n node.property = this.parseIdent(this.options.allowReserved !== \"never\");\n }\n node.computed = !!computed;\n if (optionalSupported) {\n node.optional = optional;\n }\n base = this.finishNode(node, \"MemberExpression\");\n } else if (!noCalls && this.eat(types$1.parenL)) {\n var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n var exprList = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors);\n if (maybeAsyncArrow && !optional && this.shouldParseAsyncArrow()) {\n this.checkPatternErrors(refDestructuringErrors, false);\n this.checkYieldAwaitInDefaultParams();\n if (this.awaitIdentPos > 0)\n { this.raise(this.awaitIdentPos, \"Cannot use 'await' as identifier inside an async function\"); }\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.parseSubscriptAsyncArrow(startPos, startLoc, exprList, forInit)\n }\n this.checkExpressionErrors(refDestructuringErrors, true);\n this.yieldPos = oldYieldPos || this.yieldPos;\n this.awaitPos = oldAwaitPos || this.awaitPos;\n this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;\n var node$1 = this.startNodeAt(startPos, startLoc);\n node$1.callee = base;\n node$1.arguments = exprList;\n if (optionalSupported) {\n node$1.optional = optional;\n }\n base = this.finishNode(node$1, \"CallExpression\");\n } else if (this.type === types$1.backQuote) {\n if (optional || optionalChained) {\n this.raise(this.start, \"Optional chaining cannot appear in the tag of tagged template expressions\");\n }\n var node$2 = this.startNodeAt(startPos, startLoc);\n node$2.tag = base;\n node$2.quasi = this.parseTemplate({isTagged: true});\n base = this.finishNode(node$2, \"TaggedTemplateExpression\");\n }\n return base\n };\n\n // Parse an atomic expression — either a single token that is an\n // expression, an expression started by a keyword like `function` or\n // `new`, or an expression wrapped in punctuation like `()`, `[]`,\n // or `{}`.\n\n pp$5.parseExprAtom = function(refDestructuringErrors, forInit, forNew) {\n // If a division operator appears in an expression position, the\n // tokenizer got confused, and we force it to read a regexp instead.\n if (this.type === types$1.slash) { this.readRegexp(); }\n\n var node, canBeArrow = this.potentialArrowAt === this.start;\n switch (this.type) {\n case types$1._super:\n if (!this.allowSuper)\n { this.raise(this.start, \"'super' keyword outside a method\"); }\n node = this.startNode();\n this.next();\n if (this.type === types$1.parenL && !this.allowDirectSuper)\n { this.raise(node.start, \"super() call outside constructor of a subclass\"); }\n // The `super` keyword can appear at below:\n // SuperProperty:\n // super [ Expression ]\n // super . IdentifierName\n // SuperCall:\n // super ( Arguments )\n if (this.type !== types$1.dot && this.type !== types$1.bracketL && this.type !== types$1.parenL)\n { this.unexpected(); }\n return this.finishNode(node, \"Super\")\n\n case types$1._this:\n node = this.startNode();\n this.next();\n return this.finishNode(node, \"ThisExpression\")\n\n case types$1.name:\n var startPos = this.start, startLoc = this.startLoc, containsEsc = this.containsEsc;\n var id = this.parseIdent(false);\n if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === \"async\" && !this.canInsertSemicolon() && this.eat(types$1._function)) {\n this.overrideContext(types.f_expr);\n return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true, forInit)\n }\n if (canBeArrow && !this.canInsertSemicolon()) {\n if (this.eat(types$1.arrow))\n { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false, forInit) }\n if (this.options.ecmaVersion >= 8 && id.name === \"async\" && this.type === types$1.name && !containsEsc &&\n (!this.potentialArrowInForAwait || this.value !== \"of\" || this.containsEsc)) {\n id = this.parseIdent(false);\n if (this.canInsertSemicolon() || !this.eat(types$1.arrow))\n { this.unexpected(); }\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true, forInit)\n }\n }\n return id\n\n case types$1.regexp:\n var value = this.value;\n node = this.parseLiteral(value.value);\n node.regex = {pattern: value.pattern, flags: value.flags};\n return node\n\n case types$1.num: case types$1.string:\n return this.parseLiteral(this.value)\n\n case types$1._null: case types$1._true: case types$1._false:\n node = this.startNode();\n node.value = this.type === types$1._null ? null : this.type === types$1._true;\n node.raw = this.type.keyword;\n this.next();\n return this.finishNode(node, \"Literal\")\n\n case types$1.parenL:\n var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow, forInit);\n if (refDestructuringErrors) {\n if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))\n { refDestructuringErrors.parenthesizedAssign = start; }\n if (refDestructuringErrors.parenthesizedBind < 0)\n { refDestructuringErrors.parenthesizedBind = start; }\n }\n return expr\n\n case types$1.bracketL:\n node = this.startNode();\n this.next();\n node.elements = this.parseExprList(types$1.bracketR, true, true, refDestructuringErrors);\n return this.finishNode(node, \"ArrayExpression\")\n\n case types$1.braceL:\n this.overrideContext(types.b_expr);\n return this.parseObj(false, refDestructuringErrors)\n\n case types$1._function:\n node = this.startNode();\n this.next();\n return this.parseFunction(node, 0)\n\n case types$1._class:\n return this.parseClass(this.startNode(), false)\n\n case types$1._new:\n return this.parseNew()\n\n case types$1.backQuote:\n return this.parseTemplate()\n\n case types$1._import:\n if (this.options.ecmaVersion >= 11) {\n return this.parseExprImport(forNew)\n } else {\n return this.unexpected()\n }\n\n default:\n return this.parseExprAtomDefault()\n }\n };\n\n pp$5.parseExprAtomDefault = function() {\n this.unexpected();\n };\n\n pp$5.parseExprImport = function(forNew) {\n var node = this.startNode();\n\n // Consume `import` as an identifier for `import.meta`.\n // Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.\n if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword import\"); }\n this.next();\n\n if (this.type === types$1.parenL && !forNew) {\n return this.parseDynamicImport(node)\n } else if (this.type === types$1.dot) {\n var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n meta.name = \"import\";\n node.meta = this.finishNode(meta, \"Identifier\");\n return this.parseImportMeta(node)\n } else {\n this.unexpected();\n }\n };\n\n pp$5.parseDynamicImport = function(node) {\n this.next(); // skip `(`\n\n // Parse node.source.\n node.source = this.parseMaybeAssign();\n\n if (this.options.ecmaVersion >= 16) {\n if (!this.eat(types$1.parenR)) {\n this.expect(types$1.comma);\n if (!this.afterTrailingComma(types$1.parenR)) {\n node.options = this.parseMaybeAssign();\n if (!this.eat(types$1.parenR)) {\n this.expect(types$1.comma);\n if (!this.afterTrailingComma(types$1.parenR)) {\n this.unexpected();\n }\n }\n } else {\n node.options = null;\n }\n } else {\n node.options = null;\n }\n } else {\n // Verify ending.\n if (!this.eat(types$1.parenR)) {\n var errorPos = this.start;\n if (this.eat(types$1.comma) && this.eat(types$1.parenR)) {\n this.raiseRecoverable(errorPos, \"Trailing comma is not allowed in import()\");\n } else {\n this.unexpected(errorPos);\n }\n }\n }\n\n return this.finishNode(node, \"ImportExpression\")\n };\n\n pp$5.parseImportMeta = function(node) {\n this.next(); // skip `.`\n\n var containsEsc = this.containsEsc;\n node.property = this.parseIdent(true);\n\n if (node.property.name !== \"meta\")\n { this.raiseRecoverable(node.property.start, \"The only valid meta property for import is 'import.meta'\"); }\n if (containsEsc)\n { this.raiseRecoverable(node.start, \"'import.meta' must not contain escaped characters\"); }\n if (this.options.sourceType !== \"module\" && !this.options.allowImportExportEverywhere)\n { this.raiseRecoverable(node.start, \"Cannot use 'import.meta' outside a module\"); }\n\n return this.finishNode(node, \"MetaProperty\")\n };\n\n pp$5.parseLiteral = function(value) {\n var node = this.startNode();\n node.value = value;\n node.raw = this.input.slice(this.start, this.end);\n if (node.raw.charCodeAt(node.raw.length - 1) === 110)\n { node.bigint = node.value != null ? node.value.toString() : node.raw.slice(0, -1).replace(/_/g, \"\"); }\n this.next();\n return this.finishNode(node, \"Literal\")\n };\n\n pp$5.parseParenExpression = function() {\n this.expect(types$1.parenL);\n var val = this.parseExpression();\n this.expect(types$1.parenR);\n return val\n };\n\n pp$5.shouldParseArrow = function(exprList) {\n return !this.canInsertSemicolon()\n };\n\n pp$5.parseParenAndDistinguishExpression = function(canBeArrow, forInit) {\n var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;\n if (this.options.ecmaVersion >= 6) {\n this.next();\n\n var innerStartPos = this.start, innerStartLoc = this.startLoc;\n var exprList = [], first = true, lastIsComma = false;\n var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart;\n this.yieldPos = 0;\n this.awaitPos = 0;\n // Do not save awaitIdentPos to allow checking awaits nested in parameters\n while (this.type !== types$1.parenR) {\n first ? first = false : this.expect(types$1.comma);\n if (allowTrailingComma && this.afterTrailingComma(types$1.parenR, true)) {\n lastIsComma = true;\n break\n } else if (this.type === types$1.ellipsis) {\n spreadStart = this.start;\n exprList.push(this.parseParenItem(this.parseRestBinding()));\n if (this.type === types$1.comma) {\n this.raiseRecoverable(\n this.start,\n \"Comma is not permitted after the rest element\"\n );\n }\n break\n } else {\n exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));\n }\n }\n var innerEndPos = this.lastTokEnd, innerEndLoc = this.lastTokEndLoc;\n this.expect(types$1.parenR);\n\n if (canBeArrow && this.shouldParseArrow(exprList) && this.eat(types$1.arrow)) {\n this.checkPatternErrors(refDestructuringErrors, false);\n this.checkYieldAwaitInDefaultParams();\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n return this.parseParenArrowList(startPos, startLoc, exprList, forInit)\n }\n\n if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }\n if (spreadStart) { this.unexpected(spreadStart); }\n this.checkExpressionErrors(refDestructuringErrors, true);\n this.yieldPos = oldYieldPos || this.yieldPos;\n this.awaitPos = oldAwaitPos || this.awaitPos;\n\n if (exprList.length > 1) {\n val = this.startNodeAt(innerStartPos, innerStartLoc);\n val.expressions = exprList;\n this.finishNodeAt(val, \"SequenceExpression\", innerEndPos, innerEndLoc);\n } else {\n val = exprList[0];\n }\n } else {\n val = this.parseParenExpression();\n }\n\n if (this.options.preserveParens) {\n var par = this.startNodeAt(startPos, startLoc);\n par.expression = val;\n return this.finishNode(par, \"ParenthesizedExpression\")\n } else {\n return val\n }\n };\n\n pp$5.parseParenItem = function(item) {\n return item\n };\n\n pp$5.parseParenArrowList = function(startPos, startLoc, exprList, forInit) {\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, false, forInit)\n };\n\n // New's precedence is slightly tricky. It must allow its argument to\n // be a `[]` or dot subscript expression, but not a call — at least,\n // not without wrapping it in parentheses. Thus, it uses the noCalls\n // argument to parseSubscripts to prevent it from consuming the\n // argument list.\n\n var empty = [];\n\n pp$5.parseNew = function() {\n if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword new\"); }\n var node = this.startNode();\n this.next();\n if (this.options.ecmaVersion >= 6 && this.type === types$1.dot) {\n var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n meta.name = \"new\";\n node.meta = this.finishNode(meta, \"Identifier\");\n this.next();\n var containsEsc = this.containsEsc;\n node.property = this.parseIdent(true);\n if (node.property.name !== \"target\")\n { this.raiseRecoverable(node.property.start, \"The only valid meta property for new is 'new.target'\"); }\n if (containsEsc)\n { this.raiseRecoverable(node.start, \"'new.target' must not contain escaped characters\"); }\n if (!this.allowNewDotTarget)\n { this.raiseRecoverable(node.start, \"'new.target' can only be used in functions and class static block\"); }\n return this.finishNode(node, \"MetaProperty\")\n }\n var startPos = this.start, startLoc = this.startLoc;\n node.callee = this.parseSubscripts(this.parseExprAtom(null, false, true), startPos, startLoc, true, false);\n if (this.eat(types$1.parenL)) { node.arguments = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false); }\n else { node.arguments = empty; }\n return this.finishNode(node, \"NewExpression\")\n };\n\n // Parse template expression.\n\n pp$5.parseTemplateElement = function(ref) {\n var isTagged = ref.isTagged;\n\n var elem = this.startNode();\n if (this.type === types$1.invalidTemplate) {\n if (!isTagged) {\n this.raiseRecoverable(this.start, \"Bad escape sequence in untagged template literal\");\n }\n elem.value = {\n raw: this.value.replace(/\\r\\n?/g, \"\\n\"),\n cooked: null\n };\n } else {\n elem.value = {\n raw: this.input.slice(this.start, this.end).replace(/\\r\\n?/g, \"\\n\"),\n cooked: this.value\n };\n }\n this.next();\n elem.tail = this.type === types$1.backQuote;\n return this.finishNode(elem, \"TemplateElement\")\n };\n\n pp$5.parseTemplate = function(ref) {\n if ( ref === void 0 ) ref = {};\n var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;\n\n var node = this.startNode();\n this.next();\n node.expressions = [];\n var curElt = this.parseTemplateElement({isTagged: isTagged});\n node.quasis = [curElt];\n while (!curElt.tail) {\n if (this.type === types$1.eof) { this.raise(this.pos, \"Unterminated template literal\"); }\n this.expect(types$1.dollarBraceL);\n node.expressions.push(this.parseExpression());\n this.expect(types$1.braceR);\n node.quasis.push(curElt = this.parseTemplateElement({isTagged: isTagged}));\n }\n this.next();\n return this.finishNode(node, \"TemplateLiteral\")\n };\n\n pp$5.isAsyncProp = function(prop) {\n return !prop.computed && prop.key.type === \"Identifier\" && prop.key.name === \"async\" &&\n (this.type === types$1.name || this.type === types$1.num || this.type === types$1.string || this.type === types$1.bracketL || this.type.keyword || (this.options.ecmaVersion >= 9 && this.type === types$1.star)) &&\n !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n };\n\n // Parse an object literal or binding pattern.\n\n pp$5.parseObj = function(isPattern, refDestructuringErrors) {\n var node = this.startNode(), first = true, propHash = {};\n node.properties = [];\n this.next();\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n var prop = this.parseProperty(isPattern, refDestructuringErrors);\n if (!isPattern) { this.checkPropClash(prop, propHash, refDestructuringErrors); }\n node.properties.push(prop);\n }\n return this.finishNode(node, isPattern ? \"ObjectPattern\" : \"ObjectExpression\")\n };\n\n pp$5.parseProperty = function(isPattern, refDestructuringErrors) {\n var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;\n if (this.options.ecmaVersion >= 9 && this.eat(types$1.ellipsis)) {\n if (isPattern) {\n prop.argument = this.parseIdent(false);\n if (this.type === types$1.comma) {\n this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\");\n }\n return this.finishNode(prop, \"RestElement\")\n }\n // Parse argument.\n prop.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n // To disallow trailing comma via `this.toAssignable()`.\n if (this.type === types$1.comma && refDestructuringErrors && refDestructuringErrors.trailingComma < 0) {\n refDestructuringErrors.trailingComma = this.start;\n }\n // Finish\n return this.finishNode(prop, \"SpreadElement\")\n }\n if (this.options.ecmaVersion >= 6) {\n prop.method = false;\n prop.shorthand = false;\n if (isPattern || refDestructuringErrors) {\n startPos = this.start;\n startLoc = this.startLoc;\n }\n if (!isPattern)\n { isGenerator = this.eat(types$1.star); }\n }\n var containsEsc = this.containsEsc;\n this.parsePropertyName(prop);\n if (!isPattern && !containsEsc && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {\n isAsync = true;\n isGenerator = this.options.ecmaVersion >= 9 && this.eat(types$1.star);\n this.parsePropertyName(prop);\n } else {\n isAsync = false;\n }\n this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc);\n return this.finishNode(prop, \"Property\")\n };\n\n pp$5.parseGetterSetter = function(prop) {\n var kind = prop.key.name;\n this.parsePropertyName(prop);\n prop.value = this.parseMethod(false);\n prop.kind = kind;\n var paramCount = prop.kind === \"get\" ? 0 : 1;\n if (prop.value.params.length !== paramCount) {\n var start = prop.value.start;\n if (prop.kind === \"get\")\n { this.raiseRecoverable(start, \"getter should have no params\"); }\n else\n { this.raiseRecoverable(start, \"setter should have exactly one param\"); }\n } else {\n if (prop.kind === \"set\" && prop.value.params[0].type === \"RestElement\")\n { this.raiseRecoverable(prop.value.params[0].start, \"Setter cannot use rest params\"); }\n }\n };\n\n pp$5.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc) {\n if ((isGenerator || isAsync) && this.type === types$1.colon)\n { this.unexpected(); }\n\n if (this.eat(types$1.colon)) {\n prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);\n prop.kind = \"init\";\n } else if (this.options.ecmaVersion >= 6 && this.type === types$1.parenL) {\n if (isPattern) { this.unexpected(); }\n prop.method = true;\n prop.value = this.parseMethod(isGenerator, isAsync);\n prop.kind = \"init\";\n } else if (!isPattern && !containsEsc &&\n this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === \"Identifier\" &&\n (prop.key.name === \"get\" || prop.key.name === \"set\") &&\n (this.type !== types$1.comma && this.type !== types$1.braceR && this.type !== types$1.eq)) {\n if (isGenerator || isAsync) { this.unexpected(); }\n this.parseGetterSetter(prop);\n } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === \"Identifier\") {\n if (isGenerator || isAsync) { this.unexpected(); }\n this.checkUnreserved(prop.key);\n if (prop.key.name === \"await\" && !this.awaitIdentPos)\n { this.awaitIdentPos = startPos; }\n if (isPattern) {\n prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n } else if (this.type === types$1.eq && refDestructuringErrors) {\n if (refDestructuringErrors.shorthandAssign < 0)\n { refDestructuringErrors.shorthandAssign = this.start; }\n prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n } else {\n prop.value = this.copyNode(prop.key);\n }\n prop.kind = \"init\";\n prop.shorthand = true;\n } else { this.unexpected(); }\n };\n\n pp$5.parsePropertyName = function(prop) {\n if (this.options.ecmaVersion >= 6) {\n if (this.eat(types$1.bracketL)) {\n prop.computed = true;\n prop.key = this.parseMaybeAssign();\n this.expect(types$1.bracketR);\n return prop.key\n } else {\n prop.computed = false;\n }\n }\n return prop.key = this.type === types$1.num || this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\")\n };\n\n // Initialize empty function node.\n\n pp$5.initFunction = function(node) {\n node.id = null;\n if (this.options.ecmaVersion >= 6) { node.generator = node.expression = false; }\n if (this.options.ecmaVersion >= 8) { node.async = false; }\n };\n\n // Parse object or class method.\n\n pp$5.parseMethod = function(isGenerator, isAsync, allowDirectSuper) {\n var node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n this.initFunction(node);\n if (this.options.ecmaVersion >= 6)\n { node.generator = isGenerator; }\n if (this.options.ecmaVersion >= 8)\n { node.async = !!isAsync; }\n\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n this.enterScope(functionFlags(isAsync, node.generator) | SCOPE_SUPER | (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0));\n\n this.expect(types$1.parenL);\n node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n this.checkYieldAwaitInDefaultParams();\n this.parseFunctionBody(node, false, true, false);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, \"FunctionExpression\")\n };\n\n // Parse arrow function expression with given parameters.\n\n pp$5.parseArrowExpression = function(node, params, isAsync, forInit) {\n var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n this.enterScope(functionFlags(isAsync, false) | SCOPE_ARROW);\n this.initFunction(node);\n if (this.options.ecmaVersion >= 8) { node.async = !!isAsync; }\n\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n\n node.params = this.toAssignableList(params, true);\n this.parseFunctionBody(node, true, false, forInit);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, \"ArrowFunctionExpression\")\n };\n\n // Parse function body and check parameters.\n\n pp$5.parseFunctionBody = function(node, isArrowFunction, isMethod, forInit) {\n var isExpression = isArrowFunction && this.type !== types$1.braceL;\n var oldStrict = this.strict, useStrict = false;\n\n if (isExpression) {\n node.body = this.parseMaybeAssign(forInit);\n node.expression = true;\n this.checkParams(node, false);\n } else {\n var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);\n if (!oldStrict || nonSimple) {\n useStrict = this.strictDirective(this.end);\n // If this is a strict mode function, verify that argument names\n // are not repeated, and it does not try to bind the words `eval`\n // or `arguments`.\n if (useStrict && nonSimple)\n { this.raiseRecoverable(node.start, \"Illegal 'use strict' directive in function with non-simple parameter list\"); }\n }\n // Start a new scope with regard to labels and the `inFunction`\n // flag (restore them to their old value afterwards).\n var oldLabels = this.labels;\n this.labels = [];\n if (useStrict) { this.strict = true; }\n\n // Add the params to varDeclaredNames to ensure that an error is thrown\n // if a let/const declaration in the function clashes with one of the params.\n this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params));\n // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'\n if (this.strict && node.id) { this.checkLValSimple(node.id, BIND_OUTSIDE); }\n node.body = this.parseBlock(false, undefined, useStrict && !oldStrict);\n node.expression = false;\n this.adaptDirectivePrologue(node.body.body);\n this.labels = oldLabels;\n }\n this.exitScope();\n };\n\n pp$5.isSimpleParamList = function(params) {\n for (var i = 0, list = params; i < list.length; i += 1)\n {\n var param = list[i];\n\n if (param.type !== \"Identifier\") { return false\n } }\n return true\n };\n\n // Checks function params for various disallowed patterns such as using \"eval\"\n // or \"arguments\" and duplicate parameters.\n\n pp$5.checkParams = function(node, allowDuplicates) {\n var nameHash = Object.create(null);\n for (var i = 0, list = node.params; i < list.length; i += 1)\n {\n var param = list[i];\n\n this.checkLValInnerPattern(param, BIND_VAR, allowDuplicates ? null : nameHash);\n }\n };\n\n // Parses a comma-separated list of expressions, and returns them as\n // an array. `close` is the token type that ends the list, and\n // `allowEmpty` can be turned on to allow subsequent commas with\n // nothing in between them to be parsed as `null` (which is needed\n // for array literals).\n\n pp$5.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {\n var elts = [], first = true;\n while (!this.eat(close)) {\n if (!first) {\n this.expect(types$1.comma);\n if (allowTrailingComma && this.afterTrailingComma(close)) { break }\n } else { first = false; }\n\n var elt = (void 0);\n if (allowEmpty && this.type === types$1.comma)\n { elt = null; }\n else if (this.type === types$1.ellipsis) {\n elt = this.parseSpread(refDestructuringErrors);\n if (refDestructuringErrors && this.type === types$1.comma && refDestructuringErrors.trailingComma < 0)\n { refDestructuringErrors.trailingComma = this.start; }\n } else {\n elt = this.parseMaybeAssign(false, refDestructuringErrors);\n }\n elts.push(elt);\n }\n return elts\n };\n\n pp$5.checkUnreserved = function(ref) {\n var start = ref.start;\n var end = ref.end;\n var name = ref.name;\n\n if (this.inGenerator && name === \"yield\")\n { this.raiseRecoverable(start, \"Cannot use 'yield' as identifier inside a generator\"); }\n if (this.inAsync && name === \"await\")\n { this.raiseRecoverable(start, \"Cannot use 'await' as identifier inside an async function\"); }\n if (!(this.currentThisScope().flags & SCOPE_VAR) && name === \"arguments\")\n { this.raiseRecoverable(start, \"Cannot use 'arguments' in class field initializer\"); }\n if (this.inClassStaticBlock && (name === \"arguments\" || name === \"await\"))\n { this.raise(start, (\"Cannot use \" + name + \" in class static initialization block\")); }\n if (this.keywords.test(name))\n { this.raise(start, (\"Unexpected keyword '\" + name + \"'\")); }\n if (this.options.ecmaVersion < 6 &&\n this.input.slice(start, end).indexOf(\"\\\\\") !== -1) { return }\n var re = this.strict ? this.reservedWordsStrict : this.reservedWords;\n if (re.test(name)) {\n if (!this.inAsync && name === \"await\")\n { this.raiseRecoverable(start, \"Cannot use keyword 'await' outside an async function\"); }\n this.raiseRecoverable(start, (\"The keyword '\" + name + \"' is reserved\"));\n }\n };\n\n // Parse the next token as an identifier. If `liberal` is true (used\n // when parsing properties), it will also convert keywords into\n // identifiers.\n\n pp$5.parseIdent = function(liberal) {\n var node = this.parseIdentNode();\n this.next(!!liberal);\n this.finishNode(node, \"Identifier\");\n if (!liberal) {\n this.checkUnreserved(node);\n if (node.name === \"await\" && !this.awaitIdentPos)\n { this.awaitIdentPos = node.start; }\n }\n return node\n };\n\n pp$5.parseIdentNode = function() {\n var node = this.startNode();\n if (this.type === types$1.name) {\n node.name = this.value;\n } else if (this.type.keyword) {\n node.name = this.type.keyword;\n\n // To fix https://github.com/acornjs/acorn/issues/575\n // `class` and `function` keywords push new context into this.context.\n // But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.\n // If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword\n if ((node.name === \"class\" || node.name === \"function\") &&\n (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {\n this.context.pop();\n }\n this.type = types$1.name;\n } else {\n this.unexpected();\n }\n return node\n };\n\n pp$5.parsePrivateIdent = function() {\n var node = this.startNode();\n if (this.type === types$1.privateId) {\n node.name = this.value;\n } else {\n this.unexpected();\n }\n this.next();\n this.finishNode(node, \"PrivateIdentifier\");\n\n // For validating existence\n if (this.options.checkPrivateFields) {\n if (this.privateNameStack.length === 0) {\n this.raise(node.start, (\"Private field '#\" + (node.name) + \"' must be declared in an enclosing class\"));\n } else {\n this.privateNameStack[this.privateNameStack.length - 1].used.push(node);\n }\n }\n\n return node\n };\n\n // Parses yield expression inside generator.\n\n pp$5.parseYield = function(forInit) {\n if (!this.yieldPos) { this.yieldPos = this.start; }\n\n var node = this.startNode();\n this.next();\n if (this.type === types$1.semi || this.canInsertSemicolon() || (this.type !== types$1.star && !this.type.startsExpr)) {\n node.delegate = false;\n node.argument = null;\n } else {\n node.delegate = this.eat(types$1.star);\n node.argument = this.parseMaybeAssign(forInit);\n }\n return this.finishNode(node, \"YieldExpression\")\n };\n\n pp$5.parseAwait = function(forInit) {\n if (!this.awaitPos) { this.awaitPos = this.start; }\n\n var node = this.startNode();\n this.next();\n node.argument = this.parseMaybeUnary(null, true, false, forInit);\n return this.finishNode(node, \"AwaitExpression\")\n };\n\n var pp$4 = Parser.prototype;\n\n // This function is used to raise exceptions on parse errors. It\n // takes an offset integer (into the current `input`) to indicate\n // the location of the error, attaches the position to the end\n // of the error message, and then raises a `SyntaxError` with that\n // message.\n\n pp$4.raise = function(pos, message) {\n var loc = getLineInfo(this.input, pos);\n message += \" (\" + loc.line + \":\" + loc.column + \")\";\n if (this.sourceFile) {\n message += \" in \" + this.sourceFile;\n }\n var err = new SyntaxError(message);\n err.pos = pos; err.loc = loc; err.raisedAt = this.pos;\n throw err\n };\n\n pp$4.raiseRecoverable = pp$4.raise;\n\n pp$4.curPosition = function() {\n if (this.options.locations) {\n return new Position(this.curLine, this.pos - this.lineStart)\n }\n };\n\n var pp$3 = Parser.prototype;\n\n var Scope = function Scope(flags) {\n this.flags = flags;\n // A list of var-declared names in the current lexical scope\n this.var = [];\n // A list of lexically-declared names in the current lexical scope\n this.lexical = [];\n // A list of lexically-declared FunctionDeclaration names in the current lexical scope\n this.functions = [];\n };\n\n // The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.\n\n pp$3.enterScope = function(flags) {\n this.scopeStack.push(new Scope(flags));\n };\n\n pp$3.exitScope = function() {\n this.scopeStack.pop();\n };\n\n // The spec says:\n // > At the top level of a function, or script, function declarations are\n // > treated like var declarations rather than like lexical declarations.\n pp$3.treatFunctionsAsVarInScope = function(scope) {\n return (scope.flags & SCOPE_FUNCTION) || !this.inModule && (scope.flags & SCOPE_TOP)\n };\n\n pp$3.declareName = function(name, bindingType, pos) {\n var redeclared = false;\n if (bindingType === BIND_LEXICAL) {\n var scope = this.currentScope();\n redeclared = scope.lexical.indexOf(name) > -1 || scope.functions.indexOf(name) > -1 || scope.var.indexOf(name) > -1;\n scope.lexical.push(name);\n if (this.inModule && (scope.flags & SCOPE_TOP))\n { delete this.undefinedExports[name]; }\n } else if (bindingType === BIND_SIMPLE_CATCH) {\n var scope$1 = this.currentScope();\n scope$1.lexical.push(name);\n } else if (bindingType === BIND_FUNCTION) {\n var scope$2 = this.currentScope();\n if (this.treatFunctionsAsVar)\n { redeclared = scope$2.lexical.indexOf(name) > -1; }\n else\n { redeclared = scope$2.lexical.indexOf(name) > -1 || scope$2.var.indexOf(name) > -1; }\n scope$2.functions.push(name);\n } else {\n for (var i = this.scopeStack.length - 1; i >= 0; --i) {\n var scope$3 = this.scopeStack[i];\n if (scope$3.lexical.indexOf(name) > -1 && !((scope$3.flags & SCOPE_SIMPLE_CATCH) && scope$3.lexical[0] === name) ||\n !this.treatFunctionsAsVarInScope(scope$3) && scope$3.functions.indexOf(name) > -1) {\n redeclared = true;\n break\n }\n scope$3.var.push(name);\n if (this.inModule && (scope$3.flags & SCOPE_TOP))\n { delete this.undefinedExports[name]; }\n if (scope$3.flags & SCOPE_VAR) { break }\n }\n }\n if (redeclared) { this.raiseRecoverable(pos, (\"Identifier '\" + name + \"' has already been declared\")); }\n };\n\n pp$3.checkLocalExport = function(id) {\n // scope.functions must be empty as Module code is always strict.\n if (this.scopeStack[0].lexical.indexOf(id.name) === -1 &&\n this.scopeStack[0].var.indexOf(id.name) === -1) {\n this.undefinedExports[id.name] = id;\n }\n };\n\n pp$3.currentScope = function() {\n return this.scopeStack[this.scopeStack.length - 1]\n };\n\n pp$3.currentVarScope = function() {\n for (var i = this.scopeStack.length - 1;; i--) {\n var scope = this.scopeStack[i];\n if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK)) { return scope }\n }\n };\n\n // Could be useful for `this`, `new.target`, `super()`, `super.property`, and `super[property]`.\n pp$3.currentThisScope = function() {\n for (var i = this.scopeStack.length - 1;; i--) {\n var scope = this.scopeStack[i];\n if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK) &&\n !(scope.flags & SCOPE_ARROW)) { return scope }\n }\n };\n\n var Node = function Node(parser, pos, loc) {\n this.type = \"\";\n this.start = pos;\n this.end = 0;\n if (parser.options.locations)\n { this.loc = new SourceLocation(parser, loc); }\n if (parser.options.directSourceFile)\n { this.sourceFile = parser.options.directSourceFile; }\n if (parser.options.ranges)\n { this.range = [pos, 0]; }\n };\n\n // Start an AST node, attaching a start offset.\n\n var pp$2 = Parser.prototype;\n\n pp$2.startNode = function() {\n return new Node(this, this.start, this.startLoc)\n };\n\n pp$2.startNodeAt = function(pos, loc) {\n return new Node(this, pos, loc)\n };\n\n // Finish an AST node, adding `type` and `end` properties.\n\n function finishNodeAt(node, type, pos, loc) {\n node.type = type;\n node.end = pos;\n if (this.options.locations)\n { node.loc.end = loc; }\n if (this.options.ranges)\n { node.range[1] = pos; }\n return node\n }\n\n pp$2.finishNode = function(node, type) {\n return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)\n };\n\n // Finish node at given position\n\n pp$2.finishNodeAt = function(node, type, pos, loc) {\n return finishNodeAt.call(this, node, type, pos, loc)\n };\n\n pp$2.copyNode = function(node) {\n var newNode = new Node(this, node.start, this.startLoc);\n for (var prop in node) { newNode[prop] = node[prop]; }\n return newNode\n };\n\n // This file was generated by \"bin/generate-unicode-script-values.js\". Do not modify manually!\n var scriptValuesAddedInUnicode = \"Berf Beria_Erfe Gara Garay Gukh Gurung_Khema Hrkt Katakana_Or_Hiragana Kawi Kirat_Rai Krai Nag_Mundari Nagm Ol_Onal Onao Sidetic Sidt Sunu Sunuwar Tai_Yo Tayo Todhri Todr Tolong_Siki Tols Tulu_Tigalari Tutg Unknown Zzzz\";\n\n // This file contains Unicode properties extracted from the ECMAScript specification.\n // The lists are extracted like so:\n // $$('#table-binary-unicode-properties > figure > table > tbody > tr > td:nth-child(1) code').map(el => el.innerText)\n\n // #table-binary-unicode-properties\n var ecma9BinaryProperties = \"ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS\";\n var ecma10BinaryProperties = ecma9BinaryProperties + \" Extended_Pictographic\";\n var ecma11BinaryProperties = ecma10BinaryProperties;\n var ecma12BinaryProperties = ecma11BinaryProperties + \" EBase EComp EMod EPres ExtPict\";\n var ecma13BinaryProperties = ecma12BinaryProperties;\n var ecma14BinaryProperties = ecma13BinaryProperties;\n\n var unicodeBinaryProperties = {\n 9: ecma9BinaryProperties,\n 10: ecma10BinaryProperties,\n 11: ecma11BinaryProperties,\n 12: ecma12BinaryProperties,\n 13: ecma13BinaryProperties,\n 14: ecma14BinaryProperties\n };\n\n // #table-binary-unicode-properties-of-strings\n var ecma14BinaryPropertiesOfStrings = \"Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji\";\n\n var unicodeBinaryPropertiesOfStrings = {\n 9: \"\",\n 10: \"\",\n 11: \"\",\n 12: \"\",\n 13: \"\",\n 14: ecma14BinaryPropertiesOfStrings\n };\n\n // #table-unicode-general-category-values\n var unicodeGeneralCategoryValues = \"Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu\";\n\n // #table-unicode-script-values\n var ecma9ScriptValues = \"Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb\";\n var ecma10ScriptValues = ecma9ScriptValues + \" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd\";\n var ecma11ScriptValues = ecma10ScriptValues + \" Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho\";\n var ecma12ScriptValues = ecma11ScriptValues + \" Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi\";\n var ecma13ScriptValues = ecma12ScriptValues + \" Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith\";\n var ecma14ScriptValues = ecma13ScriptValues + \" \" + scriptValuesAddedInUnicode;\n\n var unicodeScriptValues = {\n 9: ecma9ScriptValues,\n 10: ecma10ScriptValues,\n 11: ecma11ScriptValues,\n 12: ecma12ScriptValues,\n 13: ecma13ScriptValues,\n 14: ecma14ScriptValues\n };\n\n var data = {};\n function buildUnicodeData(ecmaVersion) {\n var d = data[ecmaVersion] = {\n binary: wordsRegexp(unicodeBinaryProperties[ecmaVersion] + \" \" + unicodeGeneralCategoryValues),\n binaryOfStrings: wordsRegexp(unicodeBinaryPropertiesOfStrings[ecmaVersion]),\n nonBinary: {\n General_Category: wordsRegexp(unicodeGeneralCategoryValues),\n Script: wordsRegexp(unicodeScriptValues[ecmaVersion])\n }\n };\n d.nonBinary.Script_Extensions = d.nonBinary.Script;\n\n d.nonBinary.gc = d.nonBinary.General_Category;\n d.nonBinary.sc = d.nonBinary.Script;\n d.nonBinary.scx = d.nonBinary.Script_Extensions;\n }\n\n for (var i = 0, list = [9, 10, 11, 12, 13, 14]; i < list.length; i += 1) {\n var ecmaVersion = list[i];\n\n buildUnicodeData(ecmaVersion);\n }\n\n var pp$1 = Parser.prototype;\n\n // Track disjunction structure to determine whether a duplicate\n // capture group name is allowed because it is in a separate branch.\n var BranchID = function BranchID(parent, base) {\n // Parent disjunction branch\n this.parent = parent;\n // Identifies this set of sibling branches\n this.base = base || this;\n };\n\n BranchID.prototype.separatedFrom = function separatedFrom (alt) {\n // A branch is separate from another branch if they or any of\n // their parents are siblings in a given disjunction\n for (var self = this; self; self = self.parent) {\n for (var other = alt; other; other = other.parent) {\n if (self.base === other.base && self !== other) { return true }\n }\n }\n return false\n };\n\n BranchID.prototype.sibling = function sibling () {\n return new BranchID(this.parent, this.base)\n };\n\n var RegExpValidationState = function RegExpValidationState(parser) {\n this.parser = parser;\n this.validFlags = \"gim\" + (parser.options.ecmaVersion >= 6 ? \"uy\" : \"\") + (parser.options.ecmaVersion >= 9 ? \"s\" : \"\") + (parser.options.ecmaVersion >= 13 ? \"d\" : \"\") + (parser.options.ecmaVersion >= 15 ? \"v\" : \"\");\n this.unicodeProperties = data[parser.options.ecmaVersion >= 14 ? 14 : parser.options.ecmaVersion];\n this.source = \"\";\n this.flags = \"\";\n this.start = 0;\n this.switchU = false;\n this.switchV = false;\n this.switchN = false;\n this.pos = 0;\n this.lastIntValue = 0;\n this.lastStringValue = \"\";\n this.lastAssertionIsQuantifiable = false;\n this.numCapturingParens = 0;\n this.maxBackReference = 0;\n this.groupNames = Object.create(null);\n this.backReferenceNames = [];\n this.branchID = null;\n };\n\n RegExpValidationState.prototype.reset = function reset (start, pattern, flags) {\n var unicodeSets = flags.indexOf(\"v\") !== -1;\n var unicode = flags.indexOf(\"u\") !== -1;\n this.start = start | 0;\n this.source = pattern + \"\";\n this.flags = flags;\n if (unicodeSets && this.parser.options.ecmaVersion >= 15) {\n this.switchU = true;\n this.switchV = true;\n this.switchN = true;\n } else {\n this.switchU = unicode && this.parser.options.ecmaVersion >= 6;\n this.switchV = false;\n this.switchN = unicode && this.parser.options.ecmaVersion >= 9;\n }\n };\n\n RegExpValidationState.prototype.raise = function raise (message) {\n this.parser.raiseRecoverable(this.start, (\"Invalid regular expression: /\" + (this.source) + \"/: \" + message));\n };\n\n // If u flag is given, this returns the code point at the index (it combines a surrogate pair).\n // Otherwise, this returns the code unit of the index (can be a part of a surrogate pair).\n RegExpValidationState.prototype.at = function at (i, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var s = this.source;\n var l = s.length;\n if (i >= l) {\n return -1\n }\n var c = s.charCodeAt(i);\n if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) {\n return c\n }\n var next = s.charCodeAt(i + 1);\n return next >= 0xDC00 && next <= 0xDFFF ? (c << 10) + next - 0x35FDC00 : c\n };\n\n RegExpValidationState.prototype.nextIndex = function nextIndex (i, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var s = this.source;\n var l = s.length;\n if (i >= l) {\n return l\n }\n var c = s.charCodeAt(i), next;\n if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l ||\n (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) {\n return i + 1\n }\n return i + 2\n };\n\n RegExpValidationState.prototype.current = function current (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n return this.at(this.pos, forceU)\n };\n\n RegExpValidationState.prototype.lookahead = function lookahead (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n return this.at(this.nextIndex(this.pos, forceU), forceU)\n };\n\n RegExpValidationState.prototype.advance = function advance (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n this.pos = this.nextIndex(this.pos, forceU);\n };\n\n RegExpValidationState.prototype.eat = function eat (ch, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n if (this.current(forceU) === ch) {\n this.advance(forceU);\n return true\n }\n return false\n };\n\n RegExpValidationState.prototype.eatChars = function eatChars (chs, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var pos = this.pos;\n for (var i = 0, list = chs; i < list.length; i += 1) {\n var ch = list[i];\n\n var current = this.at(pos, forceU);\n if (current === -1 || current !== ch) {\n return false\n }\n pos = this.nextIndex(pos, forceU);\n }\n this.pos = pos;\n return true\n };\n\n /**\n * Validate the flags part of a given RegExpLiteral.\n *\n * @param {RegExpValidationState} state The state to validate RegExp.\n * @returns {void}\n */\n pp$1.validateRegExpFlags = function(state) {\n var validFlags = state.validFlags;\n var flags = state.flags;\n\n var u = false;\n var v = false;\n\n for (var i = 0; i < flags.length; i++) {\n var flag = flags.charAt(i);\n if (validFlags.indexOf(flag) === -1) {\n this.raise(state.start, \"Invalid regular expression flag\");\n }\n if (flags.indexOf(flag, i + 1) > -1) {\n this.raise(state.start, \"Duplicate regular expression flag\");\n }\n if (flag === \"u\") { u = true; }\n if (flag === \"v\") { v = true; }\n }\n if (this.options.ecmaVersion >= 15 && u && v) {\n this.raise(state.start, \"Invalid regular expression flag\");\n }\n };\n\n function hasProp(obj) {\n for (var _ in obj) { return true }\n return false\n }\n\n /**\n * Validate the pattern part of a given RegExpLiteral.\n *\n * @param {RegExpValidationState} state The state to validate RegExp.\n * @returns {void}\n */\n pp$1.validateRegExpPattern = function(state) {\n this.regexp_pattern(state);\n\n // The goal symbol for the parse is |Pattern[~U, ~N]|. If the result of\n // parsing contains a |GroupName|, reparse with the goal symbol\n // |Pattern[~U, +N]| and use this result instead. Throw a *SyntaxError*\n // exception if _P_ did not conform to the grammar, if any elements of _P_\n // were not matched by the parse, or if any Early Error conditions exist.\n if (!state.switchN && this.options.ecmaVersion >= 9 && hasProp(state.groupNames)) {\n state.switchN = true;\n this.regexp_pattern(state);\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Pattern\n pp$1.regexp_pattern = function(state) {\n state.pos = 0;\n state.lastIntValue = 0;\n state.lastStringValue = \"\";\n state.lastAssertionIsQuantifiable = false;\n state.numCapturingParens = 0;\n state.maxBackReference = 0;\n state.groupNames = Object.create(null);\n state.backReferenceNames.length = 0;\n state.branchID = null;\n\n this.regexp_disjunction(state);\n\n if (state.pos !== state.source.length) {\n // Make the same messages as V8.\n if (state.eat(0x29 /* ) */)) {\n state.raise(\"Unmatched ')'\");\n }\n if (state.eat(0x5D /* ] */) || state.eat(0x7D /* } */)) {\n state.raise(\"Lone quantifier brackets\");\n }\n }\n if (state.maxBackReference > state.numCapturingParens) {\n state.raise(\"Invalid escape\");\n }\n for (var i = 0, list = state.backReferenceNames; i < list.length; i += 1) {\n var name = list[i];\n\n if (!state.groupNames[name]) {\n state.raise(\"Invalid named capture referenced\");\n }\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Disjunction\n pp$1.regexp_disjunction = function(state) {\n var trackDisjunction = this.options.ecmaVersion >= 16;\n if (trackDisjunction) { state.branchID = new BranchID(state.branchID, null); }\n this.regexp_alternative(state);\n while (state.eat(0x7C /* | */)) {\n if (trackDisjunction) { state.branchID = state.branchID.sibling(); }\n this.regexp_alternative(state);\n }\n if (trackDisjunction) { state.branchID = state.branchID.parent; }\n\n // Make the same message as V8.\n if (this.regexp_eatQuantifier(state, true)) {\n state.raise(\"Nothing to repeat\");\n }\n if (state.eat(0x7B /* { */)) {\n state.raise(\"Lone quantifier brackets\");\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Alternative\n pp$1.regexp_alternative = function(state) {\n while (state.pos < state.source.length && this.regexp_eatTerm(state)) {}\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Term\n pp$1.regexp_eatTerm = function(state) {\n if (this.regexp_eatAssertion(state)) {\n // Handle `QuantifiableAssertion Quantifier` alternative.\n // `state.lastAssertionIsQuantifiable` is true if the last eaten Assertion\n // is a QuantifiableAssertion.\n if (state.lastAssertionIsQuantifiable && this.regexp_eatQuantifier(state)) {\n // Make the same message as V8.\n if (state.switchU) {\n state.raise(\"Invalid quantifier\");\n }\n }\n return true\n }\n\n if (state.switchU ? this.regexp_eatAtom(state) : this.regexp_eatExtendedAtom(state)) {\n this.regexp_eatQuantifier(state);\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Assertion\n pp$1.regexp_eatAssertion = function(state) {\n var start = state.pos;\n state.lastAssertionIsQuantifiable = false;\n\n // ^, $\n if (state.eat(0x5E /* ^ */) || state.eat(0x24 /* $ */)) {\n return true\n }\n\n // \\b \\B\n if (state.eat(0x5C /* \\ */)) {\n if (state.eat(0x42 /* B */) || state.eat(0x62 /* b */)) {\n return true\n }\n state.pos = start;\n }\n\n // Lookahead / Lookbehind\n if (state.eat(0x28 /* ( */) && state.eat(0x3F /* ? */)) {\n var lookbehind = false;\n if (this.options.ecmaVersion >= 9) {\n lookbehind = state.eat(0x3C /* < */);\n }\n if (state.eat(0x3D /* = */) || state.eat(0x21 /* ! */)) {\n this.regexp_disjunction(state);\n if (!state.eat(0x29 /* ) */)) {\n state.raise(\"Unterminated group\");\n }\n state.lastAssertionIsQuantifiable = !lookbehind;\n return true\n }\n }\n\n state.pos = start;\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Quantifier\n pp$1.regexp_eatQuantifier = function(state, noError) {\n if ( noError === void 0 ) noError = false;\n\n if (this.regexp_eatQuantifierPrefix(state, noError)) {\n state.eat(0x3F /* ? */);\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-QuantifierPrefix\n pp$1.regexp_eatQuantifierPrefix = function(state, noError) {\n return (\n state.eat(0x2A /* * */) ||\n state.eat(0x2B /* + */) ||\n state.eat(0x3F /* ? */) ||\n this.regexp_eatBracedQuantifier(state, noError)\n )\n };\n pp$1.regexp_eatBracedQuantifier = function(state, noError) {\n var start = state.pos;\n if (state.eat(0x7B /* { */)) {\n var min = 0, max = -1;\n if (this.regexp_eatDecimalDigits(state)) {\n min = state.lastIntValue;\n if (state.eat(0x2C /* , */) && this.regexp_eatDecimalDigits(state)) {\n max = state.lastIntValue;\n }\n if (state.eat(0x7D /* } */)) {\n // SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-term\n if (max !== -1 && max < min && !noError) {\n state.raise(\"numbers out of order in {} quantifier\");\n }\n return true\n }\n }\n if (state.switchU && !noError) {\n state.raise(\"Incomplete quantifier\");\n }\n state.pos = start;\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Atom\n pp$1.regexp_eatAtom = function(state) {\n return (\n this.regexp_eatPatternCharacters(state) ||\n state.eat(0x2E /* . */) ||\n this.regexp_eatReverseSolidusAtomEscape(state) ||\n this.regexp_eatCharacterClass(state) ||\n this.regexp_eatUncapturingGroup(state) ||\n this.regexp_eatCapturingGroup(state)\n )\n };\n pp$1.regexp_eatReverseSolidusAtomEscape = function(state) {\n var start = state.pos;\n if (state.eat(0x5C /* \\ */)) {\n if (this.regexp_eatAtomEscape(state)) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatUncapturingGroup = function(state) {\n var start = state.pos;\n if (state.eat(0x28 /* ( */)) {\n if (state.eat(0x3F /* ? */)) {\n if (this.options.ecmaVersion >= 16) {\n var addModifiers = this.regexp_eatModifiers(state);\n var hasHyphen = state.eat(0x2D /* - */);\n if (addModifiers || hasHyphen) {\n for (var i = 0; i < addModifiers.length; i++) {\n var modifier = addModifiers.charAt(i);\n if (addModifiers.indexOf(modifier, i + 1) > -1) {\n state.raise(\"Duplicate regular expression modifiers\");\n }\n }\n if (hasHyphen) {\n var removeModifiers = this.regexp_eatModifiers(state);\n if (!addModifiers && !removeModifiers && state.current() === 0x3A /* : */) {\n state.raise(\"Invalid regular expression modifiers\");\n }\n for (var i$1 = 0; i$1 < removeModifiers.length; i$1++) {\n var modifier$1 = removeModifiers.charAt(i$1);\n if (\n removeModifiers.indexOf(modifier$1, i$1 + 1) > -1 ||\n addModifiers.indexOf(modifier$1) > -1\n ) {\n state.raise(\"Duplicate regular expression modifiers\");\n }\n }\n }\n }\n }\n if (state.eat(0x3A /* : */)) {\n this.regexp_disjunction(state);\n if (state.eat(0x29 /* ) */)) {\n return true\n }\n state.raise(\"Unterminated group\");\n }\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatCapturingGroup = function(state) {\n if (state.eat(0x28 /* ( */)) {\n if (this.options.ecmaVersion >= 9) {\n this.regexp_groupSpecifier(state);\n } else if (state.current() === 0x3F /* ? */) {\n state.raise(\"Invalid group\");\n }\n this.regexp_disjunction(state);\n if (state.eat(0x29 /* ) */)) {\n state.numCapturingParens += 1;\n return true\n }\n state.raise(\"Unterminated group\");\n }\n return false\n };\n // RegularExpressionModifiers ::\n // [empty]\n // RegularExpressionModifiers RegularExpressionModifier\n pp$1.regexp_eatModifiers = function(state) {\n var modifiers = \"\";\n var ch = 0;\n while ((ch = state.current()) !== -1 && isRegularExpressionModifier(ch)) {\n modifiers += codePointToString(ch);\n state.advance();\n }\n return modifiers\n };\n // RegularExpressionModifier :: one of\n // `i` `m` `s`\n function isRegularExpressionModifier(ch) {\n return ch === 0x69 /* i */ || ch === 0x6d /* m */ || ch === 0x73 /* s */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedAtom\n pp$1.regexp_eatExtendedAtom = function(state) {\n return (\n state.eat(0x2E /* . */) ||\n this.regexp_eatReverseSolidusAtomEscape(state) ||\n this.regexp_eatCharacterClass(state) ||\n this.regexp_eatUncapturingGroup(state) ||\n this.regexp_eatCapturingGroup(state) ||\n this.regexp_eatInvalidBracedQuantifier(state) ||\n this.regexp_eatExtendedPatternCharacter(state)\n )\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-InvalidBracedQuantifier\n pp$1.regexp_eatInvalidBracedQuantifier = function(state) {\n if (this.regexp_eatBracedQuantifier(state, true)) {\n state.raise(\"Nothing to repeat\");\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-SyntaxCharacter\n pp$1.regexp_eatSyntaxCharacter = function(state) {\n var ch = state.current();\n if (isSyntaxCharacter(ch)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n return false\n };\n function isSyntaxCharacter(ch) {\n return (\n ch === 0x24 /* $ */ ||\n ch >= 0x28 /* ( */ && ch <= 0x2B /* + */ ||\n ch === 0x2E /* . */ ||\n ch === 0x3F /* ? */ ||\n ch >= 0x5B /* [ */ && ch <= 0x5E /* ^ */ ||\n ch >= 0x7B /* { */ && ch <= 0x7D /* } */\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-PatternCharacter\n // But eat eager.\n pp$1.regexp_eatPatternCharacters = function(state) {\n var start = state.pos;\n var ch = 0;\n while ((ch = state.current()) !== -1 && !isSyntaxCharacter(ch)) {\n state.advance();\n }\n return state.pos !== start\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedPatternCharacter\n pp$1.regexp_eatExtendedPatternCharacter = function(state) {\n var ch = state.current();\n if (\n ch !== -1 &&\n ch !== 0x24 /* $ */ &&\n !(ch >= 0x28 /* ( */ && ch <= 0x2B /* + */) &&\n ch !== 0x2E /* . */ &&\n ch !== 0x3F /* ? */ &&\n ch !== 0x5B /* [ */ &&\n ch !== 0x5E /* ^ */ &&\n ch !== 0x7C /* | */\n ) {\n state.advance();\n return true\n }\n return false\n };\n\n // GroupSpecifier ::\n // [empty]\n // `?` GroupName\n pp$1.regexp_groupSpecifier = function(state) {\n if (state.eat(0x3F /* ? */)) {\n if (!this.regexp_eatGroupName(state)) { state.raise(\"Invalid group\"); }\n var trackDisjunction = this.options.ecmaVersion >= 16;\n var known = state.groupNames[state.lastStringValue];\n if (known) {\n if (trackDisjunction) {\n for (var i = 0, list = known; i < list.length; i += 1) {\n var altID = list[i];\n\n if (!altID.separatedFrom(state.branchID))\n { state.raise(\"Duplicate capture group name\"); }\n }\n } else {\n state.raise(\"Duplicate capture group name\");\n }\n }\n if (trackDisjunction) {\n (known || (state.groupNames[state.lastStringValue] = [])).push(state.branchID);\n } else {\n state.groupNames[state.lastStringValue] = true;\n }\n }\n };\n\n // GroupName ::\n // `<` RegExpIdentifierName `>`\n // Note: this updates `state.lastStringValue` property with the eaten name.\n pp$1.regexp_eatGroupName = function(state) {\n state.lastStringValue = \"\";\n if (state.eat(0x3C /* < */)) {\n if (this.regexp_eatRegExpIdentifierName(state) && state.eat(0x3E /* > */)) {\n return true\n }\n state.raise(\"Invalid capture group name\");\n }\n return false\n };\n\n // RegExpIdentifierName ::\n // RegExpIdentifierStart\n // RegExpIdentifierName RegExpIdentifierPart\n // Note: this updates `state.lastStringValue` property with the eaten name.\n pp$1.regexp_eatRegExpIdentifierName = function(state) {\n state.lastStringValue = \"\";\n if (this.regexp_eatRegExpIdentifierStart(state)) {\n state.lastStringValue += codePointToString(state.lastIntValue);\n while (this.regexp_eatRegExpIdentifierPart(state)) {\n state.lastStringValue += codePointToString(state.lastIntValue);\n }\n return true\n }\n return false\n };\n\n // RegExpIdentifierStart ::\n // UnicodeIDStart\n // `$`\n // `_`\n // `\\` RegExpUnicodeEscapeSequence[+U]\n pp$1.regexp_eatRegExpIdentifierStart = function(state) {\n var start = state.pos;\n var forceU = this.options.ecmaVersion >= 11;\n var ch = state.current(forceU);\n state.advance(forceU);\n\n if (ch === 0x5C /* \\ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n ch = state.lastIntValue;\n }\n if (isRegExpIdentifierStart(ch)) {\n state.lastIntValue = ch;\n return true\n }\n\n state.pos = start;\n return false\n };\n function isRegExpIdentifierStart(ch) {\n return isIdentifierStart(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */\n }\n\n // RegExpIdentifierPart ::\n // UnicodeIDContinue\n // `$`\n // `_`\n // `\\` RegExpUnicodeEscapeSequence[+U]\n // <ZWNJ>\n // <ZWJ>\n pp$1.regexp_eatRegExpIdentifierPart = function(state) {\n var start = state.pos;\n var forceU = this.options.ecmaVersion >= 11;\n var ch = state.current(forceU);\n state.advance(forceU);\n\n if (ch === 0x5C /* \\ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n ch = state.lastIntValue;\n }\n if (isRegExpIdentifierPart(ch)) {\n state.lastIntValue = ch;\n return true\n }\n\n state.pos = start;\n return false\n };\n function isRegExpIdentifierPart(ch) {\n return isIdentifierChar(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */ || ch === 0x200C /* <ZWNJ> */ || ch === 0x200D /* <ZWJ> */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-AtomEscape\n pp$1.regexp_eatAtomEscape = function(state) {\n if (\n this.regexp_eatBackReference(state) ||\n this.regexp_eatCharacterClassEscape(state) ||\n this.regexp_eatCharacterEscape(state) ||\n (state.switchN && this.regexp_eatKGroupName(state))\n ) {\n return true\n }\n if (state.switchU) {\n // Make the same message as V8.\n if (state.current() === 0x63 /* c */) {\n state.raise(\"Invalid unicode escape\");\n }\n state.raise(\"Invalid escape\");\n }\n return false\n };\n pp$1.regexp_eatBackReference = function(state) {\n var start = state.pos;\n if (this.regexp_eatDecimalEscape(state)) {\n var n = state.lastIntValue;\n if (state.switchU) {\n // For SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-atomescape\n if (n > state.maxBackReference) {\n state.maxBackReference = n;\n }\n return true\n }\n if (n <= state.numCapturingParens) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatKGroupName = function(state) {\n if (state.eat(0x6B /* k */)) {\n if (this.regexp_eatGroupName(state)) {\n state.backReferenceNames.push(state.lastStringValue);\n return true\n }\n state.raise(\"Invalid named reference\");\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-CharacterEscape\n pp$1.regexp_eatCharacterEscape = function(state) {\n return (\n this.regexp_eatControlEscape(state) ||\n this.regexp_eatCControlLetter(state) ||\n this.regexp_eatZero(state) ||\n this.regexp_eatHexEscapeSequence(state) ||\n this.regexp_eatRegExpUnicodeEscapeSequence(state, false) ||\n (!state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state)) ||\n this.regexp_eatIdentityEscape(state)\n )\n };\n pp$1.regexp_eatCControlLetter = function(state) {\n var start = state.pos;\n if (state.eat(0x63 /* c */)) {\n if (this.regexp_eatControlLetter(state)) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatZero = function(state) {\n if (state.current() === 0x30 /* 0 */ && !isDecimalDigit(state.lookahead())) {\n state.lastIntValue = 0;\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlEscape\n pp$1.regexp_eatControlEscape = function(state) {\n var ch = state.current();\n if (ch === 0x74 /* t */) {\n state.lastIntValue = 0x09; /* \\t */\n state.advance();\n return true\n }\n if (ch === 0x6E /* n */) {\n state.lastIntValue = 0x0A; /* \\n */\n state.advance();\n return true\n }\n if (ch === 0x76 /* v */) {\n state.lastIntValue = 0x0B; /* \\v */\n state.advance();\n return true\n }\n if (ch === 0x66 /* f */) {\n state.lastIntValue = 0x0C; /* \\f */\n state.advance();\n return true\n }\n if (ch === 0x72 /* r */) {\n state.lastIntValue = 0x0D; /* \\r */\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlLetter\n pp$1.regexp_eatControlLetter = function(state) {\n var ch = state.current();\n if (isControlLetter(ch)) {\n state.lastIntValue = ch % 0x20;\n state.advance();\n return true\n }\n return false\n };\n function isControlLetter(ch) {\n return (\n (ch >= 0x41 /* A */ && ch <= 0x5A /* Z */) ||\n (ch >= 0x61 /* a */ && ch <= 0x7A /* z */)\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-RegExpUnicodeEscapeSequence\n pp$1.regexp_eatRegExpUnicodeEscapeSequence = function(state, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var start = state.pos;\n var switchU = forceU || state.switchU;\n\n if (state.eat(0x75 /* u */)) {\n if (this.regexp_eatFixedHexDigits(state, 4)) {\n var lead = state.lastIntValue;\n if (switchU && lead >= 0xD800 && lead <= 0xDBFF) {\n var leadSurrogateEnd = state.pos;\n if (state.eat(0x5C /* \\ */) && state.eat(0x75 /* u */) && this.regexp_eatFixedHexDigits(state, 4)) {\n var trail = state.lastIntValue;\n if (trail >= 0xDC00 && trail <= 0xDFFF) {\n state.lastIntValue = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;\n return true\n }\n }\n state.pos = leadSurrogateEnd;\n state.lastIntValue = lead;\n }\n return true\n }\n if (\n switchU &&\n state.eat(0x7B /* { */) &&\n this.regexp_eatHexDigits(state) &&\n state.eat(0x7D /* } */) &&\n isValidUnicode(state.lastIntValue)\n ) {\n return true\n }\n if (switchU) {\n state.raise(\"Invalid unicode escape\");\n }\n state.pos = start;\n }\n\n return false\n };\n function isValidUnicode(ch) {\n return ch >= 0 && ch <= 0x10FFFF\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-IdentityEscape\n pp$1.regexp_eatIdentityEscape = function(state) {\n if (state.switchU) {\n if (this.regexp_eatSyntaxCharacter(state)) {\n return true\n }\n if (state.eat(0x2F /* / */)) {\n state.lastIntValue = 0x2F; /* / */\n return true\n }\n return false\n }\n\n var ch = state.current();\n if (ch !== 0x63 /* c */ && (!state.switchN || ch !== 0x6B /* k */)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalEscape\n pp$1.regexp_eatDecimalEscape = function(state) {\n state.lastIntValue = 0;\n var ch = state.current();\n if (ch >= 0x31 /* 1 */ && ch <= 0x39 /* 9 */) {\n do {\n state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);\n state.advance();\n } while ((ch = state.current()) >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */)\n return true\n }\n return false\n };\n\n // Return values used by character set parsing methods, needed to\n // forbid negation of sets that can match strings.\n var CharSetNone = 0; // Nothing parsed\n var CharSetOk = 1; // Construct parsed, cannot contain strings\n var CharSetString = 2; // Construct parsed, can contain strings\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClassEscape\n pp$1.regexp_eatCharacterClassEscape = function(state) {\n var ch = state.current();\n\n if (isCharacterClassEscape(ch)) {\n state.lastIntValue = -1;\n state.advance();\n return CharSetOk\n }\n\n var negate = false;\n if (\n state.switchU &&\n this.options.ecmaVersion >= 9 &&\n ((negate = ch === 0x50 /* P */) || ch === 0x70 /* p */)\n ) {\n state.lastIntValue = -1;\n state.advance();\n var result;\n if (\n state.eat(0x7B /* { */) &&\n (result = this.regexp_eatUnicodePropertyValueExpression(state)) &&\n state.eat(0x7D /* } */)\n ) {\n if (negate && result === CharSetString) { state.raise(\"Invalid property name\"); }\n return result\n }\n state.raise(\"Invalid property name\");\n }\n\n return CharSetNone\n };\n\n function isCharacterClassEscape(ch) {\n return (\n ch === 0x64 /* d */ ||\n ch === 0x44 /* D */ ||\n ch === 0x73 /* s */ ||\n ch === 0x53 /* S */ ||\n ch === 0x77 /* w */ ||\n ch === 0x57 /* W */\n )\n }\n\n // UnicodePropertyValueExpression ::\n // UnicodePropertyName `=` UnicodePropertyValue\n // LoneUnicodePropertyNameOrValue\n pp$1.regexp_eatUnicodePropertyValueExpression = function(state) {\n var start = state.pos;\n\n // UnicodePropertyName `=` UnicodePropertyValue\n if (this.regexp_eatUnicodePropertyName(state) && state.eat(0x3D /* = */)) {\n var name = state.lastStringValue;\n if (this.regexp_eatUnicodePropertyValue(state)) {\n var value = state.lastStringValue;\n this.regexp_validateUnicodePropertyNameAndValue(state, name, value);\n return CharSetOk\n }\n }\n state.pos = start;\n\n // LoneUnicodePropertyNameOrValue\n if (this.regexp_eatLoneUnicodePropertyNameOrValue(state)) {\n var nameOrValue = state.lastStringValue;\n return this.regexp_validateUnicodePropertyNameOrValue(state, nameOrValue)\n }\n return CharSetNone\n };\n\n pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) {\n if (!hasOwn(state.unicodeProperties.nonBinary, name))\n { state.raise(\"Invalid property name\"); }\n if (!state.unicodeProperties.nonBinary[name].test(value))\n { state.raise(\"Invalid property value\"); }\n };\n\n pp$1.regexp_validateUnicodePropertyNameOrValue = function(state, nameOrValue) {\n if (state.unicodeProperties.binary.test(nameOrValue)) { return CharSetOk }\n if (state.switchV && state.unicodeProperties.binaryOfStrings.test(nameOrValue)) { return CharSetString }\n state.raise(\"Invalid property name\");\n };\n\n // UnicodePropertyName ::\n // UnicodePropertyNameCharacters\n pp$1.regexp_eatUnicodePropertyName = function(state) {\n var ch = 0;\n state.lastStringValue = \"\";\n while (isUnicodePropertyNameCharacter(ch = state.current())) {\n state.lastStringValue += codePointToString(ch);\n state.advance();\n }\n return state.lastStringValue !== \"\"\n };\n\n function isUnicodePropertyNameCharacter(ch) {\n return isControlLetter(ch) || ch === 0x5F /* _ */\n }\n\n // UnicodePropertyValue ::\n // UnicodePropertyValueCharacters\n pp$1.regexp_eatUnicodePropertyValue = function(state) {\n var ch = 0;\n state.lastStringValue = \"\";\n while (isUnicodePropertyValueCharacter(ch = state.current())) {\n state.lastStringValue += codePointToString(ch);\n state.advance();\n }\n return state.lastStringValue !== \"\"\n };\n function isUnicodePropertyValueCharacter(ch) {\n return isUnicodePropertyNameCharacter(ch) || isDecimalDigit(ch)\n }\n\n // LoneUnicodePropertyNameOrValue ::\n // UnicodePropertyValueCharacters\n pp$1.regexp_eatLoneUnicodePropertyNameOrValue = function(state) {\n return this.regexp_eatUnicodePropertyValue(state)\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClass\n pp$1.regexp_eatCharacterClass = function(state) {\n if (state.eat(0x5B /* [ */)) {\n var negate = state.eat(0x5E /* ^ */);\n var result = this.regexp_classContents(state);\n if (!state.eat(0x5D /* ] */))\n { state.raise(\"Unterminated character class\"); }\n if (negate && result === CharSetString)\n { state.raise(\"Negated character class may contain strings\"); }\n return true\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassContents\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassRanges\n pp$1.regexp_classContents = function(state) {\n if (state.current() === 0x5D /* ] */) { return CharSetOk }\n if (state.switchV) { return this.regexp_classSetExpression(state) }\n this.regexp_nonEmptyClassRanges(state);\n return CharSetOk\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRanges\n // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRangesNoDash\n pp$1.regexp_nonEmptyClassRanges = function(state) {\n while (this.regexp_eatClassAtom(state)) {\n var left = state.lastIntValue;\n if (state.eat(0x2D /* - */) && this.regexp_eatClassAtom(state)) {\n var right = state.lastIntValue;\n if (state.switchU && (left === -1 || right === -1)) {\n state.raise(\"Invalid character class\");\n }\n if (left !== -1 && right !== -1 && left > right) {\n state.raise(\"Range out of order in character class\");\n }\n }\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtom\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtomNoDash\n pp$1.regexp_eatClassAtom = function(state) {\n var start = state.pos;\n\n if (state.eat(0x5C /* \\ */)) {\n if (this.regexp_eatClassEscape(state)) {\n return true\n }\n if (state.switchU) {\n // Make the same message as V8.\n var ch$1 = state.current();\n if (ch$1 === 0x63 /* c */ || isOctalDigit(ch$1)) {\n state.raise(\"Invalid class escape\");\n }\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n\n var ch = state.current();\n if (ch !== 0x5D /* ] */) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassEscape\n pp$1.regexp_eatClassEscape = function(state) {\n var start = state.pos;\n\n if (state.eat(0x62 /* b */)) {\n state.lastIntValue = 0x08; /* <BS> */\n return true\n }\n\n if (state.switchU && state.eat(0x2D /* - */)) {\n state.lastIntValue = 0x2D; /* - */\n return true\n }\n\n if (!state.switchU && state.eat(0x63 /* c */)) {\n if (this.regexp_eatClassControlLetter(state)) {\n return true\n }\n state.pos = start;\n }\n\n return (\n this.regexp_eatCharacterClassEscape(state) ||\n this.regexp_eatCharacterEscape(state)\n )\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetExpression\n // https://tc39.es/ecma262/#prod-ClassUnion\n // https://tc39.es/ecma262/#prod-ClassIntersection\n // https://tc39.es/ecma262/#prod-ClassSubtraction\n pp$1.regexp_classSetExpression = function(state) {\n var result = CharSetOk, subResult;\n if (this.regexp_eatClassSetRange(state)) ; else if (subResult = this.regexp_eatClassSetOperand(state)) {\n if (subResult === CharSetString) { result = CharSetString; }\n // https://tc39.es/ecma262/#prod-ClassIntersection\n var start = state.pos;\n while (state.eatChars([0x26, 0x26] /* && */)) {\n if (\n state.current() !== 0x26 /* & */ &&\n (subResult = this.regexp_eatClassSetOperand(state))\n ) {\n if (subResult !== CharSetString) { result = CharSetOk; }\n continue\n }\n state.raise(\"Invalid character in character class\");\n }\n if (start !== state.pos) { return result }\n // https://tc39.es/ecma262/#prod-ClassSubtraction\n while (state.eatChars([0x2D, 0x2D] /* -- */)) {\n if (this.regexp_eatClassSetOperand(state)) { continue }\n state.raise(\"Invalid character in character class\");\n }\n if (start !== state.pos) { return result }\n } else {\n state.raise(\"Invalid character in character class\");\n }\n // https://tc39.es/ecma262/#prod-ClassUnion\n for (;;) {\n if (this.regexp_eatClassSetRange(state)) { continue }\n subResult = this.regexp_eatClassSetOperand(state);\n if (!subResult) { return result }\n if (subResult === CharSetString) { result = CharSetString; }\n }\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetRange\n pp$1.regexp_eatClassSetRange = function(state) {\n var start = state.pos;\n if (this.regexp_eatClassSetCharacter(state)) {\n var left = state.lastIntValue;\n if (state.eat(0x2D /* - */) && this.regexp_eatClassSetCharacter(state)) {\n var right = state.lastIntValue;\n if (left !== -1 && right !== -1 && left > right) {\n state.raise(\"Range out of order in character class\");\n }\n return true\n }\n state.pos = start;\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetOperand\n pp$1.regexp_eatClassSetOperand = function(state) {\n if (this.regexp_eatClassSetCharacter(state)) { return CharSetOk }\n return this.regexp_eatClassStringDisjunction(state) || this.regexp_eatNestedClass(state)\n };\n\n // https://tc39.es/ecma262/#prod-NestedClass\n pp$1.regexp_eatNestedClass = function(state) {\n var start = state.pos;\n if (state.eat(0x5B /* [ */)) {\n var negate = state.eat(0x5E /* ^ */);\n var result = this.regexp_classContents(state);\n if (state.eat(0x5D /* ] */)) {\n if (negate && result === CharSetString) {\n state.raise(\"Negated character class may contain strings\");\n }\n return result\n }\n state.pos = start;\n }\n if (state.eat(0x5C /* \\ */)) {\n var result$1 = this.regexp_eatCharacterClassEscape(state);\n if (result$1) {\n return result$1\n }\n state.pos = start;\n }\n return null\n };\n\n // https://tc39.es/ecma262/#prod-ClassStringDisjunction\n pp$1.regexp_eatClassStringDisjunction = function(state) {\n var start = state.pos;\n if (state.eatChars([0x5C, 0x71] /* \\q */)) {\n if (state.eat(0x7B /* { */)) {\n var result = this.regexp_classStringDisjunctionContents(state);\n if (state.eat(0x7D /* } */)) {\n return result\n }\n } else {\n // Make the same message as V8.\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n return null\n };\n\n // https://tc39.es/ecma262/#prod-ClassStringDisjunctionContents\n pp$1.regexp_classStringDisjunctionContents = function(state) {\n var result = this.regexp_classString(state);\n while (state.eat(0x7C /* | */)) {\n if (this.regexp_classString(state) === CharSetString) { result = CharSetString; }\n }\n return result\n };\n\n // https://tc39.es/ecma262/#prod-ClassString\n // https://tc39.es/ecma262/#prod-NonEmptyClassString\n pp$1.regexp_classString = function(state) {\n var count = 0;\n while (this.regexp_eatClassSetCharacter(state)) { count++; }\n return count === 1 ? CharSetOk : CharSetString\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetCharacter\n pp$1.regexp_eatClassSetCharacter = function(state) {\n var start = state.pos;\n if (state.eat(0x5C /* \\ */)) {\n if (\n this.regexp_eatCharacterEscape(state) ||\n this.regexp_eatClassSetReservedPunctuator(state)\n ) {\n return true\n }\n if (state.eat(0x62 /* b */)) {\n state.lastIntValue = 0x08; /* <BS> */\n return true\n }\n state.pos = start;\n return false\n }\n var ch = state.current();\n if (ch < 0 || ch === state.lookahead() && isClassSetReservedDoublePunctuatorCharacter(ch)) { return false }\n if (isClassSetSyntaxCharacter(ch)) { return false }\n state.advance();\n state.lastIntValue = ch;\n return true\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedDoublePunctuator\n function isClassSetReservedDoublePunctuatorCharacter(ch) {\n return (\n ch === 0x21 /* ! */ ||\n ch >= 0x23 /* # */ && ch <= 0x26 /* & */ ||\n ch >= 0x2A /* * */ && ch <= 0x2C /* , */ ||\n ch === 0x2E /* . */ ||\n ch >= 0x3A /* : */ && ch <= 0x40 /* @ */ ||\n ch === 0x5E /* ^ */ ||\n ch === 0x60 /* ` */ ||\n ch === 0x7E /* ~ */\n )\n }\n\n // https://tc39.es/ecma262/#prod-ClassSetSyntaxCharacter\n function isClassSetSyntaxCharacter(ch) {\n return (\n ch === 0x28 /* ( */ ||\n ch === 0x29 /* ) */ ||\n ch === 0x2D /* - */ ||\n ch === 0x2F /* / */ ||\n ch >= 0x5B /* [ */ && ch <= 0x5D /* ] */ ||\n ch >= 0x7B /* { */ && ch <= 0x7D /* } */\n )\n }\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedPunctuator\n pp$1.regexp_eatClassSetReservedPunctuator = function(state) {\n var ch = state.current();\n if (isClassSetReservedPunctuator(ch)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedPunctuator\n function isClassSetReservedPunctuator(ch) {\n return (\n ch === 0x21 /* ! */ ||\n ch === 0x23 /* # */ ||\n ch === 0x25 /* % */ ||\n ch === 0x26 /* & */ ||\n ch === 0x2C /* , */ ||\n ch === 0x2D /* - */ ||\n ch >= 0x3A /* : */ && ch <= 0x3E /* > */ ||\n ch === 0x40 /* @ */ ||\n ch === 0x60 /* ` */ ||\n ch === 0x7E /* ~ */\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassControlLetter\n pp$1.regexp_eatClassControlLetter = function(state) {\n var ch = state.current();\n if (isDecimalDigit(ch) || ch === 0x5F /* _ */) {\n state.lastIntValue = ch % 0x20;\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence\n pp$1.regexp_eatHexEscapeSequence = function(state) {\n var start = state.pos;\n if (state.eat(0x78 /* x */)) {\n if (this.regexp_eatFixedHexDigits(state, 2)) {\n return true\n }\n if (state.switchU) {\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalDigits\n pp$1.regexp_eatDecimalDigits = function(state) {\n var start = state.pos;\n var ch = 0;\n state.lastIntValue = 0;\n while (isDecimalDigit(ch = state.current())) {\n state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);\n state.advance();\n }\n return state.pos !== start\n };\n function isDecimalDigit(ch) {\n return ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigits\n pp$1.regexp_eatHexDigits = function(state) {\n var start = state.pos;\n var ch = 0;\n state.lastIntValue = 0;\n while (isHexDigit(ch = state.current())) {\n state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n state.advance();\n }\n return state.pos !== start\n };\n function isHexDigit(ch) {\n return (\n (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) ||\n (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) ||\n (ch >= 0x61 /* a */ && ch <= 0x66 /* f */)\n )\n }\n function hexToInt(ch) {\n if (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) {\n return 10 + (ch - 0x41 /* A */)\n }\n if (ch >= 0x61 /* a */ && ch <= 0x66 /* f */) {\n return 10 + (ch - 0x61 /* a */)\n }\n return ch - 0x30 /* 0 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-LegacyOctalEscapeSequence\n // Allows only 0-377(octal) i.e. 0-255(decimal).\n pp$1.regexp_eatLegacyOctalEscapeSequence = function(state) {\n if (this.regexp_eatOctalDigit(state)) {\n var n1 = state.lastIntValue;\n if (this.regexp_eatOctalDigit(state)) {\n var n2 = state.lastIntValue;\n if (n1 <= 3 && this.regexp_eatOctalDigit(state)) {\n state.lastIntValue = n1 * 64 + n2 * 8 + state.lastIntValue;\n } else {\n state.lastIntValue = n1 * 8 + n2;\n }\n } else {\n state.lastIntValue = n1;\n }\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-OctalDigit\n pp$1.regexp_eatOctalDigit = function(state) {\n var ch = state.current();\n if (isOctalDigit(ch)) {\n state.lastIntValue = ch - 0x30; /* 0 */\n state.advance();\n return true\n }\n state.lastIntValue = 0;\n return false\n };\n function isOctalDigit(ch) {\n return ch >= 0x30 /* 0 */ && ch <= 0x37 /* 7 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Hex4Digits\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigit\n // And HexDigit HexDigit in https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence\n pp$1.regexp_eatFixedHexDigits = function(state, length) {\n var start = state.pos;\n state.lastIntValue = 0;\n for (var i = 0; i < length; ++i) {\n var ch = state.current();\n if (!isHexDigit(ch)) {\n state.pos = start;\n return false\n }\n state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n state.advance();\n }\n return true\n };\n\n // Object type used to represent tokens. Note that normally, tokens\n // simply exist as properties on the parser object. This is only\n // used for the onToken callback and the external tokenizer.\n\n var Token = function Token(p) {\n this.type = p.type;\n this.value = p.value;\n this.start = p.start;\n this.end = p.end;\n if (p.options.locations)\n { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }\n if (p.options.ranges)\n { this.range = [p.start, p.end]; }\n };\n\n // ## Tokenizer\n\n var pp = Parser.prototype;\n\n // Move to the next token\n\n pp.next = function(ignoreEscapeSequenceInKeyword) {\n if (!ignoreEscapeSequenceInKeyword && this.type.keyword && this.containsEsc)\n { this.raiseRecoverable(this.start, \"Escape sequence in keyword \" + this.type.keyword); }\n if (this.options.onToken)\n { this.options.onToken(new Token(this)); }\n\n this.lastTokEnd = this.end;\n this.lastTokStart = this.start;\n this.lastTokEndLoc = this.endLoc;\n this.lastTokStartLoc = this.startLoc;\n this.nextToken();\n };\n\n pp.getToken = function() {\n this.next();\n return new Token(this)\n };\n\n // If we're in an ES6 environment, make parsers iterable\n if (typeof Symbol !== \"undefined\")\n { pp[Symbol.iterator] = function() {\n var this$1$1 = this;\n\n return {\n next: function () {\n var token = this$1$1.getToken();\n return {\n done: token.type === types$1.eof,\n value: token\n }\n }\n }\n }; }\n\n // Toggle strict mode. Re-reads the next number or string to please\n // pedantic tests (`\"use strict\"; 010;` should fail).\n\n // Read a single token, updating the parser object's token-related\n // properties.\n\n pp.nextToken = function() {\n var curContext = this.curContext();\n if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }\n\n this.start = this.pos;\n if (this.options.locations) { this.startLoc = this.curPosition(); }\n if (this.pos >= this.input.length) { return this.finishToken(types$1.eof) }\n\n if (curContext.override) { return curContext.override(this) }\n else { this.readToken(this.fullCharCodeAtPos()); }\n };\n\n pp.readToken = function(code) {\n // Identifier or keyword. '\\uXXXX' sequences are allowed in\n // identifiers, so '\\' also dispatches to that.\n if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\\' */)\n { return this.readWord() }\n\n return this.getTokenFromCode(code)\n };\n\n pp.fullCharCodeAt = function(pos) {\n var code = this.input.charCodeAt(pos);\n if (code <= 0xd7ff || code >= 0xdc00) { return code }\n var next = this.input.charCodeAt(pos + 1);\n return next <= 0xdbff || next >= 0xe000 ? code : (code << 10) + next - 0x35fdc00\n };\n\n pp.fullCharCodeAtPos = function() {\n return this.fullCharCodeAt(this.pos)\n };\n\n pp.skipBlockComment = function() {\n var startLoc = this.options.onComment && this.curPosition();\n var start = this.pos, end = this.input.indexOf(\"*/\", this.pos += 2);\n if (end === -1) { this.raise(this.pos - 2, \"Unterminated comment\"); }\n this.pos = end + 2;\n if (this.options.locations) {\n for (var nextBreak = (void 0), pos = start; (nextBreak = nextLineBreak(this.input, pos, this.pos)) > -1;) {\n ++this.curLine;\n pos = this.lineStart = nextBreak;\n }\n }\n if (this.options.onComment)\n { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,\n startLoc, this.curPosition()); }\n };\n\n pp.skipLineComment = function(startSkip) {\n var start = this.pos;\n var startLoc = this.options.onComment && this.curPosition();\n var ch = this.input.charCodeAt(this.pos += startSkip);\n while (this.pos < this.input.length && !isNewLine(ch)) {\n ch = this.input.charCodeAt(++this.pos);\n }\n if (this.options.onComment)\n { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,\n startLoc, this.curPosition()); }\n };\n\n // Called at the start of the parse and after every token. Skips\n // whitespace and comments, and.\n\n pp.skipSpace = function() {\n loop: while (this.pos < this.input.length) {\n var ch = this.input.charCodeAt(this.pos);\n switch (ch) {\n case 32: case 160: // ' '\n ++this.pos;\n break\n case 13:\n if (this.input.charCodeAt(this.pos + 1) === 10) {\n ++this.pos;\n }\n case 10: case 8232: case 8233:\n ++this.pos;\n if (this.options.locations) {\n ++this.curLine;\n this.lineStart = this.pos;\n }\n break\n case 47: // '/'\n switch (this.input.charCodeAt(this.pos + 1)) {\n case 42: // '*'\n this.skipBlockComment();\n break\n case 47:\n this.skipLineComment(2);\n break\n default:\n break loop\n }\n break\n default:\n if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {\n ++this.pos;\n } else {\n break loop\n }\n }\n }\n };\n\n // Called at the end of every token. Sets `end`, `val`, and\n // maintains `context` and `exprAllowed`, and skips the space after\n // the token, so that the next one's `start` will point at the\n // right position.\n\n pp.finishToken = function(type, val) {\n this.end = this.pos;\n if (this.options.locations) { this.endLoc = this.curPosition(); }\n var prevType = this.type;\n this.type = type;\n this.value = val;\n\n this.updateContext(prevType);\n };\n\n // ### Token reading\n\n // This is the function that is called to fetch the next token. It\n // is somewhat obscure, because it works in character codes rather\n // than characters, and because operator parsing has been inlined\n // into it.\n //\n // All in the name of speed.\n //\n pp.readToken_dot = function() {\n var next = this.input.charCodeAt(this.pos + 1);\n if (next >= 48 && next <= 57) { return this.readNumber(true) }\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'\n this.pos += 3;\n return this.finishToken(types$1.ellipsis)\n } else {\n ++this.pos;\n return this.finishToken(types$1.dot)\n }\n };\n\n pp.readToken_slash = function() { // '/'\n var next = this.input.charCodeAt(this.pos + 1);\n if (this.exprAllowed) { ++this.pos; return this.readRegexp() }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.slash, 1)\n };\n\n pp.readToken_mult_modulo_exp = function(code) { // '%*'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n var tokentype = code === 42 ? types$1.star : types$1.modulo;\n\n // exponentiation operator ** and **=\n if (this.options.ecmaVersion >= 7 && code === 42 && next === 42) {\n ++size;\n tokentype = types$1.starstar;\n next = this.input.charCodeAt(this.pos + 2);\n }\n\n if (next === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(tokentype, size)\n };\n\n pp.readToken_pipe_amp = function(code) { // '|&'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === code) {\n if (this.options.ecmaVersion >= 12) {\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (next2 === 61) { return this.finishOp(types$1.assign, 3) }\n }\n return this.finishOp(code === 124 ? types$1.logicalOR : types$1.logicalAND, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(code === 124 ? types$1.bitwiseOR : types$1.bitwiseAND, 1)\n };\n\n pp.readToken_caret = function() { // '^'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.bitwiseXOR, 1)\n };\n\n pp.readToken_plus_min = function(code) { // '+-'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === code) {\n if (next === 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 62 &&\n (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {\n // A `-->` line comment\n this.skipLineComment(3);\n this.skipSpace();\n return this.nextToken()\n }\n return this.finishOp(types$1.incDec, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.plusMin, 1)\n };\n\n pp.readToken_lt_gt = function(code) { // '<>'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n if (next === code) {\n size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;\n if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(types$1.bitShift, size)\n }\n if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&\n this.input.charCodeAt(this.pos + 3) === 45) {\n // `<!--`, an XML-style comment that should be interpreted as a line comment\n this.skipLineComment(4);\n this.skipSpace();\n return this.nextToken()\n }\n if (next === 61) { size = 2; }\n return this.finishOp(types$1.relational, size)\n };\n\n pp.readToken_eq_excl = function(code) { // '=!'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === 61) { return this.finishOp(types$1.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2) }\n if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'\n this.pos += 2;\n return this.finishToken(types$1.arrow)\n }\n return this.finishOp(code === 61 ? types$1.eq : types$1.prefix, 1)\n };\n\n pp.readToken_question = function() { // '?'\n var ecmaVersion = this.options.ecmaVersion;\n if (ecmaVersion >= 11) {\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === 46) {\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (next2 < 48 || next2 > 57) { return this.finishOp(types$1.questionDot, 2) }\n }\n if (next === 63) {\n if (ecmaVersion >= 12) {\n var next2$1 = this.input.charCodeAt(this.pos + 2);\n if (next2$1 === 61) { return this.finishOp(types$1.assign, 3) }\n }\n return this.finishOp(types$1.coalesce, 2)\n }\n }\n return this.finishOp(types$1.question, 1)\n };\n\n pp.readToken_numberSign = function() { // '#'\n var ecmaVersion = this.options.ecmaVersion;\n var code = 35; // '#'\n if (ecmaVersion >= 13) {\n ++this.pos;\n code = this.fullCharCodeAtPos();\n if (isIdentifierStart(code, true) || code === 92 /* '\\' */) {\n return this.finishToken(types$1.privateId, this.readWord1())\n }\n }\n\n this.raise(this.pos, \"Unexpected character '\" + codePointToString(code) + \"'\");\n };\n\n pp.getTokenFromCode = function(code) {\n switch (code) {\n // The interpretation of a dot depends on whether it is followed\n // by a digit or another two dots.\n case 46: // '.'\n return this.readToken_dot()\n\n // Punctuation tokens.\n case 40: ++this.pos; return this.finishToken(types$1.parenL)\n case 41: ++this.pos; return this.finishToken(types$1.parenR)\n case 59: ++this.pos; return this.finishToken(types$1.semi)\n case 44: ++this.pos; return this.finishToken(types$1.comma)\n case 91: ++this.pos; return this.finishToken(types$1.bracketL)\n case 93: ++this.pos; return this.finishToken(types$1.bracketR)\n case 123: ++this.pos; return this.finishToken(types$1.braceL)\n case 125: ++this.pos; return this.finishToken(types$1.braceR)\n case 58: ++this.pos; return this.finishToken(types$1.colon)\n\n case 96: // '`'\n if (this.options.ecmaVersion < 6) { break }\n ++this.pos;\n return this.finishToken(types$1.backQuote)\n\n case 48: // '0'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === 120 || next === 88) { return this.readRadixNumber(16) } // '0x', '0X' - hex number\n if (this.options.ecmaVersion >= 6) {\n if (next === 111 || next === 79) { return this.readRadixNumber(8) } // '0o', '0O' - octal number\n if (next === 98 || next === 66) { return this.readRadixNumber(2) } // '0b', '0B' - binary number\n }\n\n // Anything else beginning with a digit is an integer, octal\n // number, or float.\n case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9\n return this.readNumber(false)\n\n // Quotes produce strings.\n case 34: case 39: // '\"', \"'\"\n return this.readString(code)\n\n // Operators are parsed inline in tiny state machines. '=' (61) is\n // often referred to. `finishOp` simply skips the amount of\n // characters it is given as second argument, and returns a token\n // of the type given by its first argument.\n case 47: // '/'\n return this.readToken_slash()\n\n case 37: case 42: // '%*'\n return this.readToken_mult_modulo_exp(code)\n\n case 124: case 38: // '|&'\n return this.readToken_pipe_amp(code)\n\n case 94: // '^'\n return this.readToken_caret()\n\n case 43: case 45: // '+-'\n return this.readToken_plus_min(code)\n\n case 60: case 62: // '<>'\n return this.readToken_lt_gt(code)\n\n case 61: case 33: // '=!'\n return this.readToken_eq_excl(code)\n\n case 63: // '?'\n return this.readToken_question()\n\n case 126: // '~'\n return this.finishOp(types$1.prefix, 1)\n\n case 35: // '#'\n return this.readToken_numberSign()\n }\n\n this.raise(this.pos, \"Unexpected character '\" + codePointToString(code) + \"'\");\n };\n\n pp.finishOp = function(type, size) {\n var str = this.input.slice(this.pos, this.pos + size);\n this.pos += size;\n return this.finishToken(type, str)\n };\n\n pp.readRegexp = function() {\n var escaped, inClass, start = this.pos;\n for (;;) {\n if (this.pos >= this.input.length) { this.raise(start, \"Unterminated regular expression\"); }\n var ch = this.input.charAt(this.pos);\n if (lineBreak.test(ch)) { this.raise(start, \"Unterminated regular expression\"); }\n if (!escaped) {\n if (ch === \"[\") { inClass = true; }\n else if (ch === \"]\" && inClass) { inClass = false; }\n else if (ch === \"/\" && !inClass) { break }\n escaped = ch === \"\\\\\";\n } else { escaped = false; }\n ++this.pos;\n }\n var pattern = this.input.slice(start, this.pos);\n ++this.pos;\n var flagsStart = this.pos;\n var flags = this.readWord1();\n if (this.containsEsc) { this.unexpected(flagsStart); }\n\n // Validate pattern\n var state = this.regexpState || (this.regexpState = new RegExpValidationState(this));\n state.reset(start, pattern, flags);\n this.validateRegExpFlags(state);\n this.validateRegExpPattern(state);\n\n // Create Literal#value property value.\n var value = null;\n try {\n value = new RegExp(pattern, flags);\n } catch (e) {\n // ESTree requires null if it failed to instantiate RegExp object.\n // https://github.com/estree/estree/blob/a27003adf4fd7bfad44de9cef372a2eacd527b1c/es5.md#regexpliteral\n }\n\n return this.finishToken(types$1.regexp, {pattern: pattern, flags: flags, value: value})\n };\n\n // Read an integer in the given radix. Return null if zero digits\n // were read, the integer value otherwise. When `len` is given, this\n // will return `null` unless the integer has exactly `len` digits.\n\n pp.readInt = function(radix, len, maybeLegacyOctalNumericLiteral) {\n // `len` is used for character escape sequences. In that case, disallow separators.\n var allowSeparators = this.options.ecmaVersion >= 12 && len === undefined;\n\n // `maybeLegacyOctalNumericLiteral` is true if it doesn't have prefix (0x,0o,0b)\n // and isn't fraction part nor exponent part. In that case, if the first digit\n // is zero then disallow separators.\n var isLegacyOctalNumericLiteral = maybeLegacyOctalNumericLiteral && this.input.charCodeAt(this.pos) === 48;\n\n var start = this.pos, total = 0, lastCode = 0;\n for (var i = 0, e = len == null ? Infinity : len; i < e; ++i, ++this.pos) {\n var code = this.input.charCodeAt(this.pos), val = (void 0);\n\n if (allowSeparators && code === 95) {\n if (isLegacyOctalNumericLiteral) { this.raiseRecoverable(this.pos, \"Numeric separator is not allowed in legacy octal numeric literals\"); }\n if (lastCode === 95) { this.raiseRecoverable(this.pos, \"Numeric separator must be exactly one underscore\"); }\n if (i === 0) { this.raiseRecoverable(this.pos, \"Numeric separator is not allowed at the first of digits\"); }\n lastCode = code;\n continue\n }\n\n if (code >= 97) { val = code - 97 + 10; } // a\n else if (code >= 65) { val = code - 65 + 10; } // A\n else if (code >= 48 && code <= 57) { val = code - 48; } // 0-9\n else { val = Infinity; }\n if (val >= radix) { break }\n lastCode = code;\n total = total * radix + val;\n }\n\n if (allowSeparators && lastCode === 95) { this.raiseRecoverable(this.pos - 1, \"Numeric separator is not allowed at the last of digits\"); }\n if (this.pos === start || len != null && this.pos - start !== len) { return null }\n\n return total\n };\n\n function stringToNumber(str, isLegacyOctalNumericLiteral) {\n if (isLegacyOctalNumericLiteral) {\n return parseInt(str, 8)\n }\n\n // `parseFloat(value)` stops parsing at the first numeric separator then returns a wrong value.\n return parseFloat(str.replace(/_/g, \"\"))\n }\n\n function stringToBigInt(str) {\n if (typeof BigInt !== \"function\") {\n return null\n }\n\n // `BigInt(value)` throws syntax error if the string contains numeric separators.\n return BigInt(str.replace(/_/g, \"\"))\n }\n\n pp.readRadixNumber = function(radix) {\n var start = this.pos;\n this.pos += 2; // 0x\n var val = this.readInt(radix);\n if (val == null) { this.raise(this.start + 2, \"Expected number in radix \" + radix); }\n if (this.options.ecmaVersion >= 11 && this.input.charCodeAt(this.pos) === 110) {\n val = stringToBigInt(this.input.slice(start, this.pos));\n ++this.pos;\n } else if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, \"Identifier directly after number\"); }\n return this.finishToken(types$1.num, val)\n };\n\n // Read an integer, octal integer, or floating-point number.\n\n pp.readNumber = function(startsWithDot) {\n var start = this.pos;\n if (!startsWithDot && this.readInt(10, undefined, true) === null) { this.raise(start, \"Invalid number\"); }\n var octal = this.pos - start >= 2 && this.input.charCodeAt(start) === 48;\n if (octal && this.strict) { this.raise(start, \"Invalid number\"); }\n var next = this.input.charCodeAt(this.pos);\n if (!octal && !startsWithDot && this.options.ecmaVersion >= 11 && next === 110) {\n var val$1 = stringToBigInt(this.input.slice(start, this.pos));\n ++this.pos;\n if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, \"Identifier directly after number\"); }\n return this.finishToken(types$1.num, val$1)\n }\n if (octal && /[89]/.test(this.input.slice(start, this.pos))) { octal = false; }\n if (next === 46 && !octal) { // '.'\n ++this.pos;\n this.readInt(10);\n next = this.input.charCodeAt(this.pos);\n }\n if ((next === 69 || next === 101) && !octal) { // 'eE'\n next = this.input.charCodeAt(++this.pos);\n if (next === 43 || next === 45) { ++this.pos; } // '+-'\n if (this.readInt(10) === null) { this.raise(start, \"Invalid number\"); }\n }\n if (isIdentifierStart(this.fullCharCodeAtPos())) { this.raise(this.pos, \"Identifier directly after number\"); }\n\n var val = stringToNumber(this.input.slice(start, this.pos), octal);\n return this.finishToken(types$1.num, val)\n };\n\n // Read a string value, interpreting backslash-escapes.\n\n pp.readCodePoint = function() {\n var ch = this.input.charCodeAt(this.pos), code;\n\n if (ch === 123) { // '{'\n if (this.options.ecmaVersion < 6) { this.unexpected(); }\n var codePos = ++this.pos;\n code = this.readHexChar(this.input.indexOf(\"}\", this.pos) - this.pos);\n ++this.pos;\n if (code > 0x10FFFF) { this.invalidStringToken(codePos, \"Code point out of bounds\"); }\n } else {\n code = this.readHexChar(4);\n }\n return code\n };\n\n pp.readString = function(quote) {\n var out = \"\", chunkStart = ++this.pos;\n for (;;) {\n if (this.pos >= this.input.length) { this.raise(this.start, \"Unterminated string constant\"); }\n var ch = this.input.charCodeAt(this.pos);\n if (ch === quote) { break }\n if (ch === 92) { // '\\'\n out += this.input.slice(chunkStart, this.pos);\n out += this.readEscapedChar(false);\n chunkStart = this.pos;\n } else if (ch === 0x2028 || ch === 0x2029) {\n if (this.options.ecmaVersion < 10) { this.raise(this.start, \"Unterminated string constant\"); }\n ++this.pos;\n if (this.options.locations) {\n this.curLine++;\n this.lineStart = this.pos;\n }\n } else {\n if (isNewLine(ch)) { this.raise(this.start, \"Unterminated string constant\"); }\n ++this.pos;\n }\n }\n out += this.input.slice(chunkStart, this.pos++);\n return this.finishToken(types$1.string, out)\n };\n\n // Reads template string tokens.\n\n var INVALID_TEMPLATE_ESCAPE_ERROR = {};\n\n pp.tryReadTemplateToken = function() {\n this.inTemplateElement = true;\n try {\n this.readTmplToken();\n } catch (err) {\n if (err === INVALID_TEMPLATE_ESCAPE_ERROR) {\n this.readInvalidTemplateToken();\n } else {\n throw err\n }\n }\n\n this.inTemplateElement = false;\n };\n\n pp.invalidStringToken = function(position, message) {\n if (this.inTemplateElement && this.options.ecmaVersion >= 9) {\n throw INVALID_TEMPLATE_ESCAPE_ERROR\n } else {\n this.raise(position, message);\n }\n };\n\n pp.readTmplToken = function() {\n var out = \"\", chunkStart = this.pos;\n for (;;) {\n if (this.pos >= this.input.length) { this.raise(this.start, \"Unterminated template\"); }\n var ch = this.input.charCodeAt(this.pos);\n if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { // '`', '${'\n if (this.pos === this.start && (this.type === types$1.template || this.type === types$1.invalidTemplate)) {\n if (ch === 36) {\n this.pos += 2;\n return this.finishToken(types$1.dollarBraceL)\n } else {\n ++this.pos;\n return this.finishToken(types$1.backQuote)\n }\n }\n out += this.input.slice(chunkStart, this.pos);\n return this.finishToken(types$1.template, out)\n }\n if (ch === 92) { // '\\'\n out += this.input.slice(chunkStart, this.pos);\n out += this.readEscapedChar(true);\n chunkStart = this.pos;\n } else if (isNewLine(ch)) {\n out += this.input.slice(chunkStart, this.pos);\n ++this.pos;\n switch (ch) {\n case 13:\n if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; }\n case 10:\n out += \"\\n\";\n break\n default:\n out += String.fromCharCode(ch);\n break\n }\n if (this.options.locations) {\n ++this.curLine;\n this.lineStart = this.pos;\n }\n chunkStart = this.pos;\n } else {\n ++this.pos;\n }\n }\n };\n\n // Reads a template token to search for the end, without validating any escape sequences\n pp.readInvalidTemplateToken = function() {\n for (; this.pos < this.input.length; this.pos++) {\n switch (this.input[this.pos]) {\n case \"\\\\\":\n ++this.pos;\n break\n\n case \"$\":\n if (this.input[this.pos + 1] !== \"{\") { break }\n // fall through\n case \"`\":\n return this.finishToken(types$1.invalidTemplate, this.input.slice(this.start, this.pos))\n\n case \"\\r\":\n if (this.input[this.pos + 1] === \"\\n\") { ++this.pos; }\n // fall through\n case \"\\n\": case \"\\u2028\": case \"\\u2029\":\n ++this.curLine;\n this.lineStart = this.pos + 1;\n break\n }\n }\n this.raise(this.start, \"Unterminated template\");\n };\n\n // Used to read escaped characters\n\n pp.readEscapedChar = function(inTemplate) {\n var ch = this.input.charCodeAt(++this.pos);\n ++this.pos;\n switch (ch) {\n case 110: return \"\\n\" // 'n' -> '\\n'\n case 114: return \"\\r\" // 'r' -> '\\r'\n case 120: return String.fromCharCode(this.readHexChar(2)) // 'x'\n case 117: return codePointToString(this.readCodePoint()) // 'u'\n case 116: return \"\\t\" // 't' -> '\\t'\n case 98: return \"\\b\" // 'b' -> '\\b'\n case 118: return \"\\u000b\" // 'v' -> '\\u000b'\n case 102: return \"\\f\" // 'f' -> '\\f'\n case 13: if (this.input.charCodeAt(this.pos) === 10) { ++this.pos; } // '\\r\\n'\n case 10: // ' \\n'\n if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }\n return \"\"\n case 56:\n case 57:\n if (this.strict) {\n this.invalidStringToken(\n this.pos - 1,\n \"Invalid escape sequence\"\n );\n }\n if (inTemplate) {\n var codePos = this.pos - 1;\n\n this.invalidStringToken(\n codePos,\n \"Invalid escape sequence in template string\"\n );\n }\n default:\n if (ch >= 48 && ch <= 55) {\n var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];\n var octal = parseInt(octalStr, 8);\n if (octal > 255) {\n octalStr = octalStr.slice(0, -1);\n octal = parseInt(octalStr, 8);\n }\n this.pos += octalStr.length - 1;\n ch = this.input.charCodeAt(this.pos);\n if ((octalStr !== \"0\" || ch === 56 || ch === 57) && (this.strict || inTemplate)) {\n this.invalidStringToken(\n this.pos - 1 - octalStr.length,\n inTemplate\n ? \"Octal literal in template string\"\n : \"Octal literal in strict mode\"\n );\n }\n return String.fromCharCode(octal)\n }\n if (isNewLine(ch)) {\n // Unicode new line characters after \\ get removed from output in both\n // template literals and strings\n if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }\n return \"\"\n }\n return String.fromCharCode(ch)\n }\n };\n\n // Used to read character escape sequences ('\\x', '\\u', '\\U').\n\n pp.readHexChar = function(len) {\n var codePos = this.pos;\n var n = this.readInt(16, len);\n if (n === null) { this.invalidStringToken(codePos, \"Bad character escape sequence\"); }\n return n\n };\n\n // Read an identifier, and return it as a string. Sets `this.containsEsc`\n // to whether the word contained a '\\u' escape.\n //\n // Incrementally adds only escaped chars, adding other chunks as-is\n // as a micro-optimization.\n\n pp.readWord1 = function() {\n this.containsEsc = false;\n var word = \"\", first = true, chunkStart = this.pos;\n var astral = this.options.ecmaVersion >= 6;\n while (this.pos < this.input.length) {\n var ch = this.fullCharCodeAtPos();\n if (isIdentifierChar(ch, astral)) {\n this.pos += ch <= 0xffff ? 1 : 2;\n } else if (ch === 92) { // \"\\\"\n this.containsEsc = true;\n word += this.input.slice(chunkStart, this.pos);\n var escStart = this.pos;\n if (this.input.charCodeAt(++this.pos) !== 117) // \"u\"\n { this.invalidStringToken(this.pos, \"Expecting Unicode escape sequence \\\\uXXXX\"); }\n ++this.pos;\n var esc = this.readCodePoint();\n if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))\n { this.invalidStringToken(escStart, \"Invalid Unicode escape\"); }\n word += codePointToString(esc);\n chunkStart = this.pos;\n } else {\n break\n }\n first = false;\n }\n return word + this.input.slice(chunkStart, this.pos)\n };\n\n // Read an identifier or keyword token. Will check for reserved\n // words when necessary.\n\n pp.readWord = function() {\n var word = this.readWord1();\n var type = types$1.name;\n if (this.keywords.test(word)) {\n type = keywords[word];\n }\n return this.finishToken(type, word)\n };\n\n // Acorn is a tiny, fast JavaScript parser written in JavaScript.\n //\n // Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and\n // various contributors and released under an MIT license.\n //\n // Git repositories for Acorn are available at\n //\n // http://marijnhaverbeke.nl/git/acorn\n // https://github.com/acornjs/acorn.git\n //\n // Please use the [github bug tracker][ghbt] to report issues.\n //\n // [ghbt]: https://github.com/acornjs/acorn/issues\n\n\n var version = \"8.16.0\";\n\n Parser.acorn = {\n Parser: Parser,\n version: version,\n defaultOptions: defaultOptions,\n Position: Position,\n SourceLocation: SourceLocation,\n getLineInfo: getLineInfo,\n Node: Node,\n TokenType: TokenType,\n tokTypes: types$1,\n keywordTypes: keywords,\n TokContext: TokContext,\n tokContexts: types,\n isIdentifierChar: isIdentifierChar,\n isIdentifierStart: isIdentifierStart,\n Token: Token,\n isNewLine: isNewLine,\n lineBreak: lineBreak,\n lineBreakG: lineBreakG,\n nonASCIIwhitespace: nonASCIIwhitespace\n };\n\n // The main exported interface (under `self.acorn` when in the\n // browser) is a `parse` function that takes a code string and returns\n // an abstract syntax tree as specified by the [ESTree spec][estree].\n //\n // [estree]: https://github.com/estree/estree\n\n function parse(input, options) {\n return Parser.parse(input, options)\n }\n\n // This function tries to parse a single expression at a given\n // offset in a string. Useful for parsing mixed-language formats\n // that embed JavaScript expressions.\n\n function parseExpressionAt(input, pos, options) {\n return Parser.parseExpressionAt(input, pos, options)\n }\n\n // Acorn is organized as a tokenizer and a recursive-descent parser.\n // The `tokenizer` export provides an interface to the tokenizer.\n\n function tokenizer(input, options) {\n return Parser.tokenizer(input, options)\n }\n\n exports.Node = Node;\n exports.Parser = Parser;\n exports.Position = Position;\n exports.SourceLocation = SourceLocation;\n exports.TokContext = TokContext;\n exports.Token = Token;\n exports.TokenType = TokenType;\n exports.defaultOptions = defaultOptions;\n exports.getLineInfo = getLineInfo;\n exports.isIdentifierChar = isIdentifierChar;\n exports.isIdentifierStart = isIdentifierStart;\n exports.isNewLine = isNewLine;\n exports.keywordTypes = keywords;\n exports.lineBreak = lineBreak;\n exports.lineBreakG = lineBreakG;\n exports.nonASCIIwhitespace = nonASCIIwhitespace;\n exports.parse = parse;\n exports.parseExpressionAt = parseExpressionAt;\n exports.tokContexts = types;\n exports.tokTypes = types$1;\n exports.tokenizer = tokenizer;\n exports.version = version;\n\n}));\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.button-group_button-group_-xrlY {\\n display: -webkit-inline-box;\\n display: -webkit-inline-flex;\\n display: -ms-inline-flexbox;\\n display: inline-flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n padding: 0 .25rem;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/units.css\",\"webpack://./src/components/button-group/button-group.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACrB1D;IACI,2BAAoB;IAApB,4BAAoB;IAApB,2BAAoB;IAApB,oBAAoB;IACpB,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,iBAAqB;AACzB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/units\\\";\\n\\n.button-group {\\n display: inline-flex;\\n flex-direction: row;\\n padding: 0 $grid-unit;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"button-group\": \"button-group_button-group_-xrlY\",\n\t\"buttonGroup\": \"button-group_button-group_-xrlY\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n.button_button_9jFbh {\\n background: none;\\n cursor: pointer;\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n -ms-user-select: none;\\n user-select: none;\\n}\\n\\n.button_button_9jFbh:active {\\n background-color: hsla(260, 60%, 60%, 0.35);\\n}\\n\\n.button_highlighted_Ek-Iy.button_button_9jFbh {\\n background-color: hsla(260, 60%, 60%, 0.35);\\n}\\n\\n.button_mod-disabled_wDlGR {\\n cursor: auto;\\n opacity: .5;\\n}\\n\\n.button_mod-disabled_wDlGR:active {\\n background: none;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/components/button/button.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACT9F;IACI,gBAAgB;IAChB,eAAe;IACf,yBAAiB;OAAjB,sBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;AACrB;;AACA;IACI,2CAAoC;AACxC;;AACA;IACI,2CAAoC;AACxC;;AACA;IACI,YAAY;IACZ,WAAW;AACf;;AACA;IACI,gBAAgB;AACpB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"@import \\\"../../css/colors.css\\\";\\n\\n.button {\\n background: none;\\n cursor: pointer;\\n user-select: none;\\n}\\n.button:active {\\n background-color: $looks-transparent;\\n}\\n.highlighted.button {\\n background-color: $looks-transparent;\\n}\\n.mod-disabled {\\n cursor: auto;\\n opacity: .5;\\n}\\n.mod-disabled:active {\\n background: none;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"button\": \"button_button_9jFbh\",\n\t\"highlighted\": \"button_highlighted_Ek-Iy\",\n\t\"mod-disabled\": \"button_mod-disabled_wDlGR\",\n\t\"modDisabled\": \"button_mod-disabled_wDlGR\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".color-button_color-button_Dmf-d {\\n height: 2rem;\\n width: 3rem;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n}\\n\\n.color-button_color-button-swatch_wuoIu {\\n position: relative;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n cursor: pointer;\\n -webkit-flex-basis: 2rem;\\n -ms-flex-preferred-size: 2rem;\\n flex-basis: 2rem;\\n -webkit-flex-shrink: 0;\\n -ms-flex-negative: 0;\\n flex-shrink: 0;\\n height: 100%;\\n border: 1px solid rgba(0, 0, 0, 0.25);\\n}\\n\\n[dir=\\\"ltr\\\"] .color-button_color-button-swatch_wuoIu {\\n border-top-left-radius: 4px;\\n border-bottom-left-radius: 4px;\\n}\\n\\n[dir=\\\"rtl\\\"] .color-button_color-button-swatch_wuoIu {\\n border-top-right-radius: 4px;\\n border-bottom-right-radius: 4px;\\n}\\n\\n.color-button_color-button-arrow_B709Q {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n -ms-user-select: none;\\n user-select: none;\\n cursor: pointer;\\n -webkit-flex-basis: 1rem;\\n -ms-flex-preferred-size: 1rem;\\n flex-basis: 1rem;\\n -webkit-flex-shrink: 0;\\n -ms-flex-negative: 0;\\n flex-shrink: 0;\\n height: 100%;\\n\\n border: 1px solid rgba(0, 0, 0, 0.25);\\n\\n -webkit-box-align: center;\\n\\n -webkit-align-items: center;\\n\\n -ms-flex-align: center;\\n\\n align-items: center;\\n -webkit-box-pack: center;\\n -webkit-justify-content: center;\\n -ms-flex-pack: center;\\n justify-content: center;\\n color: #575e75;\\n font-size: 0.75rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .color-button_color-button-arrow_B709Q {\\n border-top-right-radius: 4px;\\n border-bottom-right-radius: 4px;\\n border-left: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .color-button_color-button-arrow_B709Q {\\n border-top-left-radius: 4px;\\n border-bottom-left-radius: 4px;\\n border-right: none;\\n}\\n\\n.color-button_swatch-icon_zmoVW {\\n width: 1.75rem;\\n margin: auto;\\n /* Make sure it appears above the outline box */\\n z-index: 2;\\n}\\n\\n.color-button_outline-swatch_4u4Q0:after {\\n content: \\\"\\\";\\n position: absolute;\\n top: calc(0.5rem);\\n left: calc(0.5rem);\\n width: 0.75rem;\\n height: 0.75rem;\\n background: white;\\n border: 1px solid rgba(0, 0, 0, 0.25);\\n /* Make sure it appears below the transparent icon */\\n z-index: 1;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/components/color-button/color-button.css\"],\"names\":[],\"mappings\":\"AAAA;IACI,YAAY;IACZ,WAAW;IACX,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;AACjB;;AAEA;IACI,kBAAkB;IAClB,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,eAAe;IACf,wBAAgB;QAAhB,6BAAgB;YAAhB,gBAAgB;IAChB,sBAAc;QAAd,oBAAc;YAAd,cAAc;IACd,YAAY;IACZ,qCAAqC;AACzC;;AAEA;IACI,2BAA2B;IAC3B,8BAA8B;AAClC;;AAEA;IACI,4BAA4B;IAC5B,+BAA+B;AACnC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,yBAAiB;OAAjB,sBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;IACjB,eAAe;IACf,wBAAgB;QAAhB,6BAAgB;YAAhB,gBAAgB;IAChB,sBAAc;QAAd,oBAAc;YAAd,cAAc;IACd,YAAY;;IAEZ,qCAAqC;;IAErC,yBAAmB;;IAAnB,2BAAmB;;QAAnB,sBAAmB;;YAAnB,mBAAmB;IACnB,wBAAuB;IAAvB,+BAAuB;QAAvB,qBAAuB;YAAvB,uBAAuB;IACvB,cAAc;IACd,kBAAkB;AACtB;;AAEA;IACI,4BAA4B;IAC5B,+BAA+B;IAC/B,iBAAiB;AACrB;;AAEA;IACI,2BAA2B;IAC3B,8BAA8B;IAC9B,kBAAkB;AACtB;;AAEA;IACI,cAAc;IACd,YAAY;IACZ,+CAA+C;IAC/C,UAAU;AACd;;AAEA;IACI,WAAW;IACX,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;IAClB,cAAc;IACd,eAAe;IACf,iBAAiB;IACjB,qCAAqC;IACrC,oDAAoD;IACpD,UAAU;AACd\",\"sourcesContent\":[\".color-button {\\n height: 2rem;\\n width: 3rem;\\n display: flex;\\n}\\n\\n.color-button-swatch {\\n position: relative;\\n display: flex;\\n cursor: pointer;\\n flex-basis: 2rem;\\n flex-shrink: 0;\\n height: 100%;\\n border: 1px solid rgba(0, 0, 0, 0.25);\\n}\\n\\n[dir=\\\"ltr\\\"] .color-button-swatch {\\n border-top-left-radius: 4px;\\n border-bottom-left-radius: 4px;\\n}\\n\\n[dir=\\\"rtl\\\"] .color-button-swatch {\\n border-top-right-radius: 4px;\\n border-bottom-right-radius: 4px;\\n}\\n\\n.color-button-arrow {\\n display: flex;\\n user-select: none;\\n cursor: pointer;\\n flex-basis: 1rem;\\n flex-shrink: 0;\\n height: 100%;\\n\\n border: 1px solid rgba(0, 0, 0, 0.25);\\n\\n align-items: center;\\n justify-content: center;\\n color: #575e75;\\n font-size: 0.75rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .color-button-arrow {\\n border-top-right-radius: 4px;\\n border-bottom-right-radius: 4px;\\n border-left: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .color-button-arrow {\\n border-top-left-radius: 4px;\\n border-bottom-left-radius: 4px;\\n border-right: none;\\n}\\n\\n.swatch-icon {\\n width: 1.75rem;\\n margin: auto;\\n /* Make sure it appears above the outline box */\\n z-index: 2;\\n}\\n\\n.outline-swatch:after {\\n content: \\\"\\\";\\n position: absolute;\\n top: calc(0.5rem);\\n left: calc(0.5rem);\\n width: 0.75rem;\\n height: 0.75rem;\\n background: white;\\n border: 1px solid rgba(0, 0, 0, 0.25);\\n /* Make sure it appears below the transparent icon */\\n z-index: 1;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"color-button\": \"color-button_color-button_Dmf-d\",\n\t\"colorButton\": \"color-button_color-button_Dmf-d\",\n\t\"color-button-swatch\": \"color-button_color-button-swatch_wuoIu\",\n\t\"colorButtonSwatch\": \"color-button_color-button-swatch_wuoIu\",\n\t\"color-button-arrow\": \"color-button_color-button-arrow_B709Q\",\n\t\"colorButtonArrow\": \"color-button_color-button-arrow_B709Q\",\n\t\"swatch-icon\": \"color-button_swatch-icon_zmoVW\",\n\t\"swatchIcon\": \"color-button_swatch-icon_zmoVW\",\n\t\"outline-swatch\": \"color-button_outline-swatch_4u4Q0\",\n\t\"outlineSwatch\": \"color-button_outline-swatch_4u4Q0\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n/* Popover styles */\\n\\n.Popover-body {\\n background: white;\\n border: 1px solid #ddd;\\n padding: 4px;\\n border-radius: 4px;\\n padding: 4px;\\n -webkit-box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, .3);\\n box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, .3);\\n}\\n\\n.Popover-tipShape {\\n fill: white;\\n stroke: #ddd;\\n}\\n\\n.color-picker_clickable_toCkq {\\n cursor: pointer;\\n}\\n\\n.color-picker_swatch-row_L3tax {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-box-pack: justify;\\n -webkit-justify-content: space-between;\\n -ms-flex-pack: justify;\\n justify-content: space-between;\\n}\\n\\n.color-picker_row-header_bsOiG {\\n font-family: \\\"Helvetica Neue\\\", Helvetica, sans-serif;\\n font-size: 0.65rem;\\n color: #575E75;\\n margin: 8px;\\n}\\n\\n[dir=\\\"ltr\\\"] .color-picker_label-readout_jRRx1 {\\n margin-left: 10px;\\n}\\n\\n[dir=\\\"rtl\\\"] .color-picker_label-readout_jRRx1 {\\n margin-right: 10px;\\n}\\n\\n.color-picker_label-name_Fh33x {\\n font-weight: bold;\\n}\\n\\n.color-picker_divider_kSFdY {\\n border-top: 1px solid #ddd;\\n margin: 8px;\\n}\\n\\n.color-picker_swap-button_KUpPw {\\n margin-left: 8px;\\n margin-right: 8px;\\n}\\n\\n.color-picker_swatches_aTL2C {\\n margin: 8px;\\n}\\n\\n.color-picker_swatch_s2mbW {\\n width: 1.5rem;\\n height: 1.5rem;\\n border: 1px solid #ddd;\\n border-radius: 4px;\\n -webkit-box-sizing: content-box;\\n box-sizing: content-box;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n.color-picker_large-swatch-icon_rQJGb {\\n width: 1.75rem;\\n margin: auto;\\n}\\n\\n.color-picker_large-swatch_nfBy1 {\\n width: 2rem;\\n height: 2rem;\\n}\\n\\n.color-picker_active-swatch_9gUEo {\\n border: 1px solid #855CD6;\\n -webkit-box-shadow: 0px 0px 0px 3px hsla(260, 60%, 60%, 0.35);\\n box-shadow: 0px 0px 0px 3px hsla(260, 60%, 60%, 0.35);\\n}\\n\\n.color-picker_swatch-icon_xKjWO {\\n width: 1.5rem;\\n height: 1.5rem;\\n}\\n\\n.color-picker_inactive-gradient_SY3ld {\\n -webkit-filter: saturate(0%);\\n filter: saturate(0%);\\n}\\n\\n.color-picker_gradient-picker-row_fHjyz {\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-box-pack: center;\\n -webkit-justify-content: center;\\n -ms-flex-pack: center;\\n justify-content: center;\\n margin: 8px;\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n -ms-user-select: none;\\n user-select: none;\\n}\\n\\n[dir=\\\"ltr\\\"] .color-picker_gradient-picker-row_fHjyz > img + img {\\n margin-left: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .color-picker_gradient-picker-row_fHjyz > img + img {\\n margin-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .color-picker_gradient-swatches-row_hEq4C {\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: reverse;\\n -webkit-flex-direction: row-reverse;\\n -ms-flex-direction: row-reverse;\\n flex-direction: row-reverse;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/css/units.css\",\"webpack://./src/components/color-picker/color-picker.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACX9F;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACpB1D,mBAAmB;;AACnB;IACI,iBAAiB;IACjB,sBAAsB;IACtB,YAAY;IACZ,kBAAkB;IAClB,YAAY;IACZ,qDAA6C;YAA7C,6CAA6C;AACjD;;AAEA;IACI,WAAW;IACX,YAAY;AAChB;;AAEA;IACI,eAAe;AACnB;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,yBAA8B;IAA9B,sCAA8B;QAA9B,sBAA8B;YAA9B,8BAA8B;AAClC;;AAEA;IACI,oDAAoD;IACpD,kBAAkB;IAClB,cAAc;IACd,WAAW;AACf;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,0BAA0B;IAC1B,WAAW;AACf;;AAEA;IACI,gBAAgB;IAChB,iBAAiB;AACrB;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,aAAa;IACb,cAAc;IACd,sBAAsB;IACtB,kBAAkB;IAClB,+BAAuB;YAAvB,uBAAuB;IACvB,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,cAAc;IACd,YAAY;AAChB;;AAEA;IACI,WAAW;IACX,YAAY;AAChB;;AAEA;IACI,yBAAkC;IAClC,6DAA8C;YAA9C,qDAA8C;AAClD;;AAEA;IACI,aAAa;IACb,cAAc;AAClB;;AAEA;IACI,4BAAoB;YAApB,oBAAoB;AACxB;;AAEA;IACI,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;IACnB,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,wBAAuB;IAAvB,+BAAuB;QAAvB,qBAAuB;YAAvB,uBAAuB;IACvB,WAAW;IACX,yBAAiB;OAAjB,sBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;AACrB;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,8BAA2B;IAA3B,8BAA2B;IAA3B,mCAA2B;QAA3B,+BAA2B;YAA3B,2BAA2B;AAC/B\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/colors\\\";\\n@import \\\"../../css/units\\\";\\n\\n/* Popover styles */\\n:global(.Popover-body) {\\n background: white;\\n border: 1px solid #ddd;\\n padding: 4px;\\n border-radius: 4px;\\n padding: 4px;\\n box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, .3);\\n}\\n\\n:global(.Popover-tipShape) {\\n fill: white;\\n stroke: #ddd;\\n}\\n\\n.clickable {\\n cursor: pointer;\\n}\\n\\n.swatch-row {\\n display: flex;\\n flex-direction: row;\\n justify-content: space-between;\\n}\\n\\n.row-header {\\n font-family: \\\"Helvetica Neue\\\", Helvetica, sans-serif;\\n font-size: 0.65rem;\\n color: #575E75;\\n margin: 8px;\\n}\\n\\n[dir=\\\"ltr\\\"] .label-readout {\\n margin-left: 10px;\\n}\\n\\n[dir=\\\"rtl\\\"] .label-readout {\\n margin-right: 10px;\\n}\\n\\n.label-name {\\n font-weight: bold;\\n}\\n\\n.divider {\\n border-top: 1px solid #ddd;\\n margin: 8px;\\n}\\n\\n.swap-button {\\n margin-left: 8px;\\n margin-right: 8px;\\n}\\n\\n.swatches {\\n margin: 8px;\\n}\\n\\n.swatch {\\n width: 1.5rem;\\n height: 1.5rem;\\n border: 1px solid #ddd;\\n border-radius: 4px;\\n box-sizing: content-box;\\n display: flex;\\n align-items: center;\\n}\\n\\n.large-swatch-icon {\\n width: 1.75rem;\\n margin: auto;\\n}\\n\\n.large-swatch {\\n width: 2rem;\\n height: 2rem;\\n}\\n\\n.active-swatch {\\n border: 1px solid $looks-secondary;\\n box-shadow: 0px 0px 0px 3px $looks-transparent;\\n}\\n\\n.swatch-icon {\\n width: 1.5rem;\\n height: 1.5rem;\\n}\\n\\n.inactive-gradient {\\n filter: saturate(0%);\\n}\\n\\n.gradient-picker-row {\\n align-items: center;\\n display: flex;\\n flex-direction: row;\\n justify-content: center;\\n margin: 8px;\\n user-select: none;\\n}\\n\\n[dir=\\\"ltr\\\"] .gradient-picker-row > img + img {\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .gradient-picker-row > img + img {\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .gradient-swatches-row {\\n flex-direction: row-reverse;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"clickable\": \"color-picker_clickable_toCkq\",\n\t\"swatch-row\": \"color-picker_swatch-row_L3tax\",\n\t\"swatchRow\": \"color-picker_swatch-row_L3tax\",\n\t\"row-header\": \"color-picker_row-header_bsOiG\",\n\t\"rowHeader\": \"color-picker_row-header_bsOiG\",\n\t\"label-readout\": \"color-picker_label-readout_jRRx1\",\n\t\"labelReadout\": \"color-picker_label-readout_jRRx1\",\n\t\"label-name\": \"color-picker_label-name_Fh33x\",\n\t\"labelName\": \"color-picker_label-name_Fh33x\",\n\t\"divider\": \"color-picker_divider_kSFdY\",\n\t\"swap-button\": \"color-picker_swap-button_KUpPw\",\n\t\"swapButton\": \"color-picker_swap-button_KUpPw\",\n\t\"swatches\": \"color-picker_swatches_aTL2C\",\n\t\"swatch\": \"color-picker_swatch_s2mbW\",\n\t\"large-swatch-icon\": \"color-picker_large-swatch-icon_rQJGb\",\n\t\"largeSwatchIcon\": \"color-picker_large-swatch-icon_rQJGb\",\n\t\"large-swatch\": \"color-picker_large-swatch_nfBy1\",\n\t\"largeSwatch\": \"color-picker_large-swatch_nfBy1\",\n\t\"active-swatch\": \"color-picker_active-swatch_9gUEo\",\n\t\"activeSwatch\": \"color-picker_active-swatch_9gUEo\",\n\t\"swatch-icon\": \"color-picker_swatch-icon_xKjWO\",\n\t\"swatchIcon\": \"color-picker_swatch-icon_xKjWO\",\n\t\"inactive-gradient\": \"color-picker_inactive-gradient_SY3ld\",\n\t\"inactiveGradient\": \"color-picker_inactive-gradient_SY3ld\",\n\t\"gradient-picker-row\": \"color-picker_gradient-picker-row_fHjyz\",\n\t\"gradientPickerRow\": \"color-picker_gradient-picker-row_fHjyz\",\n\t\"gradient-swatches-row\": \"color-picker_gradient-swatches-row_hEq4C\",\n\t\"gradientSwatchesRow\": \"color-picker_gradient-swatches-row_hEq4C\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n.dropdown_dropdown_fuTSY {\\n border: 1px solid #E9EEF2;\\n border-radius: 5px;\\n overflow: visible;\\n min-width: 3.5rem;\\n color: #855CD6;\\n padding: .5rem;\\n}\\n\\n.dropdown_mod-open_elkw8 {\\n background-color: #E9EEF2;\\n}\\n\\n.dropdown_dropdown-icon_b0w6V {\\n width: .5rem;\\n height: .5rem;\\n vertical-align: middle;\\n padding-bottom: .2rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .dropdown_dropdown-icon_b0w6V {\\n margin-left: .5rem;\\n}\\n\\n[dir=\\\"rtl\\\"] .dropdown_dropdown-icon_b0w6V {\\n margin-right: .5rem;\\n}\\n\\n.dropdown_mod-caret-up_BSdAI {\\n -webkit-transform: rotate(180deg);\\n -ms-transform: rotate(180deg);\\n transform: rotate(180deg);\\n padding-bottom: 0;\\n padding-top: .2rem;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/components/dropdown/dropdown.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACP9F;IACI,yBAA8B;IAC9B,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,cAAuB;IACvB,cAAc;AAClB;;AAEA;IACI,yBAA8B;AAClC;;AAEA;IACI,YAAY;IACZ,aAAa;IACb,sBAAsB;IACtB,qBAAqB;AACzB;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,mBAAmB;AACvB;;AAEA;IACI,iCAAyB;QAAzB,6BAAyB;YAAzB,yBAAyB;IACzB,iBAAiB;IACjB,kBAAkB;AACtB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"@import '../../css/colors.css';\\n\\n$arrow-border-width: 14px;\\n\\n.dropdown {\\n border: 1px solid $form-border;\\n border-radius: 5px;\\n overflow: visible;\\n min-width: 3.5rem;\\n color: $looks-secondary;\\n padding: .5rem;\\n}\\n\\n.mod-open {\\n background-color: $form-border;\\n}\\n\\n.dropdown-icon {\\n width: .5rem;\\n height: .5rem;\\n vertical-align: middle;\\n padding-bottom: .2rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .dropdown-icon {\\n margin-left: .5rem;\\n}\\n\\n[dir=\\\"rtl\\\"] .dropdown-icon {\\n margin-right: .5rem;\\n}\\n\\n.mod-caret-up {\\n transform: rotate(180deg);\\n padding-bottom: 0;\\n padding-top: .2rem;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"dropdown\": \"dropdown_dropdown_fuTSY\",\n\t\"mod-open\": \"dropdown_mod-open_elkw8\",\n\t\"modOpen\": \"dropdown_mod-open_elkw8\",\n\t\"dropdown-icon\": \"dropdown_dropdown-icon_b0w6V\",\n\t\"dropdownIcon\": \"dropdown_dropdown-icon_b0w6V\",\n\t\"mod-caret-up\": \"dropdown_mod-caret-up_BSdAI\",\n\t\"modCaretUp\": \"dropdown_mod-caret-up_BSdAI\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.fixed-tools_row_4i\\\\+0p {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n.fixed-tools_costume-input_p5l43 {\\n width: 8rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .fixed-tools_mod-dashed-border_MnO5C {\\n border-right: 1px dashed #D9D9D9;\\n padding-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_mod-dashed-border_MnO5C {\\n border-left: 1px dashed #D9D9D9;\\n padding-left: calc(2 * .25rem);\\n}\\n\\n.fixed-tools_mod-unselect_wVcbW {\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n -ms-user-select: none;\\n user-select: none;\\n}\\n\\n.fixed-tools_button-group-button_iXqpD {\\n display: inline-block;\\n border: 1px solid #D9D9D9;\\n border-radius: 0;\\n padding: .35rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .fixed-tools_button-group-button_iXqpD {\\n border-left: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_button-group-button_iXqpD {\\n border-right: none;\\n}\\n\\n[dir=\\\"ltr\\\"] .fixed-tools_button-group-button_iXqpD:last-of-type {\\n border-top-right-radius: 0.25rem;\\n border-bottom-right-radius: 0.25rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .fixed-tools_button-group-button_iXqpD:first-of-type {\\n border-left: 1px solid #D9D9D9;\\n border-top-left-radius: 0.25rem;\\n border-bottom-left-radius: 0.25rem;\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_button-group-button_iXqpD:last-of-type {\\n border-top-left-radius: 0.25rem;\\n border-bottom-left-radius: 0.25rem;\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_button-group-button_iXqpD:first-of-type {\\n border-right: 1px solid #D9D9D9;\\n border-top-right-radius: 0.25rem;\\n border-bottom-right-radius: 0.25rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .fixed-tools_button-group-button_iXqpD.fixed-tools_mod-start-border_yh3uC {\\n border-left: 1px solid #D9D9D9;\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_button-group-button_iXqpD.fixed-tools_mod-start-border_yh3uC {\\n border-right: 1px solid #D9D9D9;\\n}\\n\\n[dir=\\\"ltr\\\"] .fixed-tools_button-group-button_iXqpD.fixed-tools_mod-no-end-border_o0JOq {\\n border-right: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_button-group-button_iXqpD.fixed-tools_mod-no-end-border_o0JOq {\\n border-left: none;\\n}\\n\\n.fixed-tools_button-group-button-icon_Y6tZ2 {\\n width: 1.25rem;\\n height: 1.25rem;\\n vertical-align: middle;\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_button-group-button-icon_Y6tZ2 {\\n -webkit-transform: scaleX(-1);\\n -ms-transform: scaleX(-1);\\n transform: scaleX(-1);\\n}\\n\\n.fixed-tools_mod-context-menu_auitG {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: vertical;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: column;\\n -ms-flex-direction: column;\\n flex-direction: column;\\n}\\n\\n.fixed-tools_mod-top-divider_R1J5f {\\n border-top: 1px solid #D9D9D9;\\n}\\n\\n.fixed-tools_mod-menu-item_me6aV {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n margin: 0 -.25rem;\\n min-width: 6.25rem;\\n padding: calc(3 * .25rem);\\n white-space: nowrap;\\n cursor: pointer;\\n -webkit-transition: 0.1s ease;\\n transition: 0.1s ease;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n font-family: \\\"Helvetica Neue\\\", Helvetica, sans-serif;\\n}\\n\\n.fixed-tools_mod-disabled_7U7bI {\\n cursor: auto;\\n}\\n\\n.fixed-tools_mod-menu-item_me6aV:hover {\\n background: hsla(260, 60%, 60%, 0.35);\\n}\\n\\n.fixed-tools_mod-disabled_7U7bI:hover {\\n background-color: transparent;\\n}\\n\\n.fixed-tools_menu-item-icon_NsQ\\\\+d {\\n margin-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .fixed-tools_menu-item-icon_NsQ\\\\+d {\\n margin-right: 0;\\n margin-left: calc(2 * .25rem);\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/css/units.css\",\"webpack://./src/components/fixed-tools/fixed-tools.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACX9F;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACpB1D;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,WAAW;AACf;;AAEA;IACI,gCAAwC;IACxC,+BAAmC;AACvC;;AAEA;IACI,+BAAuC;IACvC,8BAAkC;AACtC;;AAEA;IACI,yBAAiB;OAAjB,sBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;AACrB;;AAIA;IACI,qBAAqB;IACrB,yBAAiC;IACjC,gBAAgB;IAChB,eAAe;AACnB;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,gCAAuC;IACvC,mCAA0C;AAC9C;;AAEA;IACI,8BAAsC;IACtC,+BAAsC;IACtC,kCAAyC;AAC7C;;AAEA;IACI,+BAAsC;IACtC,kCAAyC;AAC7C;;AAEA;IACI,+BAAuC;IACvC,gCAAuC;IACvC,mCAA0C;AAC9C;;AAEA;IACI,8BAAsC;AAC1C;;AAEA;IACI,+BAAuC;AAC3C;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,cAAc;IACd,eAAe;IACf,sBAAsB;AAC1B;;AAEA;IACI,6BAAqB;QAArB,yBAAqB;YAArB,qBAAqB;AACzB;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,4BAAsB;IAAtB,6BAAsB;IAAtB,8BAAsB;QAAtB,0BAAsB;YAAtB,sBAAsB;AAC1B;;AAEA;IACI,6BAAqC;AACzC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,iBAAqB;IACrB,kBAAkB;IAClB,yBAA6B;IAC7B,mBAAmB;IACnB,eAAe;IACf,6BAAqB;IAArB,qBAAqB;IACrB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;IACnB,oDAAoD;AACxD;;AAEA;IACI,YAAY;AAChB;;AAEA;IACI,qCAA8B;AAClC;;AAEA;IACI,6BAA6B;AACjC;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,eAAe;IACf,6BAAiC;AACrC\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/colors.css\\\";\\n@import \\\"../../css/units.css\\\";\\n\\n.row {\\n display: flex;\\n flex-direction: row;\\n align-items: center;\\n}\\n\\n.costume-input {\\n width: 8rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .mod-dashed-border {\\n border-right: 1px dashed $ui-pane-border;\\n padding-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .mod-dashed-border {\\n border-left: 1px dashed $ui-pane-border;\\n padding-left: calc(2 * $grid-unit);\\n}\\n\\n.mod-unselect {\\n user-select: none;\\n}\\n\\n$border-radius: 0.25rem;\\n\\n.button-group-button {\\n display: inline-block;\\n border: 1px solid $ui-pane-border;\\n border-radius: 0;\\n padding: .35rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button {\\n border-left: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button {\\n border-right: none;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button:last-of-type {\\n border-top-right-radius: $border-radius;\\n border-bottom-right-radius: $border-radius;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button:first-of-type {\\n border-left: 1px solid $ui-pane-border;\\n border-top-left-radius: $border-radius;\\n border-bottom-left-radius: $border-radius;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button:last-of-type {\\n border-top-left-radius: $border-radius;\\n border-bottom-left-radius: $border-radius;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button:first-of-type {\\n border-right: 1px solid $ui-pane-border;\\n border-top-right-radius: $border-radius;\\n border-bottom-right-radius: $border-radius;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button.mod-start-border {\\n border-left: 1px solid $ui-pane-border;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button.mod-start-border {\\n border-right: 1px solid $ui-pane-border;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button.mod-no-end-border {\\n border-right: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button.mod-no-end-border {\\n border-left: none;\\n}\\n\\n.button-group-button-icon {\\n width: 1.25rem;\\n height: 1.25rem;\\n vertical-align: middle;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button-icon {\\n transform: scaleX(-1);\\n}\\n\\n.mod-context-menu {\\n display: flex;\\n flex-direction: column;\\n}\\n\\n.mod-top-divider {\\n border-top: 1px solid $ui-pane-border;\\n}\\n\\n.mod-menu-item {\\n display: flex;\\n margin: 0 -$grid-unit;\\n min-width: 6.25rem;\\n padding: calc(3 * $grid-unit);\\n white-space: nowrap;\\n cursor: pointer;\\n transition: 0.1s ease;\\n align-items: center;\\n font-family: \\\"Helvetica Neue\\\", Helvetica, sans-serif;\\n}\\n\\n.mod-disabled {\\n cursor: auto;\\n}\\n\\n.mod-menu-item:hover {\\n background: $looks-transparent;\\n}\\n\\n.mod-disabled:hover {\\n background-color: transparent;\\n}\\n\\n.menu-item-icon {\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .menu-item-icon {\\n margin-right: 0;\\n margin-left: calc(2 * $grid-unit);\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"row\": \"fixed-tools_row_4i+0p\",\n\t\"costume-input\": \"fixed-tools_costume-input_p5l43\",\n\t\"costumeInput\": \"fixed-tools_costume-input_p5l43\",\n\t\"mod-dashed-border\": \"fixed-tools_mod-dashed-border_MnO5C\",\n\t\"modDashedBorder\": \"fixed-tools_mod-dashed-border_MnO5C\",\n\t\"mod-unselect\": \"fixed-tools_mod-unselect_wVcbW\",\n\t\"modUnselect\": \"fixed-tools_mod-unselect_wVcbW\",\n\t\"button-group-button\": \"fixed-tools_button-group-button_iXqpD\",\n\t\"buttonGroupButton\": \"fixed-tools_button-group-button_iXqpD\",\n\t\"mod-start-border\": \"fixed-tools_mod-start-border_yh3uC\",\n\t\"modStartBorder\": \"fixed-tools_mod-start-border_yh3uC\",\n\t\"mod-no-end-border\": \"fixed-tools_mod-no-end-border_o0JOq\",\n\t\"modNoEndBorder\": \"fixed-tools_mod-no-end-border_o0JOq\",\n\t\"button-group-button-icon\": \"fixed-tools_button-group-button-icon_Y6tZ2\",\n\t\"buttonGroupButtonIcon\": \"fixed-tools_button-group-button-icon_Y6tZ2\",\n\t\"mod-context-menu\": \"fixed-tools_mod-context-menu_auitG\",\n\t\"modContextMenu\": \"fixed-tools_mod-context-menu_auitG\",\n\t\"mod-top-divider\": \"fixed-tools_mod-top-divider_R1J5f\",\n\t\"modTopDivider\": \"fixed-tools_mod-top-divider_R1J5f\",\n\t\"mod-menu-item\": \"fixed-tools_mod-menu-item_me6aV\",\n\t\"modMenuItem\": \"fixed-tools_mod-menu-item_me6aV\",\n\t\"mod-disabled\": \"fixed-tools_mod-disabled_7U7bI\",\n\t\"modDisabled\": \"fixed-tools_mod-disabled_7U7bI\",\n\t\"menu-item-icon\": \"fixed-tools_menu-item-icon_NsQ+d\",\n\t\"menuItemIcon\": \"fixed-tools_menu-item-icon_NsQ+d\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.font-dropdown_mod-menu-item_dJzxu {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n margin: 0 -.25rem;\\n min-width: 6.25rem;\\n padding: calc(2 * .25rem);\\n padding-left: calc(3 * .25rem);\\n padding-right: calc(3 * .25rem);\\n white-space: nowrap;\\n width: 8.5rem;\\n cursor: pointer;\\n -webkit-transition: 0.1s ease;\\n transition: 0.1s ease;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n.font-dropdown_mod-menu-item_dJzxu:hover {\\n background: #855CD6;\\n color: white;\\n}\\n\\n.font-dropdown_mod-context-menu_HvLHz {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: vertical;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: column;\\n -ms-flex-direction: column;\\n flex-direction: column;\\n}\\n\\n.font-dropdown_mod-unselect_qEbDA {\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n -ms-user-select: none;\\n user-select: none;\\n}\\n\\n.font-dropdown_displayed-font-name_KPlzz {\\n font-size: .8rem;\\n}\\n\\n.font-dropdown_font-dropdown_ZkGgL {\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n color: #575e75;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n font-size: 1rem;\\n -webkit-box-pack: justify;\\n -webkit-justify-content: space-between;\\n -ms-flex-pack: justify;\\n justify-content: space-between;\\n width: 8.5rem;\\n height: 2rem;\\n}\\n\\n.font-dropdown_serif_CsAlO {\\n font-family: 'Serif';\\n}\\n\\n.font-dropdown_sans-serif_Hz8vx {\\n font-family: 'Sans Serif';\\n}\\n\\n.font-dropdown_serif_CsAlO {\\n font-family: 'Serif';\\n}\\n\\n.font-dropdown_handwriting_MtiIG {\\n font-family: 'Handwriting';\\n}\\n\\n.font-dropdown_marker_TQrGu {\\n font-family: 'Marker';\\n}\\n\\n.font-dropdown_curly_2f8OQ {\\n font-family: 'Curly';\\n}\\n\\n.font-dropdown_pixel_JGb4D {\\n font-family: 'Pixel';\\n}\\n\\n.font-dropdown_chinese_9gFKG {\\n font-family: \\\"Microsoft YaHei\\\", \\\"微软雅黑\\\", STXihei, \\\"华文细黑\\\";\\n}\\n\\n.font-dropdown_japanese_mR7dT {\\n font-family: \\\"ヒラギノ角ゴ Pro W3\\\", \\\"Hiragino Kaku Gothic Pro\\\", Osaka, \\\"メイリオ\\\", Meiryo, \\\"MS Pゴシック\\\", \\\"MS PGothic\\\";\\n}\\n\\n.font-dropdown_korean_d2n4y {\\n font-family: \\\"Malgun Gothic\\\";\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/css/units.css\",\"webpack://./src/components/font-dropdown/font-dropdown.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACX9F;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACpB1D;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,iBAAqB;IACrB,kBAAkB;IAClB,yBAA6B;IAC7B,8BAAkC;IAClC,+BAAmC;IACnC,mBAAmB;IACnB,aAAa;IACb,eAAe;IACf,6BAAqB;IAArB,qBAAqB;IACrB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,mBAA4B;IAC5B,YAAY;AAChB;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,4BAAsB;IAAtB,6BAAsB;IAAtB,8BAAsB;QAAtB,0BAAsB;YAAtB,sBAAsB;AAC1B;;AAEA;IACI,yBAAiB;OAAjB,sBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;AACrB;;AAEA;IACI,gBAAgB;AACpB;;AAEA;IACI,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;IACnB,cAAoB;IACpB,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,eAAe;IACf,yBAA8B;IAA9B,sCAA8B;QAA9B,sBAA8B;YAA9B,8BAA8B;IAC9B,aAAa;IACb,YAAY;AAChB;;AAEA;IACI,oBAAoB;AACxB;;AAEA;IACI,yBAAyB;AAC7B;;AAEA;IACI,oBAAoB;AACxB;;AAEA;IACI,0BAA0B;AAC9B;;AAEA;IACI,qBAAqB;AACzB;;AAEA;IACI,oBAAoB;AACxB;;AAEA;IACI,oBAAoB;AACxB;;AAEA;IACI,uDAAuD;AAC3D;;AAEA;IACI,yGAAyG;AAC7G;;AAEA;IACI,4BAA4B;AAChC\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/colors.css\\\";\\n@import \\\"../../css/units.css\\\";\\n\\n.mod-menu-item {\\n display: flex;\\n margin: 0 -$grid-unit;\\n min-width: 6.25rem;\\n padding: calc(2 * $grid-unit);\\n padding-left: calc(3 * $grid-unit);\\n padding-right: calc(3 * $grid-unit);\\n white-space: nowrap;\\n width: 8.5rem;\\n cursor: pointer;\\n transition: 0.1s ease;\\n align-items: center;\\n}\\n\\n.mod-menu-item:hover {\\n background: $looks-secondary;\\n color: white;\\n}\\n\\n.mod-context-menu {\\n display: flex;\\n flex-direction: column;\\n}\\n\\n.mod-unselect {\\n user-select: none;\\n}\\n\\n.displayed-font-name {\\n font-size: .8rem;\\n}\\n\\n.font-dropdown {\\n align-items: center;\\n color: $text-primary;\\n display: flex;\\n font-size: 1rem;\\n justify-content: space-between;\\n width: 8.5rem;\\n height: 2rem;\\n}\\n\\n.serif {\\n font-family: 'Serif';\\n}\\n\\n.sans-serif {\\n font-family: 'Sans Serif';\\n}\\n\\n.serif {\\n font-family: 'Serif';\\n}\\n\\n.handwriting {\\n font-family: 'Handwriting';\\n}\\n\\n.marker {\\n font-family: 'Marker';\\n}\\n\\n.curly {\\n font-family: 'Curly';\\n}\\n\\n.pixel {\\n font-family: 'Pixel';\\n}\\n\\n.chinese {\\n font-family: \\\"Microsoft YaHei\\\", \\\"微软雅黑\\\", STXihei, \\\"华文细黑\\\";\\n}\\n\\n.japanese {\\n font-family: \\\"ヒラギノ角ゴ Pro W3\\\", \\\"Hiragino Kaku Gothic Pro\\\", Osaka, \\\"メイリオ\\\", Meiryo, \\\"MS Pゴシック\\\", \\\"MS PGothic\\\";\\n}\\n\\n.korean {\\n font-family: \\\"Malgun Gothic\\\";\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"mod-menu-item\": \"font-dropdown_mod-menu-item_dJzxu\",\n\t\"modMenuItem\": \"font-dropdown_mod-menu-item_dJzxu\",\n\t\"mod-context-menu\": \"font-dropdown_mod-context-menu_HvLHz\",\n\t\"modContextMenu\": \"font-dropdown_mod-context-menu_HvLHz\",\n\t\"mod-unselect\": \"font-dropdown_mod-unselect_qEbDA\",\n\t\"modUnselect\": \"font-dropdown_mod-unselect_qEbDA\",\n\t\"displayed-font-name\": \"font-dropdown_displayed-font-name_KPlzz\",\n\t\"displayedFontName\": \"font-dropdown_displayed-font-name_KPlzz\",\n\t\"font-dropdown\": \"font-dropdown_font-dropdown_ZkGgL\",\n\t\"fontDropdown\": \"font-dropdown_font-dropdown_ZkGgL\",\n\t\"serif\": \"font-dropdown_serif_CsAlO\",\n\t\"sans-serif\": \"font-dropdown_sans-serif_Hz8vx\",\n\t\"sansSerif\": \"font-dropdown_sans-serif_Hz8vx\",\n\t\"handwriting\": \"font-dropdown_handwriting_MtiIG\",\n\t\"marker\": \"font-dropdown_marker_TQrGu\",\n\t\"curly\": \"font-dropdown_curly_2f8OQ\",\n\t\"pixel\": \"font-dropdown_pixel_JGb4D\",\n\t\"chinese\": \"font-dropdown_chinese_9gFKG\",\n\t\"japanese\": \"font-dropdown_japanese_mR7dT\",\n\t\"korean\": \"font-dropdown_korean_d2n4y\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* NOTE:\\nEdited to add input-range-small\\n*/\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n.input_input-form_rYjUv {\\n height: 2rem;\\n padding: 0 0.75rem;\\n\\n font-family: \\\"Helvetica Neue\\\", Helvetica, Arial, sans-serif;\\n font-size: 0.75rem;\\n font-weight: bold;\\n color: #575e75;\\n\\n border-width: 1px;\\n border-style: solid;\\n border-color: #E9EEF2;\\n border-radius: 2rem;\\n\\n outline: none;\\n cursor: text;\\n -webkit-transition: 0.25s ease-out;\\n transition: 0.25s ease-out; /* @todo: standardize with var */\\n -webkit-box-shadow: none;\\n box-shadow: none;\\n\\n /*\\n For truncating overflowing text gracefully\\n Min-width is for a bug: https://css-tricks.com/flexbox-truncated-text\\n @todo: move this out into a mixin or a helper component\\n */\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n min-width: 0;\\n}\\n\\n.input_input-form_rYjUv:focus {\\n border-color: #855CD6;\\n -webkit-box-shadow: 0 0 0 .25rem hsla(260, 60%, 60%, 0.35);\\n box-shadow: 0 0 0 .25rem hsla(260, 60%, 60%, 0.35);\\n}\\n\\n.input_input-small_XO8vY {\\n width: 3rem;\\n text-align: center;\\n}\\n\\n.input_input-small-range_BbZnL {\\n width: 4rem;\\n text-align: center;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/components/forms/input.css\",\"webpack://./src/css/units.css\",\"webpack://./src/css/colors.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AAEpD;;CAEC;;ACND;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACvB1D;;oDAEoD;;AASL,+CAA+C;;AFA9F;IACI,YAAY;IACZ,kBAAkB;;IAElB,2DAA2D;IAC3D,kBAAkB;IAClB,iBAAiB;IACjB,cAAoB;;IAEpB,iBAAiB;IACjB,mBAAmB;IACnB,qBAA0B;IAC1B,mBAAmB;;IAEnB,aAAa;IACb,YAAY;IACZ,kCAA0B;IAA1B,0BAA0B,EAAE,gCAAgC;IAC5D,wBAAgB;YAAhB,gBAAgB;;IAEhB;;;;KAIC;IACD,gBAAgB;IAChB,uBAAuB;IACvB,mBAAmB;IACnB,YAAY;AAChB;;AAEA;IACI,qBAA8B;IAC9B,0DAA+C;YAA/C,kDAA+C;AACnD;;AAEA;IACI,WAAW;IACX,kBAAkB;AACtB;;AAEA;IACI,WAAW;IACX,kBAAkB;AACtB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* NOTE:\\nEdited to add input-range-small\\n*/\\n\\n@import \\\"../../css/units.css\\\";\\n@import \\\"../../css/colors.css\\\";\\n\\n.input-form {\\n height: 2rem;\\n padding: 0 0.75rem;\\n\\n font-family: \\\"Helvetica Neue\\\", Helvetica, Arial, sans-serif;\\n font-size: 0.75rem;\\n font-weight: bold;\\n color: $text-primary;\\n\\n border-width: 1px;\\n border-style: solid;\\n border-color: $form-border;\\n border-radius: 2rem;\\n\\n outline: none;\\n cursor: text;\\n transition: 0.25s ease-out; /* @todo: standardize with var */\\n box-shadow: none;\\n\\n /*\\n For truncating overflowing text gracefully\\n Min-width is for a bug: https://css-tricks.com/flexbox-truncated-text\\n @todo: move this out into a mixin or a helper component\\n */\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n min-width: 0;\\n}\\n\\n.input-form:focus {\\n border-color: $looks-secondary;\\n box-shadow: 0 0 0 $grid-unit $looks-transparent;\\n}\\n\\n.input-small {\\n width: 3rem;\\n text-align: center;\\n}\\n\\n.input-small-range {\\n width: 4rem;\\n text-align: center;\\n}\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"input-form\": \"input_input-form_rYjUv\",\n\t\"inputForm\": \"input_input-form_rYjUv\",\n\t\"input-small\": \"input_input-small_XO8vY\",\n\t\"inputSmall\": \"input_input-small_XO8vY\",\n\t\"input-small-range\": \"input_input-small-range_BbZnL\",\n\t\"inputSmallRange\": \"input_input-small-range_BbZnL\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n.label_input-group_RurlE {\\n display: -webkit-inline-box;\\n display: -webkit-inline-flex;\\n display: -ms-inline-flexbox;\\n display: inline-flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n[dir=\\\"ltr\\\"] .label_input-group_RurlE + .label_input-group_RurlE {\\n margin-left: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .label_input-group_RurlE + .label_input-group_RurlE {\\n margin-right: calc(2 * .25rem);\\n}\\n\\n.label_disabled_SBKdo {\\n opacity: 0.3;\\n /* Prevent any user actions */\\n pointer-events: none;\\n}\\n\\n.label_input-label_P0e01, .label_input-label-secondary_Db6Q8 {\\n font-size: 0.625rem;\\n -webkit-user-select: none;\\n -moz-user-select: none;\\n -ms-user-select: none;\\n user-select: none;\\n cursor: default;\\n}\\n\\n[dir=\\\"ltr\\\"] .label_input-label_P0e01, [dir=\\\"ltr\\\"] .label_input-label-secondary_Db6Q8{\\n margin-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .label_input-label_P0e01, [dir=\\\"ltr\\\"] .label_input-label-secondary_Db6Q8{\\n margin-left: calc(2 * .25rem);\\n}\\n\\n.label_input-label_P0e01 {\\n font-weight: bold;\\n}\\n\\n@media only screen and (max-width: 1256px) {\\n .label_input-group_RurlE {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: vertical;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: column;\\n -ms-flex-direction: column;\\n flex-direction: column;\\n -webkit-box-align: start;\\n -webkit-align-items: flex-start;\\n -ms-flex-align: start;\\n align-items: flex-start;\\n margin-top: -1rem; /* To align with the non-labeled inputs */\\n }\\n\\n .label_input-label_P0e01 {\\n font-weight: normal;\\n margin-bottom: 0.25rem;\\n }\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/components/forms/label.css\",\"webpack://./src/css/units.css\",\"webpack://./src/css/colors.css\",\"webpack://./src/components/input-group/input-group.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;ACFpD;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACvB1D;;oDAEoD;;AASL,+CAA+C;;ACT9F;IACI,2BAAoB;IAApB,4BAAoB;IAApB,2BAAoB;IAApB,oBAAoB;IACpB,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,YAAY;IACZ,6BAA6B;IAC7B,oBAAoB;AACxB;;AHZA;IACI,mBAAmB;IACnB,yBAAiB;OAAjB,sBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;IACjB,eAAe;AACnB;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI;QACI,oBAAa;QAAb,qBAAa;QAAb,oBAAa;QAAb,aAAa;QACb,4BAAsB;QAAtB,6BAAsB;QAAtB,8BAAsB;YAAtB,0BAAsB;gBAAtB,sBAAsB;QACtB,wBAAuB;QAAvB,+BAAuB;YAAvB,qBAAuB;gBAAvB,uBAAuB;QACvB,iBAAiB,EAAE,yCAAyC;IAChE;;IAEA;QACI,mBAAmB;QACnB,sBAAsB;IAC1B;AACJ\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n@import \\\"../../css/units.css\\\";\\n@import \\\"../../css/colors.css\\\";\\n@import \\\"../input-group/input-group.css\\\";\\n\\n.input-label, .input-label-secondary {\\n font-size: 0.625rem;\\n user-select: none;\\n cursor: default;\\n}\\n\\n[dir=\\\"ltr\\\"] .input-label, [dir=\\\"ltr\\\"] .input-label-secondary{\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .input-label, [dir=\\\"ltr\\\"] .input-label-secondary{\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n.input-label {\\n font-weight: bold;\\n}\\n\\n@media only screen and (max-width: $full-size-paint) {\\n .input-group {\\n display: flex;\\n flex-direction: column;\\n align-items: flex-start;\\n margin-top: -1rem; /* To align with the non-labeled inputs */\\n }\\n\\n .input-label {\\n font-weight: normal;\\n margin-bottom: 0.25rem;\\n }\\n}\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"@import '../../css/units.css';\\n\\n.input-group {\\n display: inline-flex;\\n flex-direction: row;\\n align-items: center;\\n}\\n\\n[dir=\\\"ltr\\\"] .input-group + .input-group {\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .input-group + .input-group {\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n.disabled {\\n opacity: 0.3;\\n /* Prevent any user actions */\\n pointer-events: none;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"input-group\": \"label_input-group_RurlE\",\n\t\"inputGroup\": \"label_input-group_RurlE\",\n\t\"disabled\": \"label_disabled_SBKdo\",\n\t\"input-label\": \"label_input-label_P0e01\",\n\t\"inputLabel\": \"label_input-label_P0e01\",\n\t\"input-label-secondary\": \"label_input-label-secondary_Db6Q8\",\n\t\"inputLabelSecondary\": \"label_input-label-secondary_Db6Q8\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".slider_container_FGZoi {\\n margin: 8px;\\n height: 22px;\\n width: 150px;\\n position: relative;\\n outline: none;\\n border-radius: 11px;\\n margin-bottom: 20px;\\n}\\n\\n.slider_last_CQwuY {\\n margin-bottom: 4px;\\n}\\n\\n.slider_handle_s5s-- {\\n left: 100px;\\n width: 26px;\\n height: 26px;\\n margin-top: -2px;\\n position: absolute;\\n background-color: white;\\n border-radius: 100%;\\n -webkit-box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);\\n box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);\\n -ms-touch-action: none;\\n touch-action: none;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/components/forms/slider.css\"],\"names\":[],\"mappings\":\"AAAA;IACI,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,kBAAkB;IAClB,aAAa;IACb,mBAAmB;IACnB,mBAAmB;AACvB;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,WAAW;IACX,WAAW;IACX,YAAY;IACZ,gBAAgB;IAChB,kBAAkB;IAClB,uBAAuB;IACvB,mBAAmB;IACnB,iDAAyC;YAAzC,yCAAyC;IACzC,sBAAkB;QAAlB,kBAAkB;AACtB\",\"sourcesContent\":[\".container {\\n margin: 8px;\\n height: 22px;\\n width: 150px;\\n position: relative;\\n outline: none;\\n border-radius: 11px;\\n margin-bottom: 20px;\\n}\\n\\n.last {\\n margin-bottom: 4px;\\n}\\n\\n.handle {\\n left: 100px;\\n width: 26px;\\n height: 26px;\\n margin-top: -2px;\\n position: absolute;\\n background-color: white;\\n border-radius: 100%;\\n box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);\\n touch-action: none;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"container\": \"slider_container_FGZoi\",\n\t\"last\": \"slider_last_CQwuY\",\n\t\"handle\": \"slider_handle_s5s--\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.input-group_input-group_WCF5L {\\n display: -webkit-inline-box;\\n display: -webkit-inline-flex;\\n display: -ms-inline-flexbox;\\n display: inline-flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n[dir=\\\"ltr\\\"] .input-group_input-group_WCF5L + .input-group_input-group_WCF5L {\\n margin-left: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .input-group_input-group_WCF5L + .input-group_input-group_WCF5L {\\n margin-right: calc(2 * .25rem);\\n}\\n\\n.input-group_disabled_lZ9Wt {\\n opacity: 0.3;\\n /* Prevent any user actions */\\n pointer-events: none;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/units.css\",\"webpack://./src/components/input-group/input-group.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACrB1D;IACI,2BAAoB;IAApB,4BAAoB;IAApB,2BAAoB;IAApB,oBAAoB;IACpB,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,YAAY;IACZ,6BAA6B;IAC7B,oBAAoB;AACxB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import '../../css/units.css';\\n\\n.input-group {\\n display: inline-flex;\\n flex-direction: row;\\n align-items: center;\\n}\\n\\n[dir=\\\"ltr\\\"] .input-group + .input-group {\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .input-group + .input-group {\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n.disabled {\\n opacity: 0.3;\\n /* Prevent any user actions */\\n pointer-events: none;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"input-group\": \"input-group_input-group_WCF5L\",\n\t\"inputGroup\": \"input-group_input-group_WCF5L\",\n\t\"disabled\": \"input-group_disabled_lZ9Wt\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.labeled-icon-button_mod-edit-field_-KCzg {\\n background: none;\\n border: none;\\n display: inline-block;\\n padding: .25rem .325rem;\\n outline: none;\\n border-radius: 0.25rem;\\n min-width: 3rem;\\n font-size: 0.85rem;\\n text-align: center;\\n}\\n\\n.labeled-icon-button_edit-field-icon_aGeBz {\\n width: 1.5rem;\\n height: 1.5rem;\\n -webkit-box-flex: 1;\\n -webkit-flex-grow: 1;\\n -ms-flex-positive: 1;\\n flex-grow: 1;\\n vertical-align: middle;\\n}\\n\\n.labeled-icon-button_edit-field-title_\\\\+LjHl {\\n display: block;\\n margin-top: .125rem;\\n font-size: .625rem;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/units.css\",\"webpack://./src/components/labeled-icon-button/labeled-icon-button.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACnB1D;IACI,gBAAgB;IAChB,YAAY;IACZ,qBAAqB;IACrB,uBAAuB;IACvB,aAAa;IACb,sBAA6B;IAC7B,eAAe;IACf,kBAAkB;IAClB,kBAAkB;AACtB;;AAEA;IACI,aAAa;IACb,cAAc;IACd,mBAAY;IAAZ,oBAAY;QAAZ,oBAAY;YAAZ,YAAY;IACZ,sBAAsB;AAC1B;;AAEA;IACI,cAAc;IACd,mBAAmB;IACnB,kBAAkB;AACtB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/units.css\\\";\\n\\n$border-radius: 0.25rem;\\n\\n.mod-edit-field {\\n background: none;\\n border: none;\\n display: inline-block;\\n padding: .25rem .325rem;\\n outline: none;\\n border-radius: $border-radius;\\n min-width: 3rem;\\n font-size: 0.85rem;\\n text-align: center;\\n}\\n\\n.edit-field-icon {\\n width: 1.5rem;\\n height: 1.5rem;\\n flex-grow: 1;\\n vertical-align: middle;\\n}\\n\\n.edit-field-title {\\n display: block;\\n margin-top: .125rem;\\n font-size: .625rem;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"mod-edit-field\": \"labeled-icon-button_mod-edit-field_-KCzg\",\n\t\"modEditField\": \"labeled-icon-button_mod-edit-field_-KCzg\",\n\t\"edit-field-icon\": \"labeled-icon-button_edit-field-icon_aGeBz\",\n\t\"editFieldIcon\": \"labeled-icon-button_edit-field-icon_aGeBz\",\n\t\"edit-field-title\": \"labeled-icon-button_edit-field-title_+LjHl\",\n\t\"editFieldTitle\": \"labeled-icon-button_edit-field-title_+LjHl\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".loupe_eye-dropper_qsEfW {\\n position: absolute;\\n border-radius: 100%;\\n border: 1px solid #222;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/components/loupe/loupe.css\"],\"names\":[],\"mappings\":\"AAAA;IACI,kBAAkB;IAClB,mBAAmB;IACnB,sBAAsB;AAC1B\",\"sourcesContent\":[\".eye-dropper {\\n position: absolute;\\n border-radius: 100%;\\n border: 1px solid #222;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"eye-dropper\": \"loupe_eye-dropper_qsEfW\",\n\t\"eyeDropper\": \"loupe_eye-dropper_qsEfW\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.mode-tools_mode-tools_JouAA {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n min-height: 3rem;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n.mode-tools_mode-tools-icon_wBvX0 {\\n margin-right: calc(2 * .25rem);\\n width: 2rem;\\n height: 2rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .mode-tools_mod-dashed-border_fOwhN {\\n border-right: 1px dashed #D9D9D9;\\n padding-right: calc(3 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .mode-tools_mod-dashed-border_fOwhN {\\n border-left: 1px dashed #D9D9D9;\\n padding-left: calc(3 * .25rem);\\n}\\n\\n.mode-tools_mod-labeled-icon-height_KjSSG {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n height: 2.85rem; /* for the second row so the dashed borders are equal in size */\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/css/units.css\",\"webpack://./src/components/mode-tools/mode-tools.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACX9F;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACpB1D;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,gBAAgB;IAChB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,8BAAkC;IAClC,WAAW;IACX,YAAY;AAChB;;AAEA;IACI,gCAAwC;IACxC,+BAAmC;AACvC;;AAEA;IACI,+BAAuC;IACvC,8BAAkC;AACtC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,eAAe,EAAE,+DAA+D;IAChF,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/colors.css\\\";\\n@import \\\"../../css/units.css\\\";\\n\\n.mode-tools {\\n display: flex;\\n min-height: 3rem;\\n align-items: center;\\n}\\n\\n.mode-tools-icon {\\n margin-right: calc(2 * $grid-unit);\\n width: 2rem;\\n height: 2rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .mod-dashed-border {\\n border-right: 1px dashed $ui-pane-border;\\n padding-right: calc(3 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .mod-dashed-border {\\n border-left: 1px dashed $ui-pane-border;\\n padding-left: calc(3 * $grid-unit);\\n}\\n\\n.mod-labeled-icon-height {\\n display: flex;\\n height: 2.85rem; /* for the second row so the dashed borders are equal in size */\\n align-items: center;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"mode-tools\": \"mode-tools_mode-tools_JouAA\",\n\t\"modeTools\": \"mode-tools_mode-tools_JouAA\",\n\t\"mode-tools-icon\": \"mode-tools_mode-tools-icon_wBvX0\",\n\t\"modeToolsIcon\": \"mode-tools_mode-tools-icon_wBvX0\",\n\t\"mod-dashed-border\": \"mode-tools_mod-dashed-border_fOwhN\",\n\t\"modDashedBorder\": \"mode-tools_mod-dashed-border_fOwhN\",\n\t\"mod-labeled-icon-height\": \"mode-tools_mod-labeled-icon-height_KjSSG\",\n\t\"modLabeledIconHeight\": \"mode-tools_mod-labeled-icon-height_KjSSG\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.paint-editor_editor-container_XJI6D {\\n width: 100%;\\n height: 100%;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: vertical;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: column;\\n -ms-flex-direction: column;\\n flex-direction: column;\\n padding: calc(3 * .25rem);\\n}\\n\\n.paint-editor_row_qNJro {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-box-align: center;\\n -webkit-align-items: center;\\n -ms-flex-align: center;\\n align-items: center;\\n}\\n\\n.paint-editor_editor-container-top_MzEH8 {\\n border-bottom: 1px dashed #D9D9D9;\\n padding-bottom: calc(2 * .25rem);\\n}\\n\\n.paint-editor_top-align-row_CG9zZ {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n height: 100%;\\n padding-top: calc(5 * .25rem);\\n min-width: 524px;\\n}\\n\\n.paint-editor_row_qNJro + .paint-editor_row_qNJro {\\n margin-top: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_mod-dashed-border_d3Y-E {\\n border-right: 1px dashed #D9D9D9;\\n padding-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_mod-dashed-border_d3Y-E {\\n border-left: 1px dashed #D9D9D9;\\n padding-left: calc(2 * .25rem);\\n}\\n\\n.paint-editor_mod-labeled-icon-height_4VotQ {\\n height: 2.85rem; /* for the second row so the dashed borders are equal in size */\\n}\\n\\n.paint-editor_button-group-button_71r\\\\+V {\\n display: inline-block;\\n border: 1px solid #D9D9D9;\\n border-radius: 0;\\n padding: .35rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_button-group-button_71r\\\\+V {\\n border-left: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_button-group-button_71r\\\\+V {\\n border-right: none;\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_button-group-button_71r\\\\+V:last-of-type {\\n border-top-right-radius: 0.25rem;\\n border-bottom-right-radius: 0.25rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_button-group-button_71r\\\\+V:first-of-type {\\n border-left: 1px solid #D9D9D9;\\n border-top-left-radius: 0.25rem;\\n border-bottom-left-radius: 0.25rem;\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_button-group-button_71r\\\\+V:last-of-type {\\n border-top-left-radius: 0.25rem;\\n border-bottom-left-radius: 0.25rem;\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_button-group-button_71r\\\\+V:first-of-type {\\n border-right: 1px solid #D9D9D9;\\n border-top-right-radius: 0.25rem;\\n border-bottom-right-radius: 0.25rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_button-group-button_71r\\\\+V.paint-editor_mod-start-border_brl5l {\\n border-left: 1px solid #D9D9D9;\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_button-group-button_71r\\\\+V.paint-editor_mod-start-border_brl5l {\\n border-right: 1px solid #D9D9D9;\\n}\\n\\n[dir=\\\"ltr\\\"].paint-editor_button-group-button_71r\\\\+V.paint-editor_mod-no-end-border_\\\\+9EBd {\\n border-right: none;\\n}\\n\\n[dir=\\\"rtl\\\"].paint-editor_button-group-button_71r\\\\+V.paint-editor_mod-no-end-border_\\\\+9EBd {\\n border-left: none;\\n}\\n\\n.paint-editor_button-group-button-icon_GOExF {\\n width: 1.25rem;\\n height: 1.25rem;\\n vertical-align: middle;\\n}\\n\\n.paint-editor_mod-mode-tools_kG-2T {\\n margin-left: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_mod-margin-after_lArV7 {\\n margin-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_mod-margin-after_lArV7 {\\n margin-left: calc(2 * .25rem);\\n}\\n\\n.paint-editor_controls-container_WlBVy {\\n width: 100%;\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: vertical;\\n -webkit-box-direction: normal;\\n -webkit-flex-flow: column;\\n -ms-flex-flow: column;\\n flex-flow: column;\\n -webkit-box-flex: 1;\\n -webkit-flex-grow: 1;\\n -ms-flex-positive: 1;\\n flex-grow: 1;\\n margin-left: calc(2 * .25rem);\\n margin-right: calc(2 * .25rem);\\n}\\n\\n.paint-editor_canvas-container_Qe1w8 {\\n width: 100%;\\n -webkit-box-flex: 1;\\n -webkit-flex-grow: 1;\\n -ms-flex-positive: 1;\\n flex-grow: 1;\\n min-width: 402px; /* Leave room for the border */\\n -webkit-box-sizing: content-box;\\n box-sizing: content-box;\\n border: 1px solid #e8edf1;\\n border-radius: .25rem;\\n position: relative;\\n overflow: visible;\\n}\\n\\n.paint-editor_mode-selector_ALomU {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n max-width: 7.5rem;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: row;\\n -ms-flex-direction: row;\\n flex-direction: row;\\n -webkit-flex-wrap: wrap;\\n -ms-flex-wrap: wrap;\\n flex-wrap: wrap;\\n -webkit-box-align: start;\\n -webkit-align-items: flex-start;\\n -ms-flex-align: start;\\n align-items: flex-start;\\n -webkit-align-content: flex-start;\\n -ms-flex-line-pack: start;\\n align-content: flex-start;\\n -webkit-box-pack: justify;\\n -webkit-justify-content: space-between;\\n -ms-flex-pack: justify;\\n justify-content: space-between;\\n}\\n\\n.paint-editor_zoom-controls_QQz-j {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n -webkit-box-orient: horizontal;\\n -webkit-box-direction: reverse;\\n -webkit-flex-direction: row-reverse;\\n -ms-flex-direction: row-reverse;\\n flex-direction: row-reverse;\\n}\\n\\n.paint-editor_color-picker-wrapper_KtBg6 {\\n position: absolute;\\n top: 0;\\n left: 0;\\n width: 100%;\\n height: 100%;\\n pointer-events: none;\\n}\\n\\n.paint-editor_canvas-controls_yjCGl {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n height: 36px;\\n margin-top: .25rem;\\n -webkit-box-pack: justify;\\n -webkit-justify-content: space-between;\\n -ms-flex-pack: justify;\\n justify-content: space-between;\\n}\\n\\n.paint-editor_bitmap-button_2te68 {\\n display: -webkit-box;\\n display: -webkit-flex;\\n display: -ms-flexbox;\\n display: flex;\\n border-radius: 5px;\\n background-color: #855CD6;\\n padding: calc(2 * .25rem);\\n line-height: 1.5rem;\\n font-size: calc(3 * .25rem);\\n font-weight: bold;\\n color: white;\\n -webkit-box-pack: center;\\n -webkit-justify-content: center;\\n -ms-flex-pack: center;\\n justify-content: center;\\n}\\n\\n[dir=\\\"ltr\\\"] .paint-editor_bitmap-button-icon_uLSWl {\\n margin-right: calc(2 * .25rem);\\n}\\n\\n[dir=\\\"rtl\\\"] .paint-editor_bitmap-button-icon_uLSWl {\\n margin-left: calc(2 * .25rem);\\n}\\n\\n@media only screen and (max-width: 1256px) {\\n .paint-editor_editor-container_XJI6D {\\n padding: calc(3 * .25rem) .25rem;\\n }\\n\\n .paint-editor_mode-selector_ALomU {\\n -webkit-box-orient: vertical;\\n -webkit-box-direction: normal;\\n -webkit-flex-direction: column;\\n -ms-flex-direction: column;\\n flex-direction: column;\\n -webkit-box-pack: start;\\n -webkit-justify-content: flex-start;\\n -ms-flex-pack: start;\\n justify-content: flex-start;\\n }\\n\\n .paint-editor_controls-container_WlBVy {\\n margin-right: .25rem;\\n margin-left: .25rem;\\n }\\n}\\n\\n.paint-editor_text-area_U17Si {\\n background: transparent;\\n border: none;\\n display: none;\\n margin: 0px;\\n opacity: .8;\\n outline: none;\\n overflow: hidden;\\n padding: 0px;\\n position: absolute;\\n resize: none;\\n -webkit-text-fill-color: transparent;\\n text-fill-color: transparent;\\n}\\n\\n.paint-editor_button-text_D-z\\\\+w {\\n width: 100%; /* Fixes button text wrapping in Edge */\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/css/units.css\",\"webpack://./src/components/paint-editor/paint-editor.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACX9F;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;ACpB1D;IACI,WAAW;IACX,YAAY;IACZ,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,4BAAsB;IAAtB,6BAAsB;IAAtB,8BAAsB;QAAtB,0BAAsB;YAAtB,sBAAsB;IACtB,yBAA6B;AACjC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,yBAAmB;IAAnB,2BAAmB;QAAnB,sBAAmB;YAAnB,mBAAmB;AACvB;;AAEA;IACI,iCAAyC;IACzC,gCAAoC;AACxC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,YAAY;IACZ,6BAAiC;IACjC,gBAAgB;AACpB;;AAEA;IACI,4BAAgC;AACpC;;AAEA;IACI,gCAAwC;IACxC,+BAAmC;AACvC;;AAEA;IACI,+BAAuC;IACvC,8BAAkC;AACtC;;AACA;IACI,eAAe,EAAE,+DAA+D;AACpF;;AAIA;IACI,qBAAqB;IACrB,yBAAiC;IACjC,gBAAgB;IAChB,eAAe;AACnB;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,gCAAuC;IACvC,mCAA0C;AAC9C;;AAEA;IACI,8BAAsC;IACtC,+BAAsC;IACtC,kCAAyC;AAC7C;;AAEA;IACI,+BAAsC;IACtC,kCAAyC;AAC7C;;AAEA;IACI,+BAAuC;IACvC,gCAAuC;IACvC,mCAA0C;AAC9C;;AAEA;IACI,8BAAsC;AAC1C;;AAEA;IACI,+BAAuC;AAC3C;;AAEA;IACI,kBAAkB;AACtB;;AAEA;IACI,iBAAiB;AACrB;;AAEA;IACI,cAAc;IACd,eAAe;IACf,sBAAsB;AAC1B;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI,WAAW;IACX,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,4BAAiB;IAAjB,6BAAiB;IAAjB,yBAAiB;QAAjB,qBAAiB;YAAjB,iBAAiB;IACjB,mBAAY;IAAZ,oBAAY;QAAZ,oBAAY;YAAZ,YAAY;IACZ,6BAAiC;IACjC,8BAAkC;AACtC;;AAEA;IACI,WAAW;IACX,mBAAY;IAAZ,oBAAY;QAAZ,oBAAY;YAAZ,YAAY;IACZ,gBAAgB,EAAE,8BAA8B;IAChD,+BAAuB;YAAvB,uBAAuB;IACvB,yBAAyB;IACzB,qBAAqB;IACrB,kBAAkB;IAClB,iBAAiB;AACrB;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,iBAAiB;IACjB,8BAAmB;IAAnB,6BAAmB;IAAnB,2BAAmB;QAAnB,uBAAmB;YAAnB,mBAAmB;IACnB,uBAAe;QAAf,mBAAe;YAAf,eAAe;IACf,wBAAuB;IAAvB,+BAAuB;QAAvB,qBAAuB;YAAvB,uBAAuB;IACvB,iCAAyB;QAAzB,yBAAyB;YAAzB,yBAAyB;IACzB,yBAA8B;IAA9B,sCAA8B;QAA9B,sBAA8B;YAA9B,8BAA8B;AAClC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,8BAA2B;IAA3B,8BAA2B;IAA3B,mCAA2B;QAA3B,+BAA2B;YAA3B,2BAA2B;AAC/B;;AAEA;IACI,kBAAkB;IAClB,MAAM;IACN,OAAO;IACP,WAAW;IACX,YAAY;IACZ,oBAAoB;AACxB;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,YAAY;IACZ,kBAAsB;IACtB,yBAA8B;IAA9B,sCAA8B;QAA9B,sBAA8B;YAA9B,8BAA8B;AAClC;;AAEA;IACI,oBAAa;IAAb,qBAAa;IAAb,oBAAa;IAAb,aAAa;IACb,kBAAkB;IAClB,yBAAkC;IAClC,yBAA6B;IAC7B,mBAAmB;IACnB,2BAA+B;IAC/B,iBAAiB;IACjB,YAAY;IACZ,wBAAuB;IAAvB,+BAAuB;QAAvB,qBAAuB;YAAvB,uBAAuB;AAC3B;;AAEA;IACI,8BAAkC;AACtC;;AAEA;IACI,6BAAiC;AACrC;;AAEA;IACI;QACI,gCAAwC;IAC5C;;IAEA;QACI,4BAAsB;QAAtB,6BAAsB;QAAtB,8BAAsB;YAAtB,0BAAsB;gBAAtB,sBAAsB;QACtB,uBAA2B;QAA3B,mCAA2B;YAA3B,oBAA2B;gBAA3B,2BAA2B;IAC/B;;IAEA;QACI,oBAAwB;QACxB,mBAAuB;IAC3B;AACJ;;AAEA;IACI,uBAAuB;IACvB,YAAY;IACZ,aAAa;IACb,WAAW;IACX,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,kBAAkB;IAClB,YAAY;IACZ,oCAAoC;IACpC,4BAA4B;AAChC;;AAEA;IACI,WAAW,EAAE,uCAAuC;AACxD\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import \\\"../../css/colors.css\\\";\\n@import \\\"../../css/units.css\\\";\\n\\n.editor-container {\\n width: 100%;\\n height: 100%;\\n display: flex;\\n flex-direction: column;\\n padding: calc(3 * $grid-unit);\\n}\\n\\n.row {\\n display: flex;\\n flex-direction: row;\\n align-items: center;\\n}\\n\\n.editor-container-top {\\n border-bottom: 1px dashed $ui-pane-border;\\n padding-bottom: calc(2 * $grid-unit);\\n}\\n\\n.top-align-row {\\n display: flex;\\n flex-direction: row;\\n height: 100%;\\n padding-top: calc(5 * $grid-unit);\\n min-width: 524px;\\n}\\n\\n.row + .row {\\n margin-top: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"ltr\\\"] .mod-dashed-border {\\n border-right: 1px dashed $ui-pane-border;\\n padding-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .mod-dashed-border {\\n border-left: 1px dashed $ui-pane-border;\\n padding-left: calc(2 * $grid-unit);\\n}\\n.mod-labeled-icon-height {\\n height: 2.85rem; /* for the second row so the dashed borders are equal in size */\\n}\\n\\n$border-radius: 0.25rem;\\n\\n.button-group-button {\\n display: inline-block;\\n border: 1px solid $ui-pane-border;\\n border-radius: 0;\\n padding: .35rem;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button {\\n border-left: none;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button {\\n border-right: none;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button:last-of-type {\\n border-top-right-radius: $border-radius;\\n border-bottom-right-radius: $border-radius;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button:first-of-type {\\n border-left: 1px solid $ui-pane-border;\\n border-top-left-radius: $border-radius;\\n border-bottom-left-radius: $border-radius;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button:last-of-type {\\n border-top-left-radius: $border-radius;\\n border-bottom-left-radius: $border-radius;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button:first-of-type {\\n border-right: 1px solid $ui-pane-border;\\n border-top-right-radius: $border-radius;\\n border-bottom-right-radius: $border-radius;\\n}\\n\\n[dir=\\\"ltr\\\"] .button-group-button.mod-start-border {\\n border-left: 1px solid $ui-pane-border;\\n}\\n\\n[dir=\\\"rtl\\\"] .button-group-button.mod-start-border {\\n border-right: 1px solid $ui-pane-border;\\n}\\n\\n[dir=\\\"ltr\\\"].button-group-button.mod-no-end-border {\\n border-right: none;\\n}\\n\\n[dir=\\\"rtl\\\"].button-group-button.mod-no-end-border {\\n border-left: none;\\n}\\n\\n.button-group-button-icon {\\n width: 1.25rem;\\n height: 1.25rem;\\n vertical-align: middle;\\n}\\n\\n.mod-mode-tools {\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"ltr\\\"] .mod-margin-after {\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .mod-margin-after {\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n.controls-container {\\n width: 100%;\\n display: flex;\\n flex-flow: column;\\n flex-grow: 1;\\n margin-left: calc(2 * $grid-unit);\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n.canvas-container {\\n width: 100%;\\n flex-grow: 1;\\n min-width: 402px; /* Leave room for the border */\\n box-sizing: content-box;\\n border: 1px solid #e8edf1;\\n border-radius: .25rem;\\n position: relative;\\n overflow: visible;\\n}\\n\\n.mode-selector {\\n display: flex;\\n max-width: 7.5rem;\\n flex-direction: row;\\n flex-wrap: wrap;\\n align-items: flex-start;\\n align-content: flex-start;\\n justify-content: space-between;\\n}\\n\\n.zoom-controls {\\n display: flex;\\n flex-direction: row-reverse;\\n}\\n\\n.color-picker-wrapper {\\n position: absolute;\\n top: 0;\\n left: 0;\\n width: 100%;\\n height: 100%;\\n pointer-events: none;\\n}\\n\\n.canvas-controls {\\n display: flex;\\n height: 36px;\\n margin-top: $grid-unit;\\n justify-content: space-between;\\n}\\n\\n.bitmap-button {\\n display: flex;\\n border-radius: 5px;\\n background-color: $looks-secondary;\\n padding: calc(2 * $grid-unit);\\n line-height: 1.5rem;\\n font-size: calc(3 * $grid-unit);\\n font-weight: bold;\\n color: white;\\n justify-content: center;\\n}\\n\\n[dir=\\\"ltr\\\"] .bitmap-button-icon {\\n margin-right: calc(2 * $grid-unit);\\n}\\n\\n[dir=\\\"rtl\\\"] .bitmap-button-icon {\\n margin-left: calc(2 * $grid-unit);\\n}\\n\\n@media only screen and (max-width: $full-size-paint) {\\n .editor-container {\\n padding: calc(3 * $grid-unit) $grid-unit;\\n }\\n\\n .mode-selector {\\n flex-direction: column;\\n justify-content: flex-start;\\n }\\n\\n .controls-container {\\n margin-right: $grid-unit;\\n margin-left: $grid-unit;\\n }\\n}\\n\\n.text-area {\\n background: transparent;\\n border: none;\\n display: none;\\n margin: 0px;\\n opacity: .8;\\n outline: none;\\n overflow: hidden;\\n padding: 0px;\\n position: absolute;\\n resize: none;\\n -webkit-text-fill-color: transparent;\\n text-fill-color: transparent;\\n}\\n\\n.button-text {\\n width: 100%; /* Fixes button text wrapping in Edge */\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"editor-container\": \"paint-editor_editor-container_XJI6D\",\n\t\"editorContainer\": \"paint-editor_editor-container_XJI6D\",\n\t\"row\": \"paint-editor_row_qNJro\",\n\t\"editor-container-top\": \"paint-editor_editor-container-top_MzEH8\",\n\t\"editorContainerTop\": \"paint-editor_editor-container-top_MzEH8\",\n\t\"top-align-row\": \"paint-editor_top-align-row_CG9zZ\",\n\t\"topAlignRow\": \"paint-editor_top-align-row_CG9zZ\",\n\t\"mod-dashed-border\": \"paint-editor_mod-dashed-border_d3Y-E\",\n\t\"modDashedBorder\": \"paint-editor_mod-dashed-border_d3Y-E\",\n\t\"mod-labeled-icon-height\": \"paint-editor_mod-labeled-icon-height_4VotQ\",\n\t\"modLabeledIconHeight\": \"paint-editor_mod-labeled-icon-height_4VotQ\",\n\t\"button-group-button\": \"paint-editor_button-group-button_71r+V\",\n\t\"buttonGroupButton\": \"paint-editor_button-group-button_71r+V\",\n\t\"mod-start-border\": \"paint-editor_mod-start-border_brl5l\",\n\t\"modStartBorder\": \"paint-editor_mod-start-border_brl5l\",\n\t\"mod-no-end-border\": \"paint-editor_mod-no-end-border_+9EBd\",\n\t\"modNoEndBorder\": \"paint-editor_mod-no-end-border_+9EBd\",\n\t\"button-group-button-icon\": \"paint-editor_button-group-button-icon_GOExF\",\n\t\"buttonGroupButtonIcon\": \"paint-editor_button-group-button-icon_GOExF\",\n\t\"mod-mode-tools\": \"paint-editor_mod-mode-tools_kG-2T\",\n\t\"modModeTools\": \"paint-editor_mod-mode-tools_kG-2T\",\n\t\"mod-margin-after\": \"paint-editor_mod-margin-after_lArV7\",\n\t\"modMarginAfter\": \"paint-editor_mod-margin-after_lArV7\",\n\t\"controls-container\": \"paint-editor_controls-container_WlBVy\",\n\t\"controlsContainer\": \"paint-editor_controls-container_WlBVy\",\n\t\"canvas-container\": \"paint-editor_canvas-container_Qe1w8\",\n\t\"canvasContainer\": \"paint-editor_canvas-container_Qe1w8\",\n\t\"mode-selector\": \"paint-editor_mode-selector_ALomU\",\n\t\"modeSelector\": \"paint-editor_mode-selector_ALomU\",\n\t\"zoom-controls\": \"paint-editor_zoom-controls_QQz-j\",\n\t\"zoomControls\": \"paint-editor_zoom-controls_QQz-j\",\n\t\"color-picker-wrapper\": \"paint-editor_color-picker-wrapper_KtBg6\",\n\t\"colorPickerWrapper\": \"paint-editor_color-picker-wrapper_KtBg6\",\n\t\"canvas-controls\": \"paint-editor_canvas-controls_yjCGl\",\n\t\"canvasControls\": \"paint-editor_canvas-controls_yjCGl\",\n\t\"bitmap-button\": \"paint-editor_bitmap-button_2te68\",\n\t\"bitmapButton\": \"paint-editor_bitmap-button_2te68\",\n\t\"bitmap-button-icon\": \"paint-editor_bitmap-button-icon_uLSWl\",\n\t\"bitmapButtonIcon\": \"paint-editor_bitmap-button-icon_uLSWl\",\n\t\"text-area\": \"paint-editor_text-area_U17Si\",\n\t\"textArea\": \"paint-editor_text-area_U17Si\",\n\t\"button-text\": \"paint-editor_button-text_D-z+w\",\n\t\"buttonText\": \"paint-editor_button-text_D-z+w\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".scrollable-canvas_vertical-scrollbar_JbG46, .scrollable-canvas_horizontal-scrollbar_5fYZS {\\n background: rgba(190, 190, 190, 0.8);\\n border-radius: calc(8px / 2);\\n width: 100%;\\n height: 100%;\\n}\\n.scrollable-canvas_vertical-scrollbar-wrapper_xwleo {\\n position: absolute;\\n width: calc(8px + 4px);\\n right: 0;\\n top: 4px;\\n height: calc(100% - 8px - 2 * 4px);\\n}\\n.scrollable-canvas_horizontal-scrollbar-wrapper_Hp\\\\+nZ {\\n position: absolute;\\n height: calc(8px + 4px);\\n left: 4px;\\n bottom: 0;\\n width: calc(100% - 8px - 2 * 4px);\\n}\\n.scrollable-canvas_vertical-scrollbar-hitbox_GMRRn, .scrollable-canvas_horizontal-scrollbar-hitbox_y2af2 {\\n position: absolute;\\n cursor: pointer;\\n -webkit-box-sizing: border-box;\\n box-sizing: border-box;\\n}\\n.scrollable-canvas_vertical-scrollbar-hitbox_GMRRn {\\n width: calc(8px + 4px);\\n padding-right: 4px;\\n}\\n.scrollable-canvas_horizontal-scrollbar-hitbox_y2af2 {\\n height: calc(8px + 4px);\\n padding-bottom: 4px;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/components/scrollable-canvas/scrollable-canvas.css\"],\"names\":[],\"mappings\":\"AAGA;IACI,oCAAoC;IACpC,4BAAwC;IACxC,WAAW;IACX,YAAY;AAChB;AACA;IACI,kBAAkB;IAClB,sBAAiD;IACjD,QAAQ;IACR,QAAuB;IACvB,kCAA6D;AACjE;AAEA;IACI,kBAAkB;IAClB,uBAAkD;IAClD,SAAwB;IACxB,SAAS;IACT,iCAA4D;AAChE;AAEA;IACI,kBAAkB;IAClB,eAAe;IACf,8BAAsB;YAAtB,sBAAsB;AAC1B;AAEA;IACI,sBAAiD;IACjD,kBAAiC;AACrC;AAEA;IACI,uBAAkD;IAClD,mBAAkC;AACtC\",\"sourcesContent\":[\"$scrollbar-size: 8px;\\n$scrollbar-padding: 4px;\\n\\n.vertical-scrollbar, .horizontal-scrollbar {\\n background: rgba(190, 190, 190, 0.8);\\n border-radius: calc($scrollbar-size / 2);\\n width: 100%;\\n height: 100%;\\n}\\n.vertical-scrollbar-wrapper {\\n position: absolute;\\n width: calc($scrollbar-size + $scrollbar-padding);\\n right: 0;\\n top: $scrollbar-padding;\\n height: calc(100% - $scrollbar-size - 2 * $scrollbar-padding);\\n}\\n\\n.horizontal-scrollbar-wrapper {\\n position: absolute;\\n height: calc($scrollbar-size + $scrollbar-padding);\\n left: $scrollbar-padding;\\n bottom: 0;\\n width: calc(100% - $scrollbar-size - 2 * $scrollbar-padding);\\n}\\n\\n.vertical-scrollbar-hitbox, .horizontal-scrollbar-hitbox {\\n position: absolute;\\n cursor: pointer;\\n box-sizing: border-box;\\n}\\n\\n.vertical-scrollbar-hitbox {\\n width: calc($scrollbar-size + $scrollbar-padding);\\n padding-right: $scrollbar-padding;\\n}\\n\\n.horizontal-scrollbar-hitbox {\\n height: calc($scrollbar-size + $scrollbar-padding);\\n padding-bottom: $scrollbar-padding;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"vertical-scrollbar\": \"scrollable-canvas_vertical-scrollbar_JbG46\",\n\t\"verticalScrollbar\": \"scrollable-canvas_vertical-scrollbar_JbG46\",\n\t\"horizontal-scrollbar\": \"scrollable-canvas_horizontal-scrollbar_5fYZS\",\n\t\"horizontalScrollbar\": \"scrollable-canvas_horizontal-scrollbar_5fYZS\",\n\t\"vertical-scrollbar-wrapper\": \"scrollable-canvas_vertical-scrollbar-wrapper_xwleo\",\n\t\"verticalScrollbarWrapper\": \"scrollable-canvas_vertical-scrollbar-wrapper_xwleo\",\n\t\"horizontal-scrollbar-wrapper\": \"scrollable-canvas_horizontal-scrollbar-wrapper_Hp+nZ\",\n\t\"horizontalScrollbarWrapper\": \"scrollable-canvas_horizontal-scrollbar-wrapper_Hp+nZ\",\n\t\"vertical-scrollbar-hitbox\": \"scrollable-canvas_vertical-scrollbar-hitbox_GMRRn\",\n\t\"verticalScrollbarHitbox\": \"scrollable-canvas_vertical-scrollbar-hitbox_GMRRn\",\n\t\"horizontal-scrollbar-hitbox\": \"scrollable-canvas_horizontal-scrollbar-hitbox_y2af2\",\n\t\"horizontalScrollbarHitbox\": \"scrollable-canvas_horizontal-scrollbar-hitbox_y2af2\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* 35% transparent version of looks-secondary */\\n\\n/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n\\n.tool-select-base_mod-tool-select_2bgwK {\\n display: inline-block;\\n margin: .25rem;\\n border: none;\\n border-radius: .25rem;\\n outline: none;\\n background: none;\\n padding: .25rem;\\n font-size: 0.85rem;\\n -webkit-transition: 0.2s;\\n transition: 0.2s;\\n}\\n\\n.tool-select-base_mod-tool-select_2bgwK.tool-select-base_is-selected_1CGDO {\\n background-color: #855CD6;\\n}\\n\\n.tool-select-base_mod-tool-select_2bgwK:focus {\\n outline: none;\\n}\\n\\nimg.tool-select-base_tool-select-icon_-gOoV {\\n width: 2rem;\\n height: 2rem;\\n -webkit-box-flex: 1;\\n -webkit-flex-grow: 1;\\n -ms-flex-positive: 1;\\n flex-grow: 1;\\n vertical-align: middle;\\n}\\n\\n.tool-select-base_mod-tool-select_2bgwK.tool-select-base_is-selected_1CGDO .tool-select-base_tool-select-icon_-gOoV {\\n /* Make the tool icons white while selected by making them black and inverting */\\n -webkit-filter: brightness(0) invert(1);\\n filter: brightness(0) invert(1);\\n}\\n\\n@media only screen and (max-width: 1256px) {\\n .tool-select-base_mod-tool-select_2bgwK {\\n margin: 0;\\n }\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/css/colors.css\",\"webpack://./src/css/units.css\",\"webpack://./src/components/tool-select-base/tool-select-base.css\"],\"names\":[],\"mappings\":\"AAAA;;oDAEoD;;AASL,+CAA+C;;ACX9F;;oDAEoD;;AAEpD;;;;kFAIkF;;AAelF,0DAA0D;;AClB1D;IACI,qBAAqB;IACrB,cAAkB;IAClB,YAAY;IACZ,qBAA6B;IAC7B,aAAa;IACb,gBAAgB;IAChB,eAAmB;IACnB,kBAAkB;IAClB,wBAAgB;IAAhB,gBAAgB;AACpB;;AAEA;IACI,yBAAkC;AACtC;;AAEA;IACI,aAAa;AACjB;;AAEA;IACI,WAAW;IACX,YAAY;IACZ,mBAAY;IAAZ,oBAAY;QAAZ,oBAAY;YAAZ,YAAY;IACZ,sBAAsB;AAC1B;;AAEA;IACI,gFAAgF;IAChF,uCAA+B;YAA/B,+BAA+B;AACnC;;AAEA;IACI;QACI,SAAS;IACb;AACJ\",\"sourcesContent\":[\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n$ui-pane-border: #D9D9D9;\\n$ui-pane-gray: #F9F9F9;\\n$ui-background-blue: #e8edf1;\\n\\n$text-primary: #575e75;\\n\\n$looks-secondary: #855CD6;\\n$looks-transparent: hsla(260, 60%, 60%, 0.35); /* 35% transparent version of looks-secondary */\\n\\n$red-primary: #FF661A;\\n$red-tertiary: #E64D00;\\n\\n$sound-primary: #CF63CF;\\n$sound-tertiary: #A63FA6;\\n\\n$control-primary: #FFAB19;\\n\\n$data-primary: #FF8C1A;\\n\\n$form-border: #E9EEF2;\\n\",\"/* DO NOT EDIT\\n@todo This file is copied from GUI and should be pulled out into a shared library.\\nSee https://github.com/LLK/scratch-paint/issues/13 */\\n\\n/* ACTUALLY, THIS IS EDITED ;)\\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD A VARIABLE FOR THE SMALLEST\\nGRID UNITS.\\n\\nALSO EDITED ON 11/13/2017 TO ADD IN CONTANTS FOR LAYOUT FROM `layout-contents.js`*/\\n\\n$space: 0.5rem;\\n$grid-unit: .25rem;\\n\\n$sprites-per-row: 5;\\n\\n$menu-bar-height: 3rem;\\n$sprite-info-height: 6rem;\\n$stage-menu-height: 2.75rem;\\n\\n$library-header-height: 4.375rem;\\n\\n$form-radius: calc($space / 2);\\n\\n/* layout contants from `layout-constants.js`, minus 1px */\\n$full-size: 1095px;\\n$full-size-paint: 1256px;\\n\",\"@import '../../css/colors.css';\\n@import \\\"../../css/units.css\\\";\\n\\n$border-radius: .25rem;\\n\\n.mod-tool-select {\\n display: inline-block;\\n margin: $grid-unit;\\n border: none;\\n border-radius: $border-radius;\\n outline: none;\\n background: none;\\n padding: $grid-unit;\\n font-size: 0.85rem;\\n transition: 0.2s;\\n}\\n\\n.mod-tool-select.is-selected {\\n background-color: $looks-secondary;\\n}\\n\\n.mod-tool-select:focus {\\n outline: none;\\n}\\n\\nimg.tool-select-icon {\\n width: 2rem;\\n height: 2rem;\\n flex-grow: 1;\\n vertical-align: middle;\\n}\\n\\n.mod-tool-select.is-selected .tool-select-icon {\\n /* Make the tool icons white while selected by making them black and inverting */\\n filter: brightness(0) invert(1);\\n}\\n\\n@media only screen and (max-width: $full-size-paint) {\\n .mod-tool-select {\\n margin: 0;\\n }\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"mod-tool-select\": \"tool-select-base_mod-tool-select_2bgwK\",\n\t\"modToolSelect\": \"tool-select-base_mod-tool-select_2bgwK\",\n\t\"is-selected\": \"tool-select-base_is-selected_1CGDO\",\n\t\"isSelected\": \"tool-select-base_is-selected_1CGDO\",\n\t\"tool-select-icon\": \"tool-select-base_tool-select-icon_-gOoV\",\n\t\"toolSelectIcon\": \"tool-select-base_tool-select-icon_-gOoV\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","// Imports\nvar ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = require(\"../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js\");\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../node_modules/css-loader/dist/runtime/api.js\");\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".paper-canvas_paper-canvas_J0bm2 {\\n top: 1px; /* leave room for the border */\\n left: 1px;\\n width: calc(100% - 2px);\\n height: calc(100% - 2px);\\n margin: auto;\\n position: absolute;\\n background-color: #D9E3F2;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://./src/containers/paper-canvas.css\"],\"names\":[],\"mappings\":\"AAAA;IACI,QAAQ,EAAE,8BAA8B;IACxC,SAAS;IACT,uBAAuB;IACvB,wBAAwB;IACxB,YAAY;IACZ,kBAAkB;IAClB,yBAAyB;AAC7B\",\"sourcesContent\":[\".paper-canvas {\\n top: 1px; /* leave room for the border */\\n left: 1px;\\n width: calc(100% - 2px);\\n height: calc(100% - 2px);\\n margin: auto;\\n position: absolute;\\n background-color: #D9E3F2;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"paper-canvas\": \"paper-canvas_paper-canvas_J0bm2\",\n\t\"paperCanvas\": \"paper-canvas_paper-canvas_J0bm2\"\n};\nmodule.exports = ___CSS_LOADER_EXPORT___;\n","\"use strict\";\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\n// eslint-disable-next-line func-names\nmodule.exports = function (cssWithMappingToString) {\n var list = []; // return the list of modules as css string\n\n list.toString = function toString() {\n return this.map(function (item) {\n var content = cssWithMappingToString(item);\n\n if (item[2]) {\n return \"@media \".concat(item[2], \" {\").concat(content, \"}\");\n }\n\n return content;\n }).join(\"\");\n }; // import a list of modules into the list\n // eslint-disable-next-line func-names\n\n\n list.i = function (modules, mediaQuery, dedupe) {\n if (typeof modules === \"string\") {\n // eslint-disable-next-line no-param-reassign\n modules = [[null, modules, \"\"]];\n }\n\n var alreadyImportedModules = {};\n\n if (dedupe) {\n for (var i = 0; i < this.length; i++) {\n // eslint-disable-next-line prefer-destructuring\n var id = this[i][0];\n\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n }\n\n for (var _i = 0; _i < modules.length; _i++) {\n var item = [].concat(modules[_i]);\n\n if (dedupe && alreadyImportedModules[item[0]]) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n if (mediaQuery) {\n if (!item[2]) {\n item[2] = mediaQuery;\n } else {\n item[2] = \"\".concat(mediaQuery, \" and \").concat(item[2]);\n }\n }\n\n list.push(item);\n }\n };\n\n return list;\n};","\"use strict\";\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\"); }\n\nfunction _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === \"string\") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === \"Object\" && o.constructor) n = o.constructor.name; if (n === \"Map\" || n === \"Set\") return Array.from(o); if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }\n\nfunction _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }\n\nfunction _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== \"undefined\" && arr[Symbol.iterator] || arr[\"@@iterator\"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nmodule.exports = function cssWithMappingToString(item) {\n var _item = _slicedToArray(item, 4),\n content = _item[1],\n cssMapping = _item[3];\n\n if (!cssMapping) {\n return content;\n }\n\n if (typeof btoa === \"function\") {\n // eslint-disable-next-line no-undef\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));\n var data = \"sourceMappingURL=data:application/json;charset=utf-8;base64,\".concat(base64);\n var sourceMapping = \"/*# \".concat(data, \" */\");\n var sourceURLs = cssMapping.sources.map(function (source) {\n return \"/*# sourceURL=\".concat(cssMapping.sourceRoot || \"\").concat(source, \" */\");\n });\n return [content].concat(sourceURLs).concat([sourceMapping]).join(\"\\n\");\n }\n\n return [content].join(\"\\n\");\n};","/**\n * Copyright 2013-2014 Facebook, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\"use strict\";\n\n/**\n * Constructs an enumeration with keys equal to their value.\n *\n * For example:\n *\n * var COLORS = keyMirror({blue: null, red: null});\n * var myColor = COLORS.blue;\n * var isColorValid = !!COLORS[myColor];\n *\n * The last line could not be performed if the values of the generated enum were\n * not equal to their keys.\n *\n * Input: {key1: val1, key2: val2}\n * Output: {key1: key1, key2: key2}\n *\n * @param {object} obj\n * @return {object}\n */\nvar keyMirror = function(obj) {\n var ret = {};\n var key;\n if (!(obj instanceof Object && !Array.isArray(obj))) {\n throw new Error('keyMirror(...): Argument must be an object.');\n }\n for (key in obj) {\n if (!obj.hasOwnProperty(key)) {\n continue;\n }\n ret[key] = key;\n }\n return ret;\n};\n\nmodule.exports = keyMirror;\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as the internal argument placeholder. */\nvar PLACEHOLDER = '__lodash_placeholder__';\n\n/** Used to compose bitmasks for function metadata. */\nvar BIND_FLAG = 1,\n BIND_KEY_FLAG = 2,\n CURRY_BOUND_FLAG = 4,\n CURRY_FLAG = 8,\n CURRY_RIGHT_FLAG = 16,\n PARTIAL_FLAG = 32,\n PARTIAL_RIGHT_FLAG = 64,\n ARY_FLAG = 128,\n REARG_FLAG = 256,\n FLIP_FLAG = 512;\n\n/** Used as references for various `Number` constants. */\nvar INFINITY = 1 / 0,\n MAX_SAFE_INTEGER = 9007199254740991,\n MAX_INTEGER = 1.7976931348623157e+308,\n NAN = 0 / 0;\n\n/** Used to associate wrap methods with their bit flags. */\nvar wrapFlags = [\n ['ary', ARY_FLAG],\n ['bind', BIND_FLAG],\n ['bindKey', BIND_KEY_FLAG],\n ['curry', CURRY_FLAG],\n ['curryRight', CURRY_RIGHT_FLAG],\n ['flip', FLIP_FLAG],\n ['partial', PARTIAL_FLAG],\n ['partialRight', PARTIAL_RIGHT_FLAG],\n ['rearg', REARG_FLAG]\n];\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n symbolTag = '[object Symbol]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to match wrap detail comments. */\nvar reWrapComment = /\\{(?:\\n\\/\\* \\[wrapped with .+\\] \\*\\/)?\\n?/,\n reWrapDetails = /\\{\\n\\/\\* \\[wrapped with (.+)\\] \\*/,\n reSplitDetails = /,? & /;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\nfunction apply(func, thisArg, args) {\n switch (args.length) {\n case 0: return func.call(thisArg);\n case 1: return func.call(thisArg, args[0]);\n case 2: return func.call(thisArg, args[0], args[1]);\n case 3: return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n}\n\n/**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\nfunction arrayEach(array, iteratee) {\n var index = -1,\n length = array ? array.length : 0;\n\n while (++index < length) {\n if (iteratee(array[index], index, array) === false) {\n break;\n }\n }\n return array;\n}\n\n/**\n * A specialized version of `_.includes` for arrays without support for\n * specifying an index to search from.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\nfunction arrayIncludes(array, value) {\n var length = array ? array.length : 0;\n return !!length && baseIndexOf(array, value, 0) > -1;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n}\n\n/**\n * The base implementation of `_.findIndex` and `_.findLastIndex` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {number} fromIndex The index to search from.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction baseFindIndex(array, predicate, fromIndex, fromRight) {\n var length = array.length,\n index = fromIndex + (fromRight ? 1 : -1);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (predicate(array[index], index, array)) {\n return index;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of `_.indexOf` without `fromIndex` bounds checks.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction baseIndexOf(array, value, fromIndex) {\n if (value !== value) {\n return baseFindIndex(array, baseIsNaN, fromIndex);\n }\n var index = fromIndex - 1,\n length = array.length;\n\n while (++index < length) {\n if (array[index] === value) {\n return index;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of `_.isNaN` without support for number objects.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n */\nfunction baseIsNaN(value) {\n return value !== value;\n}\n\n/**\n * Gets the number of `placeholder` occurrences in `array`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} placeholder The placeholder to search for.\n * @returns {number} Returns the placeholder count.\n */\nfunction countHolders(array, placeholder) {\n var length = array.length,\n result = 0;\n\n while (length--) {\n if (array[length] === placeholder) {\n result++;\n }\n }\n return result;\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n return object == null ? undefined : object[key];\n}\n\n/**\n * Checks if `value` is a host object in IE < 9.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n */\nfunction isHostObject(value) {\n // Many host objects are `Object` objects that can coerce to strings\n // despite having improperly defined `toString` methods.\n var result = false;\n if (value != null && typeof value.toString != 'function') {\n try {\n result = !!(value + '');\n } catch (e) {}\n }\n return result;\n}\n\n/**\n * Replaces all `placeholder` elements in `array` with an internal placeholder\n * and returns an array of their indexes.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {*} placeholder The placeholder to replace.\n * @returns {Array} Returns the new array of placeholder indexes.\n */\nfunction replaceHolders(array, placeholder) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value === placeholder || value === PLACEHOLDER) {\n array[index] = PLACEHOLDER;\n result[resIndex++] = index;\n }\n }\n return result;\n}\n\n/** Used for built-in method references. */\nvar funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Symbol = root.Symbol,\n objectCreate = Object.create,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/* Used to set `toString` methods. */\nvar defineProperty = (function() {\n var func = getNative(Object, 'defineProperty'),\n name = getNative.name;\n\n return (name && name.length > 2) ? func : undefined;\n}());\n\n/**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} prototype The object to inherit from.\n * @returns {Object} Returns the new object.\n */\nfunction baseCreate(proto) {\n return isObject(proto) ? objectCreate(proto) : {};\n}\n\n/**\n * The base implementation of `_.flatten` with support for restricting flattening.\n *\n * @private\n * @param {Array} array The array to flatten.\n * @param {number} depth The maximum recursion depth.\n * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n * @param {Array} [result=[]] The initial result value.\n * @returns {Array} Returns the new flattened array.\n */\nfunction baseFlatten(array, depth, predicate, isStrict, result) {\n var index = -1,\n length = array.length;\n\n predicate || (predicate = isFlattenable);\n result || (result = []);\n\n while (++index < length) {\n var value = array[index];\n if (depth > 0 && predicate(value)) {\n if (depth > 1) {\n // Recursively flatten arrays (susceptible to call stack limits).\n baseFlatten(value, depth - 1, predicate, isStrict, result);\n } else {\n arrayPush(result, value);\n }\n } else if (!isStrict) {\n result[result.length] = value;\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\nfunction baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\nfunction baseRest(func, start) {\n start = nativeMax(start === undefined ? (func.length - 1) : start, 0);\n return function() {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = array;\n return apply(func, this, otherArgs);\n };\n}\n\n/**\n * Creates an array that is the composition of partially applied arguments,\n * placeholders, and provided arguments into a single array of arguments.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to prepend to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\nfunction composeArgs(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersLength = holders.length,\n leftIndex = -1,\n leftLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(leftLength + rangeLength),\n isUncurried = !isCurried;\n\n while (++leftIndex < leftLength) {\n result[leftIndex] = partials[leftIndex];\n }\n while (++argsIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[holders[argsIndex]] = args[argsIndex];\n }\n }\n while (rangeLength--) {\n result[leftIndex++] = args[argsIndex++];\n }\n return result;\n}\n\n/**\n * This function is like `composeArgs` except that the arguments composition\n * is tailored for `_.partialRight`.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to append to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\nfunction composeArgsRight(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersIndex = -1,\n holdersLength = holders.length,\n rightIndex = -1,\n rightLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(rangeLength + rightLength),\n isUncurried = !isCurried;\n\n while (++argsIndex < rangeLength) {\n result[argsIndex] = args[argsIndex];\n }\n var offset = argsIndex;\n while (++rightIndex < rightLength) {\n result[offset + rightIndex] = partials[rightIndex];\n }\n while (++holdersIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[offset + holders[holdersIndex]] = args[argsIndex++];\n }\n }\n return result;\n}\n\n/**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\nfunction copyArray(source, array) {\n var index = -1,\n length = source.length;\n\n array || (array = Array(length));\n while (++index < length) {\n array[index] = source[index];\n }\n return array;\n}\n\n/**\n * Creates a function that wraps `func` to invoke it with the optional `this`\n * binding of `thisArg`.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createBind(func, bitmask, thisArg) {\n var isBind = bitmask & BIND_FLAG,\n Ctor = createCtor(func);\n\n function wrapper() {\n var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n return fn.apply(isBind ? thisArg : this, arguments);\n }\n return wrapper;\n}\n\n/**\n * Creates a function that produces an instance of `Ctor` regardless of\n * whether it was invoked as part of a `new` expression or by `call` or `apply`.\n *\n * @private\n * @param {Function} Ctor The constructor to wrap.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createCtor(Ctor) {\n return function() {\n // Use a `switch` statement to work with class constructors. See\n // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n // for more details.\n var args = arguments;\n switch (args.length) {\n case 0: return new Ctor;\n case 1: return new Ctor(args[0]);\n case 2: return new Ctor(args[0], args[1]);\n case 3: return new Ctor(args[0], args[1], args[2]);\n case 4: return new Ctor(args[0], args[1], args[2], args[3]);\n case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);\n case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);\n case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);\n }\n var thisBinding = baseCreate(Ctor.prototype),\n result = Ctor.apply(thisBinding, args);\n\n // Mimic the constructor's `return` behavior.\n // See https://es5.github.io/#x13.2.2 for more details.\n return isObject(result) ? result : thisBinding;\n };\n}\n\n/**\n * Creates a function that wraps `func` to enable currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {number} arity The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createCurry(func, bitmask, arity) {\n var Ctor = createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length,\n placeholder = getHolder(wrapper);\n\n while (index--) {\n args[index] = arguments[index];\n }\n var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)\n ? []\n : replaceHolders(args, placeholder);\n\n length -= holders.length;\n if (length < arity) {\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, undefined,\n args, holders, undefined, undefined, arity - length);\n }\n var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n return apply(fn, this, args);\n }\n return wrapper;\n}\n\n/**\n * Creates a function that wraps `func` to invoke it with optional `this`\n * binding of `thisArg`, partial application, and currying.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [partialsRight] The arguments to append to those provided\n * to the new function.\n * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {\n var isAry = bitmask & ARY_FLAG,\n isBind = bitmask & BIND_FLAG,\n isBindKey = bitmask & BIND_KEY_FLAG,\n isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),\n isFlip = bitmask & FLIP_FLAG,\n Ctor = isBindKey ? undefined : createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length;\n\n while (index--) {\n args[index] = arguments[index];\n }\n if (isCurried) {\n var placeholder = getHolder(wrapper),\n holdersCount = countHolders(args, placeholder);\n }\n if (partials) {\n args = composeArgs(args, partials, holders, isCurried);\n }\n if (partialsRight) {\n args = composeArgsRight(args, partialsRight, holdersRight, isCurried);\n }\n length -= holdersCount;\n if (isCurried && length < arity) {\n var newHolders = replaceHolders(args, placeholder);\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, thisArg,\n args, newHolders, argPos, ary, arity - length\n );\n }\n var thisBinding = isBind ? thisArg : this,\n fn = isBindKey ? thisBinding[func] : func;\n\n length = args.length;\n if (argPos) {\n args = reorder(args, argPos);\n } else if (isFlip && length > 1) {\n args.reverse();\n }\n if (isAry && ary < length) {\n args.length = ary;\n }\n if (this && this !== root && this instanceof wrapper) {\n fn = Ctor || createCtor(fn);\n }\n return fn.apply(thisBinding, args);\n }\n return wrapper;\n}\n\n/**\n * Creates a function that wraps `func` to invoke it with the `this` binding\n * of `thisArg` and `partials` prepended to the arguments it receives.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} partials The arguments to prepend to those provided to\n * the new function.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createPartial(func, bitmask, thisArg, partials) {\n var isBind = bitmask & BIND_FLAG,\n Ctor = createCtor(func);\n\n function wrapper() {\n var argsIndex = -1,\n argsLength = arguments.length,\n leftIndex = -1,\n leftLength = partials.length,\n args = Array(leftLength + argsLength),\n fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n\n while (++leftIndex < leftLength) {\n args[leftIndex] = partials[leftIndex];\n }\n while (argsLength--) {\n args[leftIndex++] = arguments[++argsIndex];\n }\n return apply(fn, isBind ? thisArg : this, args);\n }\n return wrapper;\n}\n\n/**\n * Creates a function that wraps `func` to continue currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {Function} wrapFunc The function to create the `func` wrapper.\n * @param {*} placeholder The placeholder value.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {\n var isCurry = bitmask & CURRY_FLAG,\n newHolders = isCurry ? holders : undefined,\n newHoldersRight = isCurry ? undefined : holders,\n newPartials = isCurry ? partials : undefined,\n newPartialsRight = isCurry ? undefined : partials;\n\n bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);\n bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);\n\n if (!(bitmask & CURRY_BOUND_FLAG)) {\n bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);\n }\n\n var result = wrapFunc(func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity);\n result.placeholder = placeholder;\n return setWrapToString(result, func, bitmask);\n}\n\n/**\n * Creates a function that either curries or invokes `func` with optional\n * `this` binding and partially applied arguments.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags.\n * The bitmask may be composed of the following flags:\n * 1 - `_.bind`\n * 2 - `_.bindKey`\n * 4 - `_.curry` or `_.curryRight` of a bound function\n * 8 - `_.curry`\n * 16 - `_.curryRight`\n * 32 - `_.partial`\n * 64 - `_.partialRight`\n * 128 - `_.rearg`\n * 256 - `_.ary`\n * 512 - `_.flip`\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to be partially applied.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {\n var isBindKey = bitmask & BIND_KEY_FLAG;\n if (!isBindKey && typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var length = partials ? partials.length : 0;\n if (!length) {\n bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);\n partials = holders = undefined;\n }\n ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);\n arity = arity === undefined ? arity : toInteger(arity);\n length -= holders ? holders.length : 0;\n\n if (bitmask & PARTIAL_RIGHT_FLAG) {\n var partialsRight = partials,\n holdersRight = holders;\n\n partials = holders = undefined;\n }\n\n var newData = [\n func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,\n argPos, ary, arity\n ];\n\n func = newData[0];\n bitmask = newData[1];\n thisArg = newData[2];\n partials = newData[3];\n holders = newData[4];\n arity = newData[9] = newData[9] == null\n ? (isBindKey ? 0 : func.length)\n : nativeMax(newData[9] - length, 0);\n\n if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {\n bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);\n }\n if (!bitmask || bitmask == BIND_FLAG) {\n var result = createBind(func, bitmask, thisArg);\n } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {\n result = createCurry(func, bitmask, arity);\n } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {\n result = createPartial(func, bitmask, thisArg, partials);\n } else {\n result = createHybrid.apply(undefined, newData);\n }\n return setWrapToString(result, func, bitmask);\n}\n\n/**\n * Gets the argument placeholder value for `func`.\n *\n * @private\n * @param {Function} func The function to inspect.\n * @returns {*} Returns the placeholder value.\n */\nfunction getHolder(func) {\n var object = func;\n return object.placeholder;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * Extracts wrapper details from the `source` body comment.\n *\n * @private\n * @param {string} source The source to inspect.\n * @returns {Array} Returns the wrapper details.\n */\nfunction getWrapDetails(source) {\n var match = source.match(reWrapDetails);\n return match ? match[1].split(reSplitDetails) : [];\n}\n\n/**\n * Inserts wrapper `details` in a comment at the top of the `source` body.\n *\n * @private\n * @param {string} source The source to modify.\n * @returns {Array} details The details to insert.\n * @returns {string} Returns the modified source.\n */\nfunction insertWrapDetails(source, details) {\n var length = details.length,\n lastIndex = length - 1;\n\n details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];\n details = details.join(length > 2 ? ', ' : ' ');\n return source.replace(reWrapComment, '{\\n/* [wrapped with ' + details + '] */\\n');\n}\n\n/**\n * Checks if `value` is a flattenable `arguments` object or array.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n */\nfunction isFlattenable(value) {\n return isArray(value) || isArguments(value) ||\n !!(spreadableSymbol && value && value[spreadableSymbol]);\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length &&\n (typeof value == 'number' || reIsUint.test(value)) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Reorder `array` according to the specified indexes where the element at\n * the first index is assigned as the first element, the element at\n * the second index is assigned as the second element, and so on.\n *\n * @private\n * @param {Array} array The array to reorder.\n * @param {Array} indexes The arranged array indexes.\n * @returns {Array} Returns `array`.\n */\nfunction reorder(array, indexes) {\n var arrLength = array.length,\n length = nativeMin(indexes.length, arrLength),\n oldArray = copyArray(array);\n\n while (length--) {\n var index = indexes[length];\n array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;\n }\n return array;\n}\n\n/**\n * Sets the `toString` method of `wrapper` to mimic the source of `reference`\n * with wrapper details in a comment at the top of the source body.\n *\n * @private\n * @param {Function} wrapper The function to modify.\n * @param {Function} reference The reference function.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Function} Returns `wrapper`.\n */\nvar setWrapToString = !defineProperty ? identity : function(wrapper, reference, bitmask) {\n var source = (reference + '');\n return defineProperty(wrapper, 'toString', {\n 'configurable': true,\n 'enumerable': false,\n 'value': constant(insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)))\n });\n};\n\n/**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\nfunction toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to process.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n}\n\n/**\n * Updates wrapper `details` based on `bitmask` flags.\n *\n * @private\n * @returns {Array} details The details to modify.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Array} Returns `details`.\n */\nfunction updateWrapDetails(details, bitmask) {\n arrayEach(wrapFlags, function(pair) {\n var value = '_.' + pair[0];\n if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {\n details.push(value);\n }\n });\n return details.sort();\n}\n\n/**\n * Creates a function that invokes `func` with the `this` binding of `thisArg`\n * and `partials` prepended to the arguments it receives.\n *\n * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for partially applied arguments.\n *\n * **Note:** Unlike native `Function#bind`, this method doesn't set the \"length\"\n * property of bound functions.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to bind.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * function greet(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n *\n * var object = { 'user': 'fred' };\n *\n * var bound = _.bind(greet, object, 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bind(greet, object, _, '!');\n * bound('hi');\n * // => 'hi fred!'\n */\nvar bind = baseRest(function(func, thisArg, partials) {\n var bitmask = BIND_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bind));\n bitmask |= PARTIAL_FLAG;\n }\n return createWrap(func, bitmask, thisArg, partials, holders);\n});\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 8-9 which returns 'object' for typed array and other constructors.\n var tag = isObject(value) ? objectToString.call(value) : '';\n return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a finite number.\n *\n * @static\n * @memberOf _\n * @since 4.12.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted number.\n * @example\n *\n * _.toFinite(3.2);\n * // => 3.2\n *\n * _.toFinite(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toFinite(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toFinite('3.2');\n * // => 3.2\n */\nfunction toFinite(value) {\n if (!value) {\n return value === 0 ? value : 0;\n }\n value = toNumber(value);\n if (value === INFINITY || value === -INFINITY) {\n var sign = (value < 0 ? -1 : 1);\n return sign * MAX_INTEGER;\n }\n return value === value ? value : 0;\n}\n\n/**\n * Converts `value` to an integer.\n *\n * **Note:** This method is loosely based on\n * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toInteger(3.2);\n * // => 3\n *\n * _.toInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toInteger(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toInteger('3.2');\n * // => 3\n */\nfunction toInteger(value) {\n var result = toFinite(value),\n remainder = result % 1;\n\n return result === result ? (remainder ? result - remainder : result) : 0;\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\n/**\n * Binds methods of an object to the object itself, overwriting the existing\n * method.\n *\n * **Note:** This method doesn't set the \"length\" property of bound functions.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Util\n * @param {Object} object The object to bind and assign the bound methods to.\n * @param {...(string|string[])} methodNames The object method names to bind.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var view = {\n * 'label': 'docs',\n * 'click': function() {\n * console.log('clicked ' + this.label);\n * }\n * };\n *\n * _.bindAll(view, ['click']);\n * jQuery(element).on('click', view.click);\n * // => Logs 'clicked docs' when clicked.\n */\nvar bindAll = baseRest(function(object, methodNames) {\n arrayEach(baseFlatten(methodNames, 1), function(key) {\n key = toKey(key);\n object[key] = bind(object[key], object);\n });\n return object;\n});\n\n/**\n * Creates a function that returns `value`.\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Util\n * @param {*} value The value to return from the new function.\n * @returns {Function} Returns the new constant function.\n * @example\n *\n * var objects = _.times(2, _.constant({ 'a': 1 }));\n *\n * console.log(objects);\n * // => [{ 'a': 1 }, { 'a': 1 }]\n *\n * console.log(objects[0] === objects[1]);\n * // => true\n */\nfunction constant(value) {\n return function() {\n return value;\n };\n}\n\n/**\n * This method returns the first argument it receives.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Util\n * @param {*} value Any value.\n * @returns {*} Returns `value`.\n * @example\n *\n * var object = { 'a': 1 };\n *\n * console.log(_.identity(object) === object);\n * // => true\n */\nfunction identity(value) {\n return value;\n}\n\n// Assign default placeholders.\nbind.placeholder = {};\n\nmodule.exports = bindAll;\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used as references for various `Number` constants. */\nvar INFINITY = 1 / 0,\n MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n symbolTag = '[object Symbol]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\nfunction apply(func, thisArg, args) {\n switch (args.length) {\n case 0: return func.call(thisArg);\n case 1: return func.call(thisArg, args[0]);\n case 2: return func.call(thisArg, args[0], args[1]);\n case 3: return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n}\n\n/**\n * A specialized version of `_.includes` for arrays without support for\n * specifying an index to search from.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\nfunction arrayIncludes(array, value) {\n var length = array ? array.length : 0;\n return !!length && baseIndexOf(array, value, 0) > -1;\n}\n\n/**\n * This function is like `arrayIncludes` except that it accepts a comparator.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\nfunction arrayIncludesWith(array, value, comparator) {\n var index = -1,\n length = array ? array.length : 0;\n\n while (++index < length) {\n if (comparator(value, array[index])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * A specialized version of `_.map` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\nfunction arrayMap(array, iteratee) {\n var index = -1,\n length = array ? array.length : 0,\n result = Array(length);\n\n while (++index < length) {\n result[index] = iteratee(array[index], index, array);\n }\n return result;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n}\n\n/**\n * The base implementation of `_.findIndex` and `_.findLastIndex` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {number} fromIndex The index to search from.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction baseFindIndex(array, predicate, fromIndex, fromRight) {\n var length = array.length,\n index = fromIndex + (fromRight ? 1 : -1);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (predicate(array[index], index, array)) {\n return index;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of `_.indexOf` without `fromIndex` bounds checks.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction baseIndexOf(array, value, fromIndex) {\n if (value !== value) {\n return baseFindIndex(array, baseIsNaN, fromIndex);\n }\n var index = fromIndex - 1,\n length = array.length;\n\n while (++index < length) {\n if (array[index] === value) {\n return index;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of `_.isNaN` without support for number objects.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n */\nfunction baseIsNaN(value) {\n return value !== value;\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n}\n\n/**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\nfunction baseUnary(func) {\n return function(value) {\n return func(value);\n };\n}\n\n/**\n * Checks if a cache value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction cacheHas(cache, key) {\n return cache.has(key);\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n return object == null ? undefined : object[key];\n}\n\n/**\n * Checks if `value` is a host object in IE < 9.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n */\nfunction isHostObject(value) {\n // Many host objects are `Object` objects that can coerce to strings\n // despite having improperly defined `toString` methods.\n var result = false;\n if (value != null && typeof value.toString != 'function') {\n try {\n result = !!(value + '');\n } catch (e) {}\n }\n return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Symbol = root.Symbol,\n getPrototype = overArg(Object.getPrototypeOf, Object),\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice,\n spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols,\n nativeMax = Math.max;\n\n/* Built-in method references that are verified to be native. */\nvar Map = getNative(root, 'Map'),\n nativeCreate = getNative(Object, 'create');\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n return this.has(key) && delete this.__data__[key];\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n var data = this.__data__;\n data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n this.__data__ = [];\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n this.__data__ = {\n 'hash': new Hash,\n 'map': new (Map || ListCache),\n 'string': new Hash\n };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n return getMapData(this, key)['delete'](key);\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n getMapData(this, key).set(key, value);\n return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\nfunction SetCache(values) {\n var index = -1,\n length = values ? values.length : 0;\n\n this.__data__ = new MapCache;\n while (++index < length) {\n this.add(values[index]);\n }\n}\n\n/**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\nfunction setCacheAdd(value) {\n this.__data__.set(value, HASH_UNDEFINED);\n return this;\n}\n\n/**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\nfunction setCacheHas(value) {\n return this.__data__.has(value);\n}\n\n// Add methods to `SetCache`.\nSetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\nSetCache.prototype.has = setCacheHas;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n // Safari 9 makes `arguments.length` enumerable in strict mode.\n var result = (isArray(value) || isArguments(value))\n ? baseTimes(value.length, String)\n : [];\n\n var length = result.length,\n skipIndexes = !!length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (key == 'length' || isIndex(key, length)))) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of methods like `_.difference` without support\n * for excluding multiple arrays or iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Array} values The values to exclude.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n */\nfunction baseDifference(array, values, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n isCommon = true,\n length = array.length,\n result = [],\n valuesLength = values.length;\n\n if (!length) {\n return result;\n }\n if (iteratee) {\n values = arrayMap(values, baseUnary(iteratee));\n }\n if (comparator) {\n includes = arrayIncludesWith;\n isCommon = false;\n }\n else if (values.length >= LARGE_ARRAY_SIZE) {\n includes = cacheHas;\n isCommon = false;\n values = new SetCache(values);\n }\n outer:\n while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n value = (comparator || value !== 0) ? value : 0;\n if (isCommon && computed === computed) {\n var valuesIndex = valuesLength;\n while (valuesIndex--) {\n if (values[valuesIndex] === computed) {\n continue outer;\n }\n }\n result.push(value);\n }\n else if (!includes(values, computed, comparator)) {\n result.push(value);\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.flatten` with support for restricting flattening.\n *\n * @private\n * @param {Array} array The array to flatten.\n * @param {number} depth The maximum recursion depth.\n * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n * @param {Array} [result=[]] The initial result value.\n * @returns {Array} Returns the new flattened array.\n */\nfunction baseFlatten(array, depth, predicate, isStrict, result) {\n var index = -1,\n length = array.length;\n\n predicate || (predicate = isFlattenable);\n result || (result = []);\n\n while (++index < length) {\n var value = array[index];\n if (depth > 0 && predicate(value)) {\n if (depth > 1) {\n // Recursively flatten arrays (susceptible to call stack limits).\n baseFlatten(value, depth - 1, predicate, isStrict, result);\n } else {\n arrayPush(result, value);\n }\n } else if (!isStrict) {\n result[result.length] = value;\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\nfunction baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeysIn(object) {\n if (!isObject(object)) {\n return nativeKeysIn(object);\n }\n var isProto = isPrototype(object),\n result = [];\n\n for (var key in object) {\n if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.pick` without support for individual\n * property identifiers.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} props The property identifiers to pick.\n * @returns {Object} Returns the new object.\n */\nfunction basePick(object, props) {\n object = Object(object);\n return basePickBy(object, props, function(value, key) {\n return key in object;\n });\n}\n\n/**\n * The base implementation of `_.pickBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} props The property identifiers to pick from.\n * @param {Function} predicate The function invoked per property.\n * @returns {Object} Returns the new object.\n */\nfunction basePickBy(object, props, predicate) {\n var index = -1,\n length = props.length,\n result = {};\n\n while (++index < length) {\n var key = props[index],\n value = object[key];\n\n if (predicate(value, key)) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\nfunction baseRest(func, start) {\n start = nativeMax(start === undefined ? (func.length - 1) : start, 0);\n return function() {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = array;\n return apply(func, this, otherArgs);\n };\n}\n\n/**\n * Creates an array of own and inherited enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeysIn(object) {\n return baseGetAllKeys(object, keysIn, getSymbolsIn);\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * Creates an array of the own enumerable symbol properties of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray;\n\n/**\n * Creates an array of the own and inherited enumerable symbol properties\n * of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {\n var result = [];\n while (object) {\n arrayPush(result, getSymbols(object));\n object = getPrototype(object);\n }\n return result;\n};\n\n/**\n * Checks if `value` is a flattenable `arguments` object or array.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n */\nfunction isFlattenable(value) {\n return isArray(value) || isArguments(value) ||\n !!(spreadableSymbol && value && value[spreadableSymbol]);\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length &&\n (typeof value == 'number' || reIsUint.test(value)) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n var type = typeof value;\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n ? (value !== '__proto__')\n : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n}\n\n/**\n * This function is like\n * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * except that it includes inherited enumerable properties.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction nativeKeysIn(object) {\n var result = [];\n if (object != null) {\n for (var key in Object(object)) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\nfunction toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to process.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 8-9 which returns 'object' for typed array and other constructors.\n var tag = isObject(value) ? objectToString.call(value) : '';\n return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Creates an array of the own and inherited enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keysIn(new Foo);\n * // => ['a', 'b', 'c'] (iteration order is not guaranteed)\n */\nfunction keysIn(object) {\n return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);\n}\n\n/**\n * The opposite of `_.pick`; this method creates an object composed of the\n * own and inherited enumerable string keyed properties of `object` that are\n * not omitted.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [props] The property identifiers to omit.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omit(object, ['a', 'c']);\n * // => { 'b': '2' }\n */\nvar omit = baseRest(function(object, props) {\n if (object == null) {\n return {};\n }\n props = arrayMap(baseFlatten(props, 1), toKey);\n return basePick(object, baseDifference(getAllKeysIn(object), props));\n});\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n return [];\n}\n\nmodule.exports = omit;\n","var convert = require('color-convert');\n\nmodule.exports = function (cstr) {\n var m, conv, parts, alpha;\n if (m = /^((?:rgb|hs[lv]|cmyk|xyz|lab)a?)\\s*\\(([^\\)]*)\\)/.exec(cstr)) {\n var name = m[1];\n var base = name.replace(/a$/, '');\n var size = base === 'cmyk' ? 4 : 3;\n conv = convert[base];\n \n parts = m[2].replace(/^\\s+|\\s+$/g, '')\n .split(/\\s*,\\s*/)\n .map(function (x, i) {\n if (/%$/.test(x) && i === size) {\n return parseFloat(x) / 100;\n }\n else if (/%$/.test(x)) {\n return parseFloat(x);\n }\n return parseFloat(x);\n })\n ;\n if (name === base) parts.push(1);\n alpha = parts[size] === undefined ? 1 : parts[size];\n parts = parts.slice(0, size);\n \n conv[base] = function () { return parts };\n }\n else if (/^#[A-Fa-f0-9]+$/.test(cstr)) {\n var base = cstr.replace(/^#/,'');\n var size = base.length;\n conv = convert.rgb;\n parts = base.split(size === 3 ? /(.)/ : /(..)/);\n parts = parts.filter(Boolean)\n .map(function (x) {\n if (size === 3) {\n return parseInt(x + x, 16);\n }\n else {\n return parseInt(x, 16)\n }\n })\n ;\n alpha = 1;\n conv.rgb = function () { return parts };\n if (!parts[0]) parts[0] = 0;\n if (!parts[1]) parts[1] = 0;\n if (!parts[2]) parts[2] = 0;\n }\n else {\n conv = convert.keyword;\n conv.keyword = function () { return cstr };\n parts = cstr;\n alpha = 1;\n }\n \n var res = {\n rgb: undefined,\n hsl: undefined,\n hsv: undefined,\n cmyk: undefined,\n keyword: undefined,\n hex: undefined\n };\n try { res.rgb = conv.rgb(parts) } catch (e) {}\n try { res.hsl = conv.hsl(parts) } catch (e) {}\n try { res.hsv = conv.hsv(parts) } catch (e) {}\n try { res.cmyk = conv.cmyk(parts) } catch (e) {}\n try { res.keyword = conv.keyword(parts) } catch (e) {}\n \n if (res.rgb) res.hex = '#' + res.rgb.map(function (x) {\n var s = x.toString(16);\n if (s.length === 1) return '0' + s;\n return s;\n }).join('');\n \n if (res.rgb) res.rgba = res.rgb.concat(alpha);\n if (res.hsl) res.hsla = res.hsl.concat(alpha);\n if (res.hsv) res.hsva = res.hsv.concat(alpha);\n if (res.cmyk) res.cmyka = res.cmyk.concat(alpha);\n \n return res;\n};\n","/* MIT license */\n\nmodule.exports = {\n rgb2hsl: rgb2hsl,\n rgb2hsv: rgb2hsv,\n rgb2hwb: rgb2hwb,\n rgb2cmyk: rgb2cmyk,\n rgb2keyword: rgb2keyword,\n rgb2xyz: rgb2xyz,\n rgb2lab: rgb2lab,\n rgb2lch: rgb2lch,\n\n hsl2rgb: hsl2rgb,\n hsl2hsv: hsl2hsv,\n hsl2hwb: hsl2hwb,\n hsl2cmyk: hsl2cmyk,\n hsl2keyword: hsl2keyword,\n\n hsv2rgb: hsv2rgb,\n hsv2hsl: hsv2hsl,\n hsv2hwb: hsv2hwb,\n hsv2cmyk: hsv2cmyk,\n hsv2keyword: hsv2keyword,\n\n hwb2rgb: hwb2rgb,\n hwb2hsl: hwb2hsl,\n hwb2hsv: hwb2hsv,\n hwb2cmyk: hwb2cmyk,\n hwb2keyword: hwb2keyword,\n\n cmyk2rgb: cmyk2rgb,\n cmyk2hsl: cmyk2hsl,\n cmyk2hsv: cmyk2hsv,\n cmyk2hwb: cmyk2hwb,\n cmyk2keyword: cmyk2keyword,\n\n keyword2rgb: keyword2rgb,\n keyword2hsl: keyword2hsl,\n keyword2hsv: keyword2hsv,\n keyword2hwb: keyword2hwb,\n keyword2cmyk: keyword2cmyk,\n keyword2lab: keyword2lab,\n keyword2xyz: keyword2xyz,\n\n xyz2rgb: xyz2rgb,\n xyz2lab: xyz2lab,\n xyz2lch: xyz2lch,\n\n lab2xyz: lab2xyz,\n lab2rgb: lab2rgb,\n lab2lch: lab2lch,\n\n lch2lab: lch2lab,\n lch2xyz: lch2xyz,\n lch2rgb: lch2rgb\n}\n\n\nfunction rgb2hsl(rgb) {\n var r = rgb[0]/255,\n g = rgb[1]/255,\n b = rgb[2]/255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n delta = max - min,\n h, s, l;\n\n if (max == min)\n h = 0;\n else if (r == max)\n h = (g - b) / delta;\n else if (g == max)\n h = 2 + (b - r) / delta;\n else if (b == max)\n h = 4 + (r - g)/ delta;\n\n h = Math.min(h * 60, 360);\n\n if (h < 0)\n h += 360;\n\n l = (min + max) / 2;\n\n if (max == min)\n s = 0;\n else if (l <= 0.5)\n s = delta / (max + min);\n else\n s = delta / (2 - max - min);\n\n return [h, s * 100, l * 100];\n}\n\nfunction rgb2hsv(rgb) {\n var r = rgb[0],\n g = rgb[1],\n b = rgb[2],\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n delta = max - min,\n h, s, v;\n\n if (max == 0)\n s = 0;\n else\n s = (delta/max * 1000)/10;\n\n if (max == min)\n h = 0;\n else if (r == max)\n h = (g - b) / delta;\n else if (g == max)\n h = 2 + (b - r) / delta;\n else if (b == max)\n h = 4 + (r - g) / delta;\n\n h = Math.min(h * 60, 360);\n\n if (h < 0)\n h += 360;\n\n v = ((max / 255) * 1000) / 10;\n\n return [h, s, v];\n}\n\nfunction rgb2hwb(rgb) {\n var r = rgb[0],\n g = rgb[1],\n b = rgb[2],\n h = rgb2hsl(rgb)[0],\n w = 1/255 * Math.min(r, Math.min(g, b)),\n b = 1 - 1/255 * Math.max(r, Math.max(g, b));\n\n return [h, w * 100, b * 100];\n}\n\nfunction rgb2cmyk(rgb) {\n var r = rgb[0] / 255,\n g = rgb[1] / 255,\n b = rgb[2] / 255,\n c, m, y, k;\n\n k = Math.min(1 - r, 1 - g, 1 - b);\n c = (1 - r - k) / (1 - k) || 0;\n m = (1 - g - k) / (1 - k) || 0;\n y = (1 - b - k) / (1 - k) || 0;\n return [c * 100, m * 100, y * 100, k * 100];\n}\n\nfunction rgb2keyword(rgb) {\n return reverseKeywords[JSON.stringify(rgb)];\n}\n\nfunction rgb2xyz(rgb) {\n var r = rgb[0] / 255,\n g = rgb[1] / 255,\n b = rgb[2] / 255;\n\n // assume sRGB\n r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);\n g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);\n b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);\n\n var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);\n var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);\n var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);\n\n return [x * 100, y *100, z * 100];\n}\n\nfunction rgb2lab(rgb) {\n var xyz = rgb2xyz(rgb),\n x = xyz[0],\n y = xyz[1],\n z = xyz[2],\n l, a, b;\n\n x /= 95.047;\n y /= 100;\n z /= 108.883;\n\n x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);\n y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);\n z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);\n\n l = (116 * y) - 16;\n a = 500 * (x - y);\n b = 200 * (y - z);\n\n return [l, a, b];\n}\n\nfunction rgb2lch(args) {\n return lab2lch(rgb2lab(args));\n}\n\nfunction hsl2rgb(hsl) {\n var h = hsl[0] / 360,\n s = hsl[1] / 100,\n l = hsl[2] / 100,\n t1, t2, t3, rgb, val;\n\n if (s == 0) {\n val = l * 255;\n return [val, val, val];\n }\n\n if (l < 0.5)\n t2 = l * (1 + s);\n else\n t2 = l + s - l * s;\n t1 = 2 * l - t2;\n\n rgb = [0, 0, 0];\n for (var i = 0; i < 3; i++) {\n t3 = h + 1 / 3 * - (i - 1);\n t3 < 0 && t3++;\n t3 > 1 && t3--;\n\n if (6 * t3 < 1)\n val = t1 + (t2 - t1) * 6 * t3;\n else if (2 * t3 < 1)\n val = t2;\n else if (3 * t3 < 2)\n val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;\n else\n val = t1;\n\n rgb[i] = val * 255;\n }\n\n return rgb;\n}\n\nfunction hsl2hsv(hsl) {\n var h = hsl[0],\n s = hsl[1] / 100,\n l = hsl[2] / 100,\n sv, v;\n\n if(l === 0) {\n // no need to do calc on black\n // also avoids divide by 0 error\n return [0, 0, 0];\n }\n\n l *= 2;\n s *= (l <= 1) ? l : 2 - l;\n v = (l + s) / 2;\n sv = (2 * s) / (l + s);\n return [h, sv * 100, v * 100];\n}\n\nfunction hsl2hwb(args) {\n return rgb2hwb(hsl2rgb(args));\n}\n\nfunction hsl2cmyk(args) {\n return rgb2cmyk(hsl2rgb(args));\n}\n\nfunction hsl2keyword(args) {\n return rgb2keyword(hsl2rgb(args));\n}\n\n\nfunction hsv2rgb(hsv) {\n var h = hsv[0] / 60,\n s = hsv[1] / 100,\n v = hsv[2] / 100,\n hi = Math.floor(h) % 6;\n\n var f = h - Math.floor(h),\n p = 255 * v * (1 - s),\n q = 255 * v * (1 - (s * f)),\n t = 255 * v * (1 - (s * (1 - f))),\n v = 255 * v;\n\n switch(hi) {\n case 0:\n return [v, t, p];\n case 1:\n return [q, v, p];\n case 2:\n return [p, v, t];\n case 3:\n return [p, q, v];\n case 4:\n return [t, p, v];\n case 5:\n return [v, p, q];\n }\n}\n\nfunction hsv2hsl(hsv) {\n var h = hsv[0],\n s = hsv[1] / 100,\n v = hsv[2] / 100,\n sl, l;\n\n l = (2 - s) * v;\n sl = s * v;\n sl /= (l <= 1) ? l : 2 - l;\n sl = sl || 0;\n l /= 2;\n return [h, sl * 100, l * 100];\n}\n\nfunction hsv2hwb(args) {\n return rgb2hwb(hsv2rgb(args))\n}\n\nfunction hsv2cmyk(args) {\n return rgb2cmyk(hsv2rgb(args));\n}\n\nfunction hsv2keyword(args) {\n return rgb2keyword(hsv2rgb(args));\n}\n\n// http://dev.w3.org/csswg/css-color/#hwb-to-rgb\nfunction hwb2rgb(hwb) {\n var h = hwb[0] / 360,\n wh = hwb[1] / 100,\n bl = hwb[2] / 100,\n ratio = wh + bl,\n i, v, f, n;\n\n // wh + bl cant be > 1\n if (ratio > 1) {\n wh /= ratio;\n bl /= ratio;\n }\n\n i = Math.floor(6 * h);\n v = 1 - bl;\n f = 6 * h - i;\n if ((i & 0x01) != 0) {\n f = 1 - f;\n }\n n = wh + f * (v - wh); // linear interpolation\n\n switch (i) {\n default:\n case 6:\n case 0: r = v; g = n; b = wh; break;\n case 1: r = n; g = v; b = wh; break;\n case 2: r = wh; g = v; b = n; break;\n case 3: r = wh; g = n; b = v; break;\n case 4: r = n; g = wh; b = v; break;\n case 5: r = v; g = wh; b = n; break;\n }\n\n return [r * 255, g * 255, b * 255];\n}\n\nfunction hwb2hsl(args) {\n return rgb2hsl(hwb2rgb(args));\n}\n\nfunction hwb2hsv(args) {\n return rgb2hsv(hwb2rgb(args));\n}\n\nfunction hwb2cmyk(args) {\n return rgb2cmyk(hwb2rgb(args));\n}\n\nfunction hwb2keyword(args) {\n return rgb2keyword(hwb2rgb(args));\n}\n\nfunction cmyk2rgb(cmyk) {\n var c = cmyk[0] / 100,\n m = cmyk[1] / 100,\n y = cmyk[2] / 100,\n k = cmyk[3] / 100,\n r, g, b;\n\n r = 1 - Math.min(1, c * (1 - k) + k);\n g = 1 - Math.min(1, m * (1 - k) + k);\n b = 1 - Math.min(1, y * (1 - k) + k);\n return [r * 255, g * 255, b * 255];\n}\n\nfunction cmyk2hsl(args) {\n return rgb2hsl(cmyk2rgb(args));\n}\n\nfunction cmyk2hsv(args) {\n return rgb2hsv(cmyk2rgb(args));\n}\n\nfunction cmyk2hwb(args) {\n return rgb2hwb(cmyk2rgb(args));\n}\n\nfunction cmyk2keyword(args) {\n return rgb2keyword(cmyk2rgb(args));\n}\n\n\nfunction xyz2rgb(xyz) {\n var x = xyz[0] / 100,\n y = xyz[1] / 100,\n z = xyz[2] / 100,\n r, g, b;\n\n r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);\n g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);\n b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);\n\n // assume sRGB\n r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)\n : r = (r * 12.92);\n\n g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)\n : g = (g * 12.92);\n\n b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)\n : b = (b * 12.92);\n\n r = Math.min(Math.max(0, r), 1);\n g = Math.min(Math.max(0, g), 1);\n b = Math.min(Math.max(0, b), 1);\n\n return [r * 255, g * 255, b * 255];\n}\n\nfunction xyz2lab(xyz) {\n var x = xyz[0],\n y = xyz[1],\n z = xyz[2],\n l, a, b;\n\n x /= 95.047;\n y /= 100;\n z /= 108.883;\n\n x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);\n y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);\n z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);\n\n l = (116 * y) - 16;\n a = 500 * (x - y);\n b = 200 * (y - z);\n\n return [l, a, b];\n}\n\nfunction xyz2lch(args) {\n return lab2lch(xyz2lab(args));\n}\n\nfunction lab2xyz(lab) {\n var l = lab[0],\n a = lab[1],\n b = lab[2],\n x, y, z, y2;\n\n if (l <= 8) {\n y = (l * 100) / 903.3;\n y2 = (7.787 * (y / 100)) + (16 / 116);\n } else {\n y = 100 * Math.pow((l + 16) / 116, 3);\n y2 = Math.pow(y / 100, 1/3);\n }\n\n x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);\n\n z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);\n\n return [x, y, z];\n}\n\nfunction lab2lch(lab) {\n var l = lab[0],\n a = lab[1],\n b = lab[2],\n hr, h, c;\n\n hr = Math.atan2(b, a);\n h = hr * 360 / 2 / Math.PI;\n if (h < 0) {\n h += 360;\n }\n c = Math.sqrt(a * a + b * b);\n return [l, c, h];\n}\n\nfunction lab2rgb(args) {\n return xyz2rgb(lab2xyz(args));\n}\n\nfunction lch2lab(lch) {\n var l = lch[0],\n c = lch[1],\n h = lch[2],\n a, b, hr;\n\n hr = h / 360 * 2 * Math.PI;\n a = c * Math.cos(hr);\n b = c * Math.sin(hr);\n return [l, a, b];\n}\n\nfunction lch2xyz(args) {\n return lab2xyz(lch2lab(args));\n}\n\nfunction lch2rgb(args) {\n return lab2rgb(lch2lab(args));\n}\n\nfunction keyword2rgb(keyword) {\n return cssKeywords[keyword];\n}\n\nfunction keyword2hsl(args) {\n return rgb2hsl(keyword2rgb(args));\n}\n\nfunction keyword2hsv(args) {\n return rgb2hsv(keyword2rgb(args));\n}\n\nfunction keyword2hwb(args) {\n return rgb2hwb(keyword2rgb(args));\n}\n\nfunction keyword2cmyk(args) {\n return rgb2cmyk(keyword2rgb(args));\n}\n\nfunction keyword2lab(args) {\n return rgb2lab(keyword2rgb(args));\n}\n\nfunction keyword2xyz(args) {\n return rgb2xyz(keyword2rgb(args));\n}\n\nvar cssKeywords = {\n aliceblue: [240,248,255],\n antiquewhite: [250,235,215],\n aqua: [0,255,255],\n aquamarine: [127,255,212],\n azure: [240,255,255],\n beige: [245,245,220],\n bisque: [255,228,196],\n black: [0,0,0],\n blanchedalmond: [255,235,205],\n blue: [0,0,255],\n blueviolet: [138,43,226],\n brown: [165,42,42],\n burlywood: [222,184,135],\n cadetblue: [95,158,160],\n chartreuse: [127,255,0],\n chocolate: [210,105,30],\n coral: [255,127,80],\n cornflowerblue: [100,149,237],\n cornsilk: [255,248,220],\n crimson: [220,20,60],\n cyan: [0,255,255],\n darkblue: [0,0,139],\n darkcyan: [0,139,139],\n darkgoldenrod: [184,134,11],\n darkgray: [169,169,169],\n darkgreen: [0,100,0],\n darkgrey: [169,169,169],\n darkkhaki: [189,183,107],\n darkmagenta: [139,0,139],\n darkolivegreen: [85,107,47],\n darkorange: [255,140,0],\n darkorchid: [153,50,204],\n darkred: [139,0,0],\n darksalmon: [233,150,122],\n darkseagreen: [143,188,143],\n darkslateblue: [72,61,139],\n darkslategray: [47,79,79],\n darkslategrey: [47,79,79],\n darkturquoise: [0,206,209],\n darkviolet: [148,0,211],\n deeppink: [255,20,147],\n deepskyblue: [0,191,255],\n dimgray: [105,105,105],\n dimgrey: [105,105,105],\n dodgerblue: [30,144,255],\n firebrick: [178,34,34],\n floralwhite: [255,250,240],\n forestgreen: [34,139,34],\n fuchsia: [255,0,255],\n gainsboro: [220,220,220],\n ghostwhite: [248,248,255],\n gold: [255,215,0],\n goldenrod: [218,165,32],\n gray: [128,128,128],\n green: [0,128,0],\n greenyellow: [173,255,47],\n grey: [128,128,128],\n honeydew: [240,255,240],\n hotpink: [255,105,180],\n indianred: [205,92,92],\n indigo: [75,0,130],\n ivory: [255,255,240],\n khaki: [240,230,140],\n lavender: [230,230,250],\n lavenderblush: [255,240,245],\n lawngreen: [124,252,0],\n lemonchiffon: [255,250,205],\n lightblue: [173,216,230],\n lightcoral: [240,128,128],\n lightcyan: [224,255,255],\n lightgoldenrodyellow: [250,250,210],\n lightgray: [211,211,211],\n lightgreen: [144,238,144],\n lightgrey: [211,211,211],\n lightpink: [255,182,193],\n lightsalmon: [255,160,122],\n lightseagreen: [32,178,170],\n lightskyblue: [135,206,250],\n lightslategray: [119,136,153],\n lightslategrey: [119,136,153],\n lightsteelblue: [176,196,222],\n lightyellow: [255,255,224],\n lime: [0,255,0],\n limegreen: [50,205,50],\n linen: [250,240,230],\n magenta: [255,0,255],\n maroon: [128,0,0],\n mediumaquamarine: [102,205,170],\n mediumblue: [0,0,205],\n mediumorchid: [186,85,211],\n mediumpurple: [147,112,219],\n mediumseagreen: [60,179,113],\n mediumslateblue: [123,104,238],\n mediumspringgreen: [0,250,154],\n mediumturquoise: [72,209,204],\n mediumvioletred: [199,21,133],\n midnightblue: [25,25,112],\n mintcream: [245,255,250],\n mistyrose: [255,228,225],\n moccasin: [255,228,181],\n navajowhite: [255,222,173],\n navy: [0,0,128],\n oldlace: [253,245,230],\n olive: [128,128,0],\n olivedrab: [107,142,35],\n orange: [255,165,0],\n orangered: [255,69,0],\n orchid: [218,112,214],\n palegoldenrod: [238,232,170],\n palegreen: [152,251,152],\n paleturquoise: [175,238,238],\n palevioletred: [219,112,147],\n papayawhip: [255,239,213],\n peachpuff: [255,218,185],\n peru: [205,133,63],\n pink: [255,192,203],\n plum: [221,160,221],\n powderblue: [176,224,230],\n purple: [128,0,128],\n rebeccapurple: [102, 51, 153],\n red: [255,0,0],\n rosybrown: [188,143,143],\n royalblue: [65,105,225],\n saddlebrown: [139,69,19],\n salmon: [250,128,114],\n sandybrown: [244,164,96],\n seagreen: [46,139,87],\n seashell: [255,245,238],\n sienna: [160,82,45],\n silver: [192,192,192],\n skyblue: [135,206,235],\n slateblue: [106,90,205],\n slategray: [112,128,144],\n slategrey: [112,128,144],\n snow: [255,250,250],\n springgreen: [0,255,127],\n steelblue: [70,130,180],\n tan: [210,180,140],\n teal: [0,128,128],\n thistle: [216,191,216],\n tomato: [255,99,71],\n turquoise: [64,224,208],\n violet: [238,130,238],\n wheat: [245,222,179],\n white: [255,255,255],\n whitesmoke: [245,245,245],\n yellow: [255,255,0],\n yellowgreen: [154,205,50]\n};\n\nvar reverseKeywords = {};\nfor (var key in cssKeywords) {\n reverseKeywords[JSON.stringify(cssKeywords[key])] = key;\n}\n","var conversions = require(\"./conversions\");\n\nvar convert = function() {\n return new Converter();\n}\n\nfor (var func in conversions) {\n // export Raw versions\n convert[func + \"Raw\"] = (function(func) {\n // accept array or plain args\n return function(arg) {\n if (typeof arg == \"number\")\n arg = Array.prototype.slice.call(arguments);\n return conversions[func](arg);\n }\n })(func);\n\n var pair = /(\\w+)2(\\w+)/.exec(func),\n from = pair[1],\n to = pair[2];\n\n // export rgb2hsl and [\"rgb\"][\"hsl\"]\n convert[from] = convert[from] || {};\n\n convert[from][to] = convert[func] = (function(func) { \n return function(arg) {\n if (typeof arg == \"number\")\n arg = Array.prototype.slice.call(arguments);\n \n var val = conversions[func](arg);\n if (typeof val == \"string\" || val === undefined)\n return val; // keyword\n\n for (var i = 0; i < val.length; i++)\n val[i] = Math.round(val[i]);\n return val;\n }\n })(func);\n}\n\n\n/* Converter does lazy conversion and caching */\nvar Converter = function() {\n this.convs = {};\n};\n\n/* Either get the values for a space or\n set the values for a space, depending on args */\nConverter.prototype.routeSpace = function(space, args) {\n var values = args[0];\n if (values === undefined) {\n // color.rgb()\n return this.getValues(space);\n }\n // color.rgb(10, 10, 10)\n if (typeof values == \"number\") {\n values = Array.prototype.slice.call(args); \n }\n\n return this.setValues(space, values);\n};\n \n/* Set the values for a space, invalidating cache */\nConverter.prototype.setValues = function(space, values) {\n this.space = space;\n this.convs = {};\n this.convs[space] = values;\n return this;\n};\n\n/* Get the values for a space. If there's already\n a conversion for the space, fetch it, otherwise\n compute it */\nConverter.prototype.getValues = function(space) {\n var vals = this.convs[space];\n if (!vals) {\n var fspace = this.space,\n from = this.convs[fspace];\n vals = convert[fspace][space](from);\n\n this.convs[space] = vals;\n }\n return vals;\n};\n\n[\"rgb\", \"hsl\", \"hsv\", \"cmyk\", \"keyword\"].forEach(function(space) {\n Converter.prototype[space] = function(vals) {\n return this.routeSpace(space, arguments);\n }\n});\n\nmodule.exports = convert;","\"use strict\";\n\nvar stylesInDOM = [];\nfunction getIndexByIdentifier(identifier) {\n var result = -1;\n for (var i = 0; i < stylesInDOM.length; i++) {\n if (stylesInDOM[i].identifier === identifier) {\n result = i;\n break;\n }\n }\n return result;\n}\nfunction modulesToDom(list, options) {\n var idCountMap = {};\n var identifiers = [];\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n var id = options.base ? item[0] + options.base : item[0];\n var count = idCountMap[id] || 0;\n var identifier = \"\".concat(id, \" \").concat(count);\n idCountMap[id] = count + 1;\n var indexByIdentifier = getIndexByIdentifier(identifier);\n var obj = {\n css: item[1],\n media: item[2],\n sourceMap: item[3],\n supports: item[4],\n layer: item[5]\n };\n if (indexByIdentifier !== -1) {\n stylesInDOM[indexByIdentifier].references++;\n stylesInDOM[indexByIdentifier].updater(obj);\n } else {\n var updater = addElementStyle(obj, options);\n options.byIndex = i;\n stylesInDOM.splice(i, 0, {\n identifier: identifier,\n updater: updater,\n references: 1\n });\n }\n identifiers.push(identifier);\n }\n return identifiers;\n}\nfunction addElementStyle(obj, options) {\n var api = options.domAPI(options);\n api.update(obj);\n var updater = function updater(newObj) {\n if (newObj) {\n if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {\n return;\n }\n api.update(obj = newObj);\n } else {\n api.remove();\n }\n };\n return updater;\n}\nmodule.exports = function (list, options) {\n options = options || {};\n list = list || [];\n var lastIdentifiers = modulesToDom(list, options);\n return function update(newList) {\n newList = newList || [];\n for (var i = 0; i < lastIdentifiers.length; i++) {\n var identifier = lastIdentifiers[i];\n var index = getIndexByIdentifier(identifier);\n stylesInDOM[index].references--;\n }\n var newLastIdentifiers = modulesToDom(newList, options);\n for (var _i = 0; _i < lastIdentifiers.length; _i++) {\n var _identifier = lastIdentifiers[_i];\n var _index = getIndexByIdentifier(_identifier);\n if (stylesInDOM[_index].references === 0) {\n stylesInDOM[_index].updater();\n stylesInDOM.splice(_index, 1);\n }\n }\n lastIdentifiers = newLastIdentifiers;\n };\n};","\"use strict\";\n\nvar memo = {};\n\n/* istanbul ignore next */\nfunction getTarget(target) {\n if (typeof memo[target] === \"undefined\") {\n var styleTarget = document.querySelector(target);\n\n // Special case to return head of iframe instead of iframe itself\n if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n try {\n // This will throw an exception if access to iframe is blocked\n // due to cross-origin restrictions\n styleTarget = styleTarget.contentDocument.head;\n } catch (e) {\n // istanbul ignore next\n styleTarget = null;\n }\n }\n memo[target] = styleTarget;\n }\n return memo[target];\n}\n\n/* istanbul ignore next */\nfunction insertBySelector(insert, style) {\n var target = getTarget(insert);\n if (!target) {\n throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");\n }\n target.appendChild(style);\n}\nmodule.exports = insertBySelector;","\"use strict\";\n\n/* istanbul ignore next */\nfunction insertStyleElement(options) {\n var element = document.createElement(\"style\");\n options.setAttributes(element, options.attributes);\n options.insert(element, options.options);\n return element;\n}\nmodule.exports = insertStyleElement;","\"use strict\";\n\n/* istanbul ignore next */\nfunction setAttributesWithoutAttributes(styleElement) {\n var nonce = typeof __webpack_nonce__ !== \"undefined\" ? __webpack_nonce__ : null;\n if (nonce) {\n styleElement.setAttribute(\"nonce\", nonce);\n }\n}\nmodule.exports = setAttributesWithoutAttributes;","\"use strict\";\n\n/* istanbul ignore next */\nfunction apply(styleElement, options, obj) {\n var css = \"\";\n if (obj.supports) {\n css += \"@supports (\".concat(obj.supports, \") {\");\n }\n if (obj.media) {\n css += \"@media \".concat(obj.media, \" {\");\n }\n var needLayer = typeof obj.layer !== \"undefined\";\n if (needLayer) {\n css += \"@layer\".concat(obj.layer.length > 0 ? \" \".concat(obj.layer) : \"\", \" {\");\n }\n css += obj.css;\n if (needLayer) {\n css += \"}\";\n }\n if (obj.media) {\n css += \"}\";\n }\n if (obj.supports) {\n css += \"}\";\n }\n var sourceMap = obj.sourceMap;\n if (sourceMap && typeof btoa !== \"undefined\") {\n css += \"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), \" */\");\n }\n\n // For old IE\n /* istanbul ignore if */\n options.styleTagTransform(css, styleElement, options.options);\n}\nfunction removeStyleElement(styleElement) {\n // istanbul ignore if\n if (styleElement.parentNode === null) {\n return false;\n }\n styleElement.parentNode.removeChild(styleElement);\n}\n\n/* istanbul ignore next */\nfunction domAPI(options) {\n if (typeof document === \"undefined\") {\n return {\n update: function update() {},\n remove: function remove() {}\n };\n }\n var styleElement = options.insertStyleElement(options);\n return {\n update: function update(obj) {\n apply(styleElement, options, obj);\n },\n remove: function remove() {\n removeStyleElement(styleElement);\n }\n };\n}\nmodule.exports = domAPI;","\"use strict\";\n\n/* istanbul ignore next */\nfunction styleTagTransform(css, styleElement) {\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = css;\n } else {\n while (styleElement.firstChild) {\n styleElement.removeChild(styleElement.firstChild);\n }\n styleElement.appendChild(document.createTextNode(css));\n }\n}\nmodule.exports = styleTagTransform;","module.exports = __WEBPACK_EXTERNAL_MODULE__962__;","module.exports = __WEBPACK_EXTERNAL_MODULE__749__;","module.exports = __WEBPACK_EXTERNAL_MODULE__863__;","module.exports = __WEBPACK_EXTERNAL_MODULE__629__;","module.exports = __WEBPACK_EXTERNAL_MODULE__923__;","module.exports = __WEBPACK_EXTERNAL_MODULE__53__;","module.exports = __WEBPACK_EXTERNAL_MODULE__850__;","module.exports = __WEBPACK_EXTERNAL_MODULE__582__;","module.exports = __WEBPACK_EXTERNAL_MODULE__17__;","module.exports = __WEBPACK_EXTERNAL_MODULE__0__;","/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames () {\n\t\tvar classes = '';\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (arg) {\n\t\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction parseValue (arg) {\n\t\tif (typeof arg === 'string' || typeof arg === 'number') {\n\t\t\treturn arg;\n\t\t}\n\n\t\tif (typeof arg !== 'object') {\n\t\t\treturn '';\n\t\t}\n\n\t\tif (Array.isArray(arg)) {\n\t\t\treturn classNames.apply(null, arg);\n\t\t}\n\n\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\treturn arg.toString();\n\t\t}\n\n\t\tvar classes = '';\n\n\t\tfor (var key in arg) {\n\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\tclasses = appendClass(classes, key);\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction appendClass (value, newClass) {\n\t\tif (!newClass) {\n\t\t\treturn value;\n\t\t}\n\t\n\t\tif (value) {\n\t\t\treturn value + ' ' + newClass;\n\t\t}\n\t\n\t\treturn value + newClass;\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = function(module) {\n\tvar getter = module && module.__esModule ?\n\t\tfunction() { return module['default']; } :\n\t\tfunction() { return module; };\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nc = undefined;","import minilog from 'minilog';\nminilog.enable();\n\nexport default minilog('scratch-paint');\n","import keyMirror from 'keymirror';\n\nconst Formats = keyMirror({\n BITMAP: null,\n VECTOR: null,\n // Format changes which should not trigger conversions, for instance undo\n BITMAP_SKIP_CONVERT: null,\n VECTOR_SKIP_CONVERT: null\n});\n\nconst isVector = function (format) {\n return format === Formats.VECTOR || format === Formats.VECTOR_SKIP_CONVERT;\n};\n\nconst isBitmap = function (format) {\n return format === Formats.BITMAP || format === Formats.BITMAP_SKIP_CONVERT;\n};\n\nexport {\n Formats as default,\n isVector,\n isBitmap\n};\n","import keyMirror from 'keymirror';\n\nconst vectorModesObj = {\n BRUSH: null,\n ERASER: null,\n LINE: null,\n FILL: null,\n SELECT: null,\n RESHAPE: null,\n OVAL: null,\n RECT: null,\n ROUNDED_RECT: null,\n TEXT: null\n};\nconst bitmapModesObj = {\n BIT_BRUSH: null,\n BIT_LINE: null,\n BIT_OVAL: null,\n BIT_RECT: null,\n BIT_TEXT: null,\n BIT_FILL: null,\n BIT_ERASER: null,\n BIT_SELECT: null\n};\nconst VectorModes = keyMirror(vectorModesObj);\nconst BitmapModes = keyMirror(bitmapModesObj);\nconst Modes = keyMirror({...vectorModesObj, ...bitmapModesObj});\n\nconst GradientToolsModes = keyMirror({\n FILL: null,\n SELECT: null,\n RESHAPE: null,\n OVAL: null,\n RECT: null,\n LINE: null,\n\n BIT_OVAL: null,\n BIT_RECT: null,\n BIT_SELECT: null,\n BIT_FILL: null\n});\n\nexport {\n Modes as default,\n VectorModes,\n BitmapModes,\n GradientToolsModes\n};\n","import paper from '@scratch/paper';\n\nconst getRootItem = function (item) {\n if (item.parent.className === 'Layer') {\n return item;\n }\n return getRootItem(item.parent);\n};\n\nconst isBoundsItem = function (item) {\n if (item.className === 'PointText' ||\n item.className === 'Shape' ||\n item.className === 'PlacedSymbol' ||\n item.className === 'Raster') {\n return true;\n }\n return false;\n};\n\n\nconst isPathItem = function (item) {\n return item.className === 'Path';\n};\n\n\nconst isCompoundPathItem = function (item) {\n return item.className === 'CompoundPath';\n};\n\n\nconst isGroupItem = function (item) {\n return item && item.className && item.className === 'Group';\n};\n\n\nconst isPointTextItem = function (item) {\n return item.className === 'PointText';\n};\n\n\nconst isPGTextItem = function (item) {\n return getRootItem(item).data.isPGTextItem;\n};\n\nconst setPivot = function (item, point) {\n if (isBoundsItem(item)) {\n item.pivot = item.globalToLocal(point);\n } else {\n item.pivot = point;\n }\n};\n\n\nconst getPositionInView = function (item) {\n const itemPos = new paper.Point();\n itemPos.x = item.position.x - paper.view.bounds.x;\n itemPos.y = item.position.y - paper.view.bounds.y;\n return itemPos;\n};\n\n\nconst setPositionInView = function (item, pos) {\n item.position.x = paper.view.bounds.x + pos.x;\n item.position.y = paper.view.bounds.y + pos.y;\n};\n\nexport {\n isBoundsItem,\n isPathItem,\n isCompoundPathItem,\n isGroupItem,\n isPointTextItem,\n isPGTextItem,\n setPivot,\n getPositionInView,\n setPositionInView,\n getRootItem\n};\n","import paper from '@scratch/paper';\nimport {getRootItem, isGroupItem} from './item';\nimport {clearSelection, getSelectedRootItems, setItemSelection} from './selection';\n\nconst isGroup = function (item) {\n return isGroupItem(item);\n};\n\n/**\n * Groups the given items. Other things are then deselected and the new group is selected.\n * @param {!Array<paper.Item>} items Root level items to group\n * @param {!Function} clearSelectedItems Function to clear Redux state's selected items\n * @param {!Function} setSelectedItems Function to set Redux state with new list of selected items\n * @param {!Function} onUpdateImage Function to let listeners know that SVG has changed.\n * @returns {paper.Group} the group if one is created, otherwise false.\n */\nconst groupItems = function (items, clearSelectedItems, setSelectedItems, onUpdateImage) {\n if (items.length > 0) {\n const group = new paper.Group(items);\n clearSelection(clearSelectedItems);\n setItemSelection(group, true);\n for (let i = 0; i < group.children.length; i++) {\n group.children[i].selected = true;\n }\n setSelectedItems();\n onUpdateImage();\n return group;\n }\n return false;\n};\n\n/**\n * Groups the selected items. Other things are then deselected and the new group is selected.\n * @param {!Function} clearSelectedItems Function to clear Redux state's selected items\n * @param {!Function} setSelectedItems Function to set Redux state with new list of selected items\n * @param {!Function} onUpdateImage Function to let listeners know that SVG has changed.\n * @returns {paper.Group} the group if one is created, otherwise false.\n */\nconst groupSelection = function (clearSelectedItems, setSelectedItems, onUpdateImage) {\n const items = getSelectedRootItems();\n return groupItems(items, clearSelectedItems, setSelectedItems, onUpdateImage);\n};\n\nconst _ungroupLoop = function (group, recursive, setSelectedItems) {\n // Can't ungroup items that are not groups\n if (!group || !group.children || !isGroup(group)) return;\n\n group.applyMatrix = true;\n // iterate over group children recursively\n for (let i = 0; i < group.children.length; i++) {\n let groupChild = group.children[i];\n if (groupChild instanceof paper.Group && groupChild.hasChildren()) {\n // recursion (groups can contain groups, ie. from SVG import)\n if (recursive) {\n _ungroupLoop(groupChild, recursive, setSelectedItems);\n continue;\n }\n if (groupChild.children.length === 1) {\n groupChild = groupChild.reduce();\n }\n }\n groupChild.applyMatrix = true;\n // move items from the group to the activeLayer (ungrouping)\n groupChild.insertBelow(group);\n if (setSelectedItems) {\n groupChild.selected = true;\n }\n i--;\n }\n};\n\n/**\n * Ungroups the given items. The new group is selected only if setSelectedItems is passed in.\n * onUpdateImage is called to notify listeners of a change on the SVG only if onUpdateImage is passed in.\n * The reason these arguments are optional on ungroupItems is because ungroupItems is used for parts of\n * SVG import, which shouldn't change the selection or undo state.\n * @param {!Array<paper.Item>} items Items to ungroup if they are groups\n * @param {?Function} setSelectedItems Function to set Redux state with new list of selected items\n * @param {?Function} onUpdateImage Function to let listeners know that SVG has changed.\n */\nconst ungroupItems = function (items, setSelectedItems, onUpdateImage) {\n if (items.length === 0) {\n return;\n }\n const emptyGroups = [];\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (isGroup(item) && !item.data.isPGTextItem) {\n _ungroupLoop(item, false /* recursive */, setSelectedItems);\n\n if (!item.hasChildren()) {\n emptyGroups.push(item);\n }\n } else if (setSelectedItems) {\n item.selected = true;\n }\n }\n if (setSelectedItems) {\n setSelectedItems();\n }\n // remove all empty groups after ungrouping\n for (let j = 0; j < emptyGroups.length; j++) {\n emptyGroups[j].remove();\n }\n // @todo: enable/disable grouping icons\n if (onUpdateImage) {\n onUpdateImage();\n }\n};\n\n/**\n * Ungroups the selected items. Other items are deselected and the ungrouped items are selected.\n * @param {!Function} clearSelectedItems Function to clear Redux state's selected items\n * @param {!Function} setSelectedItems Function to set Redux state with new list of selected items\n * @param {!Function} onUpdateImage Function to let listeners know that SVG has changed.\n */\nconst ungroupSelection = function (clearSelectedItems, setSelectedItems, onUpdateImage) {\n const items = getSelectedRootItems();\n clearSelection(clearSelectedItems);\n ungroupItems(items, setSelectedItems, onUpdateImage);\n};\n\nconst getItemsGroup = function (item) {\n const itemParent = item.parent;\n\n if (isGroup(itemParent)) {\n return itemParent;\n }\n return null;\n};\n\nconst isGroupChild = function (item) {\n const rootItem = getRootItem(item);\n return isGroup(rootItem);\n};\n\nconst shouldShowGroup = function () {\n const items = getSelectedRootItems();\n return items.length > 1;\n};\n\nconst shouldShowUngroup = function () {\n const items = getSelectedRootItems();\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (isGroup(item) && !item.data.isPGTextItem && item.children && item.children.length > 0) {\n return true;\n }\n }\n return false;\n};\n\nexport {\n groupSelection,\n ungroupSelection,\n groupItems,\n ungroupItems,\n getItemsGroup,\n isGroup,\n isGroupChild,\n shouldShowGroup,\n shouldShowUngroup\n};\n","const isCompoundPath = function (item) {\n return item && item.className === 'CompoundPath';\n};\n\nconst isCompoundPathChild = function (item) {\n if (item.parent) {\n return item.parent.className === 'CompoundPath';\n }\n return false;\n};\n\n\nconst getItemsCompoundPath = function (item) {\n const itemParent = item.parent;\n\n if (isCompoundPath(itemParent)) {\n return itemParent;\n }\n return null;\n \n};\n\nexport {\n isCompoundPath,\n isCompoundPathChild,\n getItemsCompoundPath\n};\n","import paper from '@scratch/paper';\n\n/** The ratio of the curve length to use for the handle length to convert squares into approximately circles. */\nconst HANDLE_RATIO = 0.3902628565;\n\nconst checkPointsClose = function (startPos, eventPoint, threshold) {\n const xOff = Math.abs(startPos.x - eventPoint.x);\n const yOff = Math.abs(startPos.y - eventPoint.y);\n if (xOff < threshold && yOff < threshold) {\n return true;\n }\n return false;\n};\n\nconst getRandomInt = function (min, max) {\n return Math.floor(Math.random() * (max - min)) + min;\n};\n\nconst getRandomBoolean = function () {\n return getRandomInt(0, 2) === 1;\n};\n\n// Thanks Mikko Mononen! https://github.com/memononen/stylii\nconst snapDeltaToAngle = function (delta, snapAngle) {\n let angle = Math.atan2(delta.y, delta.x);\n angle = Math.round(angle / snapAngle) * snapAngle;\n const dirx = Math.cos(angle);\n const diry = Math.sin(angle);\n const d = (dirx * delta.x) + (diry * delta.y);\n return new paper.Point(dirx * d, diry * d);\n};\n\nconst _getDepth = function (item) {\n let temp = item;\n let depth = 0;\n while (!(temp instanceof paper.Layer)) {\n depth++;\n if (temp.parent === null) {\n // This item isn't attached to a layer, so it's not on the canvas and can't be compared.\n return null;\n }\n temp = temp.parent;\n }\n return depth;\n};\n\nconst sortItemsByZIndex = function (a, b) {\n if (a === null || b === null) {\n return null;\n }\n\n // Get to the same depth in the project tree\n let tempA = a;\n let tempB = b;\n let aDepth = _getDepth(a);\n let bDepth = _getDepth(b);\n while (bDepth > aDepth) {\n tempB = tempB.parent;\n bDepth--;\n }\n while (aDepth > bDepth) {\n tempA = tempA.parent;\n aDepth--;\n }\n\n // Step up until they share parents. When they share parents, compare indices.\n while (tempA && tempB) {\n if (tempB === tempA) {\n return 0;\n } else if (tempB.parent === tempA.parent) {\n if (tempB.parent instanceof paper.CompoundPath) {\n // Neither is on top of the other in a compound path. Return in order of decreasing size.\n return Math.abs(tempB.area) - Math.abs(tempA.area);\n }\n return parseFloat(tempA.index) - parseFloat(tempB.index);\n }\n tempB = tempB.parent;\n tempA = tempA.parent;\n }\n\n // No shared hierarchy\n return null;\n};\n\n// Expand the size of the path by amount all around\nconst expandBy = function (path, amount) {\n const center = path.position;\n let pathArea = path.area;\n for (const seg of path.segments) {\n const delta = seg.point.subtract(center)\n .normalize()\n .multiply(amount);\n seg.point = seg.point.add(delta);\n // If that made the path area smaller, go the other way.\n if (path.area < pathArea) seg.point = seg.point.subtract(delta.multiply(2));\n pathArea = path.area;\n }\n};\n\n// Do for all nested items in groups\nconst _doRecursively = function (item, func) {\n if (item instanceof paper.Group) {\n for (const child of item.children) {\n _doRecursively(child, func);\n }\n } else {\n func(item);\n }\n};\n\n// Make item clockwise. Drill down into groups.\nconst ensureClockwise = function (root) {\n _doRecursively(root, item => {\n if (item instanceof paper.PathItem) {\n item.clockwise = true;\n }\n });\n};\n\n// Scale item and its strokes by factor\nconst scaleWithStrokes = function (root, factor, pivot) {\n _doRecursively(root, item => {\n if (item instanceof paper.PointText) {\n // Text outline size is controlled by text transform matrix, thus it's already scaled.\n return;\n }\n if (item.strokeWidth) {\n item.strokeWidth = item.strokeWidth * factor;\n }\n });\n root.scale(factor, pivot);\n};\n\n/**\n * Get the size and position of a square, as in if the user were holding the shift key down while drawing the shape,\n * from the point where the drag started and the point where the mouse is currently positioned. (Note: This also works\n * for shapes like circles (\"square ovals\"), which fill the same dimensions.)\n * @param {!paper.Point} startPos The point where the user started dragging\n * @param {!paper.Point} eventPoint The point where the user has currently dragged to\n * @returns {object} Information about the size and position of how the square should be drawn\n */\nconst getSquareDimensions = function (startPos, eventPoint) {\n // These variables are used for determining the relative quadrant that the shape will appear in.\n // So if you drag up and right, it'll show up above and to the right of where you started dragging, etc.\n let offsetX = eventPoint.x - startPos.x;\n let offsetY = eventPoint.y - startPos.y;\n\n // If the offset variables are zero, the shape ends up having zero width or height, which is bad.\n // Deal with this by forcing them to be non-zero (we arbitrarily choose 1; any non-zero value would work).\n offsetX = offsetX ? offsetX : 1;\n offsetY = offsetY ? offsetY : 1;\n\n // The length of the shape is the greater of the X and Y offsets.\n const offsetDistance = eventPoint.subtract(startPos).abs();\n const length = Math.max(offsetDistance.x, offsetDistance.y);\n\n const size = new paper.Point(\n length * offsetX / Math.abs(offsetX),\n length * offsetY / Math.abs(offsetY)\n );\n\n const position = startPos.add(size.multiply(0.5));\n\n return {size, position};\n};\n\nexport {\n HANDLE_RATIO,\n checkPointsClose,\n ensureClockwise,\n expandBy,\n getRandomInt,\n getRandomBoolean,\n getSquareDimensions,\n scaleWithStrokes,\n snapDeltaToAngle,\n sortItemsByZIndex\n};\n","import paper from '@scratch/paper';\nimport Modes from '../lib/modes';\n\nimport {getItemsGroup, isGroup} from './group';\nimport {getRootItem, isCompoundPathItem, isBoundsItem, isPathItem, isPGTextItem} from './item';\nimport {getItemsCompoundPath, isCompoundPath, isCompoundPathChild} from './compound-path';\nimport {sortItemsByZIndex} from './math';\n\n/**\n * Wrapper for paper.project.getItems that excludes our helper items\n * @param {?object} options See paper.js docs for paper.Item.getItems\n * @returns {Array<paper.Item>} items that match options\n */\nconst getItems = function (options) {\n const newMatcher = function (item) {\n return !(item instanceof paper.Layer) &&\n item.layer.data && item.layer.data.isPaintingLayer &&\n !item.locked &&\n !item.isClipMask() &&\n !(item.data && item.data.isHelperItem) &&\n (!options.match || options.match(item));\n };\n const newOptions = {...options, match: newMatcher};\n return paper.project.getItems(newOptions);\n};\n\n/**\n * @param {boolean} includeGuides True if guide layer items like the bounding box should\n * be included in the returned items.\n * @returns {Array<paper.item>} all top-level (direct descendants of a paper.Layer) items\n */\nconst getAllRootItems = function (includeGuides) {\n includeGuides = includeGuides || false;\n const allItems = [];\n for (const layer of paper.project.layers) {\n for (const child of layer.children) {\n // don't give guides back\n if (!includeGuides && child.guide) {\n continue;\n }\n allItems.push(child);\n }\n }\n return allItems;\n};\n\n/**\n * @returns {Array<paper.item>} all top-level (direct descendants of a paper.Layer) items\n * that aren't guide items or helper items.\n */\nconst getAllSelectableRootItems = function () {\n const allItems = getAllRootItems();\n const selectables = [];\n for (let i = 0; i < allItems.length; i++) {\n if (allItems[i].data && !allItems[i].data.isHelperItem) {\n selectables.push(allItems[i]);\n }\n }\n return selectables;\n};\n\nconst selectItemSegments = function (item, state) {\n if (item.children) {\n for (let i = 0; i < item.children.length; i++) {\n const child = item.children[i];\n if (child.children && child.children.length > 0) {\n selectItemSegments(child, state);\n } else {\n child.fullySelected = state;\n }\n }\n } else {\n for (let i = 0; i < item.segments.length; i++) {\n item.segments[i].selected = state;\n }\n }\n};\n\nconst _setGroupSelection = function (root, selected, fullySelected) {\n root.fullySelected = fullySelected;\n root.selected = selected;\n // select children of compound-path or group\n if (isCompoundPath(root) || isGroup(root)) {\n const children = root.children;\n if (children) {\n for (const child of children) {\n if (isGroup(child)) {\n _setGroupSelection(child, selected, fullySelected);\n } else {\n child.fullySelected = fullySelected;\n child.selected = selected;\n }\n }\n }\n }\n};\n\nconst setItemSelection = function (item, state, fullySelected) {\n const parentGroup = getItemsGroup(item);\n const itemsCompoundPath = getItemsCompoundPath(item);\n\n // if selection is in a group, select group\n if (parentGroup) {\n // do it recursive\n setItemSelection(parentGroup, state, fullySelected);\n } else if (itemsCompoundPath) {\n _setGroupSelection(itemsCompoundPath, state, fullySelected);\n } else {\n if (item.data && item.data.noSelect) {\n return;\n }\n _setGroupSelection(item, state, fullySelected);\n }\n\n};\n\n/** @returns {boolean} true if anything was selected */\nconst selectAllItems = function () {\n const items = getAllSelectableRootItems();\n if (items.length === 0) return false;\n\n for (let i = 0; i < items.length; i++) {\n setItemSelection(items[i], true);\n }\n return true;\n};\n\n/** @returns {boolean} true if anything was selected */\nconst selectAllSegments = function () {\n const items = getAllSelectableRootItems();\n if (items.length === 0) return false;\n\n for (let i = 0; i < items.length; i++) {\n selectItemSegments(items[i], true);\n }\n return true;\n};\n\n/** @param {!Function} dispatchClearSelect Function to update the Redux select state */\nconst clearSelection = function (dispatchClearSelect) {\n paper.project.deselectAll();\n dispatchClearSelect();\n};\n\n/**\n * This gets all selected non-grouped items and groups\n * (alternative to paper.project.selectedItems, which includes\n * group children in addition to the group)\n * @returns {Array<paper.Item>} in increasing Z order.\n */\nconst getSelectedRootItems = function () {\n const allItems = getAllSelectableRootItems();\n const items = [];\n\n for (const item of allItems) {\n if (item.selected) {\n items.push(item);\n } else if (item instanceof paper.CompoundPath) {\n // Consider a compound path selected if any of its paths are selected\n for (const child of item.children) {\n if (child.selected) {\n items.push(item);\n break;\n }\n }\n }\n }\n\n // sort items by index (0 at bottom)\n items.sort((a, b) => parseFloat(a.index) - parseFloat(b.index));\n return items;\n};\n\n/**\n * This gets all selected items that are as deeply nested as possible. Does not\n * return the parent groups.\n * @returns {Array<paper.Item>} in increasing Z order.\n */\nconst getSelectedLeafItems = function () {\n const allItems = paper.project.selectedItems;\n const items = [];\n\n for (let i = 0; i < allItems.length; i++) {\n const item = allItems[i];\n if (!(item instanceof paper.Layer) && !isGroup(item) && item.data && !item.data.isSelectionBound) {\n items.push(item);\n }\n }\n items.sort(sortItemsByZIndex);\n return items;\n};\n\n/**\n * This gets all selected path segments.\n * @returns {Array<paper.Segment>} selected segments\n */\nconst getSelectedSegments = function () {\n const selected = getSelectedLeafItems();\n const segments = [];\n for (const item of selected) {\n if (!item.segments) {\n continue;\n }\n for (const seg of item.segments) {\n if (seg.selected) {\n segments.push(seg);\n }\n }\n }\n return segments;\n};\n\nconst _deleteItemSelection = function (items, onUpdateImage) {\n // @todo: Update toolbar state on change\n if (items.length === 0) {\n return false;\n }\n for (let i = 0; i < items.length; i++) {\n items[i].remove();\n }\n onUpdateImage();\n return true;\n};\n\n// Return true if anything was removed\nconst _removeSelectedSegments = function (items, onUpdateImage) {\n const segmentsToRemove = [];\n\n for (let i = 0; i < items.length; i++) {\n if (!items[i].segments) continue;\n const segments = items[i].segments;\n for (let j = 0; j < segments.length; j++) {\n const seg = segments[j];\n if (seg.selected) {\n segmentsToRemove.push(seg);\n }\n }\n }\n\n let removedSegments = false;\n for (let i = 0; i < segmentsToRemove.length; i++) {\n const seg = segmentsToRemove[i];\n seg.remove();\n removedSegments = true;\n }\n if (removedSegments) {\n onUpdateImage();\n }\n return removedSegments;\n};\n\n// Return whether anything was deleted\nconst deleteSelection = function (mode, onUpdateImage) {\n if (mode === Modes.RESHAPE) {\n const selectedItems = getSelectedLeafItems();\n // If there are points selected remove them. If not delete the item selected.\n if (_removeSelectedSegments(selectedItems, onUpdateImage)) {\n return true;\n }\n return _deleteItemSelection(selectedItems, onUpdateImage);\n }\n const selectedItems = getSelectedRootItems();\n return _deleteItemSelection(selectedItems, onUpdateImage);\n};\n\nconst cloneSelection = function (recursive, onUpdateImage) {\n const selectedItems = recursive ? getSelectedLeafItems() : getSelectedRootItems();\n for (let i = 0; i < selectedItems.length; i++) {\n const item = selectedItems[i];\n item.clone();\n item.selected = false;\n }\n onUpdateImage();\n};\n\nconst _checkBoundsItem = function (selectionRect, item, event) {\n const itemBounds = new paper.Path([\n item.localToGlobal(item.internalBounds.topLeft),\n item.localToGlobal(item.internalBounds.topRight),\n item.localToGlobal(item.internalBounds.bottomRight),\n item.localToGlobal(item.internalBounds.bottomLeft)\n ]);\n itemBounds.closed = true;\n itemBounds.guide = true;\n\n for (let i = 0; i < itemBounds.segments.length; i++) {\n const seg = itemBounds.segments[i];\n if (selectionRect.contains(seg.point) ||\n (i === 0 && selectionRect.getIntersections(itemBounds).length > 0)) {\n if (event.modifiers.shift && item.selected) {\n setItemSelection(item, false);\n\n } else {\n setItemSelection(item, true);\n }\n itemBounds.remove();\n return true;\n\n }\n }\n\n itemBounds.remove();\n};\n\nconst _handleRectangularSelectionItems = function (item, event, rect, mode, root) {\n if (isPathItem(item)) {\n let segmentMode = false;\n\n // first round checks for segments inside the selectionRect\n for (let j = 0; j < item.segments.length; j++) {\n const seg = item.segments[j];\n if (rect.contains(seg.point)) {\n if (mode === Modes.RESHAPE) {\n if (event.modifiers.shift && seg.selected) {\n seg.selected = false;\n } else {\n seg.selected = true;\n }\n segmentMode = true;\n } else {\n if (event.modifiers.shift && item.selected) {\n setItemSelection(root, false);\n } else {\n setItemSelection(root, true, true /* fullySelected */);\n }\n return false;\n }\n }\n }\n\n // second round checks for path intersections\n const intersections = item.getIntersections(rect);\n if (intersections.length > 0 && !segmentMode) {\n // if in reshape mode, select the curves that intersect\n // with the selectionRect\n if (mode === Modes.RESHAPE) {\n for (let k = 0; k < intersections.length; k++) {\n const curve = intersections[k].curve;\n // intersections contains every curve twice because\n // the selectionRect intersects a circle always at\n // two points. so we skip every other curve\n if (k % 2 === 1) {\n continue;\n }\n\n if (event.modifiers.shift) {\n curve.selected = !curve.selected;\n } else {\n curve.selected = true;\n }\n }\n } else {\n if (event.modifiers.shift && item.selected) {\n setItemSelection(item, false);\n\n } else {\n setItemSelection(item, true);\n }\n return false;\n }\n }\n // @todo: Update toolbar state on change\n\n } else if (isBoundsItem(item)) {\n if (_checkBoundsItem(rect, item, event)) {\n return false;\n }\n }\n return true;\n};\n\n// if the rectangular selection found a group, drill into it recursively\nconst _rectangularSelectionGroupLoop = function (group, rect, root, event, mode) {\n for (let i = 0; i < group.children.length; i++) {\n const child = group.children[i];\n\n if (isGroup(child) || isCompoundPathItem(child)) {\n _rectangularSelectionGroupLoop(child, rect, root, event, mode);\n } else {\n _handleRectangularSelectionItems(child, event, rect, mode, root);\n }\n }\n return true;\n};\n\n/**\n * Called after drawing a selection rectangle in a select mode. In reshape mode, this\n * selects all control points and curves within the rectangle. In select mode, this\n * selects all items and groups that intersect the rectangle\n * @param {!MouseEvent} event The mouse event to draw the rectangle\n * @param {!paper.Rect} rect The selection rectangle\n * @param {Modes} mode The mode of the paint editor when drawing the rectangle\n */\nconst processRectangularSelection = function (event, rect, mode) {\n const allItems = getAllSelectableRootItems();\n\n for (let i = 0; i < allItems.length; i++) {\n const item = allItems[i];\n if (mode === Modes.RESHAPE && isPGTextItem(getRootItem(item))) {\n continue;\n }\n if (isGroup(item) || isCompoundPathItem(item)) {\n // check for item segment points inside\n _rectangularSelectionGroupLoop(item, rect, item, event, mode);\n } else {\n _handleRectangularSelectionItems(item, event, rect, mode, item);\n }\n }\n};\n\n/**\n * When switching to the select tool while having a child object of a\n * compound path selected, deselect the child and select the compound path\n * instead. (otherwise the compound path breaks because of scale-grouping)\n */\nconst selectRootItem = function () {\n const items = getSelectedLeafItems();\n for (const item of items) {\n if (isCompoundPathChild(item)) {\n const cp = getItemsCompoundPath(item);\n setItemSelection(cp, true, true /* fullySelected */);\n }\n const rootItem = getRootItem(item);\n if (item !== rootItem) {\n setItemSelection(rootItem, true, true /* fullySelected */);\n }\n }\n};\n\nexport {\n getItems,\n getAllRootItems,\n getAllSelectableRootItems,\n selectAllItems,\n selectAllSegments,\n clearSelection,\n deleteSelection,\n cloneSelection,\n setItemSelection,\n getSelectedLeafItems,\n getSelectedRootItems,\n getSelectedSegments,\n processRectangularSelection,\n selectRootItem\n};\n","import paper from '@scratch/paper';\nimport {getGuideLayer} from './layer';\nimport {getAllRootItems} from './selection';\n\nconst GUIDE_BLUE = '#009dec';\nconst GUIDE_GREY = '#aaaaaa';\n\nconst setDefaultGuideStyle = function (item) {\n item.strokeWidth = 1 / paper.view.zoom;\n item.opacity = 1;\n item.blendMode = 'normal';\n item.guide = true;\n};\n\nconst hoverItem = function (item) {\n const segments = item.segments;\n const clone = new paper.Path(segments);\n setDefaultGuideStyle(clone);\n if (item.closed) {\n clone.closed = true;\n }\n clone.parent = getGuideLayer();\n clone.position = item.position;\n clone.strokeColor = GUIDE_BLUE;\n clone.fillColor = null;\n clone.data.isHelperItem = true;\n clone.data.origItem = item;\n clone.bringToFront();\n\n return clone;\n};\n\nconst hoverBounds = function (item, expandBy) {\n let bounds = item.internalBounds;\n if (expandBy) {\n bounds = bounds.expand(expandBy);\n }\n const rect = new paper.Path.Rectangle(bounds);\n rect.matrix = item.matrix;\n setDefaultGuideStyle(rect);\n rect.parent = getGuideLayer();\n rect.strokeColor = GUIDE_BLUE;\n rect.fillColor = null;\n rect.data.isHelperItem = true;\n rect.data.origItem = item;\n rect.bringToFront();\n\n return rect;\n};\n\nconst rectSelect = function (event, color) {\n const half = new paper.Point(0.5 / paper.view.zoom, 0.5 / paper.view.zoom);\n const start = event.downPoint.add(half);\n const end = event.point.add(half);\n const rect = new paper.Path.Rectangle(start, end);\n const zoom = 1.0 / paper.view.zoom;\n setDefaultGuideStyle(rect);\n if (!color) color = GUIDE_GREY;\n rect.parent = getGuideLayer();\n rect.strokeColor = color;\n rect.data.isRectSelect = true;\n rect.data.isHelperItem = true;\n rect.dashArray = [3.0 * zoom, 3.0 * zoom];\n return rect;\n};\n\nconst getGuideColor = function () {\n return GUIDE_BLUE;\n};\n\nconst _removePaperItemsByDataTags = function (tags) {\n const allItems = getAllRootItems(true);\n for (const item of allItems) {\n for (const tag of tags) {\n if (item.data && item.data[tag]) {\n item.remove();\n }\n }\n }\n};\n\nconst _removePaperItemsByTags = function (tags) {\n const allItems = getAllRootItems(true);\n for (const item of allItems) {\n for (const tag of tags) {\n if (item[tag]) {\n item.remove();\n }\n }\n }\n};\n\nconst removeBoundsPath = function () {\n _removePaperItemsByDataTags(['isSelectionBound', 'isRotHandle', 'isScaleHandle']);\n};\n\nconst removeBoundsHandles = function () {\n _removePaperItemsByDataTags(['isRotHandle', 'isScaleHandle']);\n};\n\nconst removeAllGuides = function () {\n _removePaperItemsByTags(['guide']);\n};\n\nconst removeHitPoint = function () {\n _removePaperItemsByDataTags(['isHitPoint']);\n};\n\nconst drawHitPoint = function (point) {\n removeHitPoint();\n if (point) {\n const hitPoint = paper.Path.Circle(point, 4 / paper.view.zoom /* radius */);\n hitPoint.strokeWidth = 1 / paper.view.zoom;\n hitPoint.strokeColor = GUIDE_BLUE;\n hitPoint.fillColor = new paper.Color(1, 1, 1, 0.5);\n hitPoint.parent = getGuideLayer();\n hitPoint.data.isHitPoint = true;\n hitPoint.data.isHelperItem = true;\n }\n};\n\nexport {\n hoverItem,\n hoverBounds,\n rectSelect,\n removeAllGuides,\n removeBoundsHandles,\n removeBoundsPath,\n drawHitPoint,\n removeHitPoint,\n getGuideColor,\n setDefaultGuideStyle\n};\n","import paper from '@scratch/paper';\nimport {createCanvas, clearRaster, getRaster, hideGuideLayers, showGuideLayers} from './layer';\nimport {getGuideColor} from './guides';\nimport {clearSelection} from './selection';\nimport {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, CENTER, MAX_WORKSPACE_BOUNDS} from './view';\nimport Formats from '../lib/format';\nimport log from '../log/log';\n\nconst forEachLinePoint = function (point1, point2, callback) {\n // Bresenham line algorithm\n let x1 = ~~point1.x;\n const x2 = ~~point2.x;\n let y1 = ~~point1.y;\n const y2 = ~~point2.y;\n\n const dx = Math.abs(x2 - x1);\n const dy = Math.abs(y2 - y1);\n const sx = (x1 < x2) ? 1 : -1;\n const sy = (y1 < y2) ? 1 : -1;\n let err = dx - dy;\n\n callback(x1, y1);\n while (x1 !== x2 || y1 !== y2) {\n const e2 = err * 2;\n if (e2 > -dy) {\n err -= dy;\n x1 += sx;\n }\n if (e2 < dx) {\n err += dx;\n y1 += sy;\n }\n callback(x1, y1);\n }\n};\n\n/**\n * @param {!number} a Coefficient in ax^2 + bx + c = 0\n * @param {!number} b Coefficient in ax^2 + bx + c = 0\n * @param {!number} c Coefficient in ax^2 + bx + c = 0\n * @returns {Array<number>} Array of 2 solutions, with the larger solution first\n */\nconst solveQuadratic_ = function (a, b, c) {\n const soln1 = (-b + Math.sqrt((b * b) - (4 * a * c))) / 2 / a;\n const soln2 = (-b - Math.sqrt((b * b) - (4 * a * c))) / 2 / a;\n return soln1 > soln2 ? [soln1, soln2] : [soln2, soln1];\n};\n\n/**\n * @param {!object} options drawing options\n * @param {!number} options.centerX center of ellipse, x\n * @param {!number} options.centerY center of ellipse, y\n * @param {!number} options.radiusX major radius of ellipse\n * @param {!number} options.radiusY minor radius of ellipse\n * @param {!number} options.shearSlope slope of the sheared x axis\n * @param {?boolean} options.isFilled true if isFilled\n * @param {?Function} options.drawFn The function called on each point in the outline, used only\n * if isFilled is false.\n * @param {!CanvasRenderingContext2D} context for drawing\n * @returns {boolean} true if anything was drawn, false if not\n */\nconst drawShearedEllipse_ = function (options, context) {\n const centerX = ~~options.centerX;\n const centerY = ~~options.centerY;\n const radiusX = ~~Math.abs(options.radiusX) - .5;\n const radiusY = ~~Math.abs(options.radiusY) - .5;\n const shearSlope = options.shearSlope;\n const isFilled = options.isFilled;\n const drawFn = options.drawFn;\n if (shearSlope === Infinity || radiusX < 1 || radiusY < 1) {\n return false;\n }\n // A, B, and C represent Ax^2 + Bxy + Cy^2 = 1 coefficients in a skewed ellipse formula\n const A = (1 / radiusX / radiusX) + (shearSlope * shearSlope / radiusY / radiusY);\n const B = -2 * shearSlope / radiusY / radiusY;\n const C = 1 / radiusY / radiusY;\n // Line with slope1 intersects the ellipse where its derivative is 1\n const slope1 = ((-2 * A) - B) / ((2 * C) + B);\n // Line with slope2 intersects the ellipse where its derivative is -1\n const slope2 = (-(2 * A) + B) / (-(2 * C) + B);\n const verticalStepsFirst = slope1 > slope2;\n\n /**\n * Vertical stepping portion of ellipse drawing algorithm\n * @param {!number} startY y to start drawing from\n * @param {!Function} conditionFn function which should become true when we should stop stepping\n * @returns {object} last point drawn to the canvas, or null if no points drawn\n */\n const drawEllipseStepVertical_ = function (startY, conditionFn) {\n // Points on the ellipse\n let y = startY;\n let x = solveQuadratic_(A, B * y, (C * y * y) - 1);\n // last pixel position at which a draw was performed\n let pY;\n let pX1;\n let pX2;\n while (conditionFn(x[0], y)) {\n pY = Math.floor(y);\n pX1 = Math.floor(x[0]);\n pX2 = Math.floor(x[1]);\n if (isFilled) {\n context.fillRect(centerX - pX1 - 1, centerY + pY, pX1 - pX2 + 1, 1);\n context.fillRect(centerX + pX2, centerY - pY - 1, pX1 - pX2 + 1, 1);\n } else {\n drawFn(centerX - pX1 - 1, centerY + pY);\n drawFn(centerX + pX1, centerY - pY - 1);\n }\n y--;\n x = solveQuadratic_(A, B * y, (C * y * y) - 1);\n }\n return pX1 || pY ? {x: pX1, y: pY} : null;\n };\n\n /**\n * Horizontal stepping portion of ellipse drawing algorithm\n * @param {!number} startX x to start drawing from\n * @param {!Function} conditionFn function which should become false when we should stop stepping\n * @returns {object} last point drawn to the canvas, or null if no points drawn\n */\n const drawEllipseStepHorizontal_ = function (startX, conditionFn) {\n // Points on the ellipse\n let x = startX;\n let y = solveQuadratic_(C, B * x, (A * x * x) - 1);\n // last pixel position at which a draw was performed\n let pX;\n let pY1;\n let pY2;\n while (conditionFn(x, y[0])) {\n pX = Math.floor(x);\n pY1 = Math.floor(y[0]);\n pY2 = Math.floor(y[1]);\n if (isFilled) {\n context.fillRect(centerX - pX - 1, centerY + pY2, 1, pY1 - pY2 + 1);\n context.fillRect(centerX + pX, centerY - pY1 - 1, 1, pY1 - pY2 + 1);\n } else {\n drawFn(centerX - pX - 1, centerY + pY1);\n drawFn(centerX + pX, centerY - pY1 - 1);\n }\n x++;\n y = solveQuadratic_(C, B * x, (A * x * x) - 1);\n }\n return pX || pY1 ? {x: pX, y: pY1} : null;\n };\n\n // Last point drawn\n let lastPoint;\n if (verticalStepsFirst) {\n let forwardLeaning = false;\n if (slope1 > 0) forwardLeaning = true;\n\n // step vertically\n lastPoint = drawEllipseStepVertical_(\n forwardLeaning ? -radiusY : radiusY,\n (x, y) => {\n if (x === 0 && y > 0) return true;\n if (x === 0 && y < 0) return false;\n return y / x > slope1;\n }\n );\n // step horizontally while slope is flat\n lastPoint = drawEllipseStepHorizontal_(\n lastPoint ? -lastPoint.x + .5 : .5,\n (x, y) => y / x > slope2\n ) || {x: -lastPoint.x - .5, y: -lastPoint.y - .5};\n // step vertically until back to start\n drawEllipseStepVertical_(\n lastPoint.y - .5,\n (x, y) => {\n if (forwardLeaning) return y > -radiusY;\n return y > radiusY;\n }\n );\n } else {\n // step horizontally forward\n lastPoint = drawEllipseStepHorizontal_(\n .5,\n (x, y) => y / x > slope2\n );\n // step vertically while slope is steep\n lastPoint = drawEllipseStepVertical_(\n lastPoint ? lastPoint.y - .5 : radiusY,\n (x, y) => {\n if (x === 0 && y > 0) return true;\n if (x === 0 && y < 0) return false;\n return y / x > slope1;\n }\n ) || lastPoint;\n // step horizontally until back to start\n drawEllipseStepHorizontal_(\n -lastPoint.x + .5,\n x => x < 0\n );\n }\n return true;\n};\n\n/**\n * @param {!number} size The diameter of the brush\n * @param {!string} color The css color of the brush\n * @param {?boolean} isEraser True if we want the brush mark for the eraser\n * @returns {HTMLCanvasElement} a canvas with the brush mark printed on it\n */\nconst getBrushMark = function (size, color, isEraser) {\n size = ~~size;\n const canvas = document.createElement('canvas');\n const roundedUpRadius = Math.ceil(size / 2);\n canvas.width = roundedUpRadius * 2;\n canvas.height = roundedUpRadius * 2;\n const context = canvas.getContext('2d');\n context.imageSmoothingEnabled = false;\n context.fillStyle = isEraser ? 'white' : color;\n // Small squares for pixel artists\n if (size <= 5) {\n let offset = 0;\n if (size % 2) offset = 1;\n if (isEraser) {\n context.fillStyle = getGuideColor();\n context.fillRect(offset, offset, size, size);\n context.fillStyle = 'white';\n context.fillRect(offset + 1, offset + 1, size - 2, size - 2);\n } else {\n context.fillRect(offset, offset, size, size);\n }\n } else {\n drawShearedEllipse_({\n centerX: size / 2,\n centerY: size / 2,\n radiusX: size / 2,\n radiusY: size / 2,\n shearSlope: 0,\n isFilled: true\n }, context);\n if (isEraser) {\n // Add outline\n context.fillStyle = getGuideColor();\n drawShearedEllipse_({\n centerX: size / 2,\n centerY: size / 2,\n radiusX: size / 2,\n radiusY: size / 2,\n shearSlope: 0,\n isFilled: false,\n drawFn: (x, y) => context.fillRect(x, y, 1, 1)\n }, context);\n }\n }\n return canvas;\n};\n\n/**\n * Draw an ellipse, given the original axis-aligned radii and\n * an affine transformation. Returns false if the ellipse could\n * not be drawn; for instance, the matrix is non-invertible.\n * @param {!options} options Parameters for the ellipse\n * @param {!paper.Point} options.position Center of ellipse\n * @param {!number} options.radiusX x-aligned radius of ellipse\n * @param {!number} options.radiusY y-aligned radius of ellipse\n * @param {!paper.Matrix} options.matrix affine transformation matrix\n * @param {?boolean} options.isFilled true if isFilled\n * @param {?number} options.thickness Thickness of outline, used only if isFilled is false.\n * @param {!CanvasRenderingContext2D} context for drawing\n * @returns {boolean} true if anything was drawn, false if not\n */\nconst drawEllipse = function (options, context) {\n const positionX = options.position.x;\n const positionY = options.position.y;\n const radiusX = options.radiusX;\n const radiusY = options.radiusY;\n const matrix = options.matrix;\n const isFilled = options.isFilled;\n const thickness = options.thickness;\n let drawFn = null;\n\n if (!matrix.isInvertible()) return false;\n const inverse = matrix.clone().invert();\n\n const isGradient = context.fillStyle instanceof CanvasGradient;\n\n // If drawing a gradient, we need to draw the shape onto a temporary canvas, then draw the gradient atop that canvas\n // only where the shape appears. drawShearedEllipse draws some pixels twice, which would be a problem if the\n // gradient fades to transparent as those pixels would end up looking more opaque. Instead, mask in the gradient.\n // https://github.com/LLK/scratch-paint/issues/1152\n // Outlines are drawn as a series of brush mark images and as such can't be drawn as gradients in the first place.\n let origContext;\n let tmpCanvas;\n const {width: canvasWidth, height: canvasHeight} = context.canvas;\n if (isGradient) {\n tmpCanvas = createCanvas(canvasWidth, canvasHeight);\n origContext = context;\n context = tmpCanvas.getContext('2d');\n }\n\n if (!isFilled) {\n const brushMark = getBrushMark(thickness, isGradient ? 'black' : context.fillStyle);\n const roundedUpRadius = Math.ceil(thickness / 2);\n drawFn = (x, y) => {\n context.drawImage(brushMark, ~~x - roundedUpRadius, ~~y - roundedUpRadius);\n };\n }\n\n // Calculate the ellipse formula\n // A, B, and C represent Ax^2 + Bxy + Cy^2 = 1 coefficients in a transformed ellipse formula\n const A = (inverse.a * inverse.a / radiusX / radiusX) + (inverse.b * inverse.b / radiusY / radiusY);\n const B = (2 * inverse.a * inverse.c / radiusX / radiusX) + (2 * inverse.b * inverse.d / radiusY / radiusY);\n const C = (inverse.c * inverse.c / radiusX / radiusX) + (inverse.d * inverse.d / radiusY / radiusY);\n\n // Convert to a sheared ellipse formula. All ellipses are equivalent to some sheared axis-aligned ellipse.\n // radiusA, radiusB, and slope are parameters of a skewed ellipse with the above formula\n const radiusB = 1 / Math.sqrt(C);\n const radiusA = Math.sqrt(-4 * C / ((B * B) - (4 * A * C)));\n const slope = B / 2 / C;\n\n const wasDrawn = drawShearedEllipse_({\n centerX: positionX,\n centerY: positionY,\n radiusX: radiusA,\n radiusY: radiusB,\n shearSlope: slope,\n isFilled: isFilled,\n drawFn: drawFn\n }, context);\n\n // Mask in the gradient only where the shape was drawn, and draw it. Then draw the gradientified shape onto the\n // original canvas normally.\n if (isGradient && wasDrawn) {\n context.globalCompositeOperation = 'source-in';\n context.fillStyle = origContext.fillStyle;\n context.fillRect(0, 0, canvasWidth, canvasHeight);\n origContext.drawImage(tmpCanvas, 0, 0);\n }\n\n return wasDrawn;\n};\n\nconst rowBlank_ = function (imageData, width, y) {\n for (let x = 0; x < width; ++x) {\n if (imageData.data[(y * width << 2) + (x << 2) + 3] !== 0) return false;\n }\n return true;\n};\n\nconst columnBlank_ = function (imageData, width, x, top, bottom) {\n for (let y = top; y < bottom; ++y) {\n if (imageData.data[(y * width << 2) + (x << 2) + 3] !== 0) return false;\n }\n return true;\n};\n\n/**\n * Get bounds around the contents of a raster, trimming transparent pixels from edges.\n * Adapted from Tim Down's https://gist.github.com/timdown/021d9c8f2aabc7092df564996f5afbbf\n * @param {paper.Raster} raster The raster to get the bounds around\n * @param {paper.Rectangle} [rect] Optionally, an alternative bounding rectangle to limit the check to.\n * @returns {paper.Rectangle} The bounds around the opaque area of the passed raster\n * (or opaque within the passed rectangle)\n */\nconst getHitBounds = function (raster, rect) {\n const bounds = rect || raster.bounds;\n const width = bounds.width;\n const imageData = raster.getImageData(bounds);\n let top = 0;\n let bottom = imageData.height;\n let left = 0;\n let right = imageData.width;\n\n while (top < bottom && rowBlank_(imageData, width, top)) ++top;\n while (bottom - 1 > top && rowBlank_(imageData, width, bottom - 1)) --bottom;\n while (left < right && columnBlank_(imageData, width, left, top, bottom)) ++left;\n while (right - 1 > left && columnBlank_(imageData, width, right - 1, top, bottom)) --right;\n\n // Center an empty bitmap\n if (top === bottom) {\n top = bottom = imageData.height / 2;\n }\n if (left === right) {\n left = right = imageData.width / 2;\n }\n\n return new paper.Rectangle(left + bounds.left, top + bounds.top, right - left, bottom - top);\n};\n\nconst trim_ = function (raster) {\n const hitBounds = getHitBounds(raster);\n if (hitBounds.width && hitBounds.height) {\n return raster.getSubRaster(getHitBounds(raster));\n }\n return null;\n};\n\n/**\n * @param {boolean} shouldInsert True if the trimmed raster should be added to the active layer.\n * @returns {paper.Raster} raster layer with whitespace trimmed from ends, or null if there is\n * nothing on the raster layer.\n */\nconst getTrimmedRaster = function (shouldInsert) {\n const trimmedRaster = trim_(getRaster());\n if (!trimmedRaster) return null;\n if (shouldInsert) {\n paper.project.activeLayer.addChild(trimmedRaster);\n } else {\n trimmedRaster.remove();\n }\n return trimmedRaster;\n};\n\nconst convertToBitmap = function (clearSelectedItems, onUpdateImage, optFontInlineFn) {\n // @todo if the active layer contains only rasters, drawing them directly to the raster layer\n // would be more efficient.\n\n clearSelection(clearSelectedItems);\n\n // Export svg\n const guideLayers = hideGuideLayers(true /* includeRaster */);\n const bounds = paper.project.activeLayer.drawnBounds;\n const svg = paper.project.exportSVG({\n bounds: 'content',\n matrix: new paper.Matrix().translate(-bounds.x, -bounds.y)\n });\n showGuideLayers(guideLayers);\n\n // Get rid of anti-aliasing\n // @todo get crisp text https://github.com/LLK/scratch-paint/issues/508\n svg.setAttribute('shape-rendering', 'crispEdges');\n\n let svgString = (new XMLSerializer()).serializeToString(svg);\n if (optFontInlineFn) {\n svgString = optFontInlineFn(svgString);\n } else {\n log.error('Fonts may be converted to bitmap incorrectly if fontInlineFn prop is not set on PaintEditor.');\n }\n\n // Put anti-aliased SVG into image, and dump image back into canvas\n const img = new Image();\n img.onload = () => {\n if (img.width && img.height) {\n getRaster().drawImage(\n img,\n new paper.Point(Math.floor(bounds.topLeft.x), Math.floor(bounds.topLeft.y)));\n }\n for (let i = paper.project.activeLayer.children.length - 1; i >= 0; i--) {\n const item = paper.project.activeLayer.children[i];\n if (item.clipMask === false) {\n item.remove();\n } else {\n // Resize mask for bitmap bounds\n item.size.height = ART_BOARD_HEIGHT;\n item.size.width = ART_BOARD_WIDTH;\n item.setPosition(CENTER);\n }\n }\n onUpdateImage(false /* skipSnapshot */, Formats.BITMAP /* formatOverride */);\n };\n img.onerror = () => {\n // Fallback if browser does not support SVG data URIs in images.\n // The problem with rasterize is that it will anti-alias.\n const raster = paper.project.activeLayer.rasterize(72, false /* insert */);\n raster.onLoad = () => {\n if (raster.canvas.width && raster.canvas.height) {\n getRaster().drawImage(raster.canvas, raster.bounds.topLeft);\n }\n paper.project.activeLayer.removeChildren();\n onUpdateImage(false /* skipSnapshot */, Formats.BITMAP /* formatOverride */);\n };\n };\n // Hash tags will break image loading without being encoded first\n img.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;\n};\n\nconst convertToVector = function (clearSelectedItems, onUpdateImage) {\n clearSelection(clearSelectedItems);\n for (const item of paper.project.activeLayer.children) {\n if (item.clipMask === true) {\n // Resize mask for vector bounds\n item.size.height = MAX_WORKSPACE_BOUNDS.height;\n item.size.width = MAX_WORKSPACE_BOUNDS.width;\n item.setPosition(CENTER);\n }\n }\n getTrimmedRaster(true /* shouldInsert */);\n\n clearRaster();\n onUpdateImage(false /* skipSnapshot */, Formats.VECTOR /* formatOverride */);\n};\n\nconst getColor_ = function (x, y, context) {\n return context.getImageData(x, y, 1, 1).data;\n};\n\nconst matchesColor_ = function (x, y, imageData, oldColor) {\n const index = ((y * imageData.width) + x) * 4;\n return (\n imageData.data[index + 0] === oldColor[0] &&\n imageData.data[index + 1] === oldColor[1] &&\n imageData.data[index + 2] === oldColor[2] &&\n imageData.data[index + 3 ] === oldColor[3]\n );\n};\n\nconst colorPixel_ = function (x, y, imageData, newColor) {\n const index = ((y * imageData.width) + x) * 4;\n imageData.data[index + 0] = newColor[0];\n imageData.data[index + 1] = newColor[1];\n imageData.data[index + 2] = newColor[2];\n imageData.data[index + 3] = newColor[3];\n};\n\n/**\n * Flood fill beginning at the given point.\n * Based on http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/\n * @param {!int} x The x coordinate on the context at which to begin\n * @param {!int} y The y coordinate on the context at which to begin\n * @param {!ImageData} sourceImageData The image data to sample from. This is edited by the function.\n * @param {!ImageData} destImageData The image data to edit. May match sourceImageData. Should match\n * size of sourceImageData.\n * @param {!Array<number>} newColor The color to replace with. A length 4 array [r, g, b, a].\n * @param {!Array<number>} oldColor The color to replace. A length 4 array [r, g, b, a].\n * This must be different from newColor.\n * @param {!Array<Array<int>>} stack The stack of pixels we need to look at\n */\nconst floodFillInternal_ = function (x, y, sourceImageData, destImageData, newColor, oldColor, stack) {\n while (y > 0 && matchesColor_(x, y - 1, sourceImageData, oldColor)) {\n y--;\n }\n let lastLeftMatchedColor = false;\n let lastRightMatchedColor = false;\n for (; y < sourceImageData.height; y++) {\n if (!matchesColor_(x, y, sourceImageData, oldColor)) break;\n colorPixel_(x, y, sourceImageData, newColor);\n colorPixel_(x, y, destImageData, newColor);\n if (x > 0) {\n if (matchesColor_(x - 1, y, sourceImageData, oldColor)) {\n if (!lastLeftMatchedColor) {\n stack.push([x - 1, y]);\n lastLeftMatchedColor = true;\n }\n } else {\n lastLeftMatchedColor = false;\n }\n }\n if (x < sourceImageData.width - 1) {\n if (matchesColor_(x + 1, y, sourceImageData, oldColor)) {\n if (!lastRightMatchedColor) {\n stack.push([x + 1, y]);\n lastRightMatchedColor = true;\n }\n } else {\n lastRightMatchedColor = false;\n }\n }\n }\n};\n\n/**\n * Given a fill style string, get the color\n * @param {string} fillStyleString the fill style\n * @returns {Array<int>} Color, a length 4 array\n */\nconst fillStyleToColor_ = function (fillStyleString) {\n const tmpCanvas = document.createElement('canvas');\n tmpCanvas.width = 1;\n tmpCanvas.height = 1;\n const context = tmpCanvas.getContext('2d');\n context.fillStyle = fillStyleString;\n context.fillRect(0, 0, 1, 1);\n return context.getImageData(0, 0, 1, 1).data;\n};\n\n/**\n * Flood fill beginning at the given point\n * @param {!number} x The x coordinate on the context at which to begin\n * @param {!number} y The y coordinate on the context at which to begin\n * @param {!string} color A color string, which would go into context.fillStyle\n * @param {!HTMLCanvas2DContext} sourceContext The context from which to sample to determine where to flood fill\n * @param {!HTMLCanvas2DContext} destContext The context to which to draw. May match sourceContext. Should match\n * the size of sourceContext.\n * @returns {boolean} True if image changed, false otherwise\n */\nconst floodFill = function (x, y, color, sourceContext, destContext) {\n x = ~~x;\n y = ~~y;\n const newColor = fillStyleToColor_(color);\n const oldColor = getColor_(x, y, sourceContext);\n const sourceImageData = sourceContext.getImageData(0, 0, sourceContext.canvas.width, sourceContext.canvas.height);\n let destImageData = sourceImageData;\n if (destContext !== sourceContext) {\n destImageData = new ImageData(sourceContext.canvas.width, sourceContext.canvas.height);\n }\n if (oldColor[0] === newColor[0] &&\n oldColor[1] === newColor[1] &&\n oldColor[2] === newColor[2] &&\n oldColor[3] === newColor[3]) { // no-op\n return false;\n }\n const stack = [[x, y]];\n while (stack.length) {\n const pop = stack.pop();\n floodFillInternal_(pop[0], pop[1], sourceImageData, destImageData, newColor, oldColor, stack);\n }\n destContext.putImageData(destImageData, 0, 0);\n return true;\n};\n\n/**\n * Replace all instances of the color at the given point\n * @param {!number} x The x coordinate on the context of the start color\n * @param {!number} y The y coordinate on the context of the start color\n * @param {!string} color A color string, which would go into context.fillStyle\n * @param {!HTMLCanvas2DContext} sourceContext The context from which to sample to determine where to flood fill\n * @param {!HTMLCanvas2DContext} destContext The context to which to draw. May match sourceContext. Should match\n * @returns {boolean} True if image changed, false otherwise\n */\nconst floodFillAll = function (x, y, color, sourceContext, destContext) {\n x = ~~x;\n y = ~~y;\n const newColor = fillStyleToColor_(color);\n const oldColor = getColor_(x, y, sourceContext);\n const sourceImageData = sourceContext.getImageData(0, 0, sourceContext.canvas.width, sourceContext.canvas.height);\n let destImageData = sourceImageData;\n if (destContext !== sourceContext) {\n destImageData = new ImageData(sourceContext.canvas.width, sourceContext.canvas.height);\n }\n if (oldColor[0] === newColor[0] &&\n oldColor[1] === newColor[1] &&\n oldColor[2] === newColor[2] &&\n oldColor[3] === newColor[3]) { // no-op\n return false;\n }\n for (let i = 0; i < sourceImageData.width; i++) {\n for (let j = 0; j < sourceImageData.height; j++) {\n if (matchesColor_(i, j, sourceImageData, oldColor)) {\n colorPixel_(i, j, destImageData, newColor);\n }\n }\n }\n destContext.putImageData(destImageData, 0, 0);\n return true;\n};\n\n/**\n * @param {!paper.Shape.Rectangle} rect The rectangle to draw to the canvas\n * @param {!HTMLCanvas2DContext} context The context in which to draw\n */\nconst fillRect = function (rect, context) {\n // No rotation component to matrix\n if (rect.matrix.b === 0 && rect.matrix.c === 0) {\n const width = rect.size.width * rect.matrix.a;\n const height = rect.size.height * rect.matrix.d;\n context.fillRect(\n Math.round(rect.matrix.tx - (width / 2)),\n Math.round(rect.matrix.ty - (height / 2)),\n Math.round(width),\n Math.round(height)\n );\n return;\n }\n const startPoint = rect.matrix.transform(new paper.Point(-rect.size.width / 2, -rect.size.height / 2));\n const widthPoint = rect.matrix.transform(new paper.Point(rect.size.width / 2, -rect.size.height / 2));\n const heightPoint = rect.matrix.transform(new paper.Point(-rect.size.width / 2, rect.size.height / 2));\n const endPoint = rect.matrix.transform(new paper.Point(rect.size.width / 2, rect.size.height / 2));\n const center = rect.matrix.transform(new paper.Point());\n const points = [startPoint, widthPoint, heightPoint, endPoint].sort((a, b) => a.x - b.x);\n\n const solveY = (point1, point2, x) => {\n if (point2.x === point1.x) return center.x > point1.x ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;\n return ((point2.y - point1.y) / (point2.x - point1.x) * (x - point1.x)) + point1.y;\n };\n for (let x = Math.round(points[0].x); x < Math.round(points[3].x); x++) {\n const ys = [\n solveY(startPoint, widthPoint, x + .5),\n solveY(startPoint, heightPoint, x + .5),\n solveY(endPoint, widthPoint, x + .5),\n solveY(endPoint, heightPoint, x + .5)\n ].sort((a, b) => a - b);\n context.fillRect(x, Math.round(ys[1]), 1, Math.max(1, Math.round(ys[2]) - Math.round(ys[1])));\n }\n};\n\n/**\n * @param {!paper.Shape.Rectangle} rect The rectangle to draw to the canvas\n * @param {!number} thickness The thickness of the outline\n * @param {!HTMLCanvas2DContext} context The context in which to draw\n */\nconst outlineRect = function (rect, thickness, context) {\n const brushMark = getBrushMark(thickness, context.fillStyle);\n const roundedUpRadius = Math.ceil(thickness / 2);\n const drawFn = (x, y) => {\n context.drawImage(brushMark, ~~x - roundedUpRadius, ~~y - roundedUpRadius);\n };\n\n const isGradient = context.fillStyle instanceof CanvasGradient;\n\n // If drawing a gradient, we need to draw the shape onto a temporary canvas, then draw the gradient atop that canvas\n // only where the shape appears. Outlines are drawn as a series of brush mark images and as such can't be drawn as\n // gradients.\n let origContext;\n let tmpCanvas;\n const {width: canvasWidth, height: canvasHeight} = context.canvas;\n if (isGradient) {\n tmpCanvas = createCanvas(canvasWidth, canvasHeight);\n origContext = context;\n context = tmpCanvas.getContext('2d');\n }\n\n const startPoint = rect.matrix.transform(new paper.Point(-rect.size.width / 2, -rect.size.height / 2));\n const widthPoint = rect.matrix.transform(new paper.Point(rect.size.width / 2, -rect.size.height / 2));\n const heightPoint = rect.matrix.transform(new paper.Point(-rect.size.width / 2, rect.size.height / 2));\n const endPoint = rect.matrix.transform(new paper.Point(rect.size.width / 2, rect.size.height / 2));\n\n forEachLinePoint(startPoint, widthPoint, drawFn);\n forEachLinePoint(startPoint, heightPoint, drawFn);\n forEachLinePoint(endPoint, widthPoint, drawFn);\n forEachLinePoint(endPoint, heightPoint, drawFn);\n\n // Mask in the gradient only where the shape was drawn, and draw it. Then draw the gradientified shape onto the\n // original canvas normally.\n if (isGradient) {\n context.globalCompositeOperation = 'source-in';\n context.fillStyle = origContext.fillStyle;\n context.fillRect(0, 0, canvasWidth, canvasHeight);\n origContext.drawImage(tmpCanvas, 0, 0);\n }\n\n};\n\nconst flipBitmapHorizontal = function (canvas) {\n const tmpCanvas = createCanvas(canvas.width, canvas.height);\n const context = tmpCanvas.getContext('2d');\n context.save();\n context.scale(-1, 1);\n context.drawImage(canvas, 0, 0, -tmpCanvas.width, tmpCanvas.height);\n context.restore();\n return tmpCanvas;\n};\n\nconst flipBitmapVertical = function (canvas) {\n const tmpCanvas = createCanvas(canvas.width, canvas.height);\n const context = tmpCanvas.getContext('2d');\n context.save();\n context.scale(1, -1);\n context.drawImage(canvas, 0, 0, tmpCanvas.width, -tmpCanvas.height);\n context.restore();\n return tmpCanvas;\n};\n\nconst scaleBitmap = function (canvas, scale) {\n let tmpCanvas = createCanvas(Math.round(canvas.width * Math.abs(scale.x)), canvas.height);\n if (scale.x < 0) {\n canvas = flipBitmapHorizontal(canvas);\n }\n tmpCanvas.getContext('2d').drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);\n canvas = tmpCanvas;\n tmpCanvas = createCanvas(canvas.width, Math.round(canvas.height * Math.abs(scale.y)));\n if (scale.y < 0) {\n canvas = flipBitmapVertical(canvas);\n }\n tmpCanvas.getContext('2d').drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);\n return tmpCanvas;\n};\n\n/**\n * Given a raster, take the scale on the transform and apply it to the raster's canvas, then remove\n * the scale from the item's transform matrix. Do this only if scale.x or scale.y is less than 1.\n * @param {paper.Raster} item raster to change\n */\nconst maybeApplyScaleToCanvas_ = function (item) {\n // context.drawImage will anti-alias the image if both width and height are reduced.\n // However, it will preserve pixel colors if only one or the other is reduced, and\n // imageSmoothingEnabled is set to false. Therefore, we can avoid aliasing by scaling\n // down images in a 2 step process.\n const decomposed = item.matrix.decompose(); // Decomposition order: translate, rotate, scale, skew\n if (Math.abs(decomposed.scaling.x) < 1 && Math.abs(decomposed.scaling.y) < 1 &&\n decomposed.scaling.x !== 0 && decomposed.scaling.y !== 0) {\n item.canvas = scaleBitmap(item.canvas, decomposed.scaling);\n if (item.data && item.data.expanded) {\n item.data.expanded.canvas = scaleBitmap(item.data.expanded.canvas, decomposed.scaling);\n }\n // Remove the scale from the item's matrix\n item.matrix.append(\n new paper.Matrix().scale(new paper.Point(1 / decomposed.scaling.x, 1 / decomposed.scaling.y)));\n }\n};\n\n/**\n * Given a raster, apply its transformation matrix to its canvas. Call maybeApplyScaleToCanvas_ first\n * to avoid introducing anti-aliasing to scaled-down rasters.\n * @param {paper.Raster} item raster to resolve transform of\n * @param {paper.Raster} destination raster to draw selection to\n */\nconst commitArbitraryTransformation_ = function (item, destination) {\n // Create a canvas to perform masking\n const tmpCanvas = createCanvas();\n const context = tmpCanvas.getContext('2d');\n // Draw mask\n const rect = new paper.Shape.Rectangle(new paper.Point(), item.size);\n rect.matrix = item.matrix;\n fillRect(rect, context);\n rect.remove();\n context.globalCompositeOperation = 'source-in';\n\n // Draw image onto mask\n const m = item.matrix;\n context.transform(m.a, m.b, m.c, m.d, m.tx, m.ty);\n let canvas = item.canvas;\n if (item.data && item.data.expanded) {\n canvas = item.data.expanded.canvas;\n }\n context.transform(1, 0, 0, 1, -canvas.width / 2, -canvas.height / 2);\n context.drawImage(canvas, 0, 0);\n\n // Draw temp canvas onto raster layer\n destination.drawImage(tmpCanvas, new paper.Point());\n};\n\n/**\n * Given a raster item, take its transform matrix and apply it to its canvas. Try to avoid\n * introducing anti-aliasing.\n * @param {paper.Raster} selection raster to resolve transform of\n * @param {paper.Raster} bitmap raster to draw selection to\n */\nconst commitSelectionToBitmap = function (selection, bitmap) {\n if (!selection.matrix.isInvertible()) {\n return;\n }\n\n maybeApplyScaleToCanvas_(selection);\n commitArbitraryTransformation_(selection, bitmap);\n};\n\n/**\n * Converts a Paper.js color style (an item's fillColor or strokeColor) into a canvas-applicable color style.\n * Note that a \"color style\" as applied to an item is different from a plain paper.Color or paper.Gradient.\n * For instance, a gradient \"color style\" has origin and destination points whereas an unattached paper.Gradient\n * does not.\n * @param {paper.Color} color The color to convert to a canvas color/gradient\n * @param {CanvasRenderingContext2D} context The rendering context on which the style will be used\n * @returns {string|CanvasGradient} The canvas fill/stroke style.\n */\nconst _paperColorToCanvasStyle = function (color, context) {\n if (!color) return null;\n if (color.type === 'gradient') {\n let canvasGradient;\n const {origin, destination} = color;\n if (color.gradient.radial) {\n // Adapted from:\n // https://github.com/paperjs/paper.js/blob/b081fd72c72cd61331313c3961edb48f3dfaffbd/src/style/Color.js#L926-L935\n let {highlight} = color;\n const start = highlight || origin;\n const radius = destination.getDistance(origin);\n if (highlight) {\n const vector = highlight.subtract(origin);\n if (vector.getLength() > radius) {\n // Paper ¯\\_(ツ)_/¯\n highlight = origin.add(vector.normalize(radius - 0.1));\n }\n }\n canvasGradient = context.createRadialGradient(\n start.x, start.y,\n 0,\n origin.x, origin.y,\n radius\n );\n } else {\n canvasGradient = context.createLinearGradient(\n origin.x, origin.y,\n destination.x, destination.y\n );\n }\n\n const {stops} = color.gradient;\n // Adapted from:\n // https://github.com/paperjs/paper.js/blob/b081fd72c72cd61331313c3961edb48f3dfaffbd/src/style/Color.js#L940-L950\n for (let i = 0, len = stops.length; i < len; i++) {\n const stop = stops[i];\n const offset = stop.offset;\n canvasGradient.addColorStop(\n offset || i / (len - 1),\n stop.color.toCSS()\n );\n }\n return canvasGradient;\n }\n return color.toCSS();\n};\n\n/**\n * @param {paper.Shape.Ellipse} oval Vector oval to convert\n * @param {paper.Raster} bitmap raster to draw selection\n * @returns {bool} true if the oval was drawn\n */\nconst commitOvalToBitmap = function (oval, bitmap) {\n const radiusX = Math.abs(oval.size.width / 2);\n const radiusY = Math.abs(oval.size.height / 2);\n const context = bitmap.getContext('2d');\n const filled = oval.strokeWidth === 0;\n\n const canvasColor = _paperColorToCanvasStyle(filled ? oval.fillColor : oval.strokeColor, context);\n // If the color is null (e.g. fully transparent/\"no fill\"), don't bother drawing anything\n if (!canvasColor) return;\n\n context.fillStyle = canvasColor;\n\n const drew = drawEllipse({\n position: oval.position,\n radiusX,\n radiusY,\n matrix: oval.matrix,\n isFilled: filled,\n thickness: oval.strokeWidth / paper.view.zoom\n }, context);\n\n return drew;\n};\n\n/**\n * @param {paper.Rectangle} rect Vector rectangle to convert\n * @param {paper.Raster} bitmap raster to draw selection to\n */\nconst commitRectToBitmap = function (rect, bitmap) {\n const tmpCanvas = createCanvas();\n const context = tmpCanvas.getContext('2d');\n const filled = rect.strokeWidth === 0;\n\n const canvasColor = _paperColorToCanvasStyle(filled ? rect.fillColor : rect.strokeColor, context);\n // If the color is null (e.g. fully transparent/\"no fill\"), don't bother drawing anything\n if (!canvasColor) return;\n\n context.fillStyle = canvasColor;\n\n if (filled) {\n fillRect(rect, context);\n } else {\n outlineRect(rect, rect.strokeWidth / paper.view.zoom, context);\n }\n bitmap.drawImage(tmpCanvas, new paper.Point());\n};\n\nconst selectAllBitmap = function (clearSelectedItems) {\n clearSelection(clearSelectedItems);\n\n // Copy trimmed raster to active layer. If the raster layer was empty, nothing is selected.\n const trimmedRaster = getTrimmedRaster(true /* shouldInsert */);\n if (trimmedRaster) {\n trimmedRaster.selected = true;\n }\n\n // Clear raster layer\n clearRaster();\n};\n\nexport {\n commitSelectionToBitmap,\n commitOvalToBitmap,\n commitRectToBitmap,\n convertToBitmap,\n convertToVector,\n fillRect,\n outlineRect,\n floodFill,\n floodFillAll,\n getBrushMark,\n getHitBounds,\n getTrimmedRaster,\n drawEllipse,\n forEachLinePoint,\n flipBitmapHorizontal,\n flipBitmapVertical,\n scaleBitmap,\n selectAllBitmap\n};\n","import paper from '@scratch/paper';\nimport {CROSSHAIR_SIZE, getBackgroundGuideLayer, getDragCrosshairLayer, getRaster} from './layer';\nimport {getAllRootItems, getSelectedRootItems} from './selection';\nimport {getHitBounds} from './bitmap';\nimport log from '../log/log';\n\n// Vectors are imported and exported at SVG_ART_BOARD size.\n// Once they are imported however, both SVGs and bitmaps are on\n// canvases of ART_BOARD size.\n// (This is for backwards compatibility, to handle both assets\n// designed for 480 x 360, and bitmap resolution 2 bitmaps)\nconst SVG_ART_BOARD_WIDTH = 480;\nconst SVG_ART_BOARD_HEIGHT = 360;\nconst ART_BOARD_WIDTH = SVG_ART_BOARD_WIDTH * 2;\nconst ART_BOARD_HEIGHT = SVG_ART_BOARD_HEIGHT * 2;\nconst CENTER = new paper.Point(ART_BOARD_WIDTH / 2, ART_BOARD_HEIGHT / 2);\nconst PADDING_PERCENT = 25; // Padding as a percent of the max of width/height of the sprite\nconst BUFFER = 50; // Number of pixels of allowance around objects at the edges of the workspace\nconst MIN_RATIO = .125; // Zoom in to at least 1/8 of the screen. This way you don't end up incredibly\n// zoomed in for tiny costumes.\nconst OUTERMOST_ZOOM_LEVEL = 0.333;\nconst ART_BOARD_BOUNDS = new paper.Rectangle(0, 0, ART_BOARD_WIDTH, ART_BOARD_HEIGHT);\nconst MAX_WORKSPACE_BOUNDS = new paper.Rectangle(\n -ART_BOARD_WIDTH / 4,\n -ART_BOARD_HEIGHT / 4,\n ART_BOARD_WIDTH * 1.5,\n ART_BOARD_HEIGHT * 1.5);\n\nlet _workspaceBounds = ART_BOARD_BOUNDS;\n\nconst getWorkspaceBounds = () => _workspaceBounds;\n\n/**\n * The workspace bounds define the areas that the scroll bars can access.\n * They include at minimum the artboard, and extend to a bit beyond the\n * farthest item off the edge in any given direction (so items can't be\n * \"lost\" off the edge)\n * @param {boolean} clipEmpty Clip empty space from bounds, even if it\n * means discontinuously jumping the viewport. This should probably be\n * false unless the viewport is going to move discontinuously anyway\n * (such as in a zoom button click)\n */\nconst setWorkspaceBounds = clipEmpty => {\n const items = getAllRootItems();\n // Include the artboard and what's visible in the viewport\n let bounds = ART_BOARD_BOUNDS;\n if (!clipEmpty) {\n bounds = bounds.unite(paper.view.bounds);\n }\n // Include everything the user has drawn and a buffer around it\n for (const item of items) {\n bounds = bounds.unite(item.bounds.expand(BUFFER));\n }\n // Limit to max workspace bounds\n bounds = bounds.intersect(MAX_WORKSPACE_BOUNDS.expand(BUFFER));\n let top = bounds.top;\n let left = bounds.left;\n let bottom = bounds.bottom;\n let right = bounds.right;\n\n // Center in view if viewport is larger than workspace\n let hDiff = 0;\n let vDiff = 0;\n if (bounds.width < paper.view.bounds.width) {\n hDiff = (paper.view.bounds.width - bounds.width) / 2;\n left -= hDiff;\n right += hDiff;\n }\n if (bounds.height < paper.view.bounds.height) {\n vDiff = (paper.view.bounds.height - bounds.height) / 2;\n top -= vDiff;\n bottom += vDiff;\n }\n\n _workspaceBounds = new paper.Rectangle(left, top, right - left, bottom - top);\n};\n\nconst clampViewBounds = () => {\n const {left, right, top, bottom} = paper.project.view.bounds;\n if (left < _workspaceBounds.left) {\n paper.project.view.scrollBy(new paper.Point(_workspaceBounds.left - left, 0));\n }\n if (top < _workspaceBounds.top) {\n paper.project.view.scrollBy(new paper.Point(0, _workspaceBounds.top - top));\n }\n if (bottom > _workspaceBounds.bottom) {\n paper.project.view.scrollBy(new paper.Point(0, _workspaceBounds.bottom - bottom));\n }\n if (right > _workspaceBounds.right) {\n paper.project.view.scrollBy(new paper.Point(_workspaceBounds.right - right, 0));\n }\n setWorkspaceBounds();\n};\n\nconst resizeCrosshair = () => {\n if (getDragCrosshairLayer() && getDragCrosshairLayer().dragCrosshair) {\n getDragCrosshairLayer().dragCrosshair.scale(\n CROSSHAIR_SIZE / getDragCrosshairLayer().dragCrosshair.bounds.width / paper.view.zoom);\n }\n if (getBackgroundGuideLayer() && getBackgroundGuideLayer().dragCrosshair) {\n getBackgroundGuideLayer().dragCrosshair.scale(\n CROSSHAIR_SIZE / getBackgroundGuideLayer().dragCrosshair.bounds.width / paper.view.zoom);\n }\n};\n\n// Zoom keeping a project-space point fixed.\n// This article was helpful http://matthiasberth.com/tech/stable-zoom-and-pan-in-paperjs\nconst zoomOnFixedPoint = (deltaZoom, fixedPoint) => {\n const view = paper.view;\n const preZoomCenter = view.center;\n const newZoom = Math.max(OUTERMOST_ZOOM_LEVEL, view.zoom + deltaZoom);\n const scaling = view.zoom / newZoom;\n const preZoomOffset = fixedPoint.subtract(preZoomCenter);\n const postZoomOffset = fixedPoint.subtract(preZoomOffset.multiply(scaling))\n .subtract(preZoomCenter);\n view.zoom = newZoom;\n view.translate(postZoomOffset.multiply(-1));\n\n setWorkspaceBounds(true /* clipEmpty */);\n clampViewBounds();\n resizeCrosshair();\n};\n\n// Zoom keeping the selection center (if any) fixed.\nconst zoomOnSelection = deltaZoom => {\n let fixedPoint;\n const items = getSelectedRootItems();\n if (items.length > 0) {\n let rect = null;\n for (const item of items) {\n if (rect) {\n rect = rect.unite(item.bounds);\n } else {\n rect = item.bounds;\n }\n }\n fixedPoint = rect.center;\n } else {\n fixedPoint = paper.project.view.center;\n }\n zoomOnFixedPoint(deltaZoom, fixedPoint);\n};\n\nconst resetZoom = () => {\n paper.project.view.zoom = .5;\n setWorkspaceBounds(true /* clipEmpty */);\n resizeCrosshair();\n clampViewBounds();\n};\n\nconst pan = (dx, dy) => {\n paper.project.view.scrollBy(new paper.Point(dx, dy));\n clampViewBounds();\n};\n\n/**\n * Mouse actions are clamped to action bounds\n * @param {boolean} isBitmap True if the editor is in bitmap mode, false if it is in vector mode\n * @returns {paper.Rectangle} the bounds within which mouse events should work in the paint editor\n */\nconst getActionBounds = isBitmap => {\n if (isBitmap) {\n return ART_BOARD_BOUNDS;\n }\n return paper.view.bounds.unite(ART_BOARD_BOUNDS).intersect(MAX_WORKSPACE_BOUNDS);\n};\n\nconst zoomToFit = isBitmap => {\n resetZoom();\n let bounds;\n if (isBitmap) {\n bounds = getHitBounds(getRaster()).expand(BUFFER);\n } else {\n const items = getAllRootItems();\n for (const item of items) {\n if (bounds) {\n bounds = bounds.unite(item.bounds);\n } else {\n bounds = item.bounds;\n }\n }\n }\n if (bounds && bounds.width && bounds.height) {\n const canvas = paper.view.element;\n // Ratio of (sprite length plus padding on all sides) to viewport length.\n let ratio = paper.view.zoom *\n Math.max(\n bounds.width * (1 + (2 * PADDING_PERCENT / 100)) / canvas.clientWidth,\n bounds.height * (1 + (2 * PADDING_PERCENT / 100)) / canvas.clientHeight);\n // Clamp ratio\n ratio = Math.max(Math.min(1, ratio), MIN_RATIO);\n if (ratio < 1) {\n paper.view.center = bounds.center;\n paper.view.zoom = paper.view.zoom / ratio;\n resizeCrosshair();\n clampViewBounds();\n }\n } else {\n log.warn('No bounds!');\n }\n};\n\nexport {\n ART_BOARD_BOUNDS,\n ART_BOARD_HEIGHT,\n ART_BOARD_WIDTH,\n CENTER,\n OUTERMOST_ZOOM_LEVEL,\n SVG_ART_BOARD_WIDTH,\n SVG_ART_BOARD_HEIGHT,\n MAX_WORKSPACE_BOUNDS,\n clampViewBounds,\n getActionBounds,\n pan,\n resetZoom,\n setWorkspaceBounds,\n getWorkspaceBounds,\n resizeCrosshair,\n zoomOnSelection,\n zoomOnFixedPoint,\n zoomToFit\n};\n","import paper from '@scratch/paper';\nimport log from '../log/log';\nimport {ART_BOARD_BOUNDS, ART_BOARD_WIDTH, ART_BOARD_HEIGHT, CENTER, MAX_WORKSPACE_BOUNDS} from './view';\nimport {isGroupItem} from './item';\nimport {isBitmap, isVector} from '../lib/format';\n\nconst CHECKERBOARD_SIZE = 8;\nconst CROSSHAIR_SIZE = 16;\nconst CROSSHAIR_FULL_OPACITY = 0.75;\n\nconst _getLayer = function (layerString) {\n for (const layer of paper.project.layers) {\n if (layer.data && layer.data[layerString]) {\n return layer;\n }\n }\n};\n\nconst _getPaintingLayer = function () {\n return _getLayer('isPaintingLayer');\n};\n\n/**\n * Creates a canvas with width and height matching the art board size.\n * @param {?number} width Width of the canvas. Defaults to ART_BOARD_WIDTH.\n * @param {?number} height Height of the canvas. Defaults to ART_BOARD_HEIGHT.\n * @returns {HTMLCanvasElement} the canvas\n */\nconst createCanvas = function (width, height) {\n const canvas = document.createElement('canvas');\n canvas.width = width ? width : ART_BOARD_WIDTH;\n canvas.height = height ? height : ART_BOARD_HEIGHT;\n canvas.getContext('2d').imageSmoothingEnabled = false;\n return canvas;\n};\n\nconst clearRaster = function () {\n const layer = _getLayer('isRasterLayer');\n layer.removeChildren();\n\n // Generate blank raster\n const raster = new paper.Raster(createCanvas());\n raster.canvas.getContext('2d').imageSmoothingEnabled = false;\n raster.parent = layer;\n raster.guide = true;\n raster.locked = true;\n raster.position = CENTER;\n};\n\nconst getRaster = function () {\n const layer = _getLayer('isRasterLayer');\n // Generate blank raster\n if (layer.children.length === 0) {\n clearRaster();\n }\n return _getLayer('isRasterLayer').children[0];\n};\n\nconst getDragCrosshairLayer = function () {\n return _getLayer('isDragCrosshairLayer');\n};\n\nconst getBackgroundGuideLayer = function () {\n return _getLayer('isBackgroundGuideLayer');\n};\n\nconst _convertLayer = function (layer, format) {\n layer.bitmapBackground.visible = isBitmap(format);\n layer.vectorBackground.visible = isVector(format);\n};\n\nconst convertBackgroundGuideLayer = function (format) {\n _convertLayer(getBackgroundGuideLayer(), format);\n};\n\nconst _makeGuideLayer = function () {\n const guideLayer = new paper.Layer();\n guideLayer.data.isGuideLayer = true;\n return guideLayer;\n};\n\nconst getGuideLayer = function () {\n let layer = _getLayer('isGuideLayer');\n if (!layer) {\n layer = _makeGuideLayer();\n _getPaintingLayer().activate();\n }\n return layer;\n};\n\nconst setGuideItem = function (item) {\n item.locked = true;\n item.guide = true;\n if (isGroupItem(item)) {\n for (let i = 0; i < item.children.length; i++) {\n setGuideItem(item.children[i]);\n }\n }\n};\n\n/**\n * Removes the guide layers, e.g. for purposes of exporting the image. Must call showGuideLayers to re-add them.\n * @param {boolean} includeRaster true if the raster layer should also be hidden\n * @returns {object} an object of the removed layers, which should be passed to showGuideLayers to re-add them.\n */\nconst hideGuideLayers = function (includeRaster) {\n const backgroundGuideLayer = getBackgroundGuideLayer();\n const dragCrosshairLayer = getDragCrosshairLayer();\n const outlineLayer = _getLayer('isOutlineLayer');\n const guideLayer = getGuideLayer();\n dragCrosshairLayer.remove();\n outlineLayer.remove();\n guideLayer.remove();\n backgroundGuideLayer.remove();\n let rasterLayer;\n if (includeRaster) {\n rasterLayer = _getLayer('isRasterLayer');\n rasterLayer.remove();\n }\n return {\n dragCrosshairLayer: dragCrosshairLayer,\n outlineLayer: outlineLayer,\n guideLayer: guideLayer,\n backgroundGuideLayer: backgroundGuideLayer,\n rasterLayer: rasterLayer\n };\n};\n\n/**\n * Add back the guide layers removed by calling hideGuideLayers. This must be done before any editing operations are\n * taken in the paint editor.\n * @param {!object} guideLayers object of the removed layers, which was returned by hideGuideLayers\n */\nconst showGuideLayers = function (guideLayers) {\n const backgroundGuideLayer = guideLayers.backgroundGuideLayer;\n const dragCrosshairLayer = guideLayers.dragCrosshairLayer;\n const outlineLayer = guideLayers.outlineLayer;\n const guideLayer = guideLayers.guideLayer;\n const rasterLayer = guideLayers.rasterLayer;\n if (rasterLayer && !rasterLayer.index) {\n paper.project.addLayer(rasterLayer);\n rasterLayer.sendToBack();\n }\n if (!backgroundGuideLayer.index) {\n paper.project.addLayer(backgroundGuideLayer);\n backgroundGuideLayer.sendToBack();\n }\n if (!dragCrosshairLayer.index) {\n paper.project.addLayer(dragCrosshairLayer);\n dragCrosshairLayer.bringToFront();\n }\n if (!outlineLayer.index) {\n paper.project.addLayer(outlineLayer);\n outlineLayer.bringToFront();\n }\n if (!guideLayer.index) {\n paper.project.addLayer(guideLayer);\n guideLayer.bringToFront();\n }\n if (paper.project.activeLayer !== _getPaintingLayer()) {\n log.error(`Wrong active layer`);\n log.error(paper.project.activeLayer.data);\n }\n};\n\nconst _makePaintingLayer = function () {\n const paintingLayer = new paper.Layer();\n paintingLayer.data.isPaintingLayer = true;\n return paintingLayer;\n};\n\nconst _makeRasterLayer = function () {\n const rasterLayer = new paper.Layer();\n rasterLayer.data.isRasterLayer = true;\n clearRaster();\n return rasterLayer;\n};\n\nconst _makeBackgroundPaper = function (width, height, color, opacity) {\n // creates a checkerboard path of width * height squares in color on white\n let x = 0;\n let y = 0;\n const pathPoints = [];\n while (x < width) {\n pathPoints.push(new paper.Point(x, y));\n x++;\n pathPoints.push(new paper.Point(x, y));\n y = y === 0 ? height : 0;\n }\n y = height - 1;\n x = width;\n while (y > 0) {\n pathPoints.push(new paper.Point(x, y));\n x = (x === 0 ? width : 0);\n pathPoints.push(new paper.Point(x, y));\n y--;\n }\n const vRect = new paper.Shape.Rectangle(\n new paper.Point(0, 0),\n new paper.Point(ART_BOARD_WIDTH / CHECKERBOARD_SIZE, ART_BOARD_HEIGHT / CHECKERBOARD_SIZE));\n vRect.fillColor = '#fff';\n vRect.guide = true;\n vRect.locked = true;\n vRect.position = CENTER;\n const vPath = new paper.Path(pathPoints);\n vPath.fillRule = 'evenodd';\n vPath.fillColor = color;\n vPath.opacity = opacity;\n vPath.guide = true;\n vPath.locked = true;\n vPath.position = CENTER;\n const mask = new paper.Shape.Rectangle(MAX_WORKSPACE_BOUNDS);\n mask.position = CENTER;\n mask.guide = true;\n mask.locked = true;\n mask.scale(1 / CHECKERBOARD_SIZE);\n const vGroup = new paper.Group([vRect, vPath, mask]);\n mask.clipMask = true;\n return vGroup;\n};\n\n// Helper function for drawing a crosshair\nconst _makeCrosshair = function (opacity, parent) {\n const crosshair = new paper.Group();\n\n const vLine2 = new paper.Path.Line(new paper.Point(0, -7), new paper.Point(0, 7));\n vLine2.strokeWidth = 6;\n vLine2.strokeColor = 'white';\n vLine2.strokeCap = 'round';\n crosshair.addChild(vLine2);\n const hLine2 = new paper.Path.Line(new paper.Point(-7, 0), new paper.Point(7, 0));\n hLine2.strokeWidth = 6;\n hLine2.strokeColor = 'white';\n hLine2.strokeCap = 'round';\n crosshair.addChild(hLine2);\n const circle2 = new paper.Shape.Circle(new paper.Point(0, 0), 5.5);\n circle2.strokeWidth = 6;\n circle2.strokeColor = 'white';\n crosshair.addChild(circle2);\n\n const vLine = new paper.Path.Line(new paper.Point(0, -7), new paper.Point(0, 7));\n vLine.strokeWidth = 2;\n vLine.strokeColor = 'black';\n vLine.strokeCap = 'round';\n crosshair.addChild(vLine);\n const hLine = new paper.Path.Line(new paper.Point(-7, 0), new paper.Point(7, 0));\n hLine.strokeWidth = 2;\n hLine.strokeColor = 'black';\n hLine.strokeCap = 'round';\n crosshair.addChild(hLine);\n const circle = new paper.Shape.Circle(new paper.Point(0, 0), 5.5);\n circle.strokeWidth = 2;\n circle.strokeColor = 'black';\n crosshair.addChild(circle);\n\n setGuideItem(crosshair);\n crosshair.position = CENTER;\n crosshair.opacity = opacity;\n crosshair.parent = parent;\n crosshair.applyMatrix = false;\n parent.dragCrosshair = crosshair;\n crosshair.scale(CROSSHAIR_SIZE / crosshair.bounds.width / paper.view.zoom);\n};\n\nconst _makeDragCrosshairLayer = function () {\n const dragCrosshairLayer = new paper.Layer();\n _makeCrosshair(CROSSHAIR_FULL_OPACITY, dragCrosshairLayer);\n dragCrosshairLayer.data.isDragCrosshairLayer = true;\n dragCrosshairLayer.visible = false;\n return dragCrosshairLayer;\n};\n\nconst _makeOutlineLayer = function () {\n const outlineLayer = new paper.Layer();\n const whiteRect = new paper.Shape.Rectangle(ART_BOARD_BOUNDS.expand(1));\n whiteRect.strokeWidth = 2;\n whiteRect.strokeColor = 'white';\n setGuideItem(whiteRect);\n const blueRect = new paper.Shape.Rectangle(ART_BOARD_BOUNDS.expand(5));\n blueRect.strokeWidth = 2;\n blueRect.strokeColor = '#4280D7';\n blueRect.opacity = 0.25;\n setGuideItem(blueRect);\n outlineLayer.data.isOutlineLayer = true;\n return outlineLayer;\n};\n\nconst _makeBackgroundGuideLayer = function (format) {\n const guideLayer = new paper.Layer();\n guideLayer.locked = true;\n\n const vWorkspaceBounds = new paper.Shape.Rectangle(MAX_WORKSPACE_BOUNDS);\n vWorkspaceBounds.fillColor = '#ECF1F9';\n vWorkspaceBounds.position = CENTER;\n\n // Add 1 to the height because it's an odd number otherwise, and we want it to be even\n // so the corner of the checkerboard to line up with the center crosshair\n const vBackground = _makeBackgroundPaper(\n MAX_WORKSPACE_BOUNDS.width / CHECKERBOARD_SIZE,\n (MAX_WORKSPACE_BOUNDS.height / CHECKERBOARD_SIZE) + 1,\n '#D9E3F2', 0.55);\n vBackground.position = CENTER;\n vBackground.scaling = new paper.Point(CHECKERBOARD_SIZE, CHECKERBOARD_SIZE);\n\n const vectorBackground = new paper.Group();\n vectorBackground.addChild(vWorkspaceBounds);\n vectorBackground.addChild(vBackground);\n setGuideItem(vectorBackground);\n guideLayer.vectorBackground = vectorBackground;\n\n const bitmapBackground = _makeBackgroundPaper(\n ART_BOARD_WIDTH / CHECKERBOARD_SIZE,\n ART_BOARD_HEIGHT / CHECKERBOARD_SIZE,\n '#D9E3F2', 0.55);\n bitmapBackground.position = CENTER;\n bitmapBackground.scaling = new paper.Point(CHECKERBOARD_SIZE, CHECKERBOARD_SIZE);\n bitmapBackground.guide = true;\n bitmapBackground.locked = true;\n guideLayer.bitmapBackground = bitmapBackground;\n\n _convertLayer(guideLayer, format);\n\n _makeCrosshair(0.16, guideLayer);\n\n guideLayer.data.isBackgroundGuideLayer = true;\n return guideLayer;\n};\n\nconst setupLayers = function (format) {\n const backgroundGuideLayer = _makeBackgroundGuideLayer(format);\n _makeRasterLayer();\n const paintLayer = _makePaintingLayer();\n const dragCrosshairLayer = _makeDragCrosshairLayer();\n const outlineLayer = _makeOutlineLayer();\n const guideLayer = _makeGuideLayer();\n backgroundGuideLayer.sendToBack();\n dragCrosshairLayer.bringToFront();\n outlineLayer.bringToFront();\n guideLayer.bringToFront();\n paintLayer.activate();\n};\n\nexport {\n CROSSHAIR_SIZE,\n CROSSHAIR_FULL_OPACITY,\n createCanvas,\n hideGuideLayers,\n showGuideLayers,\n getDragCrosshairLayer,\n getGuideLayer,\n getBackgroundGuideLayer,\n convertBackgroundGuideLayer,\n clearRaster,\n getRaster,\n setGuideItem,\n setupLayers\n};\n","// undo functionality\n// modifed from https://github.com/memononen/stylii\nimport paper from '@scratch/paper';\nimport {hideGuideLayers, showGuideLayers, getRaster} from '../helper/layer';\nimport {getSelectedLeafItems} from '../helper/selection';\nimport Formats, {isVector, isBitmap} from '../lib/format';\nimport log from '../log/log';\n\n/**\n * Take an undo snapshot\n * @param {function} dispatchPerformSnapshot Callback to dispatch a state update\n * @param {Formats} format Either Formats.BITMAP or Formats.VECTOR\n */\nconst performSnapshot = function (dispatchPerformSnapshot, format) {\n if (!format) {\n log.error('Format must be specified.');\n }\n const guideLayers = hideGuideLayers();\n dispatchPerformSnapshot({\n json: paper.project.exportJSON({asString: false}),\n paintEditorFormat: format\n });\n showGuideLayers(guideLayers);\n};\n\nconst _restore = function (entry, setSelectedItems, onUpdateImage, isBitmapMode) {\n for (let i = paper.project.layers.length - 1; i >= 0; i--) {\n const layer = paper.project.layers[i];\n if (!layer.data.isBackgroundGuideLayer &&\n !layer.data.isDragCrosshairLayer &&\n !layer.data.isOutlineLayer) {\n layer.removeChildren();\n layer.remove();\n }\n }\n paper.project.importJSON(entry.json);\n setSelectedItems();\n\n // Ensure that all rasters are loaded before updating storage with new image data.\n const rastersThatNeedToLoad = [];\n const onLoad = () => {\n if (!getRaster().loaded) return;\n for (const raster of rastersThatNeedToLoad) {\n if (!raster.loaded) return;\n }\n onUpdateImage(true /* skipSnapshot */);\n };\n\n // Bitmap mode should have at most 1 selected item\n if (isBitmapMode) {\n const selectedItems = getSelectedLeafItems();\n if (selectedItems.length === 1 && selectedItems[0] instanceof paper.Raster) {\n rastersThatNeedToLoad.push(selectedItems[0]);\n if (selectedItems[0].data && selectedItems[0].data.expanded instanceof paper.Raster) {\n rastersThatNeedToLoad.push(selectedItems[0].data.expanded);\n }\n }\n }\n\n getRaster().onLoad = onLoad;\n for (const raster of rastersThatNeedToLoad) {\n raster.onLoad = onLoad;\n if (raster.loaded) raster.onLoad();\n }\n};\n\nconst performUndo = function (undoState, dispatchPerformUndo, setSelectedItems, onUpdateImage) {\n if (undoState.pointer > 0) {\n const state = undoState.stack[undoState.pointer - 1];\n _restore(state, setSelectedItems, onUpdateImage, isBitmap(state.paintEditorFormat));\n const format = isVector(state.paintEditorFormat) ? Formats.VECTOR_SKIP_CONVERT :\n isBitmap(state.paintEditorFormat) ? Formats.BITMAP_SKIP_CONVERT : null;\n dispatchPerformUndo(format);\n }\n};\n\n\nconst performRedo = function (undoState, dispatchPerformRedo, setSelectedItems, onUpdateImage) {\n if (undoState.pointer >= 0 && undoState.pointer < undoState.stack.length - 1) {\n const state = undoState.stack[undoState.pointer + 1];\n _restore(state, setSelectedItems, onUpdateImage, isBitmap(state.paintEditorFormat));\n const format = isVector(state.paintEditorFormat) ? Formats.VECTOR_SKIP_CONVERT :\n isBitmap(state.paintEditorFormat) ? Formats.BITMAP_SKIP_CONVERT : null;\n dispatchPerformRedo(format);\n }\n};\n\nconst shouldShowUndo = function (undoState) {\n return undoState.pointer > 0;\n};\n\nconst shouldShowRedo = function (undoState) {\n return (undoState.pointer > -1 && undoState.pointer !== (undoState.stack.length - 1));\n};\n\nexport {\n performSnapshot,\n performUndo,\n performRedo,\n shouldShowUndo,\n shouldShowRedo\n};\n","import log from '../log/log';\n\nconst UNDO = 'scratch-paint/undo/UNDO';\nconst REDO = 'scratch-paint/undo/REDO';\nconst SNAPSHOT = 'scratch-paint/undo/SNAPSHOT';\nconst CLEAR = 'scratch-paint/undo/CLEAR';\nconst MAX_STACK_SIZE = 100;\nconst initialState = {\n stack: [],\n pointer: -1\n};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case UNDO:\n if (state.pointer <= 0) {\n log.warn(`Can't undo, undo stack is empty`);\n return state;\n }\n return {\n stack: state.stack,\n pointer: state.pointer - 1\n };\n case REDO:\n if (state.pointer <= -1 || state.pointer === state.stack.length - 1) {\n log.warn(`Can't redo, redo stack is empty`);\n return state;\n }\n return {\n stack: state.stack,\n pointer: state.pointer + 1\n };\n case SNAPSHOT:\n if (!action.snapshot) {\n log.warn(`Couldn't create undo snapshot, no data provided`);\n return state;\n }\n // Overflowed or about to overflow\n if (state.pointer >= MAX_STACK_SIZE - 1) {\n return {\n // Make a stack of size MAX_STACK_SIZE, cutting off the oldest snapshots.\n stack: state.stack.slice(state.pointer - MAX_STACK_SIZE + 2, state.pointer + 1).concat(action.snapshot),\n pointer: MAX_STACK_SIZE - 1\n };\n }\n return {\n // Performing an action clears the redo stack\n stack: state.stack.slice(0, state.pointer + 1).concat(action.snapshot),\n pointer: state.pointer + 1\n };\n case CLEAR:\n return initialState;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst undoSnapshot = function (snapshot) {\n return {\n type: SNAPSHOT,\n snapshot: snapshot\n };\n};\n/**\n * @param {Format} format Either VECTOR_SKIP_CONVERT or BITMAP_SKIP_CONVERT\n * @returns {Action} undo action\n */\nconst undo = function (format) {\n return {\n type: UNDO,\n format: format\n };\n};\n/**\n * @param {Format} format Either VECTOR_SKIP_CONVERT or BITMAP_SKIP_CONVERT\n * @returns {Action} undo action\n */\nconst redo = function (format) {\n return {\n type: REDO,\n format: format\n };\n};\nconst clearUndoState = function () {\n return {\n type: CLEAR\n };\n};\n\nexport {\n reducer as default,\n undo,\n redo,\n undoSnapshot,\n clearUndoState,\n MAX_STACK_SIZE,\n UNDO,\n REDO\n};\n","import log from '../log/log';\nconst CHANGE_SELECTED_ITEMS = 'scratch-paint/select/CHANGE_SELECTED_ITEMS';\nconst REDRAW_SELECTION_BOX = 'scratch-paint/select/REDRAW_SELECTION_BOX';\nconst initialState = [];\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case REDRAW_SELECTION_BOX:\n if (state.length > 0) return state.slice(0); // Sends an update even though the items haven't changed\n return state;\n case CHANGE_SELECTED_ITEMS:\n if (!action.selectedItems || !(action.selectedItems instanceof Array)) {\n log.warn(`No selected items or wrong format provided: ${action.selectedItems}`);\n return state;\n }\n if (action.selectedItems.length > 1 && action.bitmapMode) {\n log.warn(`Multiselect should not be possible in bitmap mode: ${action.selectedItems}`);\n return state;\n }\n // If they are both empty, no change\n if (action.selectedItems.length === 0 && state.length === 0) {\n return state;\n }\n return action.selectedItems;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n/**\n * Set the selected item state to the given array of items\n * @param {Array<paper.Item>} selectedItems from paper.project.selectedItems\n * @param {?boolean} bitmapMode True if the items are being selected in bitmap mode\n * @returns {object} Redux action to change the selected items.\n */\nconst setSelectedItems = function (selectedItems, bitmapMode) {\n return {\n type: CHANGE_SELECTED_ITEMS,\n selectedItems: selectedItems,\n bitmapMode: bitmapMode\n };\n};\nconst clearSelectedItems = function () {\n return {\n type: CHANGE_SELECTED_ITEMS,\n selectedItems: []\n };\n};\nconst redrawSelectionBox = function () {\n return {\n type: REDRAW_SELECTION_BOX\n };\n};\n\nexport {\n reducer as default,\n redrawSelectionBox,\n setSelectedItems,\n clearSelectedItems,\n CHANGE_SELECTED_ITEMS\n};\n","import log from '../log/log';\n\nconst CHANGE_HOVERED = 'scratch-paint/hover/CHANGE_HOVERED';\nconst initialState = null;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_HOVERED:\n if (typeof action.hoveredItemId === 'undefined') {\n log.warn(`Hovered item should not be set to undefined. Use null.`);\n return state;\n } else if (typeof action.hoveredItemId === 'undefined' || isNaN(action.hoveredItemId)) {\n log.warn(`Hovered item should be an item ID number. Got: ${action.hoveredItemId}`);\n return state;\n }\n return action.hoveredItemId;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n/**\n * Set the hovered item state to the given item ID\n * @param {number} hoveredItemId The paper.Item ID of the hover indicator item.\n * @returns {object} Redux action to change the hovered item.\n */\nconst setHoveredItem = function (hoveredItemId) {\n return {\n type: CHANGE_HOVERED,\n hoveredItemId: hoveredItemId\n };\n};\n\nconst clearHoveredItem = function () {\n return {\n type: CHANGE_HOVERED,\n hoveredItemId: null\n };\n};\n\nexport {\n reducer as default,\n setHoveredItem,\n clearHoveredItem\n};\n","import log from '../log/log';\n\nconst SET = 'scratch-paint/clipboard/SET';\nconst INCREMENT_PASTE_OFFSET = 'scratch-paint/clipboard/INCREMENT_PASTE_OFFSET';\nconst CLEAR_PASTE_OFFSET = 'scratch-paint/clipboard/CLEAR_PASTE_OFFSET';\nconst initialState = {\n items: [],\n pasteOffset: 0\n};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case SET:\n if (!action.clipboardItems || !(action.clipboardItems instanceof Array) || action.clipboardItems.length === 0) {\n log.warn(`Invalid clipboard item format`);\n return state;\n }\n return {\n items: action.clipboardItems,\n pasteOffset: 1\n };\n case INCREMENT_PASTE_OFFSET:\n return {\n items: state.items,\n pasteOffset: state.pasteOffset + 1\n };\n case CLEAR_PASTE_OFFSET:\n return {\n items: state.items,\n pasteOffset: 0\n };\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst setClipboardItems = function (clipboardItems) {\n return {\n type: SET,\n clipboardItems: clipboardItems\n };\n};\n\nconst incrementPasteOffset = function () {\n return {\n type: INCREMENT_PASTE_OFFSET\n };\n};\n\nconst clearPasteOffset = function () {\n return {\n type: CLEAR_PASTE_OFFSET\n };\n};\n\nexport {\n reducer as default,\n setClipboardItems,\n incrementPasteOffset,\n clearPasteOffset\n};\n","import Formats from '../lib/format';\nimport log from '../log/log';\nimport {UNDO, REDO} from './undo';\n\nconst CHANGE_FORMAT = 'scratch-paint/formats/CHANGE_FORMAT';\nconst initialState = null;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case UNDO:\n /* falls through */\n case REDO:\n /* falls through */\n case CHANGE_FORMAT:\n if (!action.format) return state;\n if (action.format in Formats) {\n return action.format;\n }\n log.warn(`Format does not exist: ${action.format}`);\n /* falls through */\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeFormat = function (format) {\n return {\n type: CHANGE_FORMAT,\n format: format\n };\n};\n\nexport {\n reducer as default,\n changeFormat\n};\n","import paper from '@scratch/paper';\nimport log from '../log/log';\n\nconst UPDATE_VIEW_BOUNDS = 'scratch-paint/view/UPDATE_VIEW_BOUNDS';\nconst initialState = new paper.Matrix(); // Identity\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case UPDATE_VIEW_BOUNDS:\n if (!(action.viewBounds instanceof paper.Matrix)) {\n log.warn(`View bounds should be a paper.Matrix.`);\n return state;\n }\n return action.viewBounds;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n/**\n * Set the view bounds, which defines the zoom and scroll of the paper canvas.\n * @param {paper.Matrix} matrix The matrix applied to the view\n * @returns {object} Redux action to set the view bounds\n */\nconst updateViewBounds = function (matrix) {\n return {\n type: UPDATE_VIEW_BOUNDS,\n viewBounds: matrix.clone()\n };\n};\n\nexport {\n reducer as default,\n updateViewBounds\n};\n","import paper from '@scratch/paper';\nimport log from '../log/log';\n\nconst SAVE_ZOOM_LEVEL = 'scratch-paint/zoom-levels/SAVE_ZOOM_LEVEL';\nconst SET_ZOOM_LEVEL_ID = 'scratch-paint/zoom-levels/SET_ZOOM_LEVEL_ID';\nconst initialState = {};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case SET_ZOOM_LEVEL_ID:\n if (action.zoomLevelId === 'currentZoomLevelId') {\n log.warn(`currentZoomLevelId is an invalid string for zoomLevel`);\n return state;\n }\n return Object.assign({}, state, {\n currentZoomLevelId: action.zoomLevelId\n });\n case SAVE_ZOOM_LEVEL:\n return Object.assign({}, state, {\n [state.currentZoomLevelId]: action.zoomLevel\n });\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst saveZoomLevel = function (zoomLevel) {\n if (!(zoomLevel instanceof paper.Matrix)) {\n log.warn(`Not a matrix: ${zoomLevel}`);\n }\n return {\n type: SAVE_ZOOM_LEVEL,\n zoomLevel: new paper.Matrix(zoomLevel)\n };\n};\nconst setZoomLevelId = function (zoomLevelId) {\n return {\n type: SET_ZOOM_LEVEL_ID,\n zoomLevelId: zoomLevelId\n };\n};\n\nexport {\n reducer as default,\n saveZoomLevel,\n setZoomLevelId\n};\n","\n import API from \"!../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./paper-canvas.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./paper-canvas.css\";\n export default content && content.locals ? content.locals : undefined;\n","import bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport paper from '@scratch/paper';\nimport {sanitizeSvg} from '@scratch/scratch-svg-renderer';\nimport Formats from '../lib/format';\nimport log from '../log/log';\n\nimport {stripInvalidPaperData} from '../helper/strip-invalid-paper-data';\nimport {performSnapshot} from '../helper/undo';\nimport {undoSnapshot, clearUndoState} from '../reducers/undo';\nimport {isGroup, ungroupItems} from '../helper/group';\nimport {clearRaster, convertBackgroundGuideLayer, getRaster, setupLayers} from '../helper/layer';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {\n ART_BOARD_WIDTH, ART_BOARD_HEIGHT, CENTER, MAX_WORKSPACE_BOUNDS,\n clampViewBounds, resetZoom, setWorkspaceBounds, zoomToFit, resizeCrosshair\n} from '../helper/view';\nimport {ensureClockwise, scaleWithStrokes} from '../helper/math';\nimport {clearHoveredItem} from '../reducers/hover';\nimport {clearPasteOffset} from '../reducers/clipboard';\nimport {changeFormat} from '../reducers/format';\nimport {updateViewBounds} from '../reducers/view-bounds';\nimport {saveZoomLevel, setZoomLevelId} from '../reducers/zoom-levels';\n\nimport styles from './paper-canvas.css';\n\nclass PaperCanvas extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'clearQueuedImport',\n 'setCanvas',\n 'importSvg',\n 'initializeSvg',\n 'maybeZoomToFit',\n 'switchCostume',\n 'onViewResize',\n 'recalibrateSize'\n ]);\n }\n componentDidMount () {\n paper.setup(this.canvas);\n paper.view.on('resize', this.onViewResize);\n resetZoom();\n if (this.props.zoomLevelId) {\n this.props.setZoomLevelId(this.props.zoomLevelId);\n if (this.props.zoomLevels[this.props.zoomLevelId]) {\n // This is the matrix that the view should be zoomed to after image import\n this.shouldZoomToFit = this.props.zoomLevels[this.props.zoomLevelId];\n } else {\n // Zoom to fit true means find a comfortable zoom level for viewing the costume\n this.shouldZoomToFit = true;\n }\n } else {\n this.props.updateViewBounds(paper.view.matrix);\n }\n\n const context = this.canvas.getContext('2d');\n context.webkitImageSmoothingEnabled = false;\n context.imageSmoothingEnabled = false;\n\n // Don't show handles by default\n paper.settings.handleSize = 0;\n // Make layers.\n setupLayers(this.props.format);\n this.importImage(\n this.props.imageFormat, this.props.image, this.props.rotationCenterX, this.props.rotationCenterY);\n }\n componentWillReceiveProps (newProps) {\n if (this.props.imageId !== newProps.imageId) {\n this.switchCostume(newProps.imageFormat, newProps.image,\n newProps.rotationCenterX, newProps.rotationCenterY,\n this.props.zoomLevelId, newProps.zoomLevelId);\n }\n if (this.props.format !== newProps.format) {\n this.recalibrateSize();\n convertBackgroundGuideLayer(newProps.format);\n }\n }\n componentWillUnmount () {\n this.clearQueuedImport();\n // shouldZoomToFit means the zoom level hasn't been initialized yet\n if (!this.shouldZoomToFit) {\n this.props.saveZoomLevel();\n }\n paper.remove();\n }\n clearQueuedImport () {\n if (this.queuedImport) {\n window.clearTimeout(this.queuedImport);\n this.queuedImport = null;\n }\n if (this.queuedImageToLoad) {\n this.queuedImageToLoad.src = '';\n this.queuedImageToLoad.onload = null;\n this.queuedImageToLoad = null;\n }\n }\n switchCostume (format, image, rotationCenterX, rotationCenterY, oldZoomLevelId, newZoomLevelId) {\n if (oldZoomLevelId && oldZoomLevelId !== newZoomLevelId) {\n this.props.saveZoomLevel();\n }\n if (newZoomLevelId && oldZoomLevelId !== newZoomLevelId) {\n if (this.props.zoomLevels[newZoomLevelId]) {\n this.shouldZoomToFit = this.props.zoomLevels[newZoomLevelId];\n } else {\n this.shouldZoomToFit = true;\n }\n this.props.setZoomLevelId(newZoomLevelId);\n }\n for (const layer of paper.project.layers) {\n if (layer.data.isRasterLayer) {\n clearRaster();\n } else if (!layer.data.isBackgroundGuideLayer &&\n !layer.data.isDragCrosshairLayer &&\n !layer.data.isOutlineLayer) {\n layer.removeChildren();\n }\n }\n this.props.clearUndo();\n this.props.clearSelectedItems();\n this.props.clearHoveredItem();\n this.props.clearPasteOffset();\n this.importImage(format, image, rotationCenterX, rotationCenterY);\n }\n importImage (format, image, rotationCenterX, rotationCenterY) {\n // Stop any in-progress imports\n this.clearQueuedImport();\n\n if (!image) {\n this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT);\n performSnapshot(this.props.undoSnapshot, Formats.VECTOR_SKIP_CONVERT);\n this.recalibrateSize();\n return;\n }\n\n if (format === 'jpg' || format === 'png') {\n // import bitmap\n this.props.changeFormat(Formats.BITMAP_SKIP_CONVERT);\n\n const mask = new paper.Shape.Rectangle(getRaster().getBounds());\n mask.guide = true;\n mask.locked = true;\n mask.setPosition(CENTER);\n mask.clipMask = true;\n\n const imgElement = new Image();\n this.queuedImageToLoad = imgElement;\n imgElement.onload = () => {\n if (!this.queuedImageToLoad) return;\n this.queuedImageToLoad = null;\n\n if (typeof rotationCenterX === 'undefined') {\n rotationCenterX = imgElement.width / 2;\n }\n if (typeof rotationCenterY === 'undefined') {\n rotationCenterY = imgElement.height / 2;\n }\n\n getRaster().drawImage(\n imgElement,\n (ART_BOARD_WIDTH / 2) - rotationCenterX,\n (ART_BOARD_HEIGHT / 2) - rotationCenterY);\n getRaster().drawImage(\n imgElement,\n (ART_BOARD_WIDTH / 2) - rotationCenterX,\n (ART_BOARD_HEIGHT / 2) - rotationCenterY);\n\n this.maybeZoomToFit(true /* isBitmap */);\n performSnapshot(this.props.undoSnapshot, Formats.BITMAP_SKIP_CONVERT);\n this.recalibrateSize();\n };\n imgElement.src = image;\n } else if (format === 'svg') {\n this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT);\n this.importSvg(image, rotationCenterX, rotationCenterY);\n } else {\n log.error(`Didn't recognize format: ${format}. Use 'jpg', 'png' or 'svg'.`);\n this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT);\n performSnapshot(this.props.undoSnapshot, Formats.VECTOR_SKIP_CONVERT);\n this.recalibrateSize();\n }\n }\n maybeZoomToFit (isBitmapMode) {\n if (this.shouldZoomToFit instanceof paper.Matrix) {\n paper.view.matrix = this.shouldZoomToFit;\n this.props.updateViewBounds(paper.view.matrix);\n resizeCrosshair();\n } else if (this.shouldZoomToFit === true) {\n zoomToFit(isBitmapMode);\n }\n this.shouldZoomToFit = false;\n setWorkspaceBounds();\n this.props.updateViewBounds(paper.view.matrix);\n }\n importSvg (svg, rotationCenterX, rotationCenterY) {\n const paperCanvas = this;\n // Pre-process SVG to prevent parsing errors (discussion from #213)\n // 1. Remove svg: namespace on elements.\n // TODO: remove\n svg = svg.split(/<\\s*svg:/).join('<');\n svg = svg.split(/<\\/\\s*svg:/).join('</');\n // 2. Add root svg namespace if it does not exist.\n const svgAttrs = svg.match(/<svg [^>]*>/);\n if (svgAttrs && svgAttrs[0].indexOf('xmlns=') === -1) {\n svg = svg.replace(\n '<svg ', '<svg xmlns=\"http://www.w3.org/2000/svg\" ');\n }\n // 3. Strip elements and attributes that fire on DOM-insertion. paper.js\n // calls importSVG -> appendChild internally, so anything dangerous left\n // in the SVG executes against the embedding origin. DOMPurify's SVG\n // profile drops <script>, <foreignObject>, <a>, event-handler attrs,\n // and similar. Run after the namespace fixups so DOMPurify sees a\n // well-formed document.\n svg = sanitizeSvg.sanitizeSvgText(svg);\n\n // 4. Parse once: read viewBox (translated back for some costumes\n // to render correctly — paper translates it to (0, 0) on import)\n // and strip data-paper-data values that fail JSON.parse (paper.js\n // synchronously throws on these and aborts the whole import).\n const svgDom = new DOMParser().parseFromString(svg, 'text/xml');\n const modified = stripInvalidPaperData(svgDom);\n const viewBox = svgDom.documentElement.attributes.viewBox ?\n svgDom.documentElement.attributes.viewBox.value.match(/\\S+/g) : null;\n if (viewBox) {\n for (let i = 0; i < viewBox.length; i++) {\n viewBox[i] = parseFloat(viewBox[i]);\n }\n }\n if (modified) svg = new XMLSerializer().serializeToString(svgDom);\n\n paper.project.importSVG(svg, {\n expandShapes: true,\n onLoad: function (item) {\n if (!item) {\n log.error('SVG import failed:');\n log.info(svg);\n this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT);\n performSnapshot(paperCanvas.props.undoSnapshot, Formats.VECTOR_SKIP_CONVERT);\n return;\n }\n item.remove();\n\n // Without the callback, rasters' load function has not been called yet, and they are\n // positioned incorrectly\n paperCanvas.queuedImport = paperCanvas.recalibrateSize(() => {\n paperCanvas.props.updateViewBounds(paper.view.matrix);\n paperCanvas.initializeSvg(item, rotationCenterX, rotationCenterY, viewBox);\n });\n }\n });\n }\n initializeSvg (item, rotationCenterX, rotationCenterY, viewBox) {\n if (this.queuedImport) this.queuedImport = null;\n const itemWidth = item.bounds.width;\n const itemHeight = item.bounds.height;\n\n // Get reference to viewbox\n let mask;\n if (item.clipped) {\n for (const child of item.children) {\n if (child.isClipMask()) {\n mask = child;\n break;\n }\n }\n mask.clipMask = false;\n } else {\n mask = new paper.Shape.Rectangle(item.bounds);\n }\n mask.guide = true;\n mask.locked = true;\n mask.matrix = new paper.Matrix(); // Identity\n // Set the artwork to get clipped at the max costume size\n mask.size.height = MAX_WORKSPACE_BOUNDS.height;\n mask.size.width = MAX_WORKSPACE_BOUNDS.width;\n mask.setPosition(CENTER);\n paper.project.activeLayer.addChild(mask);\n mask.clipMask = true;\n\n // Reduce single item nested in groups\n if (item instanceof paper.Group && item.children.length === 1) {\n item = item.reduce();\n }\n\n ensureClockwise(item);\n scaleWithStrokes(item, 2, new paper.Point()); // Import at 2x\n\n // Apply rotation center\n if (typeof rotationCenterX !== 'undefined' && typeof rotationCenterY !== 'undefined') {\n let rotationPoint = new paper.Point(rotationCenterX, rotationCenterY);\n if (viewBox && viewBox.length >= 2 && !isNaN(viewBox[0]) && !isNaN(viewBox[1])) {\n rotationPoint = rotationPoint.subtract(viewBox[0], viewBox[1]);\n }\n item.translate(CENTER.subtract(rotationPoint.multiply(2)));\n } else {\n // Center\n item.translate(CENTER.subtract(itemWidth, itemHeight));\n }\n\n paper.project.activeLayer.insertChild(0, item);\n if (isGroup(item)) {\n // Fixes an issue where we may export empty groups\n for (const child of item.children) {\n if (isGroup(child) && child.children.length === 0) {\n child.remove();\n }\n }\n ungroupItems([item]);\n }\n\n performSnapshot(this.props.undoSnapshot, Formats.VECTOR_SKIP_CONVERT);\n this.maybeZoomToFit();\n }\n onViewResize () {\n setWorkspaceBounds(true /* clipEmpty */);\n clampViewBounds();\n // Fix incorrect paper canvas scale on browser zoom reset\n this.recalibrateSize();\n this.props.updateViewBounds(paper.view.matrix);\n }\n recalibrateSize (callback) {\n // Sets the size that Paper thinks the canvas is to the size the canvas element actually is.\n // When these are out of sync, the mouse events in the paint editor don't line up correctly.\n return window.setTimeout(() => {\n // If the component unmounts, the canvas will be removed from the page, detaching paper.view.\n // This could also be called before paper.view exists.\n // In either case, return early if so without running the callback.\n if (!paper.view) return;\n // Prevent blurriness caused if the \"CSS size\" of the element is a float--\n // setting canvas dimensions to floats floors them, but we need to round instead\n const elemSize = paper.DomElement.getSize(paper.view.element);\n elemSize.width = Math.round(elemSize.width);\n elemSize.height = Math.round(elemSize.height);\n paper.view.setViewSize(elemSize);\n\n if (callback) callback();\n }, 0);\n }\n setCanvas (canvas) {\n this.canvas = canvas;\n if (this.props.canvasRef) {\n this.props.canvasRef(canvas);\n }\n }\n render () {\n return (\n <canvas\n className={styles.paperCanvas}\n ref={this.setCanvas}\n style={{cursor: this.props.cursor}}\n resize=\"true\"\n />\n );\n }\n}\n\nPaperCanvas.propTypes = {\n canvasRef: PropTypes.func,\n changeFormat: PropTypes.func.isRequired,\n clearHoveredItem: PropTypes.func.isRequired,\n clearPasteOffset: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n clearUndo: PropTypes.func.isRequired,\n cursor: PropTypes.string,\n format: PropTypes.oneOf(Object.keys(Formats)),\n image: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.instanceOf(HTMLImageElement)\n ]),\n imageFormat: PropTypes.string, // The incoming image's data format, used during import. The user could switch this.\n imageId: PropTypes.string,\n rotationCenterX: PropTypes.number,\n rotationCenterY: PropTypes.number,\n saveZoomLevel: PropTypes.func.isRequired,\n setZoomLevelId: PropTypes.func.isRequired,\n undoSnapshot: PropTypes.func.isRequired,\n updateViewBounds: PropTypes.func.isRequired,\n zoomLevelId: PropTypes.string,\n zoomLevels: PropTypes.shape({\n currentZoomLevelId: PropTypes.string\n })\n};\nconst mapStateToProps = state => ({\n mode: state.scratchPaint.mode,\n cursor: state.scratchPaint.cursor,\n format: state.scratchPaint.format,\n zoomLevels: state.scratchPaint.zoomLevels\n});\nconst mapDispatchToProps = dispatch => ({\n undoSnapshot: snapshot => {\n dispatch(undoSnapshot(snapshot));\n },\n clearUndo: () => {\n dispatch(clearUndoState());\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearHoveredItem: () => {\n dispatch(clearHoveredItem());\n },\n clearPasteOffset: () => {\n dispatch(clearPasteOffset());\n },\n changeFormat: format => {\n dispatch(changeFormat(format));\n },\n saveZoomLevel: () => {\n dispatch(saveZoomLevel(paper.view.matrix));\n },\n setZoomLevelId: zoomLevelId => {\n dispatch(setZoomLevelId(zoomLevelId));\n },\n updateViewBounds: matrix => {\n dispatch(updateViewBounds(matrix));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(PaperCanvas);\n","/**\n * Drop `data-paper-data` attributes whose value isn't valid JSON. Paper.js\n * synchronously calls JSON.parse on this attribute during importSVG and\n * throws on malformed values, taking down the whole import. The attribute\n * is paper's own serialization metadata; if it can't parse, paper wouldn't\n * have been able to use it.\n *\n * Operates on a parsed Document in place so callers that already have one\n * (e.g. for viewBox extraction) don't pay for a second parse-and-serialize.\n * @param {Document} svgDoc - parsed SVG document; mutated in place.\n * @returns {boolean} true if any attribute was removed (caller should\n * re-serialize); false if the document was untouched.\n */\nconst stripInvalidPaperData = function (svgDoc) {\n let modified = false;\n const els = svgDoc.querySelectorAll('[data-paper-data]');\n for (let i = 0; i < els.length; i++) {\n try {\n JSON.parse(els[i].getAttribute('data-paper-data'));\n } catch {\n els[i].removeAttribute('data-paper-data');\n modified = true;\n }\n }\n return modified;\n};\n\nexport {stripInvalidPaperData};\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./scrollable-canvas.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./scrollable-canvas.css\";\n export default content && content.locals ? content.locals : undefined;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport styles from './scrollable-canvas.css';\n\nconst ScrollableCanvasComponent = props => (\n <div\n className={props.style}\n >\n {props.children}\n <div\n className={styles.horizontalScrollbarWrapper}\n style={{pointerEvents: 'none'}}\n >\n <div\n className={styles.horizontalScrollbarHitbox}\n style={{\n width: `${props.horizontalScrollLengthPercent}%`,\n left: `${props.horizontalScrollStartPercent}%`,\n pointerEvents: 'auto',\n display: `${props.hideScrollbars ||\n Math.abs(props.horizontalScrollLengthPercent - 100) < 1e-8 ? 'none' : 'block'}`\n }}\n onMouseDown={props.onHorizontalScrollbarMouseDown}\n onTouchStart={props.onHorizontalScrollbarMouseDown}\n >\n <div\n className={styles.horizontalScrollbar}\n />\n </div>\n </div>\n <div\n className={styles.verticalScrollbarWrapper}\n style={{pointerEvents: 'none'}}\n >\n <div\n className={styles.verticalScrollbarHitbox}\n style={{\n height: `${props.verticalScrollLengthPercent}%`,\n top: `${props.verticalScrollStartPercent}%`,\n pointerEvents: 'auto',\n display: `${props.hideScrollbars ||\n Math.abs(props.verticalScrollLengthPercent - 100) < 1e-8 ? 'none' : 'block'}`\n }}\n onMouseDown={props.onVerticalScrollbarMouseDown}\n onTouchStart={props.onVerticalScrollbarMouseDown}\n >\n <div\n className={styles.verticalScrollbar}\n />\n </div>\n </div>\n </div>\n);\n\nScrollableCanvasComponent.propTypes = {\n children: PropTypes.node.isRequired,\n hideScrollbars: PropTypes.bool,\n horizontalScrollLengthPercent: PropTypes.number,\n horizontalScrollStartPercent: PropTypes.number,\n onHorizontalScrollbarMouseDown: PropTypes.func.isRequired,\n onVerticalScrollbarMouseDown: PropTypes.func.isRequired,\n style: PropTypes.string,\n verticalScrollLengthPercent: PropTypes.number,\n verticalScrollStartPercent: PropTypes.number\n};\n\nexport default ScrollableCanvasComponent;\n","/* DO NOT EDIT\n@todo This file is copied from GUI and should be pulled out into a shared library.\nSee https://github.com/LLK/scratch-paint/issues/13 */\n\nconst getEventXY = e => {\n if (e.touches && e.touches[0]) {\n return {x: e.touches[0].clientX, y: e.touches[0].clientY};\n } else if (e.changedTouches && e.changedTouches[0]) {\n return {x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY};\n }\n return {x: e.clientX, y: e.clientY};\n};\n\nexport {\n getEventXY\n};\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\n\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport ScrollableCanvasComponent from '../components/scrollable-canvas/scrollable-canvas.jsx';\n\nimport {clampViewBounds, pan, zoomOnFixedPoint, getWorkspaceBounds} from '../helper/view';\nimport {updateViewBounds} from '../reducers/view-bounds';\nimport {redrawSelectionBox} from '../reducers/selected-items';\n\nimport {getEventXY} from '../lib/touch-utils';\nimport bindAll from 'lodash.bindall';\n\nclass ScrollableCanvas extends React.Component {\n static get ZOOM_INCREMENT () {\n return 0.5;\n }\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleHorizontalScrollbarMouseDown',\n 'handleHorizontalScrollbarMouseMove',\n 'handleHorizontalScrollbarMouseUp',\n 'handleVerticalScrollbarMouseDown',\n 'handleVerticalScrollbarMouseMove',\n 'handleVerticalScrollbarMouseUp',\n 'handleWheel'\n ]);\n }\n componentDidMount () {\n if (this.props.canvas) {\n this.props.canvas.addEventListener('wheel', this.handleWheel);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (nextProps.canvas) {\n if (this.props.canvas) {\n this.props.canvas.removeEventListener('wheel', this.handleWheel);\n }\n nextProps.canvas.addEventListener('wheel', this.handleWheel);\n }\n }\n handleHorizontalScrollbarMouseDown (event) {\n this.initialMouseX = getEventXY(event).x;\n this.initialScreenX = paper.view.matrix.tx;\n window.addEventListener('mousemove', this.handleHorizontalScrollbarMouseMove);\n window.addEventListener('touchmove', this.handleHorizontalScrollbarMouseMove, {passive: false});\n window.addEventListener('mouseup', this.handleHorizontalScrollbarMouseUp);\n window.addEventListener('touchend', this.handleHorizontalScrollbarMouseUp);\n event.preventDefault();\n }\n handleHorizontalScrollbarMouseMove (event) {\n const dx = this.initialMouseX - getEventXY(event).x;\n paper.view.matrix.tx = this.initialScreenX + (dx * paper.view.zoom * 2);\n clampViewBounds();\n this.props.updateViewBounds(paper.view.matrix);\n event.preventDefault();\n }\n handleHorizontalScrollbarMouseUp () {\n window.removeEventListener('mousemove', this.handleHorizontalScrollbarMouseMove);\n window.removeEventListener('touchmove', this.handleHorizontalScrollbarMouseMove, {passive: false});\n window.removeEventListener('mouseup', this.handleHorizontalScrollbarMouseUp);\n window.removeEventListener('touchend', this.handleHorizontalScrollbarMouseUp);\n this.initialMouseX = null;\n this.initialScreenX = null;\n event.preventDefault();\n }\n handleVerticalScrollbarMouseDown (event) {\n this.initialMouseY = getEventXY(event).y;\n this.initialScreenY = paper.view.matrix.ty;\n window.addEventListener('mousemove', this.handleVerticalScrollbarMouseMove);\n window.addEventListener('touchmove', this.handleVerticalScrollbarMouseMove, {passive: false});\n window.addEventListener('mouseup', this.handleVerticalScrollbarMouseUp);\n window.addEventListener('touchend', this.handleVerticalScrollbarMouseUp);\n event.preventDefault();\n }\n handleVerticalScrollbarMouseMove (event) {\n const dy = this.initialMouseY - getEventXY(event).y;\n paper.view.matrix.ty = this.initialScreenY + (dy * paper.view.zoom * 2);\n clampViewBounds();\n this.props.updateViewBounds(paper.view.matrix);\n event.preventDefault();\n }\n handleVerticalScrollbarMouseUp (event) {\n window.removeEventListener('mousemove', this.handleVerticalScrollbarMouseMove);\n window.removeEventListener('touchmove', this.handleVerticalScrollbarMouseMove, {passive: false});\n window.removeEventListener('mouseup', this.handleVerticalScrollbarMouseUp);\n window.removeEventListener('touchend', this.handleVerticalScrollbarMouseUp);\n this.initialMouseY = null;\n this.initialScreenY = null;\n event.preventDefault();\n }\n handleWheel (event) {\n // Multiplier variable, so that non-pixel-deltaModes are supported. Needed for Firefox.\n // See #529 (or LLK/scratch-blocks#1190).\n const multiplier = event.deltaMode === 0x1 ? 15 : 1;\n const deltaX = event.deltaX * multiplier;\n const deltaY = event.deltaY * multiplier;\n const canvasRect = this.props.canvas.getBoundingClientRect();\n const offsetX = event.clientX - canvasRect.left;\n const offsetY = event.clientY - canvasRect.top;\n const fixedPoint = paper.view.viewToProject(\n new paper.Point(offsetX, offsetY)\n );\n if (event.metaKey || event.ctrlKey) {\n // Zoom keeping mouse location fixed\n zoomOnFixedPoint(-deltaY / 1000, fixedPoint);\n this.props.updateViewBounds(paper.view.matrix);\n this.props.redrawSelectionBox(); // Selection handles need to be resized after zoom\n } else if (event.shiftKey && event.deltaX === 0) {\n // Scroll horizontally (based on vertical scroll delta)\n // This is needed as for some browser/system combinations which do not set deltaX.\n // See #156.\n const dx = deltaY / paper.view.zoom;\n pan(dx, 0);\n this.props.updateViewBounds(paper.view.matrix);\n } else {\n const dx = deltaX / paper.view.zoom;\n const dy = deltaY / paper.view.zoom;\n pan(dx, dy);\n this.props.updateViewBounds(paper.view.matrix);\n if (paper.tool) {\n paper.tool.view._handleMouseEvent('mousemove', event, fixedPoint);\n }\n }\n event.preventDefault();\n }\n render () {\n let widthPercent = 0;\n let heightPercent = 0;\n let topPercent = 0;\n let leftPercent = 0;\n if (paper.project) {\n const bounds = getWorkspaceBounds();\n const {x, y, width, height} = paper.view.bounds;\n widthPercent = Math.min(100, 100 * width / bounds.width);\n heightPercent = Math.min(100, 100 * height / bounds.height);\n const centerX = (x + (width / 2) - bounds.x) / bounds.width;\n const centerY = (y + (height / 2) - bounds.y) / bounds.height;\n topPercent = Math.max(0, (100 * centerY) - (heightPercent / 2));\n leftPercent = Math.max(0, (100 * centerX) - (widthPercent / 2));\n }\n return (\n <ScrollableCanvasComponent\n hideScrollbars={this.props.hideScrollbars}\n horizontalScrollLengthPercent={widthPercent}\n horizontalScrollStartPercent={leftPercent}\n style={this.props.style}\n verticalScrollLengthPercent={heightPercent}\n verticalScrollStartPercent={topPercent}\n onHorizontalScrollbarMouseDown={this.handleHorizontalScrollbarMouseDown}\n onVerticalScrollbarMouseDown={this.handleVerticalScrollbarMouseDown}\n >\n {this.props.children}\n </ScrollableCanvasComponent>\n );\n }\n}\n\nScrollableCanvas.propTypes = {\n canvas: PropTypes.instanceOf(Element),\n children: PropTypes.node.isRequired,\n hideScrollbars: PropTypes.bool,\n redrawSelectionBox: PropTypes.func.isRequired,\n style: PropTypes.string,\n updateViewBounds: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n viewBounds: state.scratchPaint.viewBounds\n});\nconst mapDispatchToProps = dispatch => ({\n redrawSelectionBox: () => {\n dispatch(redrawSelectionBox());\n },\n updateViewBounds: matrix => {\n dispatch(updateViewBounds(matrix));\n }\n});\n\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(ScrollableCanvas);\n","import keyMirror from 'keymirror';\n\nconst GradientTypes = keyMirror({\n SOLID: null,\n HORIZONTAL: null,\n VERTICAL: null,\n RADIAL: null\n});\nexport default GradientTypes;\n","import log from '../log/log';\nimport {CHANGE_SELECTED_ITEMS} from '../reducers/selected-items';\nimport {getColorsFromSelection, MIXED} from '../helper/style-path';\nimport GradientTypes from './gradient-types';\n\n// Matches hex colors\nconst hexRegex = /^#([0-9a-f]{3}){1,2}$/i;\n\nconst isValidHexColor = color => {\n if (!hexRegex.test(color) && color !== null && color !== MIXED) {\n log.warn(`Invalid hex color code: ${color}`);\n return false;\n }\n return true;\n};\n\nconst makeColorStyleReducer = ({\n // Action name for changing the primary color\n changePrimaryColorAction,\n // Action name for changing the secondary color\n changeSecondaryColorAction,\n // Action name for changing the gradient type\n changeGradientTypeAction,\n // Action name for clearing the gradient\n clearGradientAction,\n // Initial color when not set\n defaultColor,\n // The name of the property read from getColorsFromSelection to get the primary color.\n // e.g. `fillColor` or `strokeColor`.\n selectionPrimaryColorKey,\n // The name of the property read from getColorsFromSelection to get the secondary color.\n // e.g. `fillColor2` or `strokeColor2`.\n selectionSecondaryColorKey,\n // The name of the property read from getColorsFromSelection to get the gradient type.\n // e.g. `fillGradientType` or `strokeGradientType`.\n selectionGradientTypeKey\n}) => function colorReducer (state, action) {\n if (typeof state === 'undefined') {\n state = {\n primary: defaultColor,\n secondary: null,\n gradientType: GradientTypes.SOLID\n };\n }\n switch (action.type) {\n case changePrimaryColorAction:\n if (!isValidHexColor(action.color)) return state;\n return {...state, primary: action.color};\n case changeSecondaryColorAction:\n if (!isValidHexColor(action.color)) return state;\n return {...state, secondary: action.color};\n case CHANGE_SELECTED_ITEMS: {\n // Don't change state if no selection\n if (!action.selectedItems || !action.selectedItems.length) {\n return state;\n }\n const colors = getColorsFromSelection(action.selectedItems, action.bitmapMode);\n\n // Only set the primary color + gradient type if they exist in what getColorsFromSelection gave us.\n // E.g. in bitmap mode, getColorsFromSelection will not return stroke color/gradient type. This allows us to\n // preserve stroke swatch state across bitmap mode-- if getColorsFromSelection set them to null, then selecting\n // anything in bitmap mode would overwrite the stroke state.\n const newState = {...state};\n if (selectionPrimaryColorKey in colors) {\n newState.primary = colors[selectionPrimaryColorKey];\n }\n if (selectionGradientTypeKey in colors) {\n newState.gradientType = colors[selectionGradientTypeKey];\n }\n\n // Gradient type may be solid when multiple gradient types are selected.\n // In this case, changing the first color should not change the second color.\n if (\n selectionSecondaryColorKey in colors &&\n (colors[selectionGradientTypeKey] !== GradientTypes.SOLID ||\n colors[selectionSecondaryColorKey] === MIXED)\n ) {\n newState.secondary = colors[selectionSecondaryColorKey];\n }\n return newState;\n }\n case changeGradientTypeAction:\n if (action.gradientType in GradientTypes) {\n return {...state, gradientType: action.gradientType};\n }\n log.warn(`Gradient type does not exist: ${action.gradientType}`);\n return state;\n case clearGradientAction:\n return {...state, secondary: null, gradientType: GradientTypes.SOLID};\n default:\n return state;\n }\n};\n\nexport default makeColorStyleReducer;\n","import makeColorStyleReducer from '../lib/make-color-style-reducer';\n\nconst CHANGE_FILL_COLOR = 'scratch-paint/fill-style/CHANGE_FILL_COLOR';\nconst CHANGE_FILL_COLOR_2 = 'scratch-paint/fill-style/CHANGE_FILL_COLOR_2';\nconst CHANGE_FILL_GRADIENT_TYPE = 'scratch-paint/fill-style/CHANGE_FILL_GRADIENT_TYPE';\nconst CLEAR_FILL_GRADIENT = 'scratch-paint/fill-style/CLEAR_FILL_GRADIENT';\nconst DEFAULT_COLOR = '#9966FF';\n\nconst reducer = makeColorStyleReducer({\n changePrimaryColorAction: CHANGE_FILL_COLOR,\n changeSecondaryColorAction: CHANGE_FILL_COLOR_2,\n changeGradientTypeAction: CHANGE_FILL_GRADIENT_TYPE,\n clearGradientAction: CLEAR_FILL_GRADIENT,\n defaultColor: DEFAULT_COLOR,\n selectionPrimaryColorKey: 'fillColor',\n selectionSecondaryColorKey: 'fillColor2',\n selectionGradientTypeKey: 'fillGradientType'\n});\n\n// Action creators ==================================\nconst changeFillColor = function (fillColor) {\n return {\n type: CHANGE_FILL_COLOR,\n color: fillColor\n };\n};\n\nconst changeFillColor2 = function (fillColor) {\n return {\n type: CHANGE_FILL_COLOR_2,\n color: fillColor\n };\n};\n\nconst changeFillGradientType = function (gradientType) {\n return {\n type: CHANGE_FILL_GRADIENT_TYPE,\n gradientType\n };\n};\n\nconst clearFillGradient = function () {\n return {\n type: CLEAR_FILL_GRADIENT\n };\n};\n\nexport {\n reducer as default,\n changeFillColor,\n changeFillColor2,\n changeFillGradientType,\n clearFillGradient,\n DEFAULT_COLOR,\n CHANGE_FILL_GRADIENT_TYPE\n};\n","import paper from '@scratch/paper';\nimport {getSelectedLeafItems, getItems} from './selection';\nimport {isPointTextItem} from './item';\nimport {isGroup} from './group';\nimport GradientTypes from '../lib/gradient-types';\nimport {DEFAULT_COLOR} from '../reducers/fill-style';\nimport {isCompoundPathChild} from '../helper/compound-path';\nimport log from '../log/log';\n\nconst MIXED = 'scratch-paint/style-path/mixed';\n\n// Check if the item color matches the incoming color. If the item color is a gradient, we assume\n// that the incoming color never matches, since we don't support gradients yet.\nconst _colorMatch = function (itemColor, incomingColor) {\n if (itemColor && itemColor.type === 'gradient') return false;\n // Either both are null or both are the same color when converted to CSS.\n return (!itemColor && !incomingColor) ||\n (itemColor && incomingColor && itemColor.toCSS() === new paper.Color(incomingColor).toCSS());\n};\n\n// Selected items and currently active text edit items respond to color changes.\nconst _getColorStateListeners = function (textEditTargetId) {\n const items = getSelectedLeafItems();\n if (textEditTargetId) {\n const matches = getItems({\n match: item => item.id === textEditTargetId\n });\n if (matches.length) {\n items.push(matches[0]);\n }\n }\n return items;\n};\n\n/**\n * Transparent R, G, B values need to match the other color of the gradient\n * in order to form a smooth gradient, otherwise it fades through black. This\n * function gets the transparent color for a given color string.\n * @param {?string} colorToMatch CSS string of other color of gradient, or null for transparent\n * @returns {string} CSS string for matching color of transparent\n */\nconst getColorStringForTransparent = function (colorToMatch) {\n const color = new paper.Color(colorToMatch);\n color.alpha = 0;\n return color.toCSS();\n};\n\n/**\n * Generate a color that contrasts well with the passed-in color.\n * @param {string} firstColor The \"primary\" color\n * @returns {string} CSS string for generated color\n */\nconst generateSecondaryColor = function (firstColor) {\n if (firstColor === MIXED) return null;\n const color = new paper.Color(firstColor);\n if (!firstColor || color.alpha === 0) return DEFAULT_COLOR;\n\n color.type = 'hsb';\n const desaturated = color.saturation <= 0.15;\n // If the color is desaturated or dark enough that a hue shift would be hard to see, do a brightness shift.\n if (desaturated || color.brightness <= 0.4) {\n // Choose the shade that contrasts the most with the given color.\n // Use a brightness of 0.1 instead of 0 because if the brightness is 0, it's black and we lose the hue.\n color.brightness = (color.brightness < 0.55 ? 1 : 0.1);\n }\n // If the color was desaturated, don't do a hue shift, as it would be hard to see anyway.\n if (!desaturated) {\n color.hue -= 72;\n }\n // The returned color will be one of three things:\n // 1. If the color was bright and saturated (e.g. colorful), it will be that color, hue-shifted.\n // 2. If the color was dark and saturated, it will be that color, brightened and hue-shifted.\n // 3. If the color was not saturated, it will be that color, brightened or darkened as needed to contrast most.\n return color.toCSS(true /* hex */);\n};\n\n/**\n * Convert params to a paper.Color gradient object\n * @param {?string} color1 CSS string, or null for transparent\n * @param {?string} color2 CSS string, or null for transparent\n * @param {GradientType} gradientType gradient type\n * @param {paper.Rectangle} bounds Bounds of the object\n * @param {?paper.Point} [radialCenter] Where the center of a radial gradient should be, if the gradient is radial.\n * Defaults to center of bounds.\n * @param {number} [minSize] The minimum width/height of the gradient object.\n * @returns {paper.Color} Color object with gradient, may be null or color string if the gradient type is solid\n */\nconst createGradientObject = function (color1, color2, gradientType, bounds, radialCenter, minSize) {\n if (gradientType === GradientTypes.SOLID) return color1;\n if (color1 === null) {\n color1 = getColorStringForTransparent(color2);\n }\n if (color2 === null) {\n color2 = getColorStringForTransparent(color1);\n }\n\n // Force gradients to have a minimum length. If the gradient start and end points are the same or very close\n // (e.g. applying a vertical gradient to a perfectly horizontal line or vice versa), the gradient will not appear.\n if (!minSize) minSize = 1e-2;\n\n let start;\n let end;\n switch (gradientType) {\n case GradientTypes.HORIZONTAL: {\n // clone these points so that adding/subtracting doesn't affect actual bounds\n start = bounds.leftCenter.clone();\n end = bounds.rightCenter.clone();\n\n const gradientSize = Math.abs(end.x - start.x);\n if (gradientSize < minSize) {\n const sizeDiff = (minSize - gradientSize) / 2;\n end.x += sizeDiff;\n start.x -= sizeDiff;\n }\n break;\n }\n case GradientTypes.VERTICAL: {\n // clone these points so that adding/subtracting doesn't affect actual bounds\n start = bounds.topCenter.clone();\n end = bounds.bottomCenter.clone();\n\n const gradientSize = Math.abs(end.y - start.y);\n if (gradientSize < minSize) {\n const sizeDiff = (minSize - gradientSize) / 2;\n end.y += sizeDiff;\n start.y -= sizeDiff;\n }\n break;\n }\n\n case GradientTypes.RADIAL: {\n const halfLongestDimension = Math.max(bounds.width, bounds.height) / 2;\n start = radialCenter || bounds.center;\n end = start.add(new paper.Point(\n Math.max(halfLongestDimension, minSize / 2),\n 0));\n break;\n }\n }\n return {\n gradient: {\n stops: [color1, color2],\n radial: gradientType === GradientTypes.RADIAL\n },\n origin: start,\n destination: end\n };\n};\n\n/**\n * Called when setting an item's color\n * @param {string} colorString color, css format, or null if completely transparent\n * @param {number} colorIndex index of color being changed\n * @param {boolean} isSolidGradient True if is solid gradient. Sometimes the item has a gradient but the color\n * picker is set to a solid gradient. This happens when a mix of colors and gradient types is selected.\n * When changing the color in this case, the solid gradient should override the existing gradient on the item.\n * @param {?boolean} applyToStroke True if changing the selection's stroke, false if changing its fill.\n * @param {?string} textEditTargetId paper.Item.id of text editing target, if any\n * @returns {boolean} Whether the color application actually changed visibly.\n */\nconst applyColorToSelection = function (\n colorString,\n colorIndex,\n isSolidGradient,\n applyToStroke,\n textEditTargetId\n) {\n const items = _getColorStateListeners(textEditTargetId);\n let changed = false;\n for (let item of items) {\n if (item.parent instanceof paper.CompoundPath) {\n item = item.parent;\n }\n\n const itemColorProp = applyToStroke ? 'strokeColor' : 'fillColor';\n const itemColor = item[itemColorProp];\n\n if (isSolidGradient || !itemColor || !itemColor.gradient ||\n !itemColor.gradient.stops.length === 2) {\n // Applying a solid color\n if (!_colorMatch(itemColor, colorString)) {\n changed = true;\n if (isPointTextItem(item) && !colorString) {\n // Allows transparent text to be hit\n item[itemColorProp] = 'rgba(0,0,0,0)';\n } else {\n item[itemColorProp] = colorString;\n }\n }\n } else if (!_colorMatch(itemColor.gradient.stops[colorIndex].color, colorString)) {\n // Changing one color of an existing gradient\n changed = true;\n const otherIndex = colorIndex === 0 ? 1 : 0;\n if (colorString === null) {\n colorString = getColorStringForTransparent(itemColor.gradient.stops[otherIndex].color.toCSS());\n }\n const colors = [0, 0];\n colors[colorIndex] = colorString;\n // If the other color is transparent, its RGB values need to be adjusted for the gradient to be smooth\n if (itemColor.gradient.stops[otherIndex].color.alpha === 0) {\n colors[otherIndex] = getColorStringForTransparent(colorString);\n } else {\n colors[otherIndex] = itemColor.gradient.stops[otherIndex].color.toCSS();\n }\n // There seems to be a bug where setting colors on stops doesn't always update the view, so set gradient.\n itemColor.gradient = {stops: colors, radial: itemColor.gradient.radial};\n }\n }\n return changed;\n};\n\n/**\n * Called to swap gradient colors\n * @param {?boolean} applyToStroke True if changing the selection's stroke, false if changing its fill.\n * @param {?string} textEditTargetId paper.Item.id of text editing target, if any\n * @returns {boolean} Whether the color application actually changed visibly.\n */\nconst swapColorsInSelection = function (applyToStroke, textEditTargetId) {\n const items = _getColorStateListeners(textEditTargetId);\n let changed = false;\n for (const item of items) {\n // If an item is a child path, do not swap colors.\n // At some point, we'll iterate over its parent path, and we don't want to swap colors twice--\n // that would leave us right where we started.\n if (isCompoundPathChild(item)) continue;\n\n const itemColor = applyToStroke ? item.strokeColor : item.fillColor;\n if (!itemColor || !itemColor.gradient || !itemColor.gradient.stops.length === 2) {\n // Only one color; nothing to swap\n continue;\n } else if (!itemColor.gradient.stops[0].color.equals(itemColor.gradient.stops[1].color)) {\n // Changing one color of an existing gradient\n changed = true;\n const colors = [\n itemColor.gradient.stops[1].color.toCSS(),\n itemColor.gradient.stops[0].color.toCSS()\n ];\n // There seems to be a bug where setting colors on stops doesn't always update the view, so set gradient.\n itemColor.gradient = {stops: colors, radial: itemColor.gradient.radial};\n }\n }\n return changed;\n};\n\n/**\n * Called when setting gradient type\n * @param {GradientType} gradientType gradient type\n * @param {?boolean} applyToStroke True if changing the selection's stroke, false if changing its fill.\n * @param {?string} textEditTargetId paper.Item.id of text editing target, if any\n * @returns {boolean} Whether the color application actually changed visibly.\n */\nconst applyGradientTypeToSelection = function (gradientType, applyToStroke, textEditTargetId) {\n const items = _getColorStateListeners(textEditTargetId);\n let changed = false;\n for (let item of items) {\n if (item.parent instanceof paper.CompoundPath) {\n item = item.parent;\n }\n\n const itemColorProp = applyToStroke ? 'strokeColor' : 'fillColor';\n const itemColor = item[itemColorProp];\n\n const hasGradient = itemColor && itemColor.gradient;\n\n let itemColor1;\n if (itemColor === null || itemColor.alpha === 0) {\n // Transparent\n itemColor1 = null;\n } else if (!hasGradient) {\n // Solid color\n itemColor1 = itemColor.toCSS();\n } else if (!itemColor.gradient.stops[0] || itemColor.gradient.stops[0].color.alpha === 0) {\n // Gradient where first color is transparent\n itemColor1 = null;\n } else {\n // Gradient where first color is not transparent\n itemColor1 = itemColor.gradient.stops[0].color.toCSS();\n }\n\n let itemColor2;\n if (!hasGradient || !itemColor.gradient.stops[1]) {\n // If item color is solid or a gradient that has no 2nd color, set the 2nd color based on the first color\n itemColor2 = generateSecondaryColor(itemColor1);\n } else if (itemColor.gradient.stops[1].color.alpha === 0) {\n // Gradient has 2nd color which is transparent\n itemColor2 = null;\n } else {\n // Gradient has 2nd color which is not transparent\n itemColor2 = itemColor.gradient.stops[1].color.toCSS();\n }\n\n if (gradientType === GradientTypes.SOLID) {\n if (itemColor && itemColor.gradient) {\n changed = true;\n item[itemColorProp] = itemColor1;\n }\n continue;\n }\n\n // If this is a stroke, we don't display it as having a gradient in the color picker\n // if there's no stroke width. Then treat it as if it doesn't have a gradient.\n let hasDisplayGradient = hasGradient;\n if (applyToStroke) hasDisplayGradient = hasGradient && item.strokeWidth > 0;\n if (!hasDisplayGradient) {\n const noColorOriginally = !itemColor ||\n (itemColor.gradient &&\n itemColor.gradient.stops &&\n itemColor.gradient.stops[0].color.alpha === 0);\n const addingStroke = applyToStroke && item.strokeWidth === 0;\n const hasGradientNow = itemColor1 || itemColor2;\n if ((noColorOriginally || addingStroke) && hasGradientNow) {\n if (applyToStroke) {\n // Make outline visible\n item.strokeWidth = 1;\n }\n // Make the gradient black to white\n itemColor1 = 'black';\n itemColor2 = 'white';\n }\n }\n\n if (itemColor1 === null) {\n itemColor1 = getColorStringForTransparent(itemColor2);\n }\n if (itemColor2 === null) {\n itemColor2 = getColorStringForTransparent(itemColor1);\n }\n\n let gradientTypeDiffers = false;\n // If the item's gradient type differs from the gradient type we want to apply, then we change it\n switch (gradientType) {\n case GradientTypes.RADIAL: {\n const hasRadialGradient = hasDisplayGradient && itemColor.gradient.radial;\n gradientTypeDiffers = !hasRadialGradient;\n break;\n }\n case GradientTypes.HORIZONTAL: {\n const hasHorizontalGradient = hasDisplayGradient && !itemColor.gradient.radial &&\n Math.abs(itemColor.origin.y - itemColor.destination.y) < 1e-8;\n gradientTypeDiffers = !hasHorizontalGradient;\n break;\n }\n case GradientTypes.VERTICAL: {\n const hasVerticalGradient = hasDisplayGradient && !itemColor.gradient.radial &&\n Math.abs(itemColor.origin.x - itemColor.destination.x) < 1e-8;\n gradientTypeDiffers = !hasVerticalGradient;\n break;\n }\n }\n\n if (gradientTypeDiffers) {\n changed = true;\n item[itemColorProp] = createGradientObject(\n itemColor1,\n itemColor2,\n gradientType,\n item.bounds,\n null, // radialCenter\n item.strokeWidth\n );\n }\n }\n return changed;\n};\n\n/**\n * Called when setting stroke width\n * @param {number} value New stroke width\n * @param {?string} textEditTargetId paper.Item.id of text editing target, if any\n * @returns {boolean} Whether the color application actually changed visibly.\n */\nconst applyStrokeWidthToSelection = function (value, textEditTargetId) {\n let changed = false;\n const items = _getColorStateListeners(textEditTargetId);\n for (let item of items) {\n if (item.parent instanceof paper.CompoundPath) {\n item = item.parent;\n }\n if (isGroup(item)) {\n continue;\n } else if (item.strokeWidth !== value) {\n item.strokeWidth = value;\n changed = true;\n }\n }\n return changed;\n};\n\nconst _colorStateFromGradient = gradient => {\n const colorState = {};\n // Scratch only recognizes 2 color gradients\n if (gradient.stops.length === 2) {\n if (gradient.radial) {\n colorState.gradientType = GradientTypes.RADIAL;\n } else {\n // Always use horizontal for linear gradients, since horizontal and vertical gradients\n // are the same with rotation. We don't want to show MIXED just because anything is rotated.\n colorState.gradientType = GradientTypes.HORIZONTAL;\n }\n colorState.primary = gradient.stops[0].color.alpha === 0 ?\n null :\n gradient.stops[0].color.toCSS();\n colorState.secondary = gradient.stops[1].color.alpha === 0 ?\n null :\n gradient.stops[1].color.toCSS();\n } else {\n if (gradient.stops.length < 2) log.warn(`Gradient has ${gradient.stops.length} stop(s)`);\n\n colorState.primary = MIXED;\n colorState.secondary = MIXED;\n }\n\n return colorState;\n};\n\n/**\n * Get state of colors and stroke width for selection\n * @param {!Array<paper.Item>} selectedItems Selected paper items\n * @param {?boolean} bitmapMode True if the item is being selected in bitmap mode\n * @returns {?object} Object of strokeColor, strokeWidth, fillColor, thickness of the selection.\n * Gives MIXED when there are mixed values for a color, and null for transparent.\n * Gives null when there are mixed values for stroke width.\n * Thickness is line thickness, used in the bitmap editor\n */\nconst getColorsFromSelection = function (selectedItems, bitmapMode) {\n // TODO: DRY out this code\n let selectionFillColorString;\n let selectionFillColor2String;\n let selectionStrokeColorString;\n let selectionStrokeColor2String;\n let selectionStrokeWidth;\n let selectionThickness;\n let selectionFillGradientType;\n let selectionStrokeGradientType;\n let firstChild = true;\n\n for (let item of selectedItems) {\n if (item.parent instanceof paper.CompoundPath) {\n // Compound path children inherit fill and stroke color from their parent.\n item = item.parent;\n }\n let itemFillColorString;\n let itemFillColor2String;\n let itemStrokeColorString;\n let itemStrokeColor2String;\n let itemFillGradientType = GradientTypes.SOLID;\n let itemStrokeGradientType = GradientTypes.SOLID;\n\n if (!isGroup(item)) {\n if (item.fillColor) {\n // hack bc text items with null fill can't be detected by fill-hitTest anymore\n if (isPointTextItem(item) && item.fillColor.alpha === 0) {\n itemFillColorString = null;\n } else if (item.fillColor.type === 'gradient') {\n const {primary, secondary, gradientType} = _colorStateFromGradient(item.fillColor.gradient);\n itemFillColorString = primary;\n itemFillColor2String = secondary;\n itemFillGradientType = gradientType;\n } else {\n itemFillColorString = item.fillColor.alpha === 0 ?\n null :\n item.fillColor.toCSS();\n itemFillColor2String = null;\n }\n }\n if (item.strokeColor) {\n if (item.strokeColor.type === 'gradient') {\n const {primary, secondary, gradientType} = _colorStateFromGradient(item.strokeColor.gradient);\n\n let strokeColorString = primary;\n const strokeColor2String = secondary;\n let strokeGradientType = gradientType;\n\n // If the item's stroke width is 0, pretend the stroke color is null\n if (!item.strokeWidth) {\n strokeColorString = null;\n // Hide the second color. This way if you choose a second color, remove\n // the gradient, and re-add it, your second color selection is preserved.\n strokeGradientType = GradientTypes.SOLID;\n }\n\n // Stroke color is fill color in bitmap\n if (bitmapMode) {\n itemFillColorString = strokeColorString;\n itemFillColor2String = strokeColor2String;\n itemFillGradientType = strokeGradientType;\n } else {\n itemStrokeColorString = strokeColorString;\n itemStrokeColor2String = strokeColor2String;\n itemStrokeGradientType = strokeGradientType;\n }\n } else {\n const strokeColorString = item.strokeColor.alpha === 0 || !item.strokeWidth ?\n null :\n item.strokeColor.toCSS();\n\n // Stroke color is fill color in bitmap\n if (bitmapMode) {\n itemFillColorString = strokeColorString;\n } else {\n itemStrokeColorString = strokeColorString;\n }\n }\n } else {\n itemStrokeColorString = null;\n itemStrokeColor2String = null;\n }\n // check every style against the first of the items\n if (firstChild) {\n firstChild = false;\n selectionFillColorString = itemFillColorString;\n selectionFillColor2String = itemFillColor2String;\n selectionStrokeColorString = itemStrokeColorString;\n selectionStrokeColor2String = itemStrokeColor2String;\n selectionFillGradientType = itemFillGradientType;\n selectionStrokeGradientType = itemStrokeGradientType;\n selectionStrokeWidth = itemStrokeColorString || itemStrokeColor2String ? item.strokeWidth : 0;\n if (item.strokeWidth && item.data && item.data.zoomLevel) {\n selectionThickness = item.strokeWidth / item.data.zoomLevel;\n }\n }\n if (itemFillColorString !== selectionFillColorString) {\n selectionFillColorString = MIXED;\n }\n if (itemFillColor2String !== selectionFillColor2String) {\n selectionFillColor2String = MIXED;\n }\n if (itemFillGradientType !== selectionFillGradientType) {\n selectionFillGradientType = GradientTypes.SOLID;\n selectionFillColorString = MIXED;\n selectionFillColor2String = MIXED;\n }\n if (itemStrokeGradientType !== selectionStrokeGradientType) {\n selectionStrokeGradientType = GradientTypes.SOLID;\n selectionStrokeColorString = MIXED;\n selectionStrokeColor2String = MIXED;\n }\n if (itemStrokeColorString !== selectionStrokeColorString) {\n selectionStrokeColorString = MIXED;\n }\n if (itemStrokeColor2String !== selectionStrokeColor2String) {\n selectionStrokeColor2String = MIXED;\n }\n const itemStrokeWidth = itemStrokeColorString || itemStrokeColor2String ? item.strokeWidth : 0;\n if (selectionStrokeWidth !== itemStrokeWidth) {\n selectionStrokeWidth = null;\n }\n }\n }\n // Convert selection gradient type from horizontal to vertical if first item is exactly vertical\n // This is because up to this point, we assume all non-radial gradients are horizontal\n // Otherwise, if there were a mix of horizontal/vertical gradient types in the selection, they would show as MIXED\n // whereas we want them to show as horizontal (or vertical if the first item is vertical)\n if (selectedItems && selectedItems.length) {\n let firstItem = selectedItems[0];\n if (firstItem.parent instanceof paper.CompoundPath) firstItem = firstItem.parent;\n\n if (selectionFillGradientType !== GradientTypes.SOLID) {\n // Stroke color is fill color in bitmap if fill color is missing\n // TODO: this whole \"treat horizontal/vertical gradients specially\" logic is janky; refactor at some point\n const firstItemColor = (bitmapMode && firstItem.strokeColor) ? firstItem.strokeColor : firstItem.fillColor;\n const direction = firstItemColor.destination.subtract(firstItemColor.origin);\n if (Math.abs(direction.angle) === 90) {\n selectionFillGradientType = GradientTypes.VERTICAL;\n }\n }\n\n if (selectionStrokeGradientType !== GradientTypes.SOLID) {\n const direction = firstItem.strokeColor.destination.subtract(firstItem.strokeColor.origin);\n if (Math.abs(direction.angle) === 90) {\n selectionStrokeGradientType = GradientTypes.VERTICAL;\n }\n }\n }\n if (bitmapMode) {\n return {\n fillColor: selectionFillColorString ? selectionFillColorString : null,\n fillColor2: selectionFillColor2String ? selectionFillColor2String : null,\n fillGradientType: selectionFillGradientType,\n thickness: selectionThickness\n };\n }\n return {\n fillColor: selectionFillColorString ? selectionFillColorString : null,\n fillColor2: selectionFillColor2String ? selectionFillColor2String : null,\n fillGradientType: selectionFillGradientType,\n strokeColor: selectionStrokeColorString ? selectionStrokeColorString : null,\n strokeColor2: selectionStrokeColor2String ? selectionStrokeColor2String : null,\n strokeGradientType: selectionStrokeGradientType,\n strokeWidth: selectionStrokeWidth || (selectionStrokeWidth === null) ? selectionStrokeWidth : 0\n };\n};\n\nconst styleBlob = function (path, options) {\n if (options.isEraser) {\n path.fillColor = 'white';\n } else if (options.fillColor) {\n path.fillColor = options.fillColor;\n } else {\n // Make sure something visible is drawn\n path.fillColor = 'black';\n }\n};\n\nconst styleCursorPreview = function (path, options) {\n if (options.isEraser) {\n path.fillColor = 'white';\n path.strokeColor = 'cornflowerblue';\n path.strokeWidth = 1;\n } else if (options.fillColor) {\n path.fillColor = options.fillColor;\n } else {\n // Make sure something visible is drawn\n path.fillColor = 'black';\n }\n};\n\nconst styleShape = function (path, options) {\n for (const colorKey of ['fillColor', 'strokeColor']) {\n if (options[colorKey] === null) {\n path[colorKey] = null;\n } else if (options[colorKey].gradientType === GradientTypes.SOLID) {\n path[colorKey] = options[colorKey].primary;\n } else {\n const {primary, secondary, gradientType} = options[colorKey];\n path[colorKey] = createGradientObject(\n primary,\n secondary,\n gradientType,\n path.bounds,\n null, // radialCenter\n options.strokeWidth // minimum gradient size is stroke width\n );\n }\n }\n if (Object.prototype.hasOwnProperty.call(options, 'strokeWidth')) path.strokeWidth = options.strokeWidth;\n};\n\nexport {\n applyColorToSelection,\n applyGradientTypeToSelection,\n applyStrokeWidthToSelection,\n createGradientObject,\n getColorsFromSelection,\n generateSecondaryColor,\n MIXED,\n styleBlob,\n styleShape,\n styleCursorPreview,\n swapColorsInSelection\n};\n","import Modes from '../lib/modes';\nimport log from '../log/log';\n\nconst CHANGE_MODE = 'scratch-paint/modes/CHANGE_MODE';\nconst initialState = Modes.SELECT;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_MODE:\n if (action.mode in Modes) {\n return action.mode;\n }\n log.warn(`Mode does not exist: ${action.mode}`);\n /* falls through */\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeMode = function (mode) {\n return {\n type: CHANGE_MODE,\n mode: mode\n };\n};\n\nexport {\n reducer as default,\n changeMode\n};\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./button.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./button.css\";\n export default content && content.locals ? content.locals : undefined;\n","/* DO NOT EDIT\n@todo This file is copied from GUI and should be pulled out into a shared library.\nSee #13 */\n\n/* ACTUALLY, THIS IS EDITED ;)\nTHIS WAS CHANGED ON 10/25/2017 BY @mewtaylor TO ADD HANDLING FOR DISABLED STATES.*/\n\nimport classNames from 'classnames';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport styles from './button.css';\n\nconst ButtonComponent = ({\n className,\n highlighted,\n onClick,\n children,\n ...props\n}) => {\n const disabled = props.disabled || false;\n if (disabled === false) {\n // if not disabled, add `onClick()` to be applied\n // in props. If disabled, don't add `onClick()`\n props.onClick = onClick;\n }\n return (\n <span\n className={classNames(\n styles.button,\n className,\n {\n [styles.modDisabled]: disabled,\n [styles.highlighted]: highlighted\n }\n )}\n role=\"button\"\n {...props}\n >\n {children}\n </span>\n );\n};\n\nButtonComponent.propTypes = {\n children: PropTypes.node,\n className: PropTypes.string,\n disabled: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.bool\n ]),\n highlighted: PropTypes.bool,\n onClick: PropTypes.func.isRequired\n};\nexport default ButtonComponent;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./tool-select-base.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./tool-select-base.css\";\n export default content && content.locals ? content.locals : undefined;\n","import classNames from 'classnames';\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport {useIntl} from 'react-intl';\n\nimport Button from '../button/button.jsx';\n\nimport styles from './tool-select-base.css';\n\nconst ToolSelectComponent = props => {\n const intl = useIntl();\n return (\n <Button\n className={\n classNames(props.className, styles.modToolSelect, {\n [styles.isSelected]: props.isSelected\n })\n }\n disabled={props.disabled}\n title={intl.formatMessage(props.imgDescriptor)}\n onClick={props.onMouseDown}\n >\n <img\n alt={intl.formatMessage(props.imgDescriptor)}\n className={styles.toolSelectIcon}\n draggable={false}\n src={props.imgSrc}\n />\n </Button>\n );\n};\n\nToolSelectComponent.propTypes = {\n className: PropTypes.string,\n disabled: PropTypes.bool,\n imgDescriptor: PropTypes.shape({\n defaultMessage: PropTypes.string,\n description: PropTypes.string,\n id: PropTypes.string\n }).isRequired,\n imgSrc: PropTypes.string.isRequired,\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default ToolSelectComponent;\n","import {defineMessages} from 'react-intl';\n\nconst messages = defineMessages({\n brush: {\n defaultMessage: 'Brush',\n description: 'Label for the brush tool',\n id: 'paint.brushMode.brush'\n },\n eraser: {\n defaultMessage: 'Eraser',\n description: 'Label for the eraser tool',\n id: 'paint.eraserMode.eraser'\n },\n fill: {\n defaultMessage: 'Fill',\n description: 'Label for the fill tool',\n id: 'paint.fillMode.fill'\n },\n line: {\n defaultMessage: 'Line',\n description: 'Label for the line tool',\n id: 'paint.lineMode.line'\n },\n oval: {\n defaultMessage: 'Circle',\n description: 'Label for the oval-drawing tool',\n id: 'paint.ovalMode.oval'\n },\n rect: {\n defaultMessage: 'Rectangle',\n description: 'Label for the rectangle tool',\n id: 'paint.rectMode.rect'\n },\n reshape: {\n defaultMessage: 'Reshape',\n description: 'Label for the reshape tool, which allows changing the points in the lines of the vectors',\n id: 'paint.reshapeMode.reshape'\n },\n roundedRect: {\n defaultMessage: 'Rounded Rectangle',\n description: 'Label for the rounded rectangle tool',\n id: 'paint.roundedRectMode.roundedRect'\n },\n select: {\n defaultMessage: 'Select',\n description: 'Label for the select tool, which allows selecting, moving, and resizing shapes',\n id: 'paint.selectMode.select'\n },\n text: {\n defaultMessage: 'Text',\n description: 'Label for the text tool',\n id: 'paint.textMode.text'\n }\n});\n\nexport default messages;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\n\nimport brushIcon from './brush.svg';\n\nconst BitBrushModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.brush}\n imgSrc={brushIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitBrushModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitBrushModeComponent;\n","import paper from '@scratch/paper';\nimport {getRaster, getGuideLayer} from '../layer';\nimport {forEachLinePoint, getBrushMark} from '../bitmap';\n\n/**\n * Tool for drawing with the bitmap brush and eraser\n */\nclass BrushTool extends paper.Tool {\n /**\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n * @param {boolean} isEraser True if brush should erase\n */\n constructor (onUpdateImage, isEraser) {\n super();\n this.onUpdateImage = onUpdateImage;\n this.isEraser = isEraser;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseMove = this.handleMouseMove;\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseUp = this.handleMouseUp;\n\n this.colorState = null;\n this.active = false;\n this.lastPoint = null;\n this.cursorPreview = null;\n }\n setColor (color) {\n this.color = color;\n this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser || !this.color);\n }\n setBrushSize (size) {\n // For performance, make sure this is an integer\n this.size = Math.max(1, ~~size);\n this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser || !this.color);\n }\n // Draw a brush mark at the given point\n draw (x, y) {\n const roundedUpRadius = Math.ceil(this.size / 2);\n const context = getRaster().getContext('2d');\n if (this.isEraser || !this.color) {\n context.globalCompositeOperation = 'destination-out';\n }\n getRaster().drawImage(this.tmpCanvas, new paper.Point(~~x - roundedUpRadius, ~~y - roundedUpRadius));\n if (this.isEraser || !this.color) {\n context.globalCompositeOperation = 'source-over';\n }\n }\n updateCursorIfNeeded () {\n if (!this.size) {\n return;\n }\n\n // The cursor preview was unattached from the view by an outside process,\n // such as changing costumes or undo.\n if (this.cursorPreview && !this.cursorPreview.parent) {\n this.cursorPreview = null;\n }\n\n if (!this.cursorPreview || !(this.lastSize === this.size && this.lastColor === this.color)) {\n if (this.cursorPreview) {\n this.cursorPreview.remove();\n }\n\n this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser || !this.color);\n this.cursorPreview = new paper.Raster(this.tmpCanvas);\n this.cursorPreview.guide = true;\n this.cursorPreview.parent = getGuideLayer();\n this.cursorPreview.data.isHelperItem = true;\n }\n\n this.lastSize = this.size;\n this.lastColor = this.color;\n }\n handleMouseMove (event) {\n this.updateCursorIfNeeded();\n this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (this.cursorPreview) {\n this.cursorPreview.remove();\n }\n\n this.draw(event.point.x, event.point.y);\n this.lastPoint = event.point;\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n forEachLinePoint(this.lastPoint, event.point, this.draw.bind(this));\n this.lastPoint = event.point;\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n forEachLinePoint(this.lastPoint, event.point, this.draw.bind(this));\n this.onUpdateImage();\n\n this.lastPoint = null;\n this.active = false;\n\n this.updateCursorIfNeeded();\n this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);\n }\n deactivateTool () {\n this.active = false;\n this.tmpCanvas = null;\n if (this.cursorPreview) {\n this.cursorPreview.remove();\n this.cursorPreview = null;\n }\n }\n}\n\nexport default BrushTool;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport {MIXED} from '../helper/style-path';\n\nimport {changeFillColor, clearFillGradient, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {clearSelection} from '../helper/selection';\n\nimport BitBrushModeComponent from '../components/bit-brush-mode/bit-brush-mode.jsx';\nimport BitBrushTool from '../helper/bit-tools/brush-tool';\n\nclass BitBrushMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isBitBrushModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.color !== this.props.color) {\n this.tool.setColor(nextProps.color);\n }\n if (this.tool && nextProps.bitBrushSize !== this.props.bitBrushSize) {\n this.tool.setBrushSize(nextProps.bitBrushSize);\n }\n\n if (nextProps.isBitBrushModeActive && !this.props.isBitBrushModeActive) {\n this.activateTool();\n } else if (!nextProps.isBitBrushModeActive && this.props.isBitBrushModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isBitBrushModeActive !== this.props.isBitBrushModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n this.props.clearGradient();\n // Force the default brush color if fill is MIXED or transparent\n let color = this.props.color;\n if (!color || color === MIXED) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n color = DEFAULT_COLOR;\n }\n this.tool = new BitBrushTool(\n this.props.onUpdateImage\n );\n this.tool.setColor(color);\n this.tool.setBrushSize(this.props.bitBrushSize);\n\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <BitBrushModeComponent\n isSelected={this.props.isBitBrushModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitBrushMode.propTypes = {\n bitBrushSize: PropTypes.number.isRequired,\n clearGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n color: PropTypes.string,\n handleMouseDown: PropTypes.func.isRequired,\n isBitBrushModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n bitBrushSize: state.scratchPaint.bitBrushSize,\n color: state.scratchPaint.color.fillColor.primary,\n isBitBrushModeActive: state.scratchPaint.mode === Modes.BIT_BRUSH\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearGradient: () => {\n dispatch(clearFillGradient());\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_BRUSH));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitBrushMode);\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport lineIcon from './line.svg';\n\nconst BitLineComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.line}\n imgSrc={lineIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitLineComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitLineComponent;\n","import paper from '@scratch/paper';\nimport {getRaster, createCanvas, getGuideLayer} from '../layer';\nimport {forEachLinePoint, getBrushMark} from '../bitmap';\nimport {ART_BOARD_WIDTH, ART_BOARD_HEIGHT} from '../view';\n\n/**\n * Tool for drawing lines with the bitmap brush.\n */\nclass LineTool extends paper.Tool {\n /**\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (onUpdateImage) {\n super();\n this.onUpdateImage = onUpdateImage;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseMove = this.handleMouseMove;\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseUp = this.handleMouseUp;\n\n this.colorState = null;\n this.active = false;\n this.startPoint = null;\n this.cursorPreview = null;\n // Raster to which to draw\n this.drawTarget = null;\n }\n setColor (color) {\n this.color = color;\n this.tmpCanvas = getBrushMark(this.size, this.color);\n }\n setLineSize (size) {\n // For performance, make sure this is an integer\n this.size = Math.max(1, ~~size);\n this.tmpCanvas = getBrushMark(this.size, this.color);\n }\n // Draw a brush mark at the given point\n draw (x, y) {\n const roundedUpRadius = Math.ceil(this.size / 2);\n this.drawTarget.drawImage(this.tmpCanvas, new paper.Point(~~x - roundedUpRadius, ~~y - roundedUpRadius));\n }\n updateCursorIfNeeded () {\n if (!this.size) {\n return;\n }\n // The cursor preview was unattached from the view by an outside process,\n // such as changing costumes or undo.\n if (this.cursorPreview && !this.cursorPreview.parent) {\n this.cursorPreview = null;\n }\n\n if (!this.cursorPreview || !(this.lastSize === this.size && this.lastColor === this.color)) {\n if (this.cursorPreview) {\n this.cursorPreview.remove();\n }\n\n this.tmpCanvas = getBrushMark(this.size, this.color);\n this.cursorPreview = new paper.Raster(this.tmpCanvas);\n this.cursorPreview.guide = true;\n this.cursorPreview.parent = getGuideLayer();\n this.cursorPreview.data.isHelperItem = true;\n }\n this.lastSize = this.size;\n this.lastColor = this.color;\n }\n handleMouseMove (event) {\n this.updateCursorIfNeeded();\n this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (this.cursorPreview) this.cursorPreview.remove();\n\n const tmpCanvas = createCanvas();\n this.drawTarget = new paper.Raster(tmpCanvas);\n this.drawTarget.parent = getGuideLayer();\n this.drawTarget.guide = true;\n this.drawTarget.locked = true;\n this.drawTarget.position = getRaster().position;\n\n this.draw(event.point.x, event.point.y);\n this.startPoint = event.point;\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n // Clear\n const context = this.drawTarget.canvas.getContext('2d');\n context.clearRect(0, 0, ART_BOARD_WIDTH, ART_BOARD_HEIGHT);\n\n forEachLinePoint(this.startPoint, event.point, this.draw.bind(this));\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n this.drawTarget.remove();\n this.drawTarget = getRaster();\n forEachLinePoint(this.startPoint, event.point, this.draw.bind(this));\n this.drawTarget = null;\n this.onUpdateImage();\n\n this.lastPoint = null;\n this.active = false;\n\n this.updateCursorIfNeeded();\n this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);\n }\n deactivateTool () {\n this.active = false;\n this.tmpCanvas = null;\n if (this.cursorPreview) {\n this.cursorPreview.remove();\n this.cursorPreview = null;\n }\n }\n}\n\nexport default LineTool;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport {MIXED} from '../helper/style-path';\n\nimport {changeFillColor, clearFillGradient, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {clearSelection} from '../helper/selection';\n\nimport BitLineModeComponent from '../components/bit-line-mode/bit-line-mode.jsx';\nimport BitLineTool from '../helper/bit-tools/line-tool';\n\nclass BitLineMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isBitLineModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.color !== this.props.color) {\n this.tool.setColor(nextProps.color);\n }\n if (this.tool && nextProps.bitBrushSize !== this.props.bitBrushSize) {\n this.tool.setLineSize(nextProps.bitBrushSize);\n }\n\n if (nextProps.isBitLineModeActive && !this.props.isBitLineModeActive) {\n this.activateTool();\n } else if (!nextProps.isBitLineModeActive && this.props.isBitLineModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isBitLineModeActive !== this.props.isBitLineModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n this.props.clearGradient();\n // Force the default line color if fill is MIXED or transparent\n let color = this.props.color;\n if (!color || color === MIXED) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n color = DEFAULT_COLOR;\n }\n this.tool = new BitLineTool(\n this.props.onUpdateImage\n );\n this.tool.setColor(color);\n this.tool.setLineSize(this.props.bitBrushSize);\n\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <BitLineModeComponent\n isSelected={this.props.isBitLineModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitLineMode.propTypes = {\n bitBrushSize: PropTypes.number.isRequired,\n clearGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n color: PropTypes.string,\n handleMouseDown: PropTypes.func.isRequired,\n isBitLineModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n bitBrushSize: state.scratchPaint.bitBrushSize,\n color: state.scratchPaint.color.fillColor.primary,\n isBitLineModeActive: state.scratchPaint.mode === Modes.BIT_LINE\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearGradient: () => {\n dispatch(clearFillGradient());\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_LINE));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitLineMode);\n","import {PropTypes} from 'prop-types';\n\nimport GradientTypes from './gradient-types';\n\nexport default PropTypes.shape({\n primary: PropTypes.string,\n secondary: PropTypes.string,\n gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired\n});\n","const Cursors = {\n DEFAULT: 'default',\n GRAB: 'grab',\n GRABBING: 'grabbing',\n NONE: 'none',\n RESIZE_EW: 'ew-resize',\n RESIZE_NS: 'ns-resize',\n RESIZE_NESW: 'nesw-resize',\n RESIZE_NWSE: 'nwse-resize'\n};\n\nexport default Cursors;\n","const ACTIVATE_EYE_DROPPER = 'scratch-paint/eye-dropper/ACTIVATE_COLOR_PICKER';\nconst DEACTIVATE_EYE_DROPPER = 'scratch-paint/eye-dropper/DEACTIVATE_COLOR_PICKER';\n\nconst initialState = {\n active: false,\n callback: () => {}, // this will either be `onChangeFillColor` or `onChangeOutlineColor`\n previousTool: null // the tool that was previously active before eye dropper\n};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case ACTIVATE_EYE_DROPPER:\n return Object.assign(\n {},\n state,\n {\n active: true,\n callback: action.callback,\n previousTool: action.previousMode\n }\n );\n case DEACTIVATE_EYE_DROPPER:\n return Object.assign(\n {},\n state,\n {\n active: false,\n callback: () => {},\n previousTool: null\n }\n );\n default:\n return state;\n }\n};\n\nconst activateEyeDropper = function (currentMode, callback) {\n return {\n type: ACTIVATE_EYE_DROPPER,\n callback: callback,\n previousMode: currentMode\n };\n};\nconst deactivateEyeDropper = function () {\n return {\n type: DEACTIVATE_EYE_DROPPER\n };\n};\n\nexport {\n reducer as default,\n activateEyeDropper,\n deactivateEyeDropper,\n ACTIVATE_EYE_DROPPER,\n DEACTIVATE_EYE_DROPPER\n};\n","import log from '../log/log';\n\nimport Cursors from '../lib/cursors';\nimport {ACTIVATE_EYE_DROPPER, DEACTIVATE_EYE_DROPPER} from './eye-dropper';\n\nconst CHANGE_CURSOR = 'scratch-paint/cursor/CHANGE_CURSOR';\nconst initialState = Cursors.DEFAULT;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_CURSOR:\n if (typeof action.cursorString === 'undefined') {\n log.warn(`Cursor should not be set to undefined. Use 'default'.`);\n return state;\n } else if (!Object.values(Cursors).includes(action.cursorString)) {\n log.warn(`Cursor should be a valid cursor string. Got: ${action.cursorString}`);\n }\n return action.cursorString;\n case ACTIVATE_EYE_DROPPER:\n return Cursors.NONE;\n case DEACTIVATE_EYE_DROPPER:\n return Cursors.DEFAULT;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n/**\n * Set the mouse cursor state to the given string\n * @param {string} cursorString The CSS cursor string.\n * @returns {object} Redux action to change the cursor.\n */\nconst setCursor = function (cursorString) {\n return {\n type: CHANGE_CURSOR,\n cursorString: cursorString\n };\n};\n\nexport {\n reducer as default,\n setCursor\n};\n","import paper from '@scratch/paper';\nimport {getItems} from '../selection';\nimport {getActionBounds} from '../view';\nimport {BitmapModes} from '../../lib/modes';\n\nconst MIN_SCALE_FACTOR = 0.0001;\n\n/**\n * Tool to handle scaling items by pulling on the handles around the edges of the bounding\n * box when in the bounding box tool.\n */\nclass ScaleTool {\n /**\n * @param {Mode} mode Paint editor mode\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (mode, onUpdateImage) {\n this.isBitmap = mode in BitmapModes;\n this.active = false;\n this.boundsPath = null;\n this.pivot = null;\n this.origPivot = null;\n this.corner = null;\n this.origSize = null;\n this.origCenter = null;\n this.itemGroup = null;\n // Lowest item above all scale items in z index\n this.itemToInsertBelow = null;\n this.lastPoint = null;\n this.onUpdateImage = onUpdateImage;\n }\n\n /**\n * @param {!paper.HitResult} hitResult Data about the location of the mouse click\n * @param {!object} boundsPath Where the boundaries of the hit item are\n * @param {!Array.<paper.Item>} selectedItems Set of selected paper.Items\n */\n onMouseDown (hitResult, boundsPath, selectedItems) {\n if (this.active) return;\n this.active = true;\n\n const index = hitResult.item.data.index;\n this.pivot = boundsPath.bounds[this._getOpposingRectCornerNameByIndex(index)].clone();\n this.origPivot = boundsPath.bounds[this._getOpposingRectCornerNameByIndex(index)].clone();\n this.corner = boundsPath.bounds[this._getRectCornerNameByIndex(index)].clone();\n this.selectionAnchor = boundsPath.selectionAnchor;\n this.origSize = this.corner.subtract(this.pivot);\n this.origCenter = boundsPath.bounds.center;\n this.isCorner = this._isCorner(index);\n this.centered = false;\n this.lastSx = 1;\n this.lastSy = 1;\n this.boundsPath = boundsPath;\n\n // Set itemGroup\n // get item to insert below so that scaled items stay in same z position\n const items = getItems({\n match: function (item) {\n for (const scaleItem of selectedItems) {\n if (!scaleItem.isBelow(item)) {\n return false;\n }\n }\n return true;\n }\n });\n if (items.length > 0) {\n this.itemToInsertBelow = items[0];\n }\n\n this.itemGroup = new paper.Group(selectedItems);\n this.itemGroup.addChild(boundsPath);\n this.itemGroup.insertBelow(this.itemToInsertBelow);\n this.itemGroup.data.isHelperItem = true;\n }\n onMouseDrag (event) {\n if (!this.active) return;\n const point = event.point;\n const bounds = getActionBounds(this.isBitmap);\n point.x = Math.max(bounds.left, Math.min(point.x, bounds.right));\n point.y = Math.max(bounds.top, Math.min(point.y, bounds.bottom));\n\n if (!this.lastPoint) this.lastPoint = event.lastPoint;\n const delta = point.subtract(this.lastPoint);\n this.lastPoint = point;\n\n if (event.modifiers.alt) {\n this.centered = true;\n this.itemGroup.position = this.origCenter;\n this.pivot = this.origCenter;\n } else {\n if (this.centered) {\n // Reset position if we were just in alt\n this.centered = false;\n this.itemGroup.scale(1 / this.lastSx, 1 / this.lastSy, this.pivot);\n if (this.selectionAnchor) {\n this.selectionAnchor.scale(this.lastSx, this.lastSy);\n }\n this.lastSx = 1;\n this.lastSy = 1;\n }\n this.pivot = this.origPivot;\n }\n\n this.corner = this.corner.add(delta);\n let size = this.corner.subtract(this.pivot);\n if (event.modifiers.alt) {\n size = size.multiply(2);\n }\n let sx = 1.0;\n let sy = 1.0;\n if (Math.abs(this.origSize.x) > 0.0000001) {\n sx = size.x / this.origSize.x;\n }\n if (Math.abs(this.origSize.y) > 0.0000001) {\n sy = size.y / this.origSize.y;\n }\n\n const signx = sx > 0 ? 1 : -1;\n const signy = sy > 0 ? 1 : -1;\n if (this.isCorner && !event.modifiers.shift) {\n sx = sy = Math.max(Math.abs(sx), Math.abs(sy));\n sx *= signx;\n sy *= signy;\n }\n sx = signx * Math.max(Math.abs(sx), MIN_SCALE_FACTOR);\n sy = signy * Math.max(Math.abs(sy), MIN_SCALE_FACTOR);\n this.itemGroup.scale(sx / this.lastSx, sy / this.lastSy, this.pivot);\n if (this.selectionAnchor) {\n this.selectionAnchor.scale(this.lastSx / sx, this.lastSy / sy);\n }\n this.lastSx = sx;\n this.lastSy = sy;\n }\n onMouseUp () {\n if (!this.active) return;\n this.lastPoint = null;\n\n this.pivot = null;\n this.origPivot = null;\n this.corner = null;\n this.origSize = null;\n this.origCenter = null;\n this.lastSx = 1;\n this.lastSy = 1;\n this.centered = false;\n\n if (!this.itemGroup) {\n return;\n }\n this.boundsPath.remove();\n this.boundsPath = null;\n\n // mark text items as scaled (for later use on font size calc)\n for (let i = 0; i < this.itemGroup.children.length; i++) {\n const child = this.itemGroup.children[i];\n if (child.data.isPGTextItem) {\n child.data.wasScaled = true;\n }\n }\n\n if (this.itemToInsertBelow) {\n // No increment step because itemGroup.children is getting depleted\n for (const i = 0; i < this.itemGroup.children.length;) {\n this.itemGroup.children[i].insertBelow(this.itemToInsertBelow);\n }\n this.itemToInsertBelow = null;\n } else if (this.itemGroup.layer) {\n this.itemGroup.layer.addChildren(this.itemGroup.children);\n }\n this.itemGroup.remove();\n\n this.onUpdateImage();\n this.active = false;\n }\n _getRectCornerNameByIndex (index) {\n switch (index) {\n case 0:\n return 'bottomLeft';\n case 1:\n return 'leftCenter';\n case 2:\n return 'topLeft';\n case 3:\n return 'topCenter';\n case 4:\n return 'topRight';\n case 5:\n return 'rightCenter';\n case 6:\n return 'bottomRight';\n case 7:\n return 'bottomCenter';\n }\n }\n _getOpposingRectCornerNameByIndex (index) {\n switch (index) {\n case 0:\n return 'topRight';\n case 1:\n return 'rightCenter';\n case 2:\n return 'bottomRight';\n case 3:\n return 'bottomCenter';\n case 4:\n return 'bottomLeft';\n case 5:\n return 'leftCenter';\n case 6:\n return 'topLeft';\n case 7:\n return 'topCenter';\n }\n }\n _isCorner (index) {\n switch (index) {\n case 0:\n case 2:\n case 4:\n case 6:\n return true;\n default:\n return false;\n }\n }\n}\n\nexport default ScaleTool;\n","import paper from '@scratch/paper';\n\n/**\n * Tool to handle rotation when dragging the rotation handle in the bounding box tool.\n */\nclass RotateTool {\n /**\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (onUpdateImage) {\n this.rotItems = [];\n this.rotGroupPivot = null;\n this.prevRot = 90;\n this.onUpdateImage = onUpdateImage;\n }\n\n /**\n * @param {!paper.HitResult} hitResult Data about the location of the mouse click\n * @param {!object} boundsPath Where the boundaries of the hit item are\n * @param {!Array.<paper.Item>} selectedItems Set of selected paper.Items\n */\n onMouseDown (hitResult, boundsPath, selectedItems) {\n this.rotGroupPivot = boundsPath.bounds.center;\n for (const item of selectedItems) {\n // Rotate only root items\n if (item.parent instanceof paper.Layer) {\n this.rotItems.push(item);\n }\n }\n this.prevRot = 90;\n }\n onMouseDrag (event) {\n let rotAngle = (event.point.subtract(this.rotGroupPivot)).angle;\n if (event.modifiers.shift) {\n rotAngle = Math.round(rotAngle / 45) * 45;\n }\n\n for (let i = 0; i < this.rotItems.length; i++) {\n const item = this.rotItems[i];\n\n item.rotate(rotAngle - this.prevRot, this.rotGroupPivot);\n }\n\n this.prevRot = rotAngle;\n }\n onMouseUp (event) {\n if (event.event.button > 0) return; // only first mouse button\n\n this.rotItems.length = 0;\n this.rotGroupPivot = null;\n this.prevRot = 90;\n\n this.onUpdateImage();\n }\n}\n\nexport default RotateTool;\n","import paper from '@scratch/paper';\nimport Modes, {BitmapModes} from '../../lib/modes';\nimport {isGroup} from '../group';\nimport {isCompoundPathItem, getRootItem} from '../item';\nimport {checkPointsClose, snapDeltaToAngle} from '../math';\nimport {getActionBounds, CENTER} from '../view';\nimport {\n clearSelection, cloneSelection, getSelectedLeafItems, getSelectedRootItems, setItemSelection\n} from '../selection';\nimport {getDragCrosshairLayer, CROSSHAIR_FULL_OPACITY} from '../layer';\n\n/** Snap to align selection center to rotation center within this distance */\nconst SNAPPING_THRESHOLD = 4;\nconst FADE_DISTANCE = 10;\n\n/**\n * Tool to handle dragging an item to reposition it in a selection mode.\n */\nclass MoveTool {\n /**\n * @param {Modes} mode Paint editor mode\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n * @param {?Function} switchToTextTool A callback to call to switch to the text tool\n */\n constructor (mode, setSelectedItems, clearSelectedItems, onUpdateImage, switchToTextTool) {\n this.mode = mode;\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.selectedItems = null;\n this.selectionCenter = null;\n this.onUpdateImage = onUpdateImage;\n this.switchToTextTool = switchToTextTool;\n this.boundsPath = null;\n this.firstDrag = false;\n }\n\n /**\n * @param {!object} hitProperties Describes the mouse event\n * @param {!paper.HitResult} hitProperties.hitResult Data about the location of the mouse click\n * @param {?boolean} hitProperties.clone Whether to clone on mouse down (e.g. alt key held)\n * @param {?boolean} hitProperties.multiselect Whether to multiselect on mouse down (e.g. shift key held)\n * @param {?boolean} hitProperties.doubleClicked True if this is the second click in a short amount of time\n * @param {?boolean} hitProperties.subselect True if we allow selection of subgroups, false if we should\n * select the whole group.\n */\n onMouseDown (hitProperties) {\n let item = hitProperties.hitResult.item;\n if (!hitProperties.subselect) {\n const root = getRootItem(hitProperties.hitResult.item);\n item = isCompoundPathItem(root) || isGroup(root) ? root : hitProperties.hitResult.item;\n }\n if (item.selected) {\n // Double click causes all points to be selected in subselect mode. If the target is text, it\n // enters text edit.\n if (hitProperties.doubleClicked) {\n if (!hitProperties.multiselect) {\n if (this.switchToTextTool && item instanceof paper.PointText) {\n this.switchToTextTool();\n return;\n }\n clearSelection(this.clearSelectedItems);\n }\n this._select(item, true /* state */, hitProperties.subselect, true /* fullySelect */);\n } else if (hitProperties.multiselect) {\n this._select(item, false /* state */, hitProperties.subselect);\n }\n } else {\n // deselect all by default if multiselect isn't on\n if (!hitProperties.multiselect) {\n clearSelection(this.clearSelectedItems);\n }\n this._select(item, true, hitProperties.subselect);\n }\n if (hitProperties.clone) cloneSelection(hitProperties.subselect, this.onUpdateImage);\n\n this.selectedItems = this.mode === Modes.RESHAPE ? getSelectedLeafItems() : getSelectedRootItems();\n if (this.selectedItems.length === 0) {\n return;\n }\n\n let selectionBounds;\n for (const selectedItem of this.selectedItems) {\n if (selectionBounds) {\n selectionBounds = selectionBounds.unite(selectedItem.bounds);\n } else {\n selectionBounds = selectedItem.bounds;\n }\n }\n this.selectionCenter = selectionBounds.center;\n\n if (this.boundsPath) {\n this.selectedItems.push(this.boundsPath);\n }\n\n this.firstDrag = true;\n }\n setBoundsPath (boundsPath) {\n this.boundsPath = boundsPath;\n }\n /**\n * Sets the selection state of an item.\n * @param {!paper.Item} item Item to select or deselect\n * @param {?boolean} state True if item should be selected, false if deselected\n * @param {?boolean} subselect True if a subset of all points in an item are allowed to be\n * selected, false if items must be selected all or nothing.\n * @param {?boolean} fullySelect True if in addition to the item being selected, all of its\n * control points should be selected. False if the item should be selected but not its\n * points. Only relevant when subselect is true.\n */\n _select (item, state, subselect, fullySelect) {\n if (subselect) {\n item.selected = false;\n if (fullySelect) {\n item.fullySelected = state;\n } else {\n item.selected = state;\n }\n } else {\n setItemSelection(item, state);\n }\n this.setSelectedItems();\n }\n onMouseDrag (event) {\n const point = event.point;\n const actionBounds = getActionBounds(this.mode in BitmapModes);\n\n point.x = Math.max(actionBounds.left, Math.min(point.x, actionBounds.right));\n point.y = Math.max(actionBounds.top, Math.min(point.y, actionBounds.bottom));\n\n const dragVector = point.subtract(event.downPoint);\n let snapVector;\n\n // Snapping to align center. Not in reshape mode, because reshape doesn't show center crosshair\n if (!event.modifiers.shift && this.mode !== Modes.RESHAPE) {\n if (checkPointsClose(\n this.selectionCenter.add(dragVector),\n CENTER,\n SNAPPING_THRESHOLD / paper.view.zoom /* threshold */)) {\n\n snapVector = CENTER.subtract(this.selectionCenter);\n }\n }\n if (this.selectedItems.length === 0) {\n return;\n }\n\n let bounds;\n for (const item of this.selectedItems) {\n // add the position of the item before the drag started\n // for later use in the snap calculation\n if (!item.data.origPos) {\n item.data.origPos = item.position;\n }\n\n if (snapVector) {\n item.position = item.data.origPos.add(snapVector);\n } else if (event.modifiers.shift) {\n item.position = item.data.origPos.add(snapDeltaToAngle(dragVector, Math.PI / 4));\n } else {\n item.position = item.data.origPos.add(dragVector);\n }\n\n if (bounds) {\n bounds = bounds.unite(item.bounds);\n } else {\n bounds = item.bounds;\n }\n }\n\n if (this.firstDrag) {\n // Show the center crosshair above the selected item while dragging.\n getDragCrosshairLayer().visible = true;\n this.firstDrag = false;\n }\n\n // The rotation center crosshair should be opaque over the entire selection bounding box, and fade out to\n // totally transparent outside the selection bounding box.\n let opacityMultiplier = 1;\n const newCenter = this.selectionCenter.add(dragVector);\n if ((CENTER.y < bounds.top && CENTER.x < bounds.left) ||\n (CENTER.y > bounds.bottom && CENTER.x < bounds.left) ||\n (CENTER.y < bounds.top && CENTER.x > bounds.right) ||\n (CENTER.y > bounds.bottom && CENTER.x > bounds.right)) {\n\n // rotation center is to one of the 4 corners of the selection bounding box\n const distX = Math.max(CENTER.x - bounds.right, bounds.left - CENTER.x);\n const distY = Math.max(CENTER.y - bounds.bottom, bounds.top - CENTER.y);\n const dist = Math.sqrt((distX * distX) + (distY * distY));\n opacityMultiplier =\n Math.max(0, (1 - (dist / (FADE_DISTANCE / paper.view.zoom))));\n } else if (CENTER.y < bounds.top || CENTER.y > bounds.bottom) {\n // rotation center is above or below the selection bounding box\n opacityMultiplier = Math.max(0,\n (1 - ((Math.abs(CENTER.y - newCenter.y) - (bounds.height / 2)) / (FADE_DISTANCE / paper.view.zoom))));\n } else if (CENTER.x < bounds.left || CENTER.x > bounds.right) {\n // rotation center is left or right of the selection bounding box\n opacityMultiplier = Math.max(0,\n (1 - ((Math.abs(CENTER.x - newCenter.x) - (bounds.width / 2)) / (FADE_DISTANCE / paper.view.zoom))));\n } // else the rotation center is within selection bounds, always show drag crosshair at full opacity\n getDragCrosshairLayer().opacity = CROSSHAIR_FULL_OPACITY * opacityMultiplier;\n }\n onMouseUp () {\n this.firstDrag = false;\n let moved = false;\n // resetting the items origin point for the next usage\n for (const item of this.selectedItems) {\n if (item.data.origPos) {\n if (!item.position.equals(item.data.origPos)) moved = true;\n delete item.data.origPos;\n }\n }\n this.selectedItems = null;\n this.selectionCenter = null;\n\n if (moved) {\n this.onUpdateImage();\n }\n\n // Hide the crosshair we showed earlier.\n getDragCrosshairLayer().visible = false;\n }\n}\n\nexport default MoveTool;\n","import paper from '@scratch/paper';\nimport keyMirror from 'keymirror';\n\nimport {getSelectedRootItems} from '../selection';\nimport {getGuideColor, removeBoundsPath, removeBoundsHandles} from '../guides';\nimport {getGuideLayer, setGuideItem} from '../layer';\n\nimport Cursors from '../../lib/cursors';\nimport ScaleTool from './scale-tool';\nimport RotateTool from './rotate-tool';\nimport MoveTool from './move-tool';\n\nconst SELECTION_ANCHOR_SIZE = 12;\n/** SVG for the rotation icon on the bounding box */\nconst ARROW_PATH = 'M19.28,1.09C19.28.28,19,0,18.2,0c-1.67,0-3.34,0-5,0-.34,0-.88.24-1,.47a1.4,1.4,' +\n '0,0,0,.36,1.08,15.27,15.27,0,0,0,1.46,1.36A6.4,6.4,0,0,1,6.52,4,5.85,5.85,0,0,1,5.24,3,15.27,15.27,' +\n '0,0,0,6.7,1.61,1.4,1.4,0,0,0,7.06.54C7,.3,6.44.07,6.1.06c-1.67,0-3.34,0-5,0C.28,0,0,.31,0,1.12c0,1.67,' +\n '0,3.34,0,5a1.23,1.23,0,0,0,.49,1,1.22,1.22,0,0,0,1-.31A14.38,14.38,0,0,0,2.84,5.26l.73.62a9.45,9.45,' +\n '0,0,0,7.34,2,9.45,9.45,0,0,0,4.82-2.05l.73-.62a14.38,14.38,0,0,0,1.29,1.51,1.22,1.22,' +\n '0,0,0,1,.31,1.23,1.23,0,0,0,.49-1C19.31,4.43,19.29,2.76,19.28,1.09Z';\n/** Modes of the bounding box tool, which can do many things depending on how it's used. */\nconst BoundingBoxModes = keyMirror({\n SCALE: null,\n ROTATE: null,\n MOVE: null\n});\n\n/**\n * Tool that handles transforming the selection and drawing a bounding box with handles.\n * On mouse down, the type of function (move, scale, rotate) is determined based on what is clicked\n * (scale handle, rotate handle, the object itself). This determines the mode of the tool, which then\n * delegates actions to the MoveTool, RotateTool or ScaleTool accordingly.\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\nclass BoundingBoxTool {\n /**\n * @param {Modes} mode Paint editor mode\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n * @param {?Function} switchToTextTool A callback to call to switch to the text tool\n */\n constructor (mode, setSelectedItems, clearSelectedItems, setCursor, onUpdateImage, switchToTextTool) {\n this.dispatchSetCursor = setCursor;\n this.onUpdateImage = onUpdateImage;\n this.mode = null;\n this.boundsPath = null;\n this.boundsScaleHandles = [];\n this.boundsRotHandles = [];\n this._modeMap = {};\n this._modeMap[BoundingBoxModes.SCALE] = new ScaleTool(mode, onUpdateImage);\n this._modeMap[BoundingBoxModes.ROTATE] = new RotateTool(onUpdateImage);\n this._modeMap[BoundingBoxModes.MOVE] =\n new MoveTool(mode, setSelectedItems, clearSelectedItems, onUpdateImage, switchToTextTool);\n this._currentCursor = null;\n }\n\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {?Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n if (selectedItems && selectedItems.length) {\n this.setSelectionBounds();\n } else {\n this.removeBoundsPath();\n }\n }\n\n /**\n * @param {!MouseEvent} event The mouse event\n * @param {boolean} clone Whether to clone on mouse down (e.g. alt key held)\n * @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)\n * @param {?boolean} doubleClicked True if this is the second click in a short amount of time\n * @param {paper.hitOptions} hitOptions The options with which to detect whether mouse down has hit\n * anything editable\n * @returns {boolean} True if there was a hit, false otherwise\n */\n onMouseDown (event, clone, multiselect, doubleClicked, hitOptions) {\n if (event.event.button > 0) return; // only first mouse button\n const {hitResult, mode} = this._determineMode(event, multiselect, hitOptions);\n if (!hitResult) {\n if (!multiselect) {\n this.removeBoundsPath();\n }\n return false;\n }\n this.mode = mode;\n\n const hitProperties = {\n hitResult: hitResult,\n clone: clone,\n multiselect: multiselect,\n doubleClicked: doubleClicked\n };\n if (this.mode === BoundingBoxModes.MOVE) {\n this._modeMap[this.mode].onMouseDown(hitProperties);\n this.removeBoundsHandles();\n } else if (this.mode === BoundingBoxModes.SCALE) {\n this._modeMap[this.mode].onMouseDown(hitResult, this.boundsPath, getSelectedRootItems());\n this.removeBoundsHandles();\n } else if (this.mode === BoundingBoxModes.ROTATE) {\n this.setCursor(Cursors.GRABBING);\n this._modeMap[this.mode].onMouseDown(hitResult, this.boundsPath, getSelectedRootItems());\n // While transforming, don't show bounds\n this.removeBoundsPath();\n }\n\n return true;\n }\n onMouseMove (event, hitOptions) {\n this._updateCursor(event, hitOptions);\n }\n _updateCursor (event, hitOptions) {\n const {mode, hitResult} = this._determineMode(event, false, hitOptions);\n if (hitResult) {\n if (mode === BoundingBoxModes.MOVE) {\n this.setCursor(Cursors.DEFAULT);\n } else if (mode === BoundingBoxModes.ROTATE) {\n this.setCursor(Cursors.GRAB);\n } else if (mode === BoundingBoxModes.SCALE) {\n this.setSelectionBounds();\n if (this._impreciseEqual(hitResult.item.position.x, this.boundsPath.position.x)) {\n this.setCursor(Cursors.RESIZE_NS);\n } else if (this._impreciseEqual(hitResult.item.position.y, this.boundsPath.position.y)) {\n this.setCursor(Cursors.RESIZE_EW);\n } else if (\n hitResult.item.position.equals(this.boundsPath.bounds.bottomLeft) ||\n hitResult.item.position.equals(this.boundsPath.bounds.topRight)\n ) {\n this.setCursor(Cursors.RESIZE_NESW);\n } else {\n this.setCursor(Cursors.RESIZE_NWSE);\n }\n }\n } else {\n this.setCursor(Cursors.DEFAULT);\n }\n }\n _impreciseEqual (a, b) {\n // This is the same math paper.js uses to check if two numbers are \"equal\".\n return Math.abs(a - b) < 1e-8;\n }\n _determineMode (event, multiselect, hitOptions) {\n const hitResults = paper.project.hitTestAll(event.point, hitOptions);\n\n let mode;\n\n // Prefer scale to trigger over rotate, and scale and rotate to trigger over other hits\n let hitResult = hitResults[0];\n for (let i = 0; i < hitResults.length; i++) {\n if (hitResults[i].item.data && hitResults[i].item.data.isScaleHandle) {\n hitResult = hitResults[i];\n mode = BoundingBoxModes.SCALE;\n break;\n } else if (hitResults[i].item.data && hitResults[i].item.data.isRotHandle) {\n hitResult = hitResults[i];\n mode = BoundingBoxModes.ROTATE;\n }\n }\n if (!mode) {\n mode = BoundingBoxModes.MOVE;\n }\n\n return {mode, hitResult};\n }\n onMouseDrag (event) {\n if (event.event.button > 0 || !this.mode) return; // only first mouse button\n this._modeMap[this.mode].onMouseDrag(event);\n\n // Set the cursor for moving a sprite once the drag has actually started (i.e. the mouse has been moved while\n // pressed), so that the mouse doesn't \"flash\" to the grabbing cursor every time a sprite is clicked.\n if (this.mode === BoundingBoxModes.MOVE) {\n this.setCursor(Cursors.GRABBING);\n }\n }\n onMouseUp (event, hitOptions) {\n if (event.event.button > 0 || !this.mode) return; // only first mouse button\n this._modeMap[this.mode].onMouseUp(event);\n\n // After transforming, show bounds again\n this.setSelectionBounds();\n this.mode = null;\n this._updateCursor(event, hitOptions);\n }\n setSelectionBounds () {\n this.removeBoundsPath();\n\n const items = getSelectedRootItems();\n if (items.length <= 0) return;\n\n let rect = null;\n for (const item of items) {\n if (item instanceof paper.Raster && item.loaded === false) {\n item.onLoad = this.setSelectionBounds.bind(this);\n return;\n }\n\n if (rect) {\n rect = rect.unite(item.bounds);\n } else {\n rect = item.bounds;\n }\n }\n\n if (!this.boundsPath) {\n this.boundsPath = new paper.Group();\n this.boundsRect = paper.Path.Rectangle(rect);\n this.boundsRect.curves[0].divideAtTime(0.5);\n this.boundsRect.curves[2].divideAtTime(0.5);\n this.boundsRect.curves[4].divideAtTime(0.5);\n this.boundsRect.curves[6].divideAtTime(0.5);\n this.boundsPath.addChild(this.boundsRect);\n\n const vRect = new paper.Path.Rectangle({\n point: [-1, -6],\n size: [2, 12],\n radius: 1,\n insert: false\n });\n const hRect = new paper.Path.Rectangle({\n point: [-6, -1],\n size: [12, 2],\n radius: 1,\n insert: false\n });\n const anchorIcon = vRect.unite(hRect);\n\n this.boundsPath.addChild(anchorIcon);\n this.boundsPath.selectionAnchor = anchorIcon;\n this._modeMap[BoundingBoxModes.MOVE].setBoundsPath(this.boundsPath);\n }\n setGuideItem(this.boundsPath);\n this.boundsPath.data.isSelectionBound = true;\n this.boundsPath.data.isHelperItem = true;\n this.boundsPath.fillColor = null;\n this.boundsPath.parent = getGuideLayer();\n this.boundsPath.strokeWidth = 1 / paper.view.zoom;\n this.boundsPath.strokeColor = getGuideColor();\n this.boundsPath.selectionAnchor.scale(\n SELECTION_ANCHOR_SIZE / paper.view.zoom / this.boundsPath.selectionAnchor.bounds.width);\n this.boundsPath.selectionAnchor.position = rect.center;\n\n // Make a template to copy\n const boundsScaleCircleShadow =\n new paper.Path.Circle({\n center: new paper.Point(0, 0),\n radius: 5.5 / paper.view.zoom,\n fillColor: 'black',\n opacity: .12,\n data: {\n isHelperItem: true,\n noSelect: true,\n noHover: true\n }\n });\n const boundsScaleCircle =\n new paper.Path.Circle({\n center: new paper.Point(0, 0),\n radius: 4 / paper.view.zoom,\n fillColor: getGuideColor(),\n data: {\n isScaleHandle: true,\n isHelperItem: true,\n noSelect: true,\n noHover: true\n }\n });\n const boundsScaleHandle = new paper.Group([boundsScaleCircleShadow, boundsScaleCircle]);\n boundsScaleHandle.parent = getGuideLayer();\n\n for (let index = 0; index < this.boundsRect.segments.length; index++) {\n const segment = this.boundsRect.segments[index];\n\n if (index === 7) {\n const offset = new paper.Point(0, 20);\n\n const arrows = new paper.Path(ARROW_PATH);\n arrows.translate(segment.point.add(offset).add(-10.5, -5));\n\n const line = new paper.Path.Rectangle(\n segment.point.add(offset).subtract(1, 0),\n segment.point);\n\n const rotHandle = arrows.unite(line);\n line.remove();\n arrows.remove();\n rotHandle.scale(1 / paper.view.zoom, segment.point);\n rotHandle.data = {\n offset: offset,\n isRotHandle: true,\n isHelperItem: true,\n noSelect: true,\n noHover: true\n };\n rotHandle.fillColor = getGuideColor();\n rotHandle.parent = getGuideLayer();\n this.boundsRotHandles[index] = rotHandle;\n }\n\n this.boundsScaleHandles[index] = boundsScaleHandle.clone();\n this.boundsScaleHandles[index].position = segment.point;\n for (const child of this.boundsScaleHandles[index].children) {\n child.data.index = index;\n }\n this.boundsScaleHandles[index].data = {\n index: index,\n isScaleHandle: true,\n isHelperItem: true,\n noSelect: true,\n noHover: true\n };\n }\n // Remove the template\n boundsScaleHandle.remove();\n }\n removeBoundsPath () {\n removeBoundsPath();\n this.boundsPath = null;\n this.boundsRect = null;\n this.boundsScaleHandles.length = 0;\n this.boundsRotHandles.length = 0;\n }\n removeBoundsHandles () {\n removeBoundsHandles();\n this.boundsScaleHandles.length = 0;\n this.boundsRotHandles.length = 0;\n }\n deactivateTool () {\n this.removeBoundsPath();\n this.setCursor(Cursors.DEFAULT);\n }\n\n setCursor (cursorString) {\n if (this._currentCursor !== cursorString) {\n this.dispatchSetCursor(cursorString);\n this._currentCursor = cursorString;\n }\n }\n}\n\nexport default BoundingBoxTool;\n","import paper from '@scratch/paper';\nimport {getSelectedRootItems} from '../selection';\nimport {getActionBounds} from '../view';\nimport {BitmapModes} from '../../lib/modes';\n\nconst NUDGE_MORE_MULTIPLIER = 15;\n\n/**\n * Tool containing handlers for arrow key events for nudging the selection.\n * Note that this tool is built for selection mode, not reshape mode.\n */\nclass NudgeTool {\n /**\n * @param {Mode} mode Paint editor mode\n * @param {function} boundingBoxTool to control the bounding box\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (mode, boundingBoxTool, onUpdateImage) {\n this.boundingBoxTool = boundingBoxTool;\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool.isBitmap = mode in BitmapModes;\n }\n onKeyDown (event) {\n if (event.event.target instanceof HTMLInputElement) {\n // Ignore nudge if a text input field is focused\n return;\n }\n\n let nudgeAmount = 1 / paper.view.zoom;\n if (event.modifiers.shift) nudgeAmount *= NUDGE_MORE_MULTIPLIER;\n\n const selected = getSelectedRootItems();\n if (selected.length === 0) return;\n\n // Get bounds. Don't let item bounds go out of bounds.\n let rect;\n for (const item of selected) {\n if (rect) {\n rect = rect.unite(item.bounds);\n } else {\n rect = item.bounds;\n }\n }\n const bounds = getActionBounds(this.boundingBoxTool.isBitmap);\n const bottom = bounds.bottom - rect.top - 1;\n const top = bounds.top - rect.bottom + 1;\n const left = bounds.left - rect.right + 1;\n const right = bounds.right - rect.left - 1;\n\n let translation;\n if (event.key === 'up') {\n translation = new paper.Point(0, Math.min(bottom, Math.max(-nudgeAmount, top)));\n } else if (event.key === 'down') {\n translation = new paper.Point(0, Math.max(top, Math.min(nudgeAmount, bottom)));\n } else if (event.key === 'left') {\n translation = new paper.Point(Math.min(right, Math.max(-nudgeAmount, left)), 0);\n } else if (event.key === 'right') {\n translation = new paper.Point(Math.max(left, Math.min(nudgeAmount, right)), 0);\n }\n\n if (translation) {\n for (const item of selected) {\n item.translate(translation);\n }\n this.boundingBoxTool.setSelectionBounds();\n event.preventDefault();\n }\n }\n onKeyUp (event) {\n const selected = getSelectedRootItems();\n if (selected.length === 0) return;\n\n if (event.key === 'up' || event.key === 'down' || event.key === 'left' || event.key === 'right') {\n this.onUpdateImage();\n }\n }\n}\n\nexport default NudgeTool;\n","import paper from '@scratch/paper';\nimport Modes from '../../lib/modes';\nimport {styleShape} from '../style-path';\nimport {commitOvalToBitmap} from '../bitmap';\nimport {getRaster} from '../layer';\nimport {clearSelection} from '../selection';\nimport {getSquareDimensions} from '../math';\nimport BoundingBoxTool from '../selection-tools/bounding-box-tool';\nimport NudgeTool from '../selection-tools/nudge-tool';\n\n/**\n * Tool for drawing ovals.\n */\nclass OvalTool extends paper.Tool {\n static get TOLERANCE () {\n return 2;\n }\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, setCursor, onUpdateImage) {\n super();\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool = new BoundingBoxTool(\n Modes.BIT_OVAL,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage\n );\n const nudgeTool = new NudgeTool(Modes.BIT_OVAL, this.boundingBoxTool, onUpdateImage);\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = nudgeTool.onKeyUp;\n this.onKeyDown = nudgeTool.onKeyDown;\n\n this.oval = null;\n this.color = null;\n this.active = false;\n }\n getHitOptions () {\n return {\n segments: false,\n stroke: true,\n curves: false,\n fill: true,\n guide: false,\n match: hitResult =>\n (hitResult.item.data && (hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle)) ||\n hitResult.item.selected, // Allow hits on bounding box and selected only\n tolerance: OvalTool.TOLERANCE / paper.view.zoom\n };\n }\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n if ((!this.oval || !this.oval.isInserted()) &&\n selectedItems && selectedItems.length === 1 && selectedItems[0].shape === 'ellipse') {\n // Infer that an undo occurred and get back the active oval\n this.oval = selectedItems[0];\n if (this.oval.data.zoomLevel !== paper.view.zoom) {\n this.oval.strokeWidth = this.oval.strokeWidth / this.oval.data.zoomLevel * paper.view.zoom;\n this.oval.data.zoomLevel = paper.view.zoom;\n this.thickness = this.oval.strokeWidth;\n }\n this.filled = this.oval.strokeWidth === 0;\n // We don't need to set our color from the selected oval's color because the color state reducers will\n // do that for us every time the selection changes.\n } else if (this.oval && this.oval.isInserted() && !this.oval.selected) {\n // Oval got deselected\n this.commitOval();\n }\n }\n styleOval () {\n styleShape(this.oval, {\n fillColor: this.filled ? this.color : null,\n strokeColor: this.filled ? null : this.color,\n strokeWidth: this.filled ? 0 : this.thickness\n });\n }\n setColor (color) {\n this.color = color;\n if (this.oval) this.styleOval();\n }\n setFilled (filled) {\n if (this.filled === filled) return;\n this.filled = filled;\n if (this.oval && this.oval.isInserted()) {\n this.styleOval();\n this.onUpdateImage();\n }\n }\n setThickness (thickness) {\n if (this.thickness === thickness * paper.view.zoom) return;\n this.thickness = thickness * paper.view.zoom;\n if (this.oval && this.oval.isInserted() && !this.filled) {\n this.oval.strokeWidth = this.thickness;\n }\n if (this.oval && this.oval.isInserted()) {\n this.oval.data.zoomLevel = paper.view.zoom;\n this.onUpdateImage();\n }\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (this.boundingBoxTool.onMouseDown(\n event, false /* clone */, false /* multiselect */, false /* doubleClicked */, this.getHitOptions())) {\n this.isBoundingBoxMode = true;\n } else {\n this.isBoundingBoxMode = false;\n clearSelection(this.clearSelectedItems);\n this.commitOval();\n this.oval = new paper.Shape.Ellipse({\n point: event.downPoint,\n size: 0,\n strokeScaling: false\n });\n this.styleOval();\n this.oval.data = {zoomLevel: paper.view.zoom};\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseDrag(event);\n return;\n }\n\n const downPoint = new paper.Point(event.downPoint.x, event.downPoint.y);\n const point = new paper.Point(event.point.x, event.point.y);\n const squareDimensions = getSquareDimensions(event.downPoint, event.point);\n if (event.modifiers.shift) {\n this.oval.size = squareDimensions.size.abs();\n } else {\n this.oval.size = downPoint.subtract(point);\n }\n\n if (event.modifiers.alt) {\n this.oval.position = downPoint;\n } else if (event.modifiers.shift) {\n this.oval.position = squareDimensions.position;\n } else {\n this.oval.position = downPoint.subtract(this.oval.size.multiply(0.5));\n }\n this.styleOval();\n }\n handleMouseMove (event) {\n this.boundingBoxTool.onMouseMove(event, this.getHitOptions());\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseUp(event);\n this.isBoundingBoxMode = null;\n return;\n }\n\n if (this.oval) {\n if (Math.abs(this.oval.size.width * this.oval.size.height) < OvalTool.TOLERANCE / paper.view.zoom) {\n // Tiny oval created unintentionally?\n this.oval.remove();\n this.oval = null;\n } else {\n // Hit testing does not work correctly unless the width and height are positive\n this.oval.size = new paper.Point(Math.abs(this.oval.size.width), Math.abs(this.oval.size.height));\n this.oval.selected = true;\n this.styleOval();\n this.setSelectedItems();\n }\n }\n this.active = false;\n this.onUpdateImage();\n }\n commitOval () {\n if (!this.oval || !this.oval.isInserted()) return;\n\n commitOvalToBitmap(this.oval, getRaster());\n this.oval.remove();\n this.oval = null;\n }\n deactivateTool () {\n this.commitOval();\n this.boundingBoxTool.deactivateTool();\n }\n}\n\nexport default OvalTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport ovalIcon from './oval.svg';\n\nconst BitOvalComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.oval}\n imgSrc={ovalIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitOvalComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitOvalComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport {MIXED} from '../helper/style-path';\n\nimport {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {clearSelection, getSelectedLeafItems} from '../helper/selection';\nimport OvalTool from '../helper/bit-tools/oval-tool';\nimport OvalModeComponent from '../components/bit-oval-mode/bit-oval-mode.jsx';\n\nclass BitOvalMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isOvalModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool) {\n if (nextProps.color !== this.props.color) {\n this.tool.setColor(nextProps.color);\n }\n if (nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n if (nextProps.filled !== this.props.filled) {\n this.tool.setFilled(nextProps.filled);\n }\n if (nextProps.thickness !== this.props.thickness ||\n nextProps.zoom !== this.props.zoom) {\n this.tool.setThickness(nextProps.thickness);\n }\n }\n\n if (nextProps.isOvalModeActive && !this.props.isOvalModeActive) {\n this.activateTool();\n } else if (!nextProps.isOvalModeActive && this.props.isOvalModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isOvalModeActive !== this.props.isOvalModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n // Force the default brush color if fill is MIXED or transparent\n const fillColorPresent = this.props.color.primary !== MIXED && this.props.color.primary !== null;\n if (!fillColorPresent) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n }\n this.tool = new OvalTool(\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage\n );\n this.tool.setColor(this.props.color);\n this.tool.setFilled(this.props.filled);\n this.tool.setThickness(this.props.thickness);\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <OvalModeComponent\n isSelected={this.props.isOvalModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitOvalMode.propTypes = {\n clearSelectedItems: PropTypes.func.isRequired,\n color: ColorStyleProptype,\n filled: PropTypes.bool,\n handleMouseDown: PropTypes.func.isRequired,\n isOvalModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n thickness: PropTypes.number.isRequired,\n zoom: PropTypes.number.isRequired\n};\n\nconst mapStateToProps = state => ({\n color: state.scratchPaint.color.fillColor,\n filled: state.scratchPaint.fillBitmapShapes,\n isOvalModeActive: state.scratchPaint.mode === Modes.BIT_OVAL,\n selectedItems: state.scratchPaint.selectedItems,\n thickness: state.scratchPaint.bitBrushSize,\n zoom: state.scratchPaint.viewBounds.scaling.x\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setCursor: cursorString => {\n dispatch(setCursor(cursorString));\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), true /* bitmapMode */));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_OVAL));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitOvalMode);\n","import paper from '@scratch/paper';\nimport Modes from '../../lib/modes';\nimport {styleShape} from '../../helper/style-path';\nimport {commitRectToBitmap} from '../bitmap';\nimport {getRaster} from '../layer';\nimport {clearSelection} from '../selection';\nimport {getSquareDimensions} from '../math';\nimport BoundingBoxTool from '../selection-tools/bounding-box-tool';\nimport NudgeTool from '../selection-tools/nudge-tool';\n\n/**\n * Tool for drawing rects.\n */\nclass RectTool extends paper.Tool {\n static get TOLERANCE () {\n return 2;\n }\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, setCursor, onUpdateImage) {\n super();\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool = new BoundingBoxTool(\n Modes.BIT_RECT,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage\n );\n const nudgeTool = new NudgeTool(Modes.BIT_RECT, this.boundingBoxTool, onUpdateImage);\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = nudgeTool.onKeyUp;\n this.onKeyDown = nudgeTool.onKeyDown;\n\n this.rect = null;\n this.color = null;\n this.active = false;\n }\n getHitOptions () {\n return {\n segments: false,\n stroke: true,\n curves: false,\n fill: true,\n guide: false,\n match: hitResult =>\n (hitResult.item.data && (hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle)) ||\n hitResult.item.selected, // Allow hits on bounding box and selected only\n tolerance: RectTool.TOLERANCE / paper.view.zoom\n };\n }\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n if ((!this.rect || !this.rect.isInserted()) &&\n selectedItems && selectedItems.length === 1 && selectedItems[0].shape === 'rectangle') {\n // Infer that an undo occurred and get back the active rect\n this.rect = selectedItems[0];\n if (this.rect.data.zoomLevel !== paper.view.zoom) {\n this.rect.strokeWidth = this.rect.strokeWidth / this.rect.data.zoomLevel * paper.view.zoom;\n this.rect.data.zoomLevel = paper.view.zoom;\n this.thickness = this.rect.strokeWidth;\n }\n this.filled = this.rect.strokeWidth === 0;\n } else if (this.rect && this.rect.isInserted() && !this.rect.selected) {\n // Rectangle got deselected\n this.commitRect();\n }\n }\n styleRect () {\n styleShape(this.rect, {\n fillColor: this.filled ? this.color : null,\n strokeColor: this.filled ? null : this.color,\n strokeWidth: this.filled ? 0 : this.thickness\n });\n }\n setColor (color) {\n this.color = color;\n if (this.rect) this.styleRect();\n }\n setFilled (filled) {\n if (this.filled === filled) return;\n this.filled = filled;\n if (this.rect && this.rect.isInserted()) {\n this.styleRect();\n this.onUpdateImage();\n }\n }\n setThickness (thickness) {\n if (this.thickness === thickness * paper.view.zoom) return;\n this.thickness = thickness * paper.view.zoom;\n if (this.rect && this.rect.isInserted() && !this.filled) {\n this.rect.strokeWidth = this.thickness;\n }\n if (this.rect && this.rect.isInserted()) {\n this.rect.data.zoomLevel = paper.view.zoom;\n this.onUpdateImage();\n }\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (this.boundingBoxTool.onMouseDown(\n event, false /* clone */, false /* multiselect */, false /* doubleClicked */, this.getHitOptions())) {\n this.isBoundingBoxMode = true;\n } else {\n this.isBoundingBoxMode = false;\n clearSelection(this.clearSelectedItems);\n this.commitRect();\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseDrag(event);\n return;\n }\n\n const dimensions = event.point.subtract(event.downPoint);\n const baseRect = new paper.Rectangle(event.downPoint, event.point);\n const squareDimensions = getSquareDimensions(event.downPoint, event.point);\n if (event.modifiers.shift) {\n baseRect.size = squareDimensions.size.abs();\n }\n\n if (this.rect) this.rect.remove();\n this.rect = new paper.Shape.Rectangle(baseRect);\n this.rect.strokeJoin = 'round';\n this.rect.strokeScaling = false;\n this.rect.data = {zoomLevel: paper.view.zoom};\n this.styleRect();\n\n if (event.modifiers.alt) {\n this.rect.position = event.downPoint;\n } else if (event.modifiers.shift) {\n this.rect.position = squareDimensions.position;\n } else {\n this.rect.position = event.downPoint.add(dimensions.multiply(.5));\n }\n }\n handleMouseMove (event) {\n this.boundingBoxTool.onMouseMove(event, this.getHitOptions());\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseUp(event);\n this.isBoundingBoxMode = null;\n return;\n }\n\n if (this.rect) {\n if (Math.abs(this.rect.size.width * this.rect.size.height) < RectTool.TOLERANCE / paper.view.zoom) {\n // Tiny shape created unintentionally?\n this.rect.remove();\n this.rect = null;\n } else {\n // Hit testing does not work correctly unless the width and height are positive\n this.rect.size = new paper.Point(Math.abs(this.rect.size.width), Math.abs(this.rect.size.height));\n this.rect.selected = true;\n this.styleRect();\n this.setSelectedItems();\n }\n }\n this.active = false;\n this.onUpdateImage();\n }\n commitRect () {\n if (!this.rect || !this.rect.isInserted()) return;\n\n commitRectToBitmap(this.rect, getRaster());\n\n this.rect.remove();\n this.rect = null;\n }\n deactivateTool () {\n this.commitRect();\n this.boundingBoxTool.deactivateTool();\n }\n}\n\nexport default RectTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport rectIcon from './rectangle.svg';\n\nconst BitRectComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.rect}\n imgSrc={rectIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitRectComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitRectComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport {MIXED} from '../helper/style-path';\n\nimport {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {clearSelection, getSelectedLeafItems} from '../helper/selection';\nimport RectTool from '../helper/bit-tools/rect-tool';\nimport RectModeComponent from '../components/bit-rect-mode/bit-rect-mode.jsx';\n\nclass BitRectMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isRectModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool) {\n if (nextProps.color !== this.props.color) {\n this.tool.setColor(nextProps.color);\n }\n if (nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n if (nextProps.filled !== this.props.filled) {\n this.tool.setFilled(nextProps.filled);\n }\n if (nextProps.thickness !== this.props.thickness ||\n nextProps.zoom !== this.props.zoom) {\n this.tool.setThickness(nextProps.thickness);\n }\n }\n\n if (nextProps.isRectModeActive && !this.props.isRectModeActive) {\n this.activateTool();\n } else if (!nextProps.isRectModeActive && this.props.isRectModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isRectModeActive !== this.props.isRectModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n // Force the default brush color if fill is MIXED or transparent\n const fillColorPresent = this.props.color.primary !== MIXED && this.props.color.primary !== null;\n if (!fillColorPresent) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n }\n this.tool = new RectTool(\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage\n );\n this.tool.setColor(this.props.color);\n this.tool.setFilled(this.props.filled);\n this.tool.setThickness(this.props.thickness);\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <RectModeComponent\n isSelected={this.props.isRectModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitRectMode.propTypes = {\n clearSelectedItems: PropTypes.func.isRequired,\n color: ColorStyleProptype,\n filled: PropTypes.bool,\n handleMouseDown: PropTypes.func.isRequired,\n isRectModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n thickness: PropTypes.number.isRequired,\n zoom: PropTypes.number.isRequired\n};\n\nconst mapStateToProps = state => ({\n color: state.scratchPaint.color.fillColor,\n filled: state.scratchPaint.fillBitmapShapes,\n isRectModeActive: state.scratchPaint.mode === Modes.BIT_RECT,\n selectedItems: state.scratchPaint.selectedItems,\n thickness: state.scratchPaint.bitBrushSize,\n zoom: state.scratchPaint.viewBounds.scaling.x\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setCursor: cursorString => {\n dispatch(setCursor(cursorString));\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), true /* bitmapMode */));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_RECT));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitRectMode);\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport fillIcon from './fill.svg';\n\nconst BitFillComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.fill}\n imgSrc={fillIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitFillComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitFillComponent;\n","// Gradient type shown in the fill tool. This is the last gradient type explicitly chosen by the user,\n// and isn't overwritten by changing the selection.\nimport GradientTypes from '../lib/gradient-types';\nimport log from '../log/log';\nimport {CHANGE_FILL_GRADIENT_TYPE} from './fill-style';\n\nconst initialState = null;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_FILL_GRADIENT_TYPE:\n if (action.gradientType in GradientTypes) {\n return action.gradientType;\n }\n log.warn(`Gradient type does not exist: ${action.gradientType}`);\n /* falls through */\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n// Use this for user-initiated gradient type selections only.\n// See reducers/fill-style.js for other ways gradient type changes.\nconst changeGradientType = function (gradientType) {\n return {\n type: CHANGE_FILL_GRADIENT_TYPE,\n gradientType: gradientType\n };\n};\n\nexport {\n reducer as default,\n changeGradientType\n};\n","import paper from '@scratch/paper';\nimport {floodFill, floodFillAll, getHitBounds} from '../bitmap';\nimport {createGradientObject} from '../style-path';\nimport {createCanvas, getRaster} from '../layer';\nimport GradientTypes from '../../lib/gradient-types';\n\nconst TRANSPARENT = 'rgba(0,0,0,0)';\n/**\n * Tool for drawing fills.\n */\nclass FillTool extends paper.Tool {\n /**\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (onUpdateImage) {\n super();\n this.onUpdateImage = onUpdateImage;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n\n this.color = null;\n this.color2 = null;\n this.gradientType = null;\n this.active = false;\n }\n setColor (color) {\n this.color = color;\n }\n setColor2 (color2) {\n this.color2 = color2;\n }\n setGradientType (gradientType) {\n this.gradientType = gradientType;\n }\n handleMouseDown (event) {\n this.paint(event);\n }\n handleMouseDrag (event) {\n this.paint(event);\n }\n paint (event) {\n const sourceContext = getRaster().getContext('2d');\n let destContext = sourceContext;\n let color = this.color;\n // Paint to a mask instead of the original canvas when drawing\n if (this.gradientType !== GradientTypes.SOLID) {\n const tmpCanvas = createCanvas();\n destContext = tmpCanvas.getContext('2d');\n color = 'black';\n } else if (!color) {\n // Null color means transparent because that is the standard in vector\n color = TRANSPARENT;\n }\n let changed = false;\n if (event.event.shiftKey) {\n changed = floodFillAll(event.point.x, event.point.y, color, sourceContext, destContext);\n } else {\n changed = floodFill(event.point.x, event.point.y, color, sourceContext, destContext);\n }\n if (changed && this.gradientType !== GradientTypes.SOLID) {\n const raster = new paper.Raster({insert: false});\n raster.canvas = destContext.canvas;\n raster.onLoad = () => {\n raster.position = getRaster().position;\n // Erase what's already there\n getRaster().getContext().globalCompositeOperation = 'destination-out';\n getRaster().drawImage(raster.canvas, new paper.Point());\n getRaster().getContext().globalCompositeOperation = 'source-over';\n\n // Create the gradient to be masked\n const hitBounds = getHitBounds(raster);\n if (!hitBounds.area) return;\n const gradient = new paper.Shape.Rectangle({\n insert: false,\n rectangle: {\n topLeft: hitBounds.topLeft,\n bottomRight: hitBounds.bottomRight\n }\n });\n gradient.fillColor = createGradientObject(\n this.color,\n this.color2,\n this.gradientType,\n gradient.bounds,\n event.point);\n const rasterGradient = gradient.rasterize(getRaster().resolution.width, false /* insert */);\n\n // Mask gradient\n raster.getContext().globalCompositeOperation = 'source-in';\n raster.drawImage(rasterGradient.canvas, rasterGradient.bounds.topLeft);\n\n // Draw masked gradient into raster layer\n getRaster().drawImage(raster.canvas, new paper.Point());\n this.onUpdateImage();\n };\n } else if (changed) {\n this.onUpdateImage();\n }\n }\n deactivateTool () {\n }\n}\n\nexport default FillTool;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport GradientTypes from '../lib/gradient-types';\n\nimport FillModeComponent from '../components/bit-fill-mode/bit-fill-mode.jsx';\n\nimport {changeFillColor, changeFillColor2, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {changeGradientType} from '../reducers/fill-mode-gradient-type';\nimport {clearSelection} from '../helper/selection';\nimport FillTool from '../helper/bit-tools/fill-tool';\nimport {generateSecondaryColor, MIXED} from '../helper/style-path';\n\nclass BitFillMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isFillModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool) {\n if (nextProps.color !== this.props.color) {\n this.tool.setColor(nextProps.color);\n }\n if (nextProps.color2 !== this.props.color2) {\n this.tool.setColor2(nextProps.color2);\n }\n if (nextProps.fillModeGradientType !== this.props.fillModeGradientType) {\n this.tool.setGradientType(nextProps.fillModeGradientType);\n }\n }\n\n if (nextProps.isFillModeActive && !this.props.isFillModeActive) {\n this.activateTool();\n } else if (!nextProps.isFillModeActive && this.props.isFillModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isFillModeActive !== this.props.isFillModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n\n // Force the default brush color if fill is MIXED or transparent\n let color = this.props.color;\n if (this.props.color === MIXED) {\n color = DEFAULT_COLOR;\n this.props.onChangeFillColor(DEFAULT_COLOR, 0);\n }\n const gradientType = this.props.fillModeGradientType ?\n this.props.fillModeGradientType : this.props.styleGradientType;\n let color2 = this.props.color2;\n if (gradientType !== this.props.styleGradientType) {\n if (this.props.styleGradientType === GradientTypes.SOLID) {\n color2 = generateSecondaryColor(color);\n this.props.onChangeFillColor(color2, 1);\n }\n this.props.changeGradientType(gradientType);\n }\n if (this.props.color2 === MIXED) {\n color2 = generateSecondaryColor();\n this.props.onChangeFillColor(color2, 1);\n }\n this.tool = new FillTool(this.props.onUpdateImage);\n this.tool.setColor(color);\n this.tool.setColor2(color2);\n this.tool.setGradientType(gradientType);\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <FillModeComponent\n isSelected={this.props.isFillModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitFillMode.propTypes = {\n changeGradientType: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n color: PropTypes.string,\n color2: PropTypes.string,\n styleGradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n fillModeGradientType: PropTypes.oneOf(Object.keys(GradientTypes)),\n handleMouseDown: PropTypes.func.isRequired,\n isFillModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n fillModeGradientType: state.scratchPaint.fillMode.gradientType, // Last user-selected gradient type\n color: state.scratchPaint.color.fillColor.primary,\n color2: state.scratchPaint.color.fillColor.secondary,\n styleGradientType: state.scratchPaint.color.fillColor.gradientType,\n isFillModeActive: state.scratchPaint.mode === Modes.BIT_FILL\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n changeGradientType: gradientType => {\n dispatch(changeGradientType(gradientType));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_FILL));\n },\n onChangeFillColor: (fillColor, index) => {\n if (index === 0) {\n dispatch(changeFillColor(fillColor));\n } else if (index === 1) {\n dispatch(changeFillColor2(fillColor));\n }\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitFillMode);\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport messages from '../../lib/messages.js';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\n\nimport eraserIcon from './eraser.svg';\n\nconst BitEraserComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.eraser}\n imgSrc={eraserIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitEraserComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitEraserComponent;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\n\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {clearSelection} from '../helper/selection';\n\nimport BitEraserModeComponent from '../components/bit-eraser-mode/bit-eraser-mode.jsx';\nimport BitBrushTool from '../helper/bit-tools/brush-tool';\n\nclass BitEraserMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isBitEraserModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.bitEraserSize !== this.props.bitEraserSize) {\n this.tool.setBrushSize(nextProps.bitEraserSize);\n }\n \n if (nextProps.isBitEraserModeActive && !this.props.isBitEraserModeActive) {\n this.activateTool();\n } else if (!nextProps.isBitEraserModeActive && this.props.isBitEraserModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isBitEraserModeActive !== this.props.isBitEraserModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n this.tool = new BitBrushTool(\n this.props.onUpdateImage,\n true /* isEraser */\n );\n this.tool.setBrushSize(this.props.bitEraserSize);\n\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <BitEraserModeComponent\n isSelected={this.props.isBitEraserModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitEraserMode.propTypes = {\n bitEraserSize: PropTypes.number.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n isBitEraserModeActive: PropTypes.bool.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n bitEraserSize: state.scratchPaint.bitEraserSize,\n isBitEraserModeActive: state.scratchPaint.mode === Modes.BIT_ERASER\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_ERASER));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitEraserMode);\n","import paper from '@scratch/paper';\nimport {rectSelect} from '../guides';\nimport {clearSelection, processRectangularSelection} from '../selection';\nimport {getRaster} from '../layer';\nimport {ART_BOARD_WIDTH, ART_BOARD_HEIGHT} from '../view';\nimport {getHitBounds} from '../../helper/bitmap';\n\n/** Tool to handle drag selection. A dotted line box appears and everything enclosed is selected. */\nclass SelectionBoxTool {\n /**\n * @param {!Modes} mode Current paint editor mode\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n */\n constructor (mode, setSelectedItems, clearSelectedItems) {\n this.selectionRect = null;\n this.mode = mode;\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n }\n /**\n * @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)\n */\n onMouseDown (multiselect) {\n if (!multiselect) {\n clearSelection(this.clearSelectedItems);\n this.clearSelectedItems();\n }\n }\n onMouseDrag (event) {\n if (event.event.button > 0) return; // only first mouse button\n if (this.selectionRect) {\n this.selectionRect.remove();\n }\n this.selectionRect = rectSelect(event);\n }\n onMouseUpVector (event) {\n if (event.event.button > 0) return; // only first mouse button\n if (this.selectionRect) {\n processRectangularSelection(event, this.selectionRect, this.mode);\n this.selectionRect.remove();\n this.selectionRect = null;\n this.setSelectedItems();\n }\n }\n onMouseUpBitmap (event) {\n if (event.event.button > 0) return; // only first mouse button\n if (this.selectionRect) {\n let rect = new paper.Rectangle({\n from: new paper.Point(\n Math.max(0, Math.round(this.selectionRect.bounds.topLeft.x)),\n Math.max(0, Math.round(this.selectionRect.bounds.topLeft.y))),\n to: new paper.Point(\n Math.min(ART_BOARD_WIDTH, Math.round(this.selectionRect.bounds.bottomRight.x)),\n Math.min(ART_BOARD_HEIGHT, Math.round(this.selectionRect.bounds.bottomRight.y)))\n });\n\n // Trim/tighten selection bounds inwards to only the opaque region, excluding transparent pixels\n rect = getHitBounds(getRaster(), rect);\n\n if (rect.area) {\n // Pull selected raster to active layer\n const raster = getRaster().getSubRaster(rect);\n raster.parent = paper.project.activeLayer;\n raster.canvas.getContext('2d').imageSmoothingEnabled = false;\n raster.selected = true;\n // Gather a bit of extra data so that we can avoid aliasing at edges\n const expanded = getRaster().getSubRaster(rect.expand(4));\n expanded.remove();\n raster.data = {expanded: expanded};\n\n // Clear area from raster layer\n const context = getRaster().getContext(true /* modify */);\n context.clearRect(rect.x, rect.y, rect.width, rect.height);\n this.setSelectedItems();\n }\n\n // Remove dotted rectangle\n this.selectionRect.remove();\n this.selectionRect = null;\n }\n }\n}\n\nexport default SelectionBoxTool;\n","import paper from '@scratch/paper';\nimport Modes from '../../lib/modes';\n\nimport {getRaster} from '../layer';\nimport {commitSelectionToBitmap} from '../bitmap';\n\nimport BoundingBoxTool from '../selection-tools/bounding-box-tool';\nimport NudgeTool from '../selection-tools/nudge-tool';\nimport SelectionBoxTool from '../selection-tools/selection-box-tool';\n\n/**\n * paper.Tool that handles select mode in bitmap. This is made up of 2 subtools.\n * - The selection box tool is active when the user clicks an empty space and drags.\n * It selects all items in the rectangle.\n * - The bounding box tool is active if the user clicks on a non-empty space. It handles\n * reshaping the selection.\n */\nclass SelectTool extends paper.Tool {\n /** The distance within which mouse events count as a hit against an item */\n static get TOLERANCE () {\n return 2;\n }\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, setCursor, onUpdateImage) {\n super();\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool = new BoundingBoxTool(\n Modes.BIT_SELECT,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage\n );\n const nudgeTool = new NudgeTool(Modes.BIT_SELECT, this.boundingBoxTool, onUpdateImage);\n this.selectionBoxTool = new SelectionBoxTool(Modes.BIT_SELECT, setSelectedItems, clearSelectedItems);\n this.selectionBoxMode = false;\n this.selection = null;\n this.active = false;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = nudgeTool.onKeyUp;\n this.onKeyDown = nudgeTool.onKeyDown;\n\n this.boundingBoxTool.setSelectionBounds();\n }\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n if (this.selection && this.selection.parent && !this.selection.selected) {\n // Selection got deselected\n this.commitSelection();\n }\n if ((!this.selection || !this.selection.parent) &&\n selectedItems && selectedItems.length === 1 && selectedItems[0] instanceof paper.Raster) {\n // Track the new active selection. This may happen via undo, paste, or drag to select.\n this.selection = selectedItems[0];\n }\n }\n /**\n * Returns the hit options to use when conducting hit tests.\n * @returns {object} See paper.Item.hitTest for definition of options\n */\n getHitOptions () {\n // Tolerance needs to be scaled when the view is zoomed in in order to represent the same\n // distance for the user to move the mouse.\n return {\n segments: true,\n stroke: true,\n curves: true,\n fill: true,\n guide: false,\n tolerance: SelectTool.TOLERANCE / paper.view.zoom,\n match: hitResult => {\n // Don't match helper items, unless they are handles.\n if (!hitResult.item.data || !hitResult.item.data.isHelperItem) return true;\n return hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle;\n }\n };\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n // If bounding box tool does not find an item that was hit, rasterize the old selection,\n // then use selection box tool.\n if (!this.boundingBoxTool\n .onMouseDown(\n event,\n event.modifiers.alt,\n event.modifiers.shift,\n false /* doubleClicked */,\n this.getHitOptions())) {\n this.commitSelection();\n this.selectionBoxMode = true;\n this.selectionBoxTool.onMouseDown(event.modifiers.shift);\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.selectionBoxMode) {\n this.selectionBoxTool.onMouseDrag(event);\n } else {\n this.boundingBoxTool.onMouseDrag(event);\n }\n }\n handleMouseMove (event) {\n this.boundingBoxTool.onMouseMove(event, this.getHitOptions());\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.selectionBoxMode) {\n this.selectionBoxTool.onMouseUpBitmap(event);\n } else {\n this.boundingBoxTool.onMouseUp(event);\n }\n this.selectionBoxMode = false;\n this.active = false;\n }\n commitSelection () {\n if (!this.selection || !this.selection.parent) return;\n\n commitSelectionToBitmap(this.selection, getRaster());\n this.selection.remove();\n this.selection = null;\n this.onUpdateImage();\n }\n deactivateTool () {\n this.commitSelection();\n this.boundingBoxTool.deactivateTool();\n this.boundingBoxTool = null;\n this.selectionBoxTool = null;\n }\n}\n\nexport default SelectTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport selectIcon from './marquee.svg';\n\nconst BitSelectComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.select}\n imgSrc={selectIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitSelectComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitSelectComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\n\nimport {clearFillGradient} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport BitSelectTool from '../helper/bit-tools/select-tool';\nimport SelectModeComponent from '../components/bit-select-mode/bit-select-mode.jsx';\n\nclass BitSelectMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isSelectModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n\n if (nextProps.isSelectModeActive && !this.props.isSelectModeActive) {\n this.activateTool();\n } else if (!nextProps.isSelectModeActive && this.props.isSelectModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isSelectModeActive !== this.props.isSelectModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n this.props.clearGradient();\n this.tool = new BitSelectTool(\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage\n );\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <SelectModeComponent\n isSelected={this.props.isSelectModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBitSelectMode.propTypes = {\n clearGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n isSelectModeActive: PropTypes.bool.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n isSelectModeActive: state.scratchPaint.mode === Modes.BIT_SELECT,\n selectedItems: state.scratchPaint.selectedItems\n});\nconst mapDispatchToProps = dispatch => ({\n clearGradient: () => {\n dispatch(clearFillGradient());\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setCursor: cursorType => {\n dispatch(setCursor(cursorType));\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems()));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BIT_SELECT));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BitSelectMode);\n","/* DO NOT EDIT\n@todo This file is copied from GUI and should be pulled out into a shared library.\nSee https://github.com/LLK/scratch-paint/issues/13 */\n\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport stylePropType from 'react-style-proptype';\n\nconst getRandomColor = (function () {\n // In \"DEBUG\" mode this is used to output a random background color for each\n // box. The function gives the same \"random\" set for each seed, allowing re-\n // renders of the same content to give the same random display.\n const random = (function (seed) {\n let mW = seed;\n let mZ = 987654321;\n const mask = 0xffffffff;\n return function () {\n mZ = ((36969 * (mZ & 65535)) + (mZ >> 16)) & mask;\n mW = ((18000 * (mW & 65535)) + (mW >> 16)) & mask;\n let result = ((mZ << 16) + mW) & mask;\n result /= 4294967296;\n return result + 1;\n };\n }(601));\n return function () {\n const r = Math.max(parseInt(random() * 100, 10) % 256, 1);\n const g = Math.max(parseInt(random() * 100, 10) % 256, 1);\n const b = Math.max(parseInt(random() * 100, 10) % 256, 1);\n return `rgb(${r},${g},${b})`;\n };\n}());\n\nconst Box = props => {\n const {\n alignContent,\n alignItems,\n alignSelf,\n basis,\n children,\n className,\n componentRef,\n direction,\n element,\n grow,\n height,\n justifyContent,\n width,\n wrap,\n shrink,\n style,\n ...componentProps\n } = props;\n return React.createElement(element, {\n className: className,\n ref: componentRef,\n style: Object.assign(\n {\n alignContent: alignContent,\n alignItems: alignItems,\n alignSelf: alignSelf,\n flexBasis: basis,\n flexDirection: direction,\n flexGrow: grow,\n flexShrink: shrink,\n flexWrap: wrap,\n justifyContent: justifyContent,\n width: width,\n height: height\n },\n process.env.DEBUG ? { // eslint-disable-line no-undef\n backgroundColor: getRandomColor(),\n outline: `1px solid black`\n } : {},\n style\n ),\n ...componentProps\n }, children);\n};\nBox.propTypes = {\n /** Defines how the browser distributes space between and around content items vertically within this box. */\n alignContent: PropTypes.oneOf([\n 'flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch'\n ]),\n /** Defines how the browser distributes space between and around flex items horizontally within this box. */\n alignItems: PropTypes.oneOf([\n 'flex-start', 'flex-end', 'center', 'baseline', 'stretch'\n ]),\n /** Specifies how this box should be aligned inside of its container (requires the container to be flexable). */\n alignSelf: PropTypes.oneOf([\n 'auto', 'flex-start', 'flex-end', 'center', 'baseline', 'stretch'\n ]),\n /** Specifies the initial length of this box */\n basis: PropTypes.oneOfType([\n PropTypes.number,\n PropTypes.oneOf(['auto'])\n ]),\n /** Specifies the the HTML nodes which will be child elements of this box. */\n children: PropTypes.node,\n /** Specifies the class name that will be set on this box */\n className: PropTypes.string,\n /**\n * A callback function whose first parameter is the underlying dom elements.\n * This call back will be executed immediately after the component is mounted or unmounted\n */\n componentRef: PropTypes.func,\n /** https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction */\n direction: PropTypes.oneOf([\n 'row', 'row-reverse', 'column', 'column-reverse'\n ]),\n /** Specifies the type of HTML element of this box. Defaults to div. */\n element: PropTypes.string,\n /** Specifies the flex grow factor of a flex item. */\n grow: PropTypes.number,\n /** The height in pixels (if specified as a number) or a string if different units are required. */\n height: PropTypes.oneOfType([\n PropTypes.number,\n PropTypes.string\n ]),\n /** https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content */\n justifyContent: PropTypes.oneOf([\n 'flex-start', 'flex-end', 'center', 'space-between', 'space-around'\n ]),\n /** Specifies the flex shrink factor of a flex item. */\n shrink: PropTypes.number,\n /** An object whose keys are css property names and whose values correspond the the css property. */\n style: stylePropType,\n /** The width in pixels (if specified as a number) or a string if different units are required. */\n width: PropTypes.oneOfType([\n PropTypes.number,\n PropTypes.string\n ]),\n /** How whitespace should wrap within this block. */\n wrap: PropTypes.oneOf([\n 'nowrap', 'wrap', 'wrap-reverse'\n ])\n};\nBox.defaultProps = {\n element: 'div',\n style: {}\n};\nexport default Box;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./button-group.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./button-group.css\";\n export default content && content.locals ? content.locals : undefined;\n","import classNames from 'classnames';\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nimport styles from './button-group.css';\n\nconst ButtonGroup = props => (\n <div className={classNames(props.className, styles.buttonGroup)}>\n {props.children}\n </div>\n);\n\nButtonGroup.propTypes = {\n children: PropTypes.node.isRequired,\n className: PropTypes.string\n};\n\nexport default ButtonGroup;\n","// Broadbrush based on http://paperjs.org/tutorials/interaction/working-with-mouse-vectors/\nimport paper from '@scratch/paper';\nimport {styleBlob} from '../../helper/style-path';\nimport log from '../../log/log';\n\n/**\n * Broad brush functions to add as listeners on the mouse. Call them when the corresponding mouse event happens\n * to get the broad brush behavior.\n *\n * Broad brush draws strokes by drawing points equidistant from the mouse event, perpendicular to the\n * direction of motion. Shortcomings are that this path can cross itself, and 180 degree turns result\n * in a flat edge.\n * @param {!Tool} tool paper.js mouse object\n */\nclass BroadBrushHelper {\n constructor () {\n // Direction vector of the last mouse move\n this.lastVec = null;\n // End point of the last mouse move\n this.lastPoint = null;\n // The path of the brush stroke we are building\n this.finalPath = null;\n // Number of points of finalPath that have already been processed\n this.smoothed = 0;\n // Number of steps to wait before performing another amortized smooth\n this.smoothingThreshold = 20;\n // Mouse moves since mouse down\n this.steps = 0;\n // End caps round out corners and are not merged into the path until the end.\n this.endCaps = [];\n }\n\n onBroadMouseDown (event, tool, options) {\n this.steps = 0;\n this.smoothed = 0;\n this.lastVec = null;\n tool.minDistance = Math.min(5, Math.max(2 / paper.view.zoom, options.brushSize / 2));\n tool.maxDistance = options.brushSize;\n if (event.event.button > 0) return; // only first mouse button\n\n this.finalPath = new paper.Path.Circle({\n center: event.point,\n radius: options.brushSize / 2\n });\n styleBlob(this.finalPath, options);\n this.lastPoint = event.point;\n }\n\n onBroadMouseDrag (event, tool, options) {\n this.steps++;\n const step = (event.delta).normalize(options.brushSize / 2);\n\n // Add an end cap if the mouse has changed direction very quickly\n if (this.lastVec) {\n const angle = this.lastVec.getDirectedAngle(step);\n if (Math.abs(angle) > 126) {\n // This will cause us to skip simplifying this sharp angle. Running simplify on\n // sharp angles causes the stroke to blob outwards.\n this.simplify(1);\n this.smoothed++;\n\n // If the angle is large, the broad brush tends to leave behind a flat edge.\n // This code makes a shape to fill in that flat edge with a rounded cap.\n const circ = new paper.Path.Circle(this.lastPoint, options.brushSize / 2);\n circ.fillColor = options.fillColor;\n const rect = new paper.Path.Rectangle(\n this.lastPoint.subtract(new paper.Point(-options.brushSize / 2, 0)),\n this.lastPoint.subtract(new paper.Point(options.brushSize / 2, this.lastVec.length))\n );\n rect.fillColor = options.fillColor;\n rect.rotate(this.lastVec.angle - 90, this.lastPoint);\n const rect2 = new paper.Path.Rectangle(\n event.point.subtract(new paper.Point(-options.brushSize / 2, 0)),\n event.point.subtract(new paper.Point(options.brushSize / 2, event.delta.length))\n );\n rect2.fillColor = options.fillColor;\n rect2.rotate(step.angle - 90, event.point);\n this.endCaps.push(this.union(circ, this.union(rect, rect2)));\n }\n }\n step.angle += 90;\n\n // Move the first point out away from the drag so that the end of the path is rounded\n if (this.steps === 1) {\n // Replace circle with path\n this.finalPath.remove();\n this.finalPath = new paper.Path();\n const handleVec = event.delta.normalize(options.brushSize / 2);\n this.finalPath.add(new paper.Segment(\n this.lastPoint.subtract(handleVec),\n handleVec.rotate(-90),\n handleVec.rotate(90)\n ));\n styleBlob(this.finalPath, options);\n this.finalPath.insert(0, new paper.Segment(this.lastPoint.subtract(step)));\n this.finalPath.add(new paper.Segment(this.lastPoint.add(step)));\n }\n\n // Update angle of the last brush step's points to match the average angle of the last mouse vector and this\n // mouse vector (aka the vertex normal).\n if (this.lastVec) {\n const lastNormal = this.lastVec.normalize(options.brushSize / 2).rotate(90);\n const averageNormal = new paper.Point(\n lastNormal.x + step.x,\n lastNormal.y + step.y\n ).normalize(options.brushSize / 2);\n\n this.finalPath.segments[0].point = this.lastPoint.subtract(averageNormal);\n this.finalPath.segments[this.finalPath.segments.length - 1].point = this.lastPoint.add(averageNormal);\n }\n\n this.finalPath.add(event.point.add(step));\n this.finalPath.insert(0, event.point.subtract(step));\n\n if (this.finalPath.segments.length > this.smoothed + (this.smoothingThreshold * 2)) {\n this.simplify(1);\n }\n\n this.lastVec = event.delta;\n this.lastPoint = event.point;\n }\n\n /**\n * Simplify the path so that it looks almost the same while trying to have a reasonable number of handles.\n * Without this, there would be 2 handles for every mouse move, which would make the path produced basically\n * uneditable. This version of simplify keeps track of how much of the path has already been simplified to\n * avoid repeating work.\n * @param {number} threshold The simplify algorithm must try to stay within this distance of the actual line.\n * The algorithm will be faster and able to remove more points the higher this number is.\n * Note that 1 is about the lowest this algorithm can do (the result is about the same when 1 is\n * passed in as when 0 is passed in)\n */\n simplify (threshold) {\n // Length of the current path\n const length = this.finalPath.segments.length;\n // Number of new points added to front and end of path since last simplify\n const newPoints = Math.floor((length - this.smoothed) / 2) + 1;\n\n // Where to cut. Don't go past the rounded start of the line (so there's always a tempPathMid)\n const firstCutoff = Math.min(newPoints + 1, Math.floor((length / 2)));\n const lastCutoff = Math.max(length - 1 - newPoints, Math.floor(length / 2) + 1);\n if (firstCutoff <= 1 || lastCutoff >= length - 1) {\n // Entire path is simplified already\n return;\n }\n // Cut the path into 3 segments: the 2 ends where the new points are, and the middle, which will be\n // staying the same\n const tempPath1 = new paper.Path(this.finalPath.segments.slice(1, firstCutoff));\n const tempPathMid = new paper.Path(this.finalPath.segments.slice(firstCutoff, lastCutoff));\n const tempPath2 = new paper.Path(this.finalPath.segments.slice(lastCutoff, length - 1));\n\n // Run simplify on the new ends. We need to graft the old handles back onto the newly\n // simplified paths, since simplify removes the in handle from the start of the path, and\n // the out handle from the end of the path it's simplifying.\n const oldPath1End = tempPath1.segments[tempPath1.segments.length - 1];\n const oldPath2End = tempPath2.segments[0];\n tempPath1.simplify(threshold);\n tempPath2.simplify(threshold);\n const newPath1End = tempPath1.segments[tempPath1.segments.length - 1];\n const newPath2End = tempPath2.segments[0];\n newPath1End.handleOut = oldPath1End.handleOut;\n newPath2End.handleIn = oldPath2End.handleIn;\n\n // Delete the old contents of finalPath and replace it with the newly simplified segments, concatenated\n this.finalPath.removeSegments(1, this.finalPath.segments.length - 1);\n this.finalPath.insertSegments(1, tempPath1.segments.concat(tempPathMid.segments).concat(tempPath2.segments));\n\n // Remove temp paths\n tempPath1.remove();\n tempPath2.remove();\n tempPathMid.remove();\n\n // Update how many points have been smoothed so far so that we don't redo work when\n // simplify is called next time.\n this.smoothed = Math.max(2, this.finalPath.segments.length);\n }\n\n /**\n * Like paper.Path.unite, but it removes the original 2 paths\n * @param {paper.Path} path1 to merge\n * @param {paper.Path} path2 to merge\n * @returns {paper.Path} merged path. Original paths 1 and 2 will be removed from the view.\n */\n union (path1, path2) {\n const temp = path1.unite(path2);\n path1.remove();\n path2.remove();\n return temp;\n }\n\n onBroadMouseUp (event, tool, options) {\n // If there was only a single click, draw a circle.\n if (this.steps === 0) {\n this.endCaps.length = 0;\n return this.finalPath;\n }\n\n let delta = this.lastVec;\n\n // If the mouse up is at the same point as the mouse drag event then we need\n // the second to last point to get the right direction vector for the end cap\n if (!event.point.equals(this.lastPoint)) {\n // The given event.delta is the difference between the mouse down coords and the mouse up coords,\n // but we want the difference between the last mouse drag coords and the mouse up coords.\n delta = event.point.subtract(this.lastPoint);\n const step = delta.normalize(options.brushSize / 2);\n step.angle += 90;\n\n const top = event.point.add(step);\n const bottom = event.point.subtract(step);\n this.finalPath.add(top);\n this.finalPath.insert(0, bottom);\n }\n\n // Simplify before adding end cap so cap doesn't get warped\n this.simplify(1);\n const handleVec = delta.normalize(options.brushSize / 2);\n this.finalPath.add(new paper.Segment(\n event.point.add(handleVec),\n handleVec.rotate(90),\n handleVec.rotate(-90)\n ));\n this.finalPath.closePath();\n\n // Resolve self-crossings\n const newPath =\n this.finalPath\n .resolveCrossings()\n .reorient(true /* nonZero */, true /* clockwise */)\n .reduce({simplify: true});\n if (newPath !== this.finalPath) {\n newPath.copyAttributes(this.finalPath);\n newPath.fillColor = this.finalPath.fillColor;\n this.finalPath.remove();\n this.finalPath = newPath;\n }\n\n // Try to merge end caps\n for (const cap of this.endCaps) {\n const temp = this.union(this.finalPath, cap);\n if (temp.area >= this.finalPath.area &&\n !(temp instanceof paper.CompoundPath && !(this.finalPath instanceof paper.CompoundPath))) {\n this.finalPath = temp;\n } else {\n // If the union of the two shapes is smaller than the original shape,\n // or it caused the path to become a compound path,\n // then there must have been a glitch with paperjs's unite function.\n // In this case, skip merging that segment. It's not great, but it's\n // better than losing the whole path for instance. (Unfortunately, this\n // happens reasonably often to scribbles, and this code doesn't catch\n // all of the failures.)\n this.finalPath.insertAbove(temp);\n temp.remove();\n log.warn('Skipping a merge.');\n }\n }\n this.endCaps.length = 0;\n\n return this.finalPath;\n }\n}\n\nexport default BroadBrushHelper;\n","import paper from '@scratch/paper';\nimport {styleBlob} from '../../helper/style-path';\n\n/**\n * Segment brush functions to add as listeners on the mouse. Call them when the corresponding mouse event happens\n * to get the broad brush behavior.\n *\n * Segment brush draws by creating a rounded rectangle for each mouse move event and merging all of\n * those shapes. Unlike the broad brush, the resulting shape will not self-intersect and when you make\n * 180 degree turns, you will get a rounded point as expected. Shortcomings include that performance is\n * worse, especially as the number of segments to join increase, and that there are problems in paper.js\n * with union on shapes with curves, so that chunks of the union tend to disappear.\n * (https://github.com/paperjs/paper.js/issues/1321)\n * @param {!Tool} tool paper.js mouse object\n */\nclass SegmentBrushHelper {\n constructor () {\n this.lastPoint = null;\n this.finalPath = null;\n this.firstCircle = null;\n }\n onSegmentMouseDown (event, tool, options) {\n if (event.event.button > 0) return; // only first mouse button\n\n tool.minDistance = 2 / paper.view.zoom;\n tool.maxDistance = options.brushSize;\n\n this.firstCircle = new paper.Path.Circle({\n center: event.point,\n radius: options.brushSize / 2\n });\n this.finalPath = this.firstCircle;\n styleBlob(this.finalPath, options);\n this.lastPoint = event.point;\n }\n\n onSegmentMouseDrag (event, tool, options) {\n if (event.event.button > 0) return; // only first mouse button\n\n const step = (event.delta).normalize(options.brushSize / 2);\n const handleVec = step.clone();\n handleVec.length = options.brushSize / 2;\n handleVec.angle += 90;\n\n const path = new paper.Path();\n\n styleBlob(path, options);\n\n // Add handles to round the end caps\n path.add(new paper.Segment(this.lastPoint.subtract(step), handleVec.multiply(-1), handleVec));\n step.angle += 90;\n\n path.add(event.lastPoint.add(step));\n path.insert(0, event.lastPoint.subtract(step));\n path.add(event.point.add(step));\n path.insert(0, event.point.subtract(step));\n\n // Add end cap\n step.angle -= 90;\n path.add(new paper.Segment(event.point.add(step), handleVec, handleVec.multiply(-1)));\n path.closed = true;\n // The unite function on curved paths does not always work (sometimes deletes half the path)\n // so we have to flatten.\n path.flatten(Math.min(5, options.brushSize / 5));\n\n this.lastPoint = event.point;\n const newPath = this.finalPath.unite(path);\n path.remove();\n this.finalPath.remove();\n this.finalPath = newPath;\n }\n\n onSegmentMouseUp (event) {\n if (event.event.button > 0) return; // only first mouse button\n\n // TODO: This smoothing tends to cut off large portions of the path! Would like to eventually\n // add back smoothing, maybe a custom implementation that only applies to a subset of the line?\n\n // Smooth the path. Make it unclosed first because smoothing of closed\n // paths tends to cut off the path.\n if (this.finalPath.segments && this.finalPath.segments.length > 4) {\n this.finalPath.closed = false;\n this.finalPath.simplify(2);\n this.finalPath.closed = true;\n // Merge again with the first point, since it gets distorted when we unclose the path.\n const temp = this.finalPath.unite(this.firstCircle);\n this.finalPath.remove();\n this.finalPath = temp;\n }\n return this.finalPath;\n }\n}\n\nexport default SegmentBrushHelper;\n","import paper from '@scratch/paper';\nimport log from '../../log/log';\nimport BroadBrushHelper from './broad-brush-helper';\nimport SegmentBrushHelper from './segment-brush-helper';\nimport {MIXED, styleCursorPreview} from '../../helper/style-path';\nimport {clearSelection, getItems} from '../../helper/selection';\nimport {getGuideLayer, setGuideItem} from '../../helper/layer';\nimport {isCompoundPathChild} from '../compound-path';\n\n/**\n * Shared code for the brush and eraser mode. Adds functions on the paper tool object\n * to handle mouse events, which are delegated to broad-brush-helper and segment-brush-helper\n * based on the brushSize in the state.\n */\nclass Blobbiness {\n static get BROAD () {\n return 'broadbrush';\n }\n static get SEGMENT () {\n return 'segmentbrush';\n }\n\n // If brush size >= threshold use segment brush, else use broadbrush\n // Segment brush has performance issues at low threshold, but broad brush has weird corners\n // which get more obvious the bigger it is\n static get THRESHOLD () {\n return 30 / paper.view.zoom;\n }\n\n /**\n * @param {function} onUpdateImage call when the drawing has changed to let listeners know\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n */\n constructor (onUpdateImage, clearSelectedItems) {\n this.broadBrushHelper = new BroadBrushHelper();\n this.segmentBrushHelper = new SegmentBrushHelper();\n this.onUpdateImage = onUpdateImage;\n this.clearSelectedItems = clearSelectedItems;\n\n // The following are stored to check whether these have changed and the cursor preview needs to be redrawn.\n this.strokeColor = null;\n this.brushSize = null;\n this.fillColor = null;\n }\n\n /**\n * Set configuration options for a blob\n * @param {!object} options Configuration\n * @param {!number} options.brushSize Width of blob marking made by mouse\n * @param {!boolean} options.isEraser Whether the stroke should be treated as an erase path. If false,\n * the stroke is an additive path.\n * @param {?string} options.fillColor Color of the brush stroke.\n * @param {?string} options.strokeColor Color of the brush outline.\n * @param {?number} options.strokeWidth Width of the brush outline.\n */\n setOptions (options) {\n const oldFillColor = this.options ? this.options.fillColor : 'black';\n const oldStrokeColor = this.options ? this.options.strokeColor : null;\n const oldStrokeWidth = this.options ? this.options.strokeWidth : null;\n // If values are mixed, it means the color was set by a selection contained multiple values.\n // In this case keep drawing with the previous values if any. (For stroke width, null indicates\n // mixed, because stroke width is required to be a number)\n this.options = {\n ...options,\n fillColor: options.fillColor === MIXED ? oldFillColor : options.fillColor,\n strokeColor: options.strokeColor === MIXED ? oldStrokeColor : options.strokeColor,\n strokeWidth: options.strokeWidth === null ? oldStrokeWidth : options.strokeWidth\n };\n this.resizeCursorIfNeeded();\n }\n\n /**\n * Adds handlers on the mouse tool to draw blobs. Initialize with configuration options for a blob.\n * @param {!object} options Configuration\n * @param {!number} options.brushSize Width of blob marking made by mouse\n * @param {!boolean} options.isEraser Whether the stroke should be treated as an erase path. If false,\n * the stroke is an additive path.\n * @param {?string} options.fillColor Color of the brush stroke.\n * @param {?string} options.strokeColor Color of the brush outline.\n * @param {?number} options.strokeWidth Width of the brush outline.\n */\n activateTool (options) {\n this.tool = new paper.Tool();\n this.cursorPreviewLastPoint = new paper.Point(-10000, -10000);\n this.setOptions(options);\n this.tool.active = false;\n this.tool.fixedDistance = 1;\n\n const blob = this;\n this.tool.onMouseMove = function (event) {\n blob.resizeCursorIfNeeded(event.point);\n styleCursorPreview(blob.cursorPreview, blob.options);\n blob.cursorPreview.bringToFront();\n blob.cursorPreview.position = event.point;\n };\n\n this.tool.onMouseDown = function (event) {\n blob.resizeCursorIfNeeded(event.point);\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (blob.options.brushSize < Blobbiness.THRESHOLD) {\n blob.brush = Blobbiness.BROAD;\n blob.broadBrushHelper.onBroadMouseDown(event, blob.tool, blob.options);\n } else {\n blob.brush = Blobbiness.SEGMENT;\n blob.segmentBrushHelper.onSegmentMouseDown(event, blob.tool, blob.options);\n }\n blob.cursorPreview.bringToFront();\n blob.cursorPreview.position = event.point;\n };\n\n this.tool.onMouseDrag = function (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n if (blob.brush === Blobbiness.BROAD) {\n blob.broadBrushHelper.onBroadMouseDrag(event, blob.tool, blob.options);\n } else if (blob.brush === Blobbiness.SEGMENT) {\n blob.segmentBrushHelper.onSegmentMouseDrag(event, blob.tool, blob.options);\n } else {\n log.warn(`Brush type does not exist: ${blob.brush}`);\n }\n\n blob.cursorPreview.bringToFront();\n blob.cursorPreview.position = event.point;\n };\n\n this.tool.onMouseUp = function (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n let lastPath;\n if (blob.brush === Blobbiness.BROAD) {\n lastPath = blob.broadBrushHelper.onBroadMouseUp(event, blob.tool, blob.options);\n } else if (blob.brush === Blobbiness.SEGMENT) {\n lastPath = blob.segmentBrushHelper.onSegmentMouseUp(event, blob.tool, blob.options);\n } else {\n log.warn(`Brush type does not exist: ${blob.brush}`);\n }\n\n if (blob.options.isEraser) {\n blob.mergeEraser(lastPath);\n } else {\n blob.mergeBrush(lastPath);\n }\n\n // Remove cursor preview during snapshot, then bring it back\n blob.cursorPreview.remove();\n blob.onUpdateImage();\n blob.cursorPreview.parent = getGuideLayer();\n\n // Reset\n blob.brush = null;\n this.fixedDistance = 1;\n this.active = false;\n };\n this.tool.activate();\n }\n\n resizeCursorIfNeeded (point) {\n if (!this.options) {\n return;\n }\n // The cursor preview was unattached from the view by an outside process,\n // such as changing costumes or undo.\n if (this.cursorPreview && !this.cursorPreview.parent) {\n this.cursorPreview = null;\n }\n if (this.cursorPreview &&\n this.brushSize === this.options.brushSize &&\n this.fillColor === this.options.fillColor &&\n this.strokeColor === this.options.strokeColor &&\n this.cursorPreviewLastPoint.equals(point)) {\n return;\n }\n if (typeof point !== 'undefined') {\n this.cursorPreviewLastPoint = point;\n }\n\n if (!this.cursorPreview) {\n this.cursorPreview = new paper.Shape.Ellipse({\n point: this.cursorPreviewLastPoint,\n size: this.options.brushSize / 2\n });\n this.cursorPreview.parent = getGuideLayer();\n this.cursorPreview.data.isHelperItem = true;\n setGuideItem(this.cursorPreview);\n }\n this.cursorPreview.position = this.cursorPreviewLastPoint;\n this.cursorPreview.radius = this.options.brushSize / 2;\n this.brushSize = this.options.brushSize;\n this.fillColor = this.options.fillColor;\n this.strokeColor = this.options.strokeColor;\n styleCursorPreview(this.cursorPreview, this.options);\n }\n\n mergeBrush (lastPath) {\n const blob = this;\n\n // Get all path items to merge with\n const paths = getItems({\n match: function (item) {\n return blob.isMergeable(lastPath, item) &&\n item.parent instanceof paper.Layer; // don't merge with nested in group\n }\n });\n\n let mergedPath = lastPath;\n let i;\n // Move down z order to first overlapping item\n for (i = paths.length - 1; i >= 0 && !this.touches(paths[i], lastPath); i--) {\n continue;\n }\n let mergedPathIndex = i;\n for (; i >= 0; i--) {\n if (!this.touches(paths[i], lastPath)) {\n continue;\n }\n if (!paths[i].getFillColor()) {\n // Ignore for merge. Paths without fill need to be in paths though,\n // since they can visibly change if z order changes\n } else if (this.colorMatch(paths[i], lastPath)) {\n // Make sure the new shape isn't overlapped by anything that would\n // visibly change if we change its z order\n for (let j = mergedPathIndex; j > i; j--) {\n if (this.touches(paths[j], paths[i])) {\n continue;\n }\n }\n // Merge same fill color\n const tempPath = mergedPath.unite(paths[i]);\n tempPath.strokeColor = paths[i].strokeColor;\n tempPath.strokeWidth = paths[i].strokeWidth;\n if (mergedPath === lastPath) {\n tempPath.insertAbove(paths[i]); // First intersected path determines z position of the new path\n } else {\n tempPath.insertAbove(mergedPath); // Rest of merges join z index of merged path\n mergedPathIndex--; // Removed an item, so the merged path index decreases\n }\n mergedPath.remove();\n mergedPath = tempPath;\n paths[i].remove();\n paths.splice(i, 1);\n }\n }\n }\n\n mergeEraser (lastPath) {\n const blob = this;\n\n // Get all path items to merge with\n // If there are selected items, try to erase from amongst those.\n let items = getItems({\n match: function (item) {\n return item.selected && blob.isMergeable(lastPath, item) &&\n blob.touches(lastPath, item) &&\n // Boolean operations will produce incorrect results if directly applied to compound path children,\n // so exclude those. Their parents are also selected so boolean operations will apply to them.\n !isCompoundPathChild(item);\n },\n class: paper.PathItem\n });\n // Eraser didn't hit anything selected, so assume they meant to erase from all instead of from subset\n // and deselect the selection\n if (items.length === 0) {\n clearSelection(this.clearSelectedItems);\n items = getItems({\n match: function (item) {\n return blob.isMergeable(lastPath, item) &&\n blob.touches(lastPath, item) &&\n !isCompoundPathChild(item);\n },\n class: paper.PathItem\n });\n }\n\n for (let i = items.length - 1; i >= 0; i--) {\n if (items[i] instanceof paper.Path && (!items[i].fillColor || items[i].fillColor._alpha === 0)) {\n // Gather path segments\n const subpaths = [];\n const firstSeg = items[i];\n const intersections = firstSeg.getIntersections(lastPath);\n for (let j = intersections.length - 1; j >= 0; j--) {\n const split = firstSeg.splitAt(intersections[j]);\n if (split) {\n split.insertAbove(firstSeg);\n subpaths.push(split);\n }\n }\n subpaths.push(firstSeg);\n\n // Remove the ones that are within the eraser stroke boundary\n for (let k = subpaths.length - 1; k >= 0; k--) {\n const segMidpoint = subpaths[k].getLocationAt(subpaths[k].length / 2).point;\n if (lastPath.contains(segMidpoint)) {\n subpaths[k].remove();\n subpaths.splice(k, 1);\n }\n }\n lastPath.remove();\n continue;\n }\n\n // Erase\n const newPath = items[i].subtract(lastPath);\n newPath.insertBelow(items[i]);\n\n // Gather path segments\n const subpaths = [];\n if (items[i] instanceof paper.Path && !items[i].closed) {\n const firstSeg = items[i].clone();\n const intersections = firstSeg.getIntersections(lastPath);\n // keep first and last segments\n for (let j = intersections.length - 1; j >= 0; j--) {\n const split = firstSeg.splitAt(intersections[j]);\n split.insertAbove(firstSeg);\n subpaths.push(split);\n }\n subpaths.push(firstSeg);\n }\n\n // Remove the ones that are within the eraser stroke boundary, or are already part of new path.\n // This way subpaths only remain if they didn't get turned into a shape by subtract.\n for (let k = subpaths.length - 1; k >= 0; k--) {\n const segMidpoint = subpaths[k].getLocationAt(subpaths[k].length / 2).point;\n if (lastPath.contains(segMidpoint) || newPath.contains(segMidpoint)) {\n subpaths[k].remove();\n subpaths.splice(k, 1);\n }\n }\n\n if (newPath.children) {\n this.separateCompoundPath(newPath);\n newPath.remove();\n }\n items[i].remove();\n }\n lastPath.remove();\n }\n\n separateCompoundPath (compoundPath) {\n if (!compoundPath.isClockwise()) {\n compoundPath.reverse();\n }\n // Divide topologically separate shapes into their own compound paths, instead of\n // everything being stuck together.\n const clockwiseChildren = [];\n const ccwChildren = [];\n for (let j = compoundPath.children.length - 1; j >= 0; j--) {\n const child = compoundPath.children[j];\n if (child.isClockwise()) {\n clockwiseChildren.push(child);\n } else {\n ccwChildren.push(child);\n }\n }\n\n // Sort by area smallest to largest\n clockwiseChildren.sort((a, b) => a.area - b.area);\n ccwChildren.sort((a, b) => Math.abs(a.area) - Math.abs(b.area));\n // Go smallest to largest non-hole, so larger non-holes don't get the smaller pieces' holes\n for (let j = 0; j < clockwiseChildren.length; j++) {\n const cw = clockwiseChildren[j];\n cw.copyAttributes(compoundPath);\n cw.fillColor = compoundPath.fillColor;\n cw.strokeColor = compoundPath.strokeColor;\n cw.strokeWidth = compoundPath.strokeWidth;\n cw.insertAbove(compoundPath);\n\n // Go backward since we are deleting elements. Backwards is largest to smallest hole.\n let newCw = cw;\n for (let k = ccwChildren.length - 1; k >= 0; k--) {\n const ccw = ccwChildren[k];\n if (this.firstEnclosesSecond(cw, ccw)) {\n const temp = newCw.subtract(ccw);\n temp.insertAbove(compoundPath);\n newCw.remove();\n newCw = temp;\n ccw.remove();\n ccwChildren.splice(k, 1);\n }\n }\n }\n }\n\n colorMatch (existingPath, addedPath) {\n // Note: transparent fill colors do notdetect as touching\n return existingPath.getFillColor().equals(addedPath.getFillColor()) &&\n (addedPath.getStrokeColor() === existingPath.getStrokeColor() || // both null\n (addedPath.getStrokeColor() &&\n addedPath.getStrokeColor().equals(existingPath.getStrokeColor()))) &&\n addedPath.getStrokeWidth() === existingPath.getStrokeWidth() &&\n this.touches(existingPath, addedPath);\n }\n\n touches (path1, path2) {\n // Two shapes are touching if their paths intersect\n if (path1 && path2 && path1.intersects(path2)) {\n return true;\n }\n return this.firstEnclosesSecond(path1, path2) || this.firstEnclosesSecond(path2, path1);\n }\n\n firstEnclosesSecond (path1, path2) {\n // Two shapes are also touching if one is completely inside the other\n if (path1 && path2 && path2.firstSegment && path2.firstSegment.point &&\n path1.hitTest(path2.firstSegment.point)) {\n return true;\n }\n // TODO: clean up these no point paths\n return false;\n }\n\n matchesAnyChild (group, path) {\n for (const child of group.children) {\n if (child.children && this.matchesAnyChild(path, child)) {\n return true;\n }\n if (path === child) {\n return true;\n }\n }\n return false;\n }\n\n isMergeable (newPath, existingPath) {\n // Path or compound path\n if (!(existingPath instanceof paper.PathItem)) {\n return;\n }\n if (newPath.children) {\n if (this.matchesAnyChild(newPath, existingPath)) { // Don't merge with children of self\n return false;\n }\n }\n return existingPath !== newPath; // don't merge with self\n }\n\n deactivateTool () {\n if (this.cursorPreview) {\n this.cursorPreview.remove();\n this.cursorPreview = null;\n }\n this.tool.remove();\n this.tool = null;\n }\n}\n\nexport default Blobbiness;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport brushIcon from './brush.svg';\n\nconst BrushModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.brush}\n imgSrc={brushIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBrushModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BrushModeComponent;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport Blobbiness from '../helper/blob-tools/blob';\nimport {MIXED} from '../helper/style-path';\n\nimport {changeFillColor, clearFillGradient, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {clearSelection} from '../helper/selection';\n\nimport BrushModeComponent from '../components/brush-mode/brush-mode.jsx';\n\nclass BrushMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n this.blob = new Blobbiness(\n this.props.onUpdateImage, this.props.clearSelectedItems);\n }\n componentDidMount () {\n if (this.props.isBrushModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (nextProps.isBrushModeActive && !this.props.isBrushModeActive) {\n this.activateTool();\n } else if (!nextProps.isBrushModeActive && this.props.isBrushModeActive) {\n this.deactivateTool();\n } else if (nextProps.isBrushModeActive && this.props.isBrushModeActive) {\n const {fillColor, strokeColor, strokeWidth} = nextProps.colorState;\n this.blob.setOptions({\n isEraser: false,\n fillColor: fillColor.primary,\n strokeColor: strokeColor.primary,\n strokeWidth,\n ...nextProps.brushModeState\n });\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isBrushModeActive !== this.props.isBrushModeActive;\n }\n componentWillUnmount () {\n if (this.blob.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n // TODO: Instead of clearing selection, consider a kind of \"draw inside\"\n // analogous to how selection works with eraser\n clearSelection(this.props.clearSelectedItems);\n this.props.clearGradient();\n // Force the default brush color if fill is MIXED or transparent\n const fillColor = this.props.colorState.fillColor.primary;\n if (fillColor === MIXED || fillColor === null) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n }\n this.blob.activateTool({\n isEraser: false,\n ...this.props.colorState,\n ...this.props.brushModeState\n });\n }\n deactivateTool () {\n this.blob.deactivateTool();\n }\n render () {\n return (\n <BrushModeComponent\n isSelected={this.props.isBrushModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nBrushMode.propTypes = {\n brushModeState: PropTypes.shape({\n brushSize: PropTypes.number.isRequired\n }),\n clearGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n colorState: PropTypes.shape({\n fillColor: ColorStyleProptype,\n strokeColor: ColorStyleProptype,\n strokeWidth: PropTypes.number\n }).isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n isBrushModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n brushModeState: state.scratchPaint.brushMode,\n colorState: state.scratchPaint.color,\n isBrushModeActive: state.scratchPaint.mode === Modes.BRUSH\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearGradient: () => {\n dispatch(clearFillGradient());\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.BRUSH));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(BrushMode);\n","import log from '../log/log';\n\nconst CHANGE_ERASER_SIZE = 'scratch-paint/eraser-mode/CHANGE_ERASER_SIZE';\nconst initialState = {brushSize: 40};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_ERASER_SIZE:\n if (isNaN(action.brushSize)) {\n log.warn(`Invalid brush size: ${action.brushSize}`);\n return state;\n }\n return {brushSize: Math.max(1, action.brushSize)};\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeBrushSize = function (brushSize) {\n return {\n type: CHANGE_ERASER_SIZE,\n brushSize: brushSize\n };\n};\n\nexport {\n reducer as default,\n changeBrushSize\n};\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport eraserIcon from './eraser.svg';\n\nconst EraserModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.eraser}\n imgSrc={eraserIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nEraserModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default EraserModeComponent;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport Blobbiness from '../helper/blob-tools/blob';\nimport {changeBrushSize} from '../reducers/eraser-mode';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport EraserModeComponent from '../components/eraser-mode/eraser-mode.jsx';\nimport {changeMode} from '../reducers/modes';\n\nclass EraserMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n this.blob = new Blobbiness(\n this.props.onUpdateImage, this.props.clearSelectedItems);\n }\n componentDidMount () {\n if (this.props.isEraserModeActive) {\n this.activateTool();\n }\n }\n componentWillReceiveProps (nextProps) {\n if (nextProps.isEraserModeActive && !this.props.isEraserModeActive) {\n this.activateTool();\n } else if (!nextProps.isEraserModeActive && this.props.isEraserModeActive) {\n this.deactivateTool();\n } else if (nextProps.isEraserModeActive && this.props.isEraserModeActive) {\n this.blob.setOptions({\n isEraser: true,\n ...nextProps.eraserModeState\n });\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isEraserModeActive !== this.props.isEraserModeActive;\n }\n componentWillUnmount () {\n if (this.blob.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n this.blob.activateTool({isEraser: true, ...this.props.eraserModeState});\n }\n deactivateTool () {\n this.blob.deactivateTool();\n }\n render () {\n return (\n <EraserModeComponent\n isSelected={this.props.isEraserModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nEraserMode.propTypes = {\n clearSelectedItems: PropTypes.func.isRequired,\n eraserModeState: PropTypes.shape({\n brushSize: PropTypes.number.isRequired\n }),\n handleMouseDown: PropTypes.func.isRequired,\n isEraserModeActive: PropTypes.bool.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n eraserModeState: state.scratchPaint.eraserMode,\n isEraserModeActive: state.scratchPaint.mode === Modes.ERASER\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n changeBrushSize: brushSize => {\n dispatch(changeBrushSize(brushSize));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.ERASER));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(EraserMode);\n","import log from '../log/log';\nimport {CHANGE_FILL_GRADIENT_TYPE} from './fill-style';\nimport GradientTypes from '../lib/gradient-types';\n\nconst CHANGE_COLOR_INDEX = 'scratch-paint/color-index/CHANGE_COLOR_INDEX';\nconst initialState = 0;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_COLOR_INDEX:\n if (action.index !== 1 && action.index !== 0) {\n log.warn(`Invalid color index: ${action.index}`);\n return state;\n }\n return action.index;\n case CHANGE_FILL_GRADIENT_TYPE:\n if (action.gradientType === GradientTypes.SOLID) return 0;\n /* falls through */\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeColorIndex = function (index) {\n return {\n type: CHANGE_COLOR_INDEX,\n index: index\n };\n};\n\nexport {\n reducer as default,\n changeColorIndex\n};\n","const OPEN_MODAL = 'scratch-paint/modals/OPEN_MODAL';\nconst CLOSE_MODAL = 'scratch-paint/modals/CLOSE_MODAL';\n\nconst MODAL_FILL_COLOR = 'fillColor';\nconst MODAL_STROKE_COLOR = 'strokeColor';\n\nconst initialState = {\n [MODAL_FILL_COLOR]: false,\n [MODAL_STROKE_COLOR]: false\n};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case OPEN_MODAL:\n return Object.assign({}, initialState, {\n [action.modal]: true\n });\n case CLOSE_MODAL:\n return Object.assign({}, initialState, {\n [action.modal]: false\n });\n default:\n return state;\n }\n};\n\nconst openModal = function (modal) {\n return {\n type: OPEN_MODAL,\n modal: modal\n };\n};\n\nconst closeModal = function (modal) {\n return {\n type: CLOSE_MODAL,\n modal: modal\n };\n};\n\n// Action creators ==================================\n\nconst openFillColor = function () {\n return openModal(MODAL_FILL_COLOR);\n};\n\nconst openStrokeColor = function () {\n return openModal(MODAL_STROKE_COLOR);\n};\n\nconst closeFillColor = function () {\n return closeModal(MODAL_FILL_COLOR);\n};\n\nconst closeStrokeColor = function () {\n return closeModal(MODAL_STROKE_COLOR);\n};\n\nexport {\n reducer as default,\n openFillColor,\n openStrokeColor,\n closeFillColor,\n closeStrokeColor\n};\n","import PropTypes from 'prop-types';\n\n// intlShape was removed in react-intl@3 and replaced with a TypeScript interface.\n// These are some of the commonly used properties from the intl object.\nconst intlShape = PropTypes.shape({\n locale: PropTypes.string.isRequired,\n formatMessage: PropTypes.func.isRequired\n});\n\nexport default intlShape;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./color-button.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./color-button.css\";\n export default content && content.locals ? content.locals : undefined;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\n\nimport {MIXED} from '../../helper/style-path';\n\nimport noFillIcon from './no-fill.svg';\nimport mixedFillIcon from './mixed-fill.svg';\nimport styles from './color-button.css';\nimport GradientTypes from '../../lib/gradient-types';\nimport log from '../../log/log';\n\nconst colorToBackground = (color, color2, gradientType) => {\n if (color === MIXED || (gradientType !== GradientTypes.SOLID && color2 === MIXED)) return 'white';\n if (color === null) color = 'white';\n if (color2 === null) color2 = 'white';\n switch (gradientType) {\n case GradientTypes.SOLID: return color;\n case GradientTypes.HORIZONTAL: return `linear-gradient(to right, ${color}, ${color2})`;\n case GradientTypes.VERTICAL: return `linear-gradient(${color}, ${color2})`;\n case GradientTypes.RADIAL: return `radial-gradient(${color}, ${color2})`;\n default: log.error(`Unrecognized gradient type: ${gradientType}`);\n }\n};\n\nconst ColorButtonComponent = props => (\n <div\n className={styles.colorButton}\n onClick={props.onClick}\n >\n <div\n className={classNames(styles.colorButtonSwatch, {\n [styles.outlineSwatch]: props.outline && !(props.color === MIXED)\n })}\n style={{\n background: colorToBackground(props.color, props.color2, props.gradientType)\n }}\n >\n {props.color === null && (props.gradientType === GradientTypes.SOLID || props.color2 === null) ? (\n <img\n className={styles.swatchIcon}\n draggable={false}\n src={noFillIcon}\n />\n ) : ((props.color === MIXED || (props.gradientType !== GradientTypes.SOLID && props.color2 === MIXED) ? (\n <img\n className={styles.swatchIcon}\n draggable={false}\n src={mixedFillIcon}\n />\n ) : null))}\n </div>\n <div className={styles.colorButtonArrow}>▾</div>\n </div>\n);\n\nColorButtonComponent.propTypes = {\n color: PropTypes.string,\n color2: PropTypes.string,\n gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n onClick: PropTypes.func.isRequired,\n outline: PropTypes.bool.isRequired\n};\n\nColorButtonComponent.defaultProps = {\n outline: false\n};\n\nexport default ColorButtonComponent;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./slider.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./slider.css\";\n export default content && content.locals ? content.locals : undefined;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport bindAll from 'lodash.bindall';\nimport classNames from 'classnames';\nimport {getEventXY} from '../../lib/touch-utils';\n\nimport styles from './slider.css';\n\nconst CONTAINER_WIDTH = 150;\nconst HANDLE_WIDTH = 26;\n\nclass SliderComponent extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleMouseDown',\n 'handleMouseUp',\n 'handleMouseMove',\n 'handleClickBackground',\n 'setBackground',\n 'setHandle'\n ]);\n\n // Distance from the left edge of the slider handle to the mouse down/click event\n this.handleClickOffset = 0;\n }\n\n handleMouseDown (event) {\n document.addEventListener('mousemove', this.handleMouseMove);\n document.addEventListener('mouseup', this.handleMouseUp);\n document.addEventListener('touchmove', this.handleMouseMove, {passive: false});\n document.addEventListener('touchend', this.handleMouseUp);\n\n this.handleClickOffset = getEventXY(event).x - this.handle.getBoundingClientRect().left;\n }\n\n handleMouseUp () {\n document.removeEventListener('mousemove', this.handleMouseMove);\n document.removeEventListener('mouseup', this.handleMouseUp);\n document.removeEventListener('touchmove', this.handleMouseMove, {passive: false});\n document.removeEventListener('touchend', this.handleMouseUp);\n }\n\n handleMouseMove (event) {\n event.preventDefault();\n this.props.onChange(this.scaleMouseToSliderPosition(event));\n }\n\n handleClickBackground (event) {\n // Because the slider handle is a child of the \"background\" element this handler is registered to, it calls this\n // when clicked as well. We only want to change the slider value if the user clicked on the background itself.\n if (event.target !== this.background) return;\n // Move slider handle's center to the cursor\n this.handleClickOffset = HANDLE_WIDTH / 2;\n this.props.onChange(this.scaleMouseToSliderPosition(event));\n }\n\n scaleMouseToSliderPosition (event){\n const {x} = getEventXY(event);\n const backgroundBBox = this.background.getBoundingClientRect();\n const scaledX = x - backgroundBBox.left - this.handleClickOffset;\n return Math.max(0, Math.min(100, 100 * scaledX / (backgroundBBox.width - HANDLE_WIDTH)));\n }\n\n setBackground (ref) {\n this.background = ref;\n }\n\n setHandle (ref) {\n this.handle = ref;\n }\n\n render () {\n const halfHandleWidth = HANDLE_WIDTH / 2;\n const pixelMin = halfHandleWidth;\n const pixelMax = CONTAINER_WIDTH - halfHandleWidth;\n const handleOffset = pixelMin +\n ((pixelMax - pixelMin) * (this.props.value / 100)) -\n halfHandleWidth;\n return (\n <div\n className={classNames({\n [styles.container]: true,\n [styles.last]: this.props.lastSlider\n })}\n ref={this.setBackground}\n style={{\n backgroundImage: this.props.background\n }}\n onClick={this.handleClickBackground}\n >\n <div\n className={styles.handle}\n ref={this.setHandle}\n style={{\n left: `${handleOffset}px`\n }}\n onMouseDown={this.handleMouseDown}\n onTouchStart={this.handleMouseDown}\n />\n </div>\n );\n }\n}\n\nSliderComponent.propTypes = {\n background: PropTypes.string,\n lastSlider: PropTypes.bool,\n onChange: PropTypes.func.isRequired,\n value: PropTypes.number.isRequired\n};\n\nSliderComponent.defaultProps = {\n background: 'yellow'\n};\n\nexport default SliderComponent;\nexport {CONTAINER_WIDTH, HANDLE_WIDTH};\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./labeled-icon-button.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./labeled-icon-button.css\";\n export default content && content.locals ? content.locals : undefined;\n","/* @todo This file should be pulled out into a shared library with scratch-gui,\nconsolidating this component with icon-button.jsx in gui.\nSee #13 */\n\nimport classNames from 'classnames';\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nimport Button from '../button/button.jsx';\n\nimport styles from './labeled-icon-button.css';\n\nconst LabeledIconButton = ({\n className,\n hideLabel,\n imgAlt,\n imgSrc,\n onClick,\n title,\n ...props\n}) => (\n <Button\n className={classNames(className, styles.modEditField)}\n onClick={onClick}\n {...props}\n >\n <img\n alt={imgAlt || title}\n className={styles.editFieldIcon}\n draggable={false}\n src={imgSrc}\n title={title}\n />\n {!hideLabel && <span className={styles.editFieldTitle}>{title}</span>}\n </Button>\n);\n\nLabeledIconButton.propTypes = {\n className: PropTypes.string,\n hideLabel: PropTypes.bool,\n highlighted: PropTypes.bool,\n imgAlt: PropTypes.string,\n imgSrc: PropTypes.string.isRequired,\n onClick: PropTypes.func.isRequired,\n title: PropTypes.string.isRequired\n};\n\nexport default LabeledIconButton;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./color-picker.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./color-picker.css\";\n export default content && content.locals ? content.locals : undefined;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport {defineMessages, FormattedMessage, injectIntl} from 'react-intl';\nimport intlShape from '../../lib/intl-shape.js';\n\nimport classNames from 'classnames';\nimport parseColor from 'parse-color';\n\nimport Slider, {CONTAINER_WIDTH, HANDLE_WIDTH} from '../forms/slider.jsx';\nimport LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';\nimport styles from './color-picker.css';\nimport GradientTypes from '../../lib/gradient-types';\nimport {MIXED} from '../../helper/style-path';\n\nimport eyeDropperIcon from './icons/eye-dropper.svg';\nimport noFillIcon from '../color-button/no-fill.svg';\nimport mixedFillIcon from '../color-button/mixed-fill.svg';\nimport fillHorzGradientIcon from './icons/fill-horz-gradient-enabled.svg';\nimport fillRadialIcon from './icons/fill-radial-enabled.svg';\nimport fillSolidIcon from './icons/fill-solid-enabled.svg';\nimport fillVertGradientIcon from './icons/fill-vert-gradient-enabled.svg';\nimport swapIcon from './icons/swap.svg';\nimport Modes from '../../lib/modes';\n\nconst hsvToHex = (h, s, v) =>\n // Scale hue back up to [0, 360] from [0, 100]\n parseColor(`hsv(${3.6 * h}, ${s}, ${v})`).hex\n;\n\nconst messages = defineMessages({\n swap: {\n defaultMessage: 'Swap',\n description: 'Label for button that swaps the two colors in a gradient',\n id: 'paint.colorPicker.swap'\n }\n});\nclass ColorPickerComponent extends React.Component {\n _makeBackground (channel) {\n const stops = [];\n // Generate the color slider background CSS gradients by adding\n // color stops depending on the slider.\n for (let n = 100; n >= 0; n -= 10) {\n switch (channel) {\n case 'hue':\n stops.push(hsvToHex(n, this.props.saturation, this.props.brightness));\n break;\n case 'saturation':\n stops.push(hsvToHex(this.props.hue, n, this.props.brightness));\n break;\n case 'brightness':\n stops.push(hsvToHex(this.props.hue, this.props.saturation, n));\n break;\n default:\n throw new Error(`Unknown channel for color sliders: ${channel}`);\n }\n }\n\n // The sliders are a rounded capsule shape, and the slider handles are circles. As a consequence, when the\n // slider handle is fully to one side, its center is actually moved away from the start/end of the slider by\n // the slider handle's radius, meaning that the effective range of the slider excludes the rounded caps.\n // To compensate for this, position the first stop to where the rounded cap ends, and position the last stop\n // to where the rounded cap begins.\n const halfHandleWidth = HANDLE_WIDTH / 2;\n stops[0] += ` 0 ${halfHandleWidth}px`;\n stops[stops.length - 1] += ` ${CONTAINER_WIDTH - halfHandleWidth}px 100%`;\n\n return `linear-gradient(to left, ${stops.join(',')})`;\n }\n render () {\n return (\n <div\n className={styles.colorPickerContainer}\n dir={this.props.rtl ? 'rtl' : 'ltr'}\n >\n {this.props.shouldShowGradientTools ? (\n <div>\n <div className={styles.row}>\n <div className={styles.gradientPickerRow}>\n <img\n className={classNames({\n [styles.inactiveGradient]: this.props.gradientType !== GradientTypes.SOLID,\n [styles.clickable]: true\n })}\n draggable={false}\n src={fillSolidIcon}\n onClick={this.props.onChangeGradientTypeSolid}\n />\n <img\n className={classNames({\n [styles.inactiveGradient]:\n this.props.gradientType !== GradientTypes.HORIZONTAL,\n [styles.clickable]: true\n })}\n draggable={false}\n src={fillHorzGradientIcon}\n onClick={this.props.onChangeGradientTypeHorizontal}\n />\n <img\n className={classNames({\n [styles.inactiveGradient]: this.props.gradientType !== GradientTypes.VERTICAL,\n [styles.clickable]: true\n })}\n draggable={false}\n src={fillVertGradientIcon}\n onClick={this.props.onChangeGradientTypeVertical}\n />\n <img\n className={classNames({\n [styles.inactiveGradient]: this.props.gradientType !== GradientTypes.RADIAL,\n [styles.clickable]: true\n })}\n draggable={false}\n src={fillRadialIcon}\n onClick={this.props.onChangeGradientTypeRadial}\n />\n </div>\n </div>\n <div className={styles.divider} />\n {this.props.gradientType === GradientTypes.SOLID ? null : (\n <div className={styles.row}>\n <div\n className={classNames(\n styles.gradientPickerRow,\n styles.gradientSwatchesRow\n )}\n >\n <div\n className={classNames({\n [styles.clickable]: true,\n [styles.swatch]: true,\n [styles.largeSwatch]: true,\n [styles.activeSwatch]: this.props.colorIndex === 0\n })}\n style={{\n backgroundColor: this.props.color === null || this.props.color === MIXED ?\n 'white' : this.props.color\n }}\n onClick={this.props.onSelectColor}\n >\n {this.props.color === null ? (\n <img\n className={styles.largeSwatchIcon}\n draggable={false}\n src={noFillIcon}\n />\n ) : this.props.color === MIXED ? (\n <img\n className={styles.largeSwatchIcon}\n draggable={false}\n src={mixedFillIcon}\n />\n ) : null}\n </div>\n <LabeledIconButton\n className={styles.swapButton}\n imgSrc={swapIcon}\n title={this.props.intl.formatMessage(messages.swap)}\n onClick={this.props.onSwap}\n />\n <div\n className={classNames({\n [styles.clickable]: true,\n [styles.swatch]: true,\n [styles.largeSwatch]: true,\n [styles.activeSwatch]: this.props.colorIndex === 1\n })}\n style={{\n backgroundColor: this.props.color2 === null || this.props.color2 === MIXED ?\n 'white' : this.props.color2\n }}\n onClick={this.props.onSelectColor2}\n >\n {this.props.color2 === null ? (\n <img\n className={styles.largeSwatchIcon}\n draggable={false}\n src={noFillIcon}\n />\n ) : this.props.color2 === MIXED ? (\n <img\n className={styles.largeSwatchIcon}\n draggable={false}\n src={mixedFillIcon}\n />\n ) : null}\n </div>\n </div>\n </div>\n )}\n </div>\n ) : null}\n <div className={styles.row}>\n <div className={styles.rowHeader}>\n <span className={styles.labelName}>\n <FormattedMessage\n defaultMessage=\"Color\"\n description=\"Label for the hue component in the color picker\"\n id=\"paint.paintEditor.hue\"\n />\n </span>\n <span className={styles.labelReadout}>\n {Math.round(this.props.hue)}\n </span>\n </div>\n <div className={styles.rowSlider}>\n <Slider\n background={this._makeBackground('hue')}\n value={this.props.hue}\n onChange={this.props.onHueChange}\n />\n </div>\n </div>\n <div className={styles.row}>\n <div className={styles.rowHeader}>\n <span className={styles.labelName}>\n <FormattedMessage\n defaultMessage=\"Saturation\"\n description=\"Label for the saturation component in the color picker\"\n id=\"paint.paintEditor.saturation\"\n />\n </span>\n <span className={styles.labelReadout}>\n {Math.round(this.props.saturation)}\n </span>\n </div>\n <div className={styles.rowSlider}>\n <Slider\n background={this._makeBackground('saturation')}\n value={this.props.saturation}\n onChange={this.props.onSaturationChange}\n />\n </div>\n </div>\n <div className={styles.row}>\n <div className={styles.rowHeader}>\n <span className={styles.labelName}>\n <FormattedMessage\n defaultMessage=\"Brightness\"\n description=\"Label for the brightness component in the color picker\"\n id=\"paint.paintEditor.brightness\"\n />\n </span>\n <span className={styles.labelReadout}>\n {Math.round(this.props.brightness)}\n </span>\n </div>\n <div className={styles.rowSlider}>\n <Slider\n lastSlider\n background={this._makeBackground('brightness')}\n value={this.props.brightness}\n onChange={this.props.onBrightnessChange}\n />\n </div>\n </div>\n <div className={styles.swatchRow}>\n <div className={styles.swatches}>\n {this.props.mode === Modes.BIT_LINE ||\n this.props.mode === Modes.BIT_RECT ||\n this.props.mode === Modes.BIT_OVAL ||\n this.props.mode === Modes.BIT_TEXT ? null :\n (<div\n className={classNames({\n [styles.clickable]: true,\n [styles.swatch]: true,\n [styles.activeSwatch]:\n (this.props.colorIndex === 0 && this.props.color === null) ||\n (this.props.colorIndex === 1 && this.props.color2 === null)\n })}\n onClick={this.props.onTransparent}\n >\n <img\n className={styles.swatchIcon}\n draggable={false}\n src={noFillIcon}\n />\n </div>)\n }\n </div>\n <div className={styles.swatches}>\n <div\n className={classNames({\n [styles.clickable]: true,\n [styles.swatch]: true,\n [styles.activeSwatch]: this.props.isEyeDropping\n })}\n onClick={this.props.onActivateEyeDropper}\n >\n <img\n className={styles.swatchIcon}\n draggable={false}\n src={eyeDropperIcon}\n />\n </div>\n </div>\n </div>\n </div>\n );\n }\n}\n\nColorPickerComponent.propTypes = {\n brightness: PropTypes.number.isRequired,\n color: PropTypes.string,\n color2: PropTypes.string,\n colorIndex: PropTypes.number.isRequired,\n gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n hue: PropTypes.number.isRequired,\n intl: intlShape.isRequired,\n isEyeDropping: PropTypes.bool.isRequired,\n mode: PropTypes.oneOf(Object.keys(Modes)),\n onActivateEyeDropper: PropTypes.func.isRequired,\n onBrightnessChange: PropTypes.func.isRequired,\n onChangeGradientTypeHorizontal: PropTypes.func.isRequired,\n onChangeGradientTypeRadial: PropTypes.func.isRequired,\n onChangeGradientTypeSolid: PropTypes.func.isRequired,\n onChangeGradientTypeVertical: PropTypes.func.isRequired,\n onHueChange: PropTypes.func.isRequired,\n onSaturationChange: PropTypes.func.isRequired,\n onSelectColor: PropTypes.func.isRequired,\n onSelectColor2: PropTypes.func.isRequired,\n onSwap: PropTypes.func,\n onTransparent: PropTypes.func.isRequired,\n rtl: PropTypes.bool.isRequired,\n saturation: PropTypes.number.isRequired,\n shouldShowGradientTools: PropTypes.bool.isRequired\n};\n\nexport default injectIntl(ColorPickerComponent);\n","import bindAll from 'lodash.bindall';\nimport {connect} from 'react-redux';\nimport paper from '@scratch/paper';\nimport parseColor from 'parse-color';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport {changeColorIndex} from '../reducers/color-index';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {activateEyeDropper} from '../reducers/eye-dropper';\nimport GradientTypes from '../lib/gradient-types';\n\nimport ColorPickerComponent from '../components/color-picker/color-picker.jsx';\nimport {MIXED} from '../helper/style-path';\nimport Modes from '../lib/modes';\n\nconst colorStringToHsv = hexString => {\n const hsv = parseColor(hexString).hsv;\n // Hue comes out in [0, 360], limit to [0, 100]\n hsv[0] = hsv[0] / 3.6;\n // Black is parsed as {0, 0, 0}, but turn saturation up to 100\n // to make it easier to see slider values.\n if (hsv[1] === 0 && hsv[2] === 0) {\n hsv[1] = 100;\n }\n return hsv;\n};\n\nconst hsvToHex = (h, s, v) =>\n // Scale hue back up to [0, 360] from [0, 100]\n parseColor(`hsv(${3.6 * h}, ${s}, ${v})`).hex\n;\n\n// Important! This component ignores new color props except when isEyeDropping\n// This is to make the HSV <=> RGB conversion stable. The sliders manage their\n// own changes until unmounted or color changes with props.isEyeDropping = true.\nclass ColorPicker extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'getHsv',\n 'handleChangeGradientTypeHorizontal',\n 'handleChangeGradientTypeRadial',\n 'handleChangeGradientTypeSolid',\n 'handleChangeGradientTypeVertical',\n 'handleHueChange',\n 'handleSaturationChange',\n 'handleBrightnessChange',\n 'handleTransparent',\n 'handleActivateEyeDropper'\n ]);\n\n const color = props.colorIndex === 0 ? props.color : props.color2;\n const hsv = this.getHsv(color);\n this.state = {\n hue: hsv[0],\n saturation: hsv[1],\n brightness: hsv[2]\n };\n }\n componentWillReceiveProps (newProps) {\n const color = newProps.colorIndex === 0 ? this.props.color : this.props.color2;\n const newColor = newProps.colorIndex === 0 ? newProps.color : newProps.color2;\n const colorSetByEyedropper = this.props.isEyeDropping && color !== newColor;\n if (colorSetByEyedropper || this.props.colorIndex !== newProps.colorIndex) {\n const hsv = this.getHsv(newColor);\n this.setState({\n hue: hsv[0],\n saturation: hsv[1],\n brightness: hsv[2]\n });\n }\n }\n getHsv (color) {\n const isTransparent = color === null;\n const isMixed = color === MIXED;\n return isTransparent || isMixed ?\n [50, 100, 100] : colorStringToHsv(color);\n }\n handleHueChange (hue) {\n this.setState({hue: hue}, () => {\n this.handleColorChange();\n });\n }\n handleSaturationChange (saturation) {\n this.setState({saturation: saturation}, () => {\n this.handleColorChange();\n });\n }\n handleBrightnessChange (brightness) {\n this.setState({brightness: brightness}, () => {\n this.handleColorChange();\n });\n }\n handleColorChange () {\n this.props.onChangeColor(hsvToHex(\n this.state.hue,\n this.state.saturation,\n this.state.brightness\n ));\n }\n handleTransparent () {\n this.props.onChangeColor(null);\n }\n handleActivateEyeDropper () {\n this.props.onActivateEyeDropper(\n paper.tool, // get the currently active tool from paper\n this.props.onChangeColor\n );\n }\n handleChangeGradientTypeHorizontal () {\n this.props.onChangeGradientType(GradientTypes.HORIZONTAL);\n }\n handleChangeGradientTypeRadial () {\n this.props.onChangeGradientType(GradientTypes.RADIAL);\n }\n handleChangeGradientTypeSolid () {\n this.props.onChangeGradientType(GradientTypes.SOLID);\n }\n handleChangeGradientTypeVertical () {\n this.props.onChangeGradientType(GradientTypes.VERTICAL);\n }\n render () {\n return (\n <ColorPickerComponent\n brightness={this.state.brightness}\n color={this.props.color}\n color2={this.props.color2}\n colorIndex={this.props.colorIndex}\n gradientType={this.props.gradientType}\n hue={this.state.hue}\n isEyeDropping={this.props.isEyeDropping}\n mode={this.props.mode}\n rtl={this.props.rtl}\n saturation={this.state.saturation}\n shouldShowGradientTools={this.props.shouldShowGradientTools}\n onActivateEyeDropper={this.handleActivateEyeDropper}\n onBrightnessChange={this.handleBrightnessChange}\n onChangeGradientTypeHorizontal={this.handleChangeGradientTypeHorizontal}\n onChangeGradientTypeRadial={this.handleChangeGradientTypeRadial}\n onChangeGradientTypeSolid={this.handleChangeGradientTypeSolid}\n onChangeGradientTypeVertical={this.handleChangeGradientTypeVertical}\n onHueChange={this.handleHueChange}\n onSaturationChange={this.handleSaturationChange}\n onSelectColor={this.props.onSelectColor}\n onSelectColor2={this.props.onSelectColor2}\n onSwap={this.props.onSwap}\n onTransparent={this.handleTransparent}\n />\n );\n }\n}\n\nColorPicker.propTypes = {\n color: PropTypes.string,\n color2: PropTypes.string,\n colorIndex: PropTypes.number.isRequired,\n gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n isEyeDropping: PropTypes.bool.isRequired,\n mode: PropTypes.oneOf(Object.keys(Modes)),\n onActivateEyeDropper: PropTypes.func.isRequired,\n onChangeColor: PropTypes.func.isRequired,\n onChangeGradientType: PropTypes.func,\n onSelectColor: PropTypes.func.isRequired,\n onSelectColor2: PropTypes.func.isRequired,\n onSwap: PropTypes.func,\n rtl: PropTypes.bool.isRequired,\n shouldShowGradientTools: PropTypes.bool.isRequired\n};\n\nconst mapStateToProps = state => ({\n colorIndex: state.scratchPaint.fillMode.colorIndex,\n isEyeDropping: state.scratchPaint.color.eyeDropper.active,\n mode: state.scratchPaint.mode,\n rtl: state.scratchPaint.layout.rtl\n});\n\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n onActivateEyeDropper: (currentTool, callback) => {\n dispatch(activateEyeDropper(currentTool, callback));\n },\n onSelectColor: () => {\n dispatch(changeColorIndex(0));\n },\n onSelectColor2: () => {\n dispatch(changeColorIndex(1));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(ColorPicker);\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./input-group.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./input-group.css\";\n export default content && content.locals ? content.locals : undefined;\n","import classNames from 'classnames';\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nimport styles from './input-group.css';\n\nconst InputGroup = props => (\n <div\n className={classNames(props.className, styles.inputGroup, {\n [styles.disabled]: props.disabled\n })}\n dir={props.rtl ? 'rtl' : ''}\n >\n {props.children}\n </div>\n);\n\nInputGroup.propTypes = {\n children: PropTypes.node.isRequired,\n className: PropTypes.string,\n disabled: PropTypes.bool,\n rtl: PropTypes.bool\n};\n\nexport default InputGroup;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./label.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./label.css\";\n export default content && content.locals ? content.locals : undefined;\n","/* DO NOT EDIT\n@todo This file is copied from GUI and should be pulled out into a shared library.\nSee https://github.com/LLK/scratch-paint/issues/13 */\n\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport styles from './label.css';\n\nconst Label = props => (\n <label className={styles.inputGroup}>\n <span className={props.secondary ? styles.inputLabelSecondary : styles.inputLabel}>\n {props.text}\n </span>\n {props.children}\n </label>\n);\n\nLabel.propTypes = {\n children: PropTypes.node,\n secondary: PropTypes.bool,\n text: PropTypes.string.isRequired\n};\n\nLabel.defaultProps = {\n secondary: false\n};\n\nexport default Label;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport Popover from 'react-popover';\n\nimport ColorButton from './color-button/color-button.jsx';\nimport ColorPicker from '../containers/color-picker.jsx';\nimport InputGroup from './input-group/input-group.jsx';\nimport Label from './forms/label.jsx';\n\nimport GradientTypes from '../lib/gradient-types';\n\nconst ColorIndicatorComponent = props => (\n <InputGroup\n className={props.className}\n disabled={props.disabled}\n >\n <Popover\n body={\n <ColorPicker\n color={props.color}\n color2={props.color2}\n gradientType={props.gradientType}\n shouldShowGradientTools={props.shouldShowGradientTools}\n onChangeColor={props.onChangeColor}\n onChangeGradientType={props.onChangeGradientType}\n onSwap={props.onSwap}\n />\n }\n isOpen={props.colorModalVisible}\n preferPlace=\"below\"\n onOuterAction={props.onCloseColor}\n >\n <Label text={props.label}>\n <ColorButton\n color={props.color}\n color2={props.color2}\n gradientType={props.gradientType}\n onClick={props.onOpenColor}\n outline={props.outline}\n />\n </Label>\n </Popover>\n </InputGroup>\n);\n\nColorIndicatorComponent.propTypes = {\n className: PropTypes.string,\n disabled: PropTypes.bool.isRequired,\n color: PropTypes.string,\n color2: PropTypes.string,\n colorModalVisible: PropTypes.bool.isRequired,\n gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n label: PropTypes.string.isRequired,\n onChangeColor: PropTypes.func.isRequired,\n onChangeGradientType: PropTypes.func.isRequired,\n onCloseColor: PropTypes.func.isRequired,\n onOpenColor: PropTypes.func.isRequired,\n onSwap: PropTypes.func.isRequired,\n outline: PropTypes.bool.isRequired,\n shouldShowGradientTools: PropTypes.bool.isRequired\n};\n\nexport default ColorIndicatorComponent;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport bindAll from 'lodash.bindall';\nimport parseColor from 'parse-color';\nimport {injectIntl} from 'react-intl';\nimport intlShape from '../lib/intl-shape.js';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport Formats, {isBitmap} from '../lib/format';\nimport GradientTypes from '../lib/gradient-types';\n\nimport ColorIndicatorComponent from '../components/color-indicator.jsx';\nimport {applyColorToSelection,\n applyGradientTypeToSelection,\n applyStrokeWidthToSelection,\n generateSecondaryColor,\n swapColorsInSelection,\n MIXED} from '../helper/style-path';\n\nconst makeColorIndicator = (label, isStroke) => {\n class ColorIndicator extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleChangeColor',\n 'handleChangeGradientType',\n 'handleCloseColor',\n 'handleSwap'\n ]);\n\n // Flag to track whether an svg-update-worthy change has been made\n this._hasChanged = false;\n }\n componentWillReceiveProps (newProps) {\n const {colorModalVisible, onUpdateImage} = this.props;\n if (colorModalVisible && !newProps.colorModalVisible) {\n // Submit the new SVG, which also stores a single undo/redo action.\n if (this._hasChanged) onUpdateImage();\n this._hasChanged = false;\n }\n }\n handleChangeColor (newColor) {\n // Stroke-selector-specific logic: if we change the stroke color from \"none\" to something visible, ensure\n // there's a nonzero stroke width. If we change the stroke color to \"none\", set the stroke width to zero.\n if (isStroke) {\n\n // Whether the old color style in this color indicator was null (completely transparent).\n // If it's a solid color, this means that the first color is null.\n // If it's a gradient, this means both colors are null.\n const oldStyleWasNull = this.props.gradientType === GradientTypes.SOLID ?\n this.props.color === null :\n this.props.color === null && this.props.color2 === null;\n\n const otherColor = this.props.colorIndex === 1 ? this.props.color : this.props.color2;\n // Whether the new color style in this color indicator is null.\n const newStyleIsNull = this.props.gradientType === GradientTypes.SOLID ?\n newColor === null :\n newColor === null && otherColor === null;\n\n if (oldStyleWasNull && !newStyleIsNull) {\n this._hasChanged = applyStrokeWidthToSelection(1, this.props.textEditTarget) || this._hasChanged;\n this.props.onChangeStrokeWidth(1);\n } else if (!oldStyleWasNull && newStyleIsNull) {\n this._hasChanged = applyStrokeWidthToSelection(0, this.props.textEditTarget) || this._hasChanged;\n this.props.onChangeStrokeWidth(0);\n }\n }\n\n const formatIsBitmap = isBitmap(this.props.format);\n // Apply color and update redux, but do not update svg until picker closes.\n const isDifferent = applyColorToSelection(\n newColor,\n this.props.colorIndex,\n this.props.gradientType === GradientTypes.SOLID,\n // In bitmap mode, only the fill color selector is used, but it applies to stroke if fillBitmapShapes\n // is set to true via the \"Fill\"/\"Outline\" selector button\n isStroke || (formatIsBitmap && !this.props.fillBitmapShapes),\n this.props.textEditTarget);\n this._hasChanged = this._hasChanged || isDifferent;\n this.props.onChangeColor(newColor, this.props.colorIndex);\n }\n handleChangeGradientType (gradientType) {\n const formatIsBitmap = isBitmap(this.props.format);\n // Apply color and update redux, but do not update svg until picker closes.\n const isDifferent = applyGradientTypeToSelection(\n gradientType,\n isStroke || (formatIsBitmap && !this.props.fillBitmapShapes),\n this.props.textEditTarget);\n this._hasChanged = this._hasChanged || isDifferent;\n const hasSelectedItems = getSelectedLeafItems().length > 0;\n if (hasSelectedItems) {\n if (isDifferent) {\n // Recalculates the swatch colors\n this.props.setSelectedItems(this.props.format);\n }\n }\n if (this.props.gradientType === GradientTypes.SOLID && gradientType !== GradientTypes.SOLID) {\n // Generate color 2 and change to the 2nd swatch when switching from solid to gradient\n if (!hasSelectedItems) {\n this.props.onChangeColor(generateSecondaryColor(this.props.color), 1);\n }\n this.props.onChangeColorIndex(1);\n }\n if (this.props.onChangeGradientType) this.props.onChangeGradientType(gradientType);\n }\n handleCloseColor () {\n // If the eyedropper is currently being used, don't\n // close the color menu.\n if (this.props.isEyeDropping) return;\n\n // Otherwise, close the color menu and\n // also reset the color index to indicate\n // that `color1` is selected.\n this.props.onCloseColor();\n this.props.onChangeColorIndex(0);\n }\n handleSwap () {\n if (getSelectedLeafItems().length) {\n const formatIsBitmap = isBitmap(this.props.format);\n const isDifferent = swapColorsInSelection(\n isStroke || (formatIsBitmap && !this.props.fillBitmapShapes),\n this.props.textEditTarget);\n this.props.setSelectedItems(this.props.format);\n this._hasChanged = this._hasChanged || isDifferent;\n } else {\n let color1 = this.props.color;\n let color2 = this.props.color2;\n color1 = color1 === null || color1 === MIXED ? color1 : parseColor(color1).hex;\n color2 = color2 === null || color2 === MIXED ? color2 : parseColor(color2).hex;\n this.props.onChangeColor(color1, 1);\n this.props.onChangeColor(color2, 0);\n }\n }\n render () {\n return (\n <ColorIndicatorComponent\n {...this.props}\n label={this.props.intl.formatMessage(label)}\n outline={isStroke}\n onChangeColor={this.handleChangeColor}\n onChangeGradientType={this.handleChangeGradientType}\n onCloseColor={this.handleCloseColor}\n onSwap={this.handleSwap}\n />\n );\n }\n }\n\n ColorIndicator.propTypes = {\n colorIndex: PropTypes.number.isRequired,\n disabled: PropTypes.bool.isRequired,\n color: PropTypes.string,\n color2: PropTypes.string,\n colorModalVisible: PropTypes.bool.isRequired,\n fillBitmapShapes: PropTypes.bool.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)),\n gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n intl: intlShape,\n isEyeDropping: PropTypes.bool.isRequired,\n onChangeColorIndex: PropTypes.func.isRequired,\n onChangeColor: PropTypes.func.isRequired,\n onChangeGradientType: PropTypes.func,\n onChangeStrokeWidth: PropTypes.func,\n onCloseColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n textEditTarget: PropTypes.number\n };\n\n return injectIntl(ColorIndicator);\n};\n\nexport default makeColorIndicator;\n","import {connect} from 'react-redux';\nimport {defineMessages} from 'react-intl';\n\nimport {changeColorIndex} from '../reducers/color-index';\nimport {changeFillColor, changeFillColor2} from '../reducers/fill-style';\nimport {changeGradientType} from '../reducers/fill-mode-gradient-type';\nimport {openFillColor, closeFillColor} from '../reducers/modals';\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {setSelectedItems} from '../reducers/selected-items';\nimport Modes, {GradientToolsModes} from '../lib/modes';\nimport {isBitmap} from '../lib/format';\n\nimport makeColorIndicator from './color-indicator.jsx';\n\nconst messages = defineMessages({\n label: {\n id: 'paint.paintEditor.fill',\n description: 'Label for the color picker for the fill color',\n defaultMessage: 'Fill'\n }\n});\n\nconst FillColorIndicator = makeColorIndicator(messages.label, false);\n\nconst mapStateToProps = state => ({\n colorIndex: state.scratchPaint.fillMode.colorIndex,\n disabled: state.scratchPaint.mode === Modes.LINE,\n color: state.scratchPaint.color.fillColor.primary,\n color2: state.scratchPaint.color.fillColor.secondary,\n colorModalVisible: state.scratchPaint.modals.fillColor,\n fillBitmapShapes: state.scratchPaint.fillBitmapShapes,\n format: state.scratchPaint.format,\n gradientType: state.scratchPaint.color.fillColor.gradientType,\n isEyeDropping: state.scratchPaint.color.eyeDropper.active,\n mode: state.scratchPaint.mode,\n shouldShowGradientTools: state.scratchPaint.mode in GradientToolsModes,\n textEditTarget: state.scratchPaint.textEditTarget\n});\n\nconst mapDispatchToProps = dispatch => ({\n onChangeColorIndex: index => {\n dispatch(changeColorIndex(index));\n },\n onChangeColor: (fillColor, index) => {\n if (index === 0) {\n dispatch(changeFillColor(fillColor));\n } else if (index === 1) {\n dispatch(changeFillColor2(fillColor));\n }\n },\n onOpenColor: () => {\n dispatch(openFillColor());\n },\n onCloseColor: () => {\n dispatch(closeFillColor());\n },\n onChangeGradientType: gradientType => {\n dispatch(changeGradientType(gradientType));\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(FillColorIndicator);\n","import paper from '@scratch/paper';\nimport {isBoundsItem, getRootItem} from './item';\nimport {hoverBounds, hoverItem} from './guides';\nimport {isGroupChild} from './group';\nimport {sortItemsByZIndex} from './math';\n\n/**\n * @param {!MouseEvent} event mouse event\n * @param {?object} hitOptions hit options to use\n * @param {?boolean} subselect Whether items within groups can be hovered. If false, the\n * entire group should be hovered.\n * @returns {paper.Item} the hovered item or null if there is none\n */\nconst getHoveredItem = function (event, hitOptions, subselect) {\n const oldMatch = hitOptions.match;\n hitOptions.match = hitResult => {\n if (hitResult.item.data && hitResult.item.data.noHover) return false;\n return oldMatch ? oldMatch(hitResult) : true;\n };\n const hitResults = paper.project.hitTestAll(event.point, hitOptions);\n if (hitResults.length === 0) {\n return null;\n }\n\n // Get highest z-index result\n let hitResult;\n for (const result of hitResults) {\n if (!hitResult || sortItemsByZIndex(hitResult.item, result.item) < 0) {\n hitResult = result;\n }\n }\n const item = hitResult.item;\n // If the hovered item is already selected, then there should be no hovered item.\n if (!item || item.selected) {\n return null;\n }\n\n let hoverGuide;\n if (isBoundsItem(item)) {\n hoverGuide = hoverBounds(item);\n } else if (!subselect && isGroupChild(item)) {\n hoverGuide = hoverBounds(getRootItem(item));\n } else {\n hoverGuide = hoverItem(item);\n }\n hoverGuide.data.hitResult = hitResult;\n\n return hoverGuide;\n};\n\nexport {\n getHoveredItem\n};\n","import paper from '@scratch/paper';\nimport {getHoveredItem} from '../hover';\nimport {expandBy} from '../math';\nimport {createGradientObject} from '../style-path';\nimport GradientTypes from '../../lib/gradient-types';\n\nclass FillTool extends paper.Tool {\n static get TOLERANCE () {\n return 2;\n }\n /**\n * @param {function} setHoveredItem Callback to set the hovered item\n * @param {function} clearHoveredItem Callback to clear the hovered item\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setHoveredItem, clearHoveredItem, onUpdateImage) {\n super();\n this.setHoveredItem = setHoveredItem;\n this.clearHoveredItem = clearHoveredItem;\n this.onUpdateImage = onUpdateImage;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseUp = this.handleMouseUp;\n\n // Color to fill with\n this.fillColor = null;\n this.fillColor2 = null;\n this.gradientType = null;\n\n // The path that's being hovered over.\n this.fillItem = null;\n // The style property that we're applying the color to (either fill or stroke).\n this.fillProperty = null;\n // If we're hovering over a hole in a compound path, we can't just recolor it. This is the\n // added item that's the same shape as the hole that's drawn over the hole when we fill a hole.\n this.addedFillItem = null;\n this.fillItemOrigColor = null;\n this.prevHoveredItemId = null;\n }\n getHitOptions () {\n const isAlmostClosedPath = function (item) {\n return item instanceof paper.Path && item.segments.length > 2 &&\n item.lastSegment.point.getDistance(item.firstSegment.point) < 8;\n };\n return {\n segments: false,\n stroke: true,\n curves: false,\n fill: true,\n guide: false,\n match: function (hitResult) {\n // Allow fills to be hit only if the item has a fill already or the path is closed/nearly closed\n const hitFill = hitResult.item.hasFill() || hitResult.item.closed || isAlmostClosedPath(hitResult.item);\n if (hitResult.item instanceof paper.Path &&\n // Disallow hits that don't qualify for the fill criteria, but only if they're fills\n (hitFill || hitResult.type !== 'fill')) {\n return true;\n }\n if (hitResult.item instanceof paper.PointText) {\n return true;\n }\n },\n hitUnfilledPaths: true,\n // If the color is transparent/none, then we need to be able to hit \"invisible\" outlines so that we don't\n // prevent ourselves from hitting an outline when we make it transparent via the fill preview, causing it to\n // flicker back and forth between transparent/its previous color as we hit it, then stop hitting it, etc.\n // If the color *is* visible, then don't hit \"invisible\" outlines, since this would add visible outlines to\n // non-outlined shapes when you hovered over where their outlines would be.\n hitUnstrokedPaths: this.gradientType === GradientTypes.SOLID && this.fillColor === null,\n tolerance: FillTool.TOLERANCE / paper.view.zoom\n };\n }\n setFillColor (fillColor) {\n this.fillColor = fillColor;\n }\n setFillColor2 (fillColor2) {\n this.fillColor2 = fillColor2;\n }\n setGradientType (gradientType) {\n this.gradientType = gradientType;\n }\n /**\n * To be called when the hovered item changes. When the select tool hovers over a\n * new item, it compares against this to see if a hover item change event needs to\n * be fired.\n * @param {paper.Item} prevHoveredItemId ID of the highlight item that indicates the mouse is\n * over a given item currently\n */\n setPrevHoveredItemId (prevHoveredItemId) {\n this.prevHoveredItemId = prevHoveredItemId;\n }\n updateFillPreview (event) {\n const hoveredItem = getHoveredItem(event, this.getHitOptions(), true /* subselect */);\n if ((!hoveredItem && this.prevHoveredItemId) || // There is no longer a hovered item\n (hoveredItem && !this.prevHoveredItemId) || // There is now a hovered item\n (hoveredItem && this.prevHoveredItemId &&\n hoveredItem.id !== this.prevHoveredItemId)) { // hovered item changed\n this.setHoveredItem(hoveredItem ? hoveredItem.id : null);\n }\n const hitItem = hoveredItem ? hoveredItem.data.origItem : null;\n const hitType = hoveredItem ? hoveredItem.data.hitResult.type : null;\n\n // The hit \"target\" changes if we switch items or switch between fill/outline on the same item\n const hitTargetChanged = hitItem !== this.fillItem || hitType !== this.fillProperty;\n\n // Still hitting the same thing\n if (!hitTargetChanged) {\n // Only radial gradient needs to be updated\n if (this.gradientType === GradientTypes.RADIAL) {\n this._setFillItemColor(this.fillColor, this.fillColor2, this.gradientType, event.point);\n }\n return;\n }\n if (this.fillItem) {\n if (this.addedFillItem) {\n this.addedFillItem.remove();\n this.addedFillItem = null;\n } else {\n this._setFillItemColor(this.fillItemOrigColor);\n }\n this.fillItemOrigColor = null;\n this.fillItem = null;\n this.fillProperty = null;\n }\n if (hitItem) {\n this.fillItem = hitItem;\n this.fillProperty = hitType;\n const colorProp = hitType === 'fill' ? 'fillColor' : 'strokeColor';\n this.fillItemOrigColor = hitItem[colorProp];\n if (hitItem.parent instanceof paper.CompoundPath && hitItem.area < 0 && hitType === 'fill') { // hole\n if (!this.fillColor) {\n // Hole filled with transparent is no-op\n this.fillItem = null;\n this.fillProperty = null;\n this.fillItemOrigColor = null;\n return;\n }\n // Make an item to fill the hole\n this.addedFillItem = hitItem.clone();\n this.addedFillItem.setClockwise(true);\n this.addedFillItem.data.noHover = true;\n this.addedFillItem.data.origItem = hitItem;\n // This usually fixes it so there isn't a teeny tiny gap in between the fill and the outline\n // when filling in a hole\n expandBy(this.addedFillItem, .1);\n this.addedFillItem.insertAbove(hitItem.parent);\n } else if (this.fillItem.parent instanceof paper.CompoundPath) {\n this.fillItemOrigColor = hitItem.parent[colorProp];\n }\n this._setFillItemColor(this.fillColor, this.fillColor2, this.gradientType, event.point);\n }\n }\n handleMouseDown (event) {\n // on touch, the user might touch-and-hold to preview what the fill tool would do\n // if they don't move their finger at all after the \"mouse down\" event\n // then this might be our only chance to give them a good preview\n this.updateFillPreview(event);\n }\n handleMouseMove (event) {\n this.updateFillPreview(event);\n }\n handleMouseUp (event) {\n if (event.event.button > 0) return; // only first mouse button\n if (this.fillItem) {\n // If the hole we're filling in is the same color as the parent, and parent has no outline, remove the hole\n if (this.addedFillItem &&\n this._noStroke(this.fillItem.parent) &&\n this.addedFillItem.fillColor.type !== 'gradient' &&\n this.fillItem.parent.fillColor.toCSS() === this.addedFillItem.fillColor.toCSS()) {\n this.addedFillItem.remove();\n this.addedFillItem = null;\n let parent = this.fillItem.parent;\n this.fillItem.remove();\n parent = parent.reduce();\n parent.fillColor = this.fillColor;\n } else if (this.addedFillItem) {\n // Fill in a hole.\n this.addedFillItem.data.noHover = false;\n } else if (!this.fillColor &&\n this.fillItem.data &&\n this.fillItem.data.origItem) {\n // Filling a hole filler with transparent returns it to being gone\n // instead of making a shape that's transparent\n const group = this.fillItem.parent;\n this.fillItem.remove();\n if (!(group instanceof paper.Layer) && group.children.length === 1) {\n group.reduce();\n }\n }\n\n this.clearHoveredItem();\n this.fillItem = null;\n this.fillProperty = null;\n this.addedFillItem = null;\n this.fillItemOrigColor = null;\n this.onUpdateImage();\n }\n }\n _noStroke (item) {\n return !item.strokeColor ||\n item.strokeColor.alpha === 0 ||\n item.strokeWidth === 0;\n }\n // Either pass in a fully defined paper.Color as color1,\n // or pass in 2 color strings, a gradient type, and a pointer location\n _setFillItemColor (color1, color2, gradientType, pointerLocation) {\n const item = this._getFillItem();\n if (!item) return;\n const colorProp = this.fillProperty === 'fill' ? 'fillColor' : 'strokeColor';\n // Only create a gradient if specifically requested, else use color1 directly\n // This ensures we do not set a gradient by accident (see scratch-paint#830).\n if (gradientType && gradientType !== GradientTypes.SOLID) {\n item[colorProp] = createGradientObject(\n color1,\n color2,\n gradientType,\n item.bounds,\n pointerLocation,\n item.strokeWidth\n );\n } else {\n item[colorProp] = color1;\n }\n }\n _getFillItem () {\n if (this.addedFillItem) {\n return this.addedFillItem;\n } else if (this.fillItem && this.fillItem.parent instanceof paper.CompoundPath) {\n return this.fillItem.parent;\n }\n return this.fillItem;\n }\n deactivateTool () {\n if (this.fillItem) {\n this._setFillItemColor(this.fillItemOrigColor);\n this.fillItemOrigColor = null;\n this.fillItem = null;\n this.fillProperty = null;\n }\n this.clearHoveredItem();\n this.setHoveredItem = null;\n this.clearHoveredItem = null;\n }\n}\n\nexport default FillTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport fillIcon from './fill.svg';\n\nconst FillModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.fill}\n imgSrc={fillIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nFillModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default FillModeComponent;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport GradientTypes from '../lib/gradient-types';\nimport FillTool from '../helper/tools/fill-tool';\nimport {generateSecondaryColor, MIXED} from '../helper/style-path';\n\nimport {changeFillColor, changeFillColor2, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {clearSelection} from '../helper/selection';\nimport {clearHoveredItem, setHoveredItem} from '../reducers/hover';\nimport {changeGradientType} from '../reducers/fill-mode-gradient-type';\n\nimport FillModeComponent from '../components/fill-mode/fill-mode.jsx';\n\nclass FillMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isFillModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool) {\n if (nextProps.fillColor !== this.props.fillColor) {\n this.tool.setFillColor(nextProps.fillColor);\n }\n if (nextProps.fillColor2 !== this.props.fillColor2) {\n this.tool.setFillColor2(nextProps.fillColor2);\n }\n if (nextProps.hoveredItemId !== this.props.hoveredItemId) {\n this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);\n }\n if (nextProps.fillModeGradientType !== this.props.fillModeGradientType) {\n this.tool.setGradientType(nextProps.fillModeGradientType);\n }\n }\n\n if (nextProps.isFillModeActive && !this.props.isFillModeActive) {\n this.activateTool();\n } else if (!nextProps.isFillModeActive && this.props.isFillModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isFillModeActive !== this.props.isFillModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n\n // Force the default fill color if fill is MIXED\n let fillColor = this.props.fillColor;\n if (this.props.fillColor === MIXED) {\n fillColor = DEFAULT_COLOR;\n this.props.onChangeFillColor(DEFAULT_COLOR, 0);\n }\n const gradientType = this.props.fillModeGradientType ?\n this.props.fillModeGradientType : this.props.fillStyleGradientType;\n let fillColor2 = this.props.fillColor2;\n if (gradientType !== this.props.fillStyleGradientType) {\n if (this.props.fillStyleGradientType === GradientTypes.SOLID) {\n fillColor2 = generateSecondaryColor(fillColor);\n this.props.onChangeFillColor(fillColor2, 1);\n }\n this.props.changeGradientType(gradientType);\n }\n if (this.props.fillColor2 === MIXED) {\n fillColor2 = generateSecondaryColor(fillColor);\n this.props.onChangeFillColor(fillColor2, 1);\n }\n this.tool = new FillTool(\n this.props.setHoveredItem,\n this.props.clearHoveredItem,\n this.props.onUpdateImage\n );\n this.tool.setFillColor(fillColor);\n this.tool.setFillColor2(fillColor2);\n this.tool.setGradientType(gradientType);\n this.tool.setPrevHoveredItemId(this.props.hoveredItemId);\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <FillModeComponent\n isSelected={this.props.isFillModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nFillMode.propTypes = {\n changeGradientType: PropTypes.func.isRequired,\n clearHoveredItem: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n fillColor: PropTypes.string,\n fillColor2: PropTypes.string,\n fillStyleGradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,\n fillModeGradientType: PropTypes.oneOf(Object.keys(GradientTypes)),\n handleMouseDown: PropTypes.func.isRequired,\n hoveredItemId: PropTypes.number,\n isFillModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n setHoveredItem: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n fillModeGradientType: state.scratchPaint.fillMode.gradientType, // Last user-selected gradient type\n fillColor: state.scratchPaint.color.fillColor.primary,\n fillColor2: state.scratchPaint.color.fillColor.secondary,\n fillStyleGradientType: state.scratchPaint.color.fillColor.gradientType, // Selected item(s)' gradient type\n hoveredItemId: state.scratchPaint.hoveredItemId,\n isFillModeActive: state.scratchPaint.mode === Modes.FILL\n});\nconst mapDispatchToProps = dispatch => ({\n setHoveredItem: hoveredItemId => {\n dispatch(setHoveredItem(hoveredItemId));\n },\n clearHoveredItem: () => {\n dispatch(clearHoveredItem());\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n changeGradientType: gradientType => {\n dispatch(changeGradientType(gradientType));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.FILL));\n },\n onChangeFillColor: (fillColor, index) => {\n if (index === 0) {\n dispatch(changeFillColor(fillColor));\n } else if (index === 1) {\n dispatch(changeFillColor2(fillColor));\n }\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(FillMode);\n","import paper from '@scratch/paper';\nimport {getItems} from './selection';\n\n/**\n * @param {paper.Point} point1 point 1\n * @param {paper.Point} point2 point 2\n * @param {number} tolerance Distance allowed between points that are \"touching\"\n * @returns {boolean} true if points are within the tolerance distance.\n */\nconst touching = function (point1, point2, tolerance) {\n return point1.getDistance(point2, true) < Math.pow(tolerance / paper.view.zoom, 2);\n};\n\n/**\n * @param {!paper.Point} point Point to check line endpoint hits against\n * @param {!number} tolerance Distance within which it counts as a hit\n * @param {?paper.Path} excludePath Path to exclude from hit test, if any. For instance, you\n * are drawing a line and don't want it to snap to its own start point.\n * @returns {object} data about the end point of an unclosed path, if any such point is within the\n * tolerance distance of the given point, or null if none exists.\n */\nconst endPointHit = function (point, tolerance, excludePath) {\n const lines = getItems({\n class: paper.Path\n });\n // Prefer more recent lines\n for (let i = lines.length - 1; i >= 0; i--) {\n if (lines[i].closed) {\n continue;\n }\n if (!(lines[i].parent instanceof paper.Layer)) {\n // Don't connect to lines inside of groups\n continue;\n }\n if (excludePath && lines[i] === excludePath) {\n continue;\n }\n if (lines[i].firstSegment && touching(lines[i].firstSegment.point, point, tolerance)) {\n return {\n path: lines[i],\n segment: lines[i].firstSegment,\n isFirst: true\n };\n }\n if (lines[i].lastSegment && touching(lines[i].lastSegment.point, point, tolerance)) {\n return {\n path: lines[i],\n segment: lines[i].lastSegment,\n isFirst: false\n };\n }\n }\n return null;\n};\n\nexport {\n endPointHit,\n touching\n};\n","import log from '../log/log';\nimport {CHANGE_SELECTED_ITEMS} from './selected-items';\nimport {getColorsFromSelection} from '../helper/style-path';\n\nconst CHANGE_STROKE_WIDTH = 'scratch-paint/stroke-width/CHANGE_STROKE_WIDTH';\nconst MAX_STROKE_WIDTH = 100;\nconst initialState = 4;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_STROKE_WIDTH:\n if (isNaN(action.strokeWidth)) {\n log.warn(`Invalid brush size: ${action.strokeWidth}`);\n return state;\n }\n return Math.min(MAX_STROKE_WIDTH, Math.max(0, action.strokeWidth));\n case CHANGE_SELECTED_ITEMS:\n // Don't change state if no selection\n if (!action.selectedItems || !action.selectedItems.length) {\n return state;\n }\n // Bitmap mode doesn't have stroke width\n if (action.bitmapMode) {\n return state;\n }\n return getColorsFromSelection(action.selectedItems, action.bitmapMode).strokeWidth;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeStrokeWidth = function (strokeWidth) {\n return {\n type: CHANGE_STROKE_WIDTH,\n strokeWidth: strokeWidth\n };\n};\n\nexport {\n reducer as default,\n changeStrokeWidth,\n CHANGE_STROKE_WIDTH,\n MAX_STROKE_WIDTH\n};\n","import makeColorStyleReducer from '../lib/make-color-style-reducer';\n\nconst CHANGE_STROKE_COLOR = 'scratch-paint/stroke-style/CHANGE_STROKE_COLOR';\nconst CHANGE_STROKE_COLOR_2 = 'scratch-paint/stroke-style/CHANGE_STROKE_COLOR_2';\nconst CHANGE_STROKE_GRADIENT_TYPE = 'scratch-paint/stroke-style/CHANGE_STROKE_GRADIENT_TYPE';\nconst CLEAR_STROKE_GRADIENT = 'scratch-paint/stroke-style/CLEAR_STROKE_GRADIENT';\nconst DEFAULT_COLOR = '#000000';\n\nimport {CHANGE_STROKE_WIDTH} from './stroke-width';\n\nconst reducer = makeColorStyleReducer({\n changePrimaryColorAction: CHANGE_STROKE_COLOR,\n changeSecondaryColorAction: CHANGE_STROKE_COLOR_2,\n changeGradientTypeAction: CHANGE_STROKE_GRADIENT_TYPE,\n clearGradientAction: CLEAR_STROKE_GRADIENT,\n defaultColor: DEFAULT_COLOR,\n selectionPrimaryColorKey: 'strokeColor',\n selectionSecondaryColorKey: 'strokeColor2',\n selectionGradientTypeKey: 'strokeGradientType'\n});\n\n// This is mostly the same as the generated reducer, but with one piece of extra logic to set the color to null when the\n// stroke width is set to 0.\n// https://redux.js.org/recipes/structuring-reducers/reusing-reducer-logic\nconst strokeReducer = function (state, action) {\n if (action.type === CHANGE_STROKE_WIDTH && Math.max(action.strokeWidth, 0) === 0) {\n // TODO: this preserves the gradient type when you change the stroke width to 0.\n // Alternatively, we could set gradientType to SOLID instead of setting secondary to null, but since\n // the stroke width is automatically set to 0 as soon as a \"null\" color is detected (including a gradient for\n // which both colors are null), that would change the gradient type back to solid if you selected null for both\n // gradient colors.\n return {...state, primary: null, secondary: null};\n }\n\n return reducer(state, action);\n};\n\n// Action creators ==================================\nconst changeStrokeColor = function (strokeColor) {\n return {\n type: CHANGE_STROKE_COLOR,\n color: strokeColor\n };\n};\n\nconst changeStrokeColor2 = function (strokeColor) {\n return {\n type: CHANGE_STROKE_COLOR_2,\n color: strokeColor\n };\n};\n\nconst changeStrokeGradientType = function (gradientType) {\n return {\n type: CHANGE_STROKE_GRADIENT_TYPE,\n gradientType\n };\n};\n\nconst clearStrokeGradient = function () {\n return {\n type: CLEAR_STROKE_GRADIENT\n };\n};\n\nexport {\n strokeReducer as default,\n changeStrokeColor,\n changeStrokeColor2,\n changeStrokeGradientType,\n clearStrokeGradient,\n DEFAULT_COLOR,\n CHANGE_STROKE_GRADIENT_TYPE\n};\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport lineIcon from './line.svg';\n\nconst LineModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.line}\n imgSrc={lineIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nLineModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default LineModeComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport {clearSelection} from '../helper/selection';\nimport {endPointHit, touching} from '../helper/snapping';\nimport {drawHitPoint, removeHitPoint} from '../helper/guides';\nimport {styleShape, MIXED} from '../helper/style-path';\nimport {changeStrokeColor, clearStrokeGradient} from '../reducers/stroke-style';\nimport {changeStrokeWidth} from '../reducers/stroke-width';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems} from '../reducers/selected-items';\nimport {snapDeltaToAngle} from '../helper/math';\n\nimport LineModeComponent from '../components/line-mode/line-mode.jsx';\n\nclass LineMode extends React.Component {\n static get SNAP_TOLERANCE () {\n return 6;\n }\n static get DEFAULT_COLOR () {\n return '#000000';\n }\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool',\n 'drawHitPoint',\n 'onMouseDown',\n 'onMouseMove',\n 'onMouseDrag',\n 'onMouseUp'\n ]);\n }\n componentDidMount () {\n if (this.props.isLineModeActive) {\n this.activateTool();\n }\n }\n componentWillReceiveProps (nextProps) {\n if (nextProps.isLineModeActive && !this.props.isLineModeActive) {\n this.activateTool();\n } else if (!nextProps.isLineModeActive && this.props.isLineModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isLineModeActive !== this.props.isLineModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n // Force the default line color if stroke is MIXED or transparent\n const strokeColor1 = this.props.colorState.strokeColor.primary;\n const strokeColor2 = this.props.colorState.strokeColor.secondary;\n if (strokeColor1 === MIXED ||\n (strokeColor1 === null &&\n (strokeColor2 === null || strokeColor2 === MIXED))) {\n this.props.onChangeStrokeColor(LineMode.DEFAULT_COLOR);\n }\n if (strokeColor2 === MIXED) {\n this.props.clearStrokeGradient();\n }\n // Force a minimum stroke width\n if (!this.props.colorState.strokeWidth) {\n this.props.onChangeStrokeWidth(1);\n }\n this.tool = new paper.Tool();\n this.active = false;\n\n this.path = null;\n this.hitResult = null;\n\n const lineMode = this;\n this.tool.onMouseDown = function (event) {\n if (event.event.button > 0) return; // only first mouse button\n lineMode.onMouseDown(event);\n };\n this.tool.onMouseMove = function (event) {\n lineMode.onMouseMove(event);\n };\n this.tool.onMouseDrag = function (event) {\n if (event.event.button > 0) return; // only first mouse button\n lineMode.onMouseDrag(event);\n };\n this.tool.onMouseUp = function (event) {\n if (event.event.button > 0) return; // only first mouse button\n lineMode.onMouseUp(event);\n };\n\n this.tool.activate();\n }\n onMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n // If you click near a point, continue that line instead of making a new line\n this.hitResult = endPointHit(event.point, LineMode.SNAP_TOLERANCE);\n if (this.hitResult) {\n this.path = this.hitResult.path;\n styleShape(this.path, {\n fillColor: null,\n strokeColor: this.props.colorState.strokeColor,\n strokeWidth: this.props.colorState.strokeWidth\n });\n if (this.hitResult.isFirst) {\n this.path.reverse();\n }\n\n this.path.lastSegment.handleOut = null; // Make sure added line isn't made curvy\n this.path.add(this.hitResult.segment.point); // Add second point, which is what will move when dragged\n }\n\n // If not near other path, start a new path\n if (!this.path) {\n this.path = new paper.Path();\n this.path.strokeCap = 'round';\n styleShape(this.path, {\n fillColor: null,\n strokeColor: this.props.colorState.strokeColor,\n strokeWidth: this.props.colorState.strokeWidth\n });\n\n this.path.add(event.point);\n this.path.add(event.point); // Add second point, which is what will move when dragged\n }\n }\n drawHitPoint (hitResult) {\n // If near another path's endpoint, draw hit point to indicate that paths would merge\n if (hitResult) {\n const hitPath = hitResult.path;\n if (hitResult.isFirst) {\n drawHitPoint(hitPath.firstSegment.point);\n } else {\n drawHitPoint(hitPath.lastSegment.point);\n }\n }\n }\n onMouseMove (event) {\n if (this.hitResult) {\n removeHitPoint();\n }\n this.hitResult = endPointHit(event.point, LineMode.SNAP_TOLERANCE);\n this.drawHitPoint(this.hitResult);\n }\n onMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n // Clear the last hit result\n if (this.hitResult) {\n removeHitPoint();\n this.hitResult = null;\n }\n\n // If shift is held, act like event.point always lies on a straight or 45 degree line from the last point\n let endPoint = event.point;\n if (event.modifiers.shift) {\n const line = event.point.subtract(this.path.lastSegment.previous.point);\n endPoint = this.path.lastSegment.previous.point.add(snapDeltaToAngle(line, Math.PI / 4));\n }\n\n // Find an end point that endPoint is close to (to snap lines together)\n if (this.path &&\n !this.path.closed &&\n this.path.segments.length > 3 &&\n touching(this.path.firstSegment.point, endPoint, LineMode.SNAP_TOLERANCE)) {\n this.hitResult = {\n path: this.path,\n segment: this.path.firstSegment,\n isFirst: true\n };\n } else {\n this.hitResult = endPointHit(endPoint, LineMode.SNAP_TOLERANCE, this.path);\n }\n\n // If shift is being held, we shouldn't snap to end points that change the slope by too much.\n // In that case, clear the hit result.\n if (this.hitResult && event.modifiers.shift) {\n const lineToSnap = this.hitResult.segment.point.subtract(this.path.lastSegment.previous.point);\n const lineToEndPoint = endPoint.subtract(this.path.lastSegment.previous.point);\n if (lineToSnap.normalize().getDistance(lineToEndPoint.normalize()) > 1e-2) {\n this.hitResult = null;\n }\n }\n\n // If near another path's endpoint, or this path's beginpoint, clip to it to suggest\n // joining/closing the paths.\n if (this.hitResult) {\n this.drawHitPoint(this.hitResult);\n this.path.lastSegment.point = this.hitResult.segment.point;\n } else {\n this.path.lastSegment.point = endPoint;\n }\n\n styleShape(this.path, {\n fillColor: null,\n strokeColor: this.props.colorState.strokeColor,\n strokeWidth: this.props.colorState.strokeWidth\n });\n }\n onMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n // If I single clicked, don't do anything\n if (this.path.segments.length < 2 ||\n (this.path.segments.length === 2 &&\n touching(this.path.firstSegment.point, event.point, LineMode.SNAP_TOLERANCE) &&\n !this.hitResult)) { // Let lines be short if you're connecting them\n this.path.remove();\n this.path = null;\n return;\n } else if (!this.hitResult &&\n touching(this.path.lastSegment.point, this.path.segments[this.path.segments.length - 2].point,\n LineMode.SNAP_TOLERANCE)) {\n // Single click or short drag on an existing path end point\n this.path.removeSegment(this.path.segments.length - 1);\n this.path = null;\n return;\n }\n // If I intersect other line end points, join or close\n if (this.hitResult) {\n this.path.removeSegment(this.path.segments.length - 1);\n if (this.path.firstSegment.point.equals(this.hitResult.segment.point)) {\n this.path.firstSegment.handleIn = null; // Make sure added line isn't made curvy\n // close path\n this.path.closed = true;\n } else {\n // joining two paths\n if (!this.hitResult.isFirst) {\n this.hitResult.path.reverse();\n }\n this.hitResult.path.firstSegment.handleIn = null; // Make sure added line isn't made curvy\n this.path.join(this.hitResult.path);\n }\n removeHitPoint();\n this.hitResult = null;\n }\n\n styleShape(this.path, {\n fillColor: null,\n strokeColor: this.props.colorState.strokeColor,\n strokeWidth: this.props.colorState.strokeWidth\n });\n\n if (this.path) {\n this.props.onUpdateImage();\n this.path = null;\n }\n this.active = false;\n }\n deactivateTool () {\n this.tool.remove();\n this.tool = null;\n if (this.hitResult) {\n removeHitPoint();\n this.hitResult = null;\n }\n if (this.path) {\n this.path = null;\n }\n }\n render () {\n return (\n <LineModeComponent\n isSelected={this.props.isLineModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nLineMode.propTypes = {\n clearSelectedItems: PropTypes.func.isRequired,\n clearStrokeGradient: PropTypes.func.isRequired,\n colorState: PropTypes.shape({\n fillColor: ColorStyleProptype,\n strokeColor: ColorStyleProptype,\n strokeWidth: PropTypes.number\n }).isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n isLineModeActive: PropTypes.bool.isRequired,\n onChangeStrokeColor: PropTypes.func.isRequired,\n onChangeStrokeWidth: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n colorState: state.scratchPaint.color,\n isLineModeActive: state.scratchPaint.mode === Modes.LINE\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearStrokeGradient: () => {\n dispatch(clearStrokeGradient());\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.LINE));\n },\n onChangeStrokeColor: strokeColor => {\n dispatch(changeStrokeColor(strokeColor));\n },\n onChangeStrokeWidth: strokeWidth => {\n dispatch(changeStrokeWidth(strokeWidth));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(LineMode);\n","import paper from '@scratch/paper';\nimport {createCanvas, getRaster, getBackgroundGuideLayer} from '../layer';\n\nconst LOUPE_RADIUS = 20;\nconst ZOOM_SCALE = 3;\n\nclass EyeDropperTool extends paper.Tool {\n constructor (canvas, width, height, pixelRatio, zoom, offsetX, offsetY, isBitmap) {\n super();\n\n const layer = isBitmap ? getRaster().layer : paper.project.activeLayer;\n const contentRaster3x = layer.rasterize(\n 72 * ZOOM_SCALE * paper.view.zoom, false /* insert */, paper.view.bounds);\n const backgroundRaster3x = getBackgroundGuideLayer().rasterize(\n 72 * ZOOM_SCALE * paper.view.zoom, false /* insert */, paper.view.bounds);\n\n // Canvas from which loupe is cut, shows art and grid\n this.bufferCanvas = createCanvas(canvas.width * ZOOM_SCALE, canvas.height * ZOOM_SCALE);\n const bufferCanvasContext = this.bufferCanvas.getContext('2d');\n // Canvas to sample colors from; just the art\n this.colorCanvas = createCanvas(canvas.width * ZOOM_SCALE, canvas.height * ZOOM_SCALE);\n const colorCanvasContext = this.colorCanvas.getContext('2d');\n\n backgroundRaster3x.onLoad = () => {\n bufferCanvasContext.drawImage(backgroundRaster3x.canvas, 0, 0);\n contentRaster3x.onLoad = () => {\n colorCanvasContext.drawImage(contentRaster3x.canvas, 0, 0);\n bufferCanvasContext.drawImage(this.colorCanvas, 0, 0);\n this.bufferLoaded = true;\n };\n if (contentRaster3x.loaded) contentRaster3x.onLoad();\n };\n\n this.onMouseDown = this.handleMouseDown;\n this.onMouseUp = this.handleMouseUp;\n this.onMouseMove = this.handleMouseMove;\n\n this.canvas = canvas;\n this.pixelRatio = pixelRatio;\n this.zoom = zoom;\n this.offsetX = offsetX;\n this.offsetY = offsetY;\n this.width = width * this.zoom * this.pixelRatio;\n this.height = height * this.zoom * this.pixelRatio;\n this.rect = canvas.getBoundingClientRect();\n this.colorString = '';\n this.pickX = -1;\n this.pickY = -1;\n this.hideLoupe = true;\n }\n handleMouseMove (event) {\n // Set the pickX/Y for the color picker loop to pick up\n this.pickX = (event.point.x - this.offsetX) * this.zoom * this.pixelRatio;\n this.pickY = (event.point.y - this.offsetY) * this.zoom * this.pixelRatio;\n\n // check if the x/y are outside of the canvas\n this.hideLoupe = this.pickX > this.width ||\n this.pickX < 0 ||\n this.pickY > this.height ||\n this.pickY < 0;\n }\n handleMouseDown (event) {\n // Nothing special on mousedown, just send to move handler which will show the loupe,\n // and the mouse up handler submits the color. This allows touch to drag\n // with the loupe visible to find the correct color\n this.handleMouseMove(event);\n }\n handleMouseUp () {\n if (!this.hideLoupe) {\n const colorInfo = this.getColorInfo(this.pickX, this.pickY, this.hideLoupe);\n if (!colorInfo) return;\n if (colorInfo.color[3] === 0) {\n // Alpha 0\n this.colorString = null;\n return;\n }\n const r = colorInfo.color[0];\n const g = colorInfo.color[1];\n const b = colorInfo.color[2];\n\n // from https://github.com/LLK/scratch-gui/blob/77e54a80a31b6cd4684d4b2a70f1aeec671f229e/src/containers/stage.jsx#L218-L222\n // formats the color info from the canvas into hex for parsing by the color picker\n const componentToString = c => {\n const hex = c.toString(16);\n return hex.length === 1 ? `0${hex}` : hex;\n };\n this.colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`;\n }\n }\n getColorInfo (x, y, hideLoupe) {\n const artX = x / this.pixelRatio;\n const artY = y / this.pixelRatio;\n if (!this.bufferLoaded) return null;\n const colorContext = this.colorCanvas.getContext('2d');\n const bufferContext = this.bufferCanvas.getContext('2d');\n const colors = colorContext.getImageData(artX * ZOOM_SCALE, artY * ZOOM_SCALE, 1, 1);\n return {\n x: x,\n y: y,\n color: colors.data,\n data: bufferContext.getImageData(\n ZOOM_SCALE * (artX - LOUPE_RADIUS),\n ZOOM_SCALE * (artY - LOUPE_RADIUS),\n LOUPE_RADIUS * 2 * ZOOM_SCALE,\n LOUPE_RADIUS * 2 * ZOOM_SCALE\n ).data,\n hideLoupe: hideLoupe\n };\n }\n}\n\nexport {\n EyeDropperTool as default,\n LOUPE_RADIUS,\n ZOOM_SCALE\n};\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./loupe.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./loupe.css\";\n export default content && content.locals ? content.locals : undefined;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport bindAll from 'lodash.bindall';\n\nimport Box from '../box/box.jsx';\n\nimport {LOUPE_RADIUS, ZOOM_SCALE} from '../../helper/tools/eye-dropper';\n\nimport styles from './loupe.css';\n\nclass LoupeComponent extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'setCanvas'\n ]);\n }\n componentDidUpdate () {\n this.draw();\n }\n draw () {\n const boxSize = 5;\n const boxLineWidth = 1;\n const colorRingWidth = 15;\n const loupeRadius = ZOOM_SCALE * LOUPE_RADIUS;\n const loupeDiameter = loupeRadius * 2;\n\n const color = this.props.colorInfo.color;\n\n const ctx = this.canvas.getContext('2d');\n this.canvas.width = loupeDiameter;\n this.canvas.height = loupeDiameter;\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, loupeDiameter, loupeDiameter);\n\n // In order to scale the image data, must draw to a tmp canvas first\n const tmpCanvas = document.createElement('canvas');\n tmpCanvas.width = loupeDiameter;\n tmpCanvas.height = loupeDiameter;\n const tmpCtx = tmpCanvas.getContext('2d');\n const imageData = tmpCtx.createImageData(\n loupeDiameter, loupeDiameter\n );\n\n // Since the color info comes from elsewhere there is no guarantee\n // about the size. Make sure it matches to prevent data.set from throwing.\n // See issue #966 for example of how that can happen.\n if (this.props.colorInfo.data.length === imageData.data.length) {\n imageData.data.set(this.props.colorInfo.data);\n } else {\n console.warn('Image data size mismatch drawing loupe'); // eslint-disable-line no-console\n }\n\n tmpCtx.putImageData(imageData, 0, 0);\n\n // Scale the loupe canvas and draw the zoomed image\n ctx.drawImage(tmpCanvas, 0, 0);\n\n // Draw an outlined square at the cursor position (cursor is hidden)\n ctx.lineWidth = boxLineWidth;\n ctx.strokeStyle = 'black';\n ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;\n ctx.beginPath();\n ctx.rect(loupeRadius - (boxSize / 2), loupeRadius - (boxSize / 2), boxSize, boxSize);\n ctx.fill();\n ctx.stroke();\n\n // Draw a thick ring around the loupe showing the current color\n ctx.strokeStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;\n ctx.lineWidth = colorRingWidth;\n ctx.beginPath();\n ctx.moveTo(loupeDiameter, loupeDiameter);\n ctx.arc(loupeRadius, loupeRadius, loupeRadius, 0, 2 * Math.PI);\n ctx.stroke();\n }\n setCanvas (element) {\n this.canvas = element;\n // Make sure to draw a frame when this component is first mounted\n // Check for null ref because refs are called with null when unmounted\n if (this.canvas) {\n this.draw();\n }\n }\n render () {\n const {\n colorInfo,\n pixelRatio,\n ...boxProps\n } = this.props;\n const loupeDiameter = ZOOM_SCALE * LOUPE_RADIUS * 2;\n return (\n <Box\n {...boxProps}\n className={styles.eyeDropper}\n componentRef={this.setCanvas}\n element=\"canvas\"\n height={LOUPE_RADIUS * 2}\n style={{\n top: (colorInfo.y / pixelRatio) - (loupeDiameter / 2),\n left: (colorInfo.x / pixelRatio) - (loupeDiameter / 2),\n width: loupeDiameter,\n height: loupeDiameter\n }}\n width={LOUPE_RADIUS * 2}\n />\n );\n }\n}\n\nLoupeComponent.propTypes = {\n colorInfo: PropTypes.shape({\n color: PropTypes.instanceOf(Uint8ClampedArray), // this is the [r,g,b,a] array\n x: PropTypes.number,\n y: PropTypes.number,\n data: PropTypes.instanceOf(Uint8ClampedArray)\n }),\n pixelRatio: PropTypes.number.isRequired\n};\n\nexport default LoupeComponent;\n","import {getSelectedRootItems} from './selection';\n\nconst bringToFront = function (onUpdateImage) {\n const items = getSelectedRootItems();\n for (const item of items) {\n item.bringToFront();\n }\n onUpdateImage();\n};\n\nconst sendToBack = function (onUpdateImage) {\n const items = getSelectedRootItems();\n for (let i = items.length - 1; i >= 0; i--) {\n items[i].sendToBack();\n }\n onUpdateImage();\n};\n\nconst bringForward = function (onUpdateImage) {\n const items = getSelectedRootItems();\n // Already at front\n if (items.length === 0 || !items[items.length - 1].nextSibling) {\n return;\n }\n\n const nextSibling = items[items.length - 1].nextSibling;\n for (let i = items.length - 1; i >= 0; i--) {\n items[i].insertAbove(nextSibling);\n }\n onUpdateImage();\n};\n\nconst sendBackward = function (onUpdateImage) {\n const items = getSelectedRootItems();\n // Already at front\n if (items.length === 0 || !items[0].previousSibling) {\n return;\n }\n\n const previousSibling = items[0].previousSibling;\n for (const item of items) {\n item.insertBelow(previousSibling);\n }\n onUpdateImage();\n};\n\nconst shouldShowSendBackward = function () {\n const items = getSelectedRootItems();\n if (items.length === 0 || !items[0].previousSibling) {\n return false;\n }\n return true;\n};\n\nconst shouldShowBringForward = function () {\n const items = getSelectedRootItems();\n if (items.length === 0 || !items[items.length - 1].nextSibling) {\n return false;\n }\n return true;\n};\n\nexport {\n bringToFront,\n sendToBack,\n bringForward,\n sendBackward,\n shouldShowBringForward,\n shouldShowSendBackward\n};\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./dropdown.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./dropdown.css\";\n export default content && content.locals ? content.locals : undefined;\n","import bindAll from 'lodash.bindall';\nimport classNames from 'classnames';\nimport Popover from 'react-popover';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport styles from './dropdown.css';\n\nimport dropdownIcon from './dropdown-caret.svg';\n\nclass Dropdown extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleClosePopover',\n 'handleToggleOpenState',\n 'isOpen'\n ]);\n this.state = {\n isOpen: false\n };\n }\n handleClosePopover () {\n this.setState({\n isOpen: false\n });\n }\n handleToggleOpenState () {\n const newState = !this.state.isOpen;\n this.setState({\n isOpen: newState\n });\n if (newState && this.props.onOpen) {\n this.props.onOpen();\n }\n }\n isOpen () {\n return this.state.isOpen;\n }\n render () {\n return (\n <Popover\n body={this.props.popoverContent}\n isOpen={this.state.isOpen}\n preferPlace=\"below\"\n onOuterAction={this.props.onOuterAction ?\n this.props.onOuterAction : this.handleClosePopover}\n {...this.props}\n >\n <div\n className={classNames(styles.dropdown, this.props.className, {\n [styles.modOpen]: this.state.isOpen,\n [styles.modClosed]: !this.state.isOpen\n })}\n onClick={this.handleToggleOpenState}\n >\n {this.props.children}\n <img\n className={classNames(styles.dropdownIcon, {\n [styles.modCaretUp]: this.state.isOpen\n })}\n draggable={false}\n src={dropdownIcon}\n />\n </div>\n </Popover>\n );\n }\n}\n\nDropdown.propTypes = {\n children: PropTypes.node.isRequired,\n className: PropTypes.string,\n onOpen: PropTypes.func,\n onOuterAction: PropTypes.func,\n popoverContent: PropTypes.node.isRequired\n};\n\nexport default Dropdown;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./input.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./input.css\";\n export default content && content.locals ? content.locals : undefined;\n","/* DO NOT EDIT\n@todo This file is copied from GUI and should be pulled out into a shared library.\nSee https://github.com/LLK/scratch-paint/issues/13 */\n\n/* NOTE:\nEdited to add range prop\n*/\n\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport classNames from 'classnames';\n\nimport styles from './input.css';\n\nconst Input = props => {\n const {small, range, ...componentProps} = props;\n return (\n <input\n {...componentProps}\n className={classNames(\n styles.inputForm,\n props.className,\n {\n [styles.inputSmall]: small && !range,\n [styles.inputSmallRange]: small && range\n }\n )}\n />\n );\n};\n\nInput.propTypes = {\n className: PropTypes.string,\n range: PropTypes.bool,\n small: PropTypes.bool\n};\n\nInput.defaultProps = {\n range: false,\n small: false\n};\n\nexport default Input;\n","export default {\n fullSizeEditorMinWidth: 1274\n};\n","const localeTooBig = [\n 'ab',\n 'ca',\n 'cy',\n 'de',\n 'et',\n 'el',\n 'ga',\n 'gd',\n 'gl',\n 'mi',\n 'nl',\n 'ja',\n 'ja-Hira',\n 'nb',\n 'nn',\n 'rap',\n 'th',\n 'sr',\n 'sk',\n 'sl',\n 'fi',\n 'sv',\n 'sw',\n 'vi',\n 'tr',\n 'uk'\n];\n\nconst hideLabel = locale => localeTooBig.includes(locale);\n\nexport {\n hideLabel\n};\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./fixed-tools.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./fixed-tools.css\";\n export default content && content.locals ? content.locals : undefined;\n","import classNames from 'classnames';\nimport {connect} from 'react-redux';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport MediaQuery from 'react-responsive';\n\nimport {shouldShowGroup, shouldShowUngroup} from '../../helper/group';\nimport {shouldShowBringForward, shouldShowSendBackward} from '../../helper/order';\n\nimport BufferedInputHOC from '../forms/buffered-input-hoc.jsx';\nimport Button from '../button/button.jsx';\nimport ButtonGroup from '../button-group/button-group.jsx';\nimport Dropdown from '../dropdown/dropdown.jsx';\nimport {defineMessages, useIntl} from 'react-intl';\nimport Formats, {isVector} from '../../lib/format';\nimport Input from '../forms/input.jsx';\nimport InputGroup from '../input-group/input-group.jsx';\nimport Label from '../forms/label.jsx';\nimport LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';\nimport layout from '../../lib/layout-constants';\nimport {hideLabel} from '../../lib/hide-label';\nimport styles from './fixed-tools.css';\n\nimport groupIcon from './icons/group.svg';\nimport redoIcon from './icons/redo.svg';\nimport sendBackIcon from './icons/send-back.svg';\nimport sendBackwardIcon from './icons/send-backward.svg';\nimport sendForwardIcon from './icons/send-forward.svg';\nimport sendFrontIcon from './icons/send-front.svg';\nimport undoIcon from './icons/undo.svg';\nimport ungroupIcon from './icons/ungroup.svg';\n\nconst BufferedInput = BufferedInputHOC(Input);\nconst messages = defineMessages({\n costume: {\n id: 'paint.paintEditor.costume',\n description: 'Label for the name of a costume',\n defaultMessage: 'Costume'\n },\n group: {\n defaultMessage: 'Group',\n description: 'Label for the button to group shapes',\n id: 'paint.paintEditor.group'\n },\n ungroup: {\n defaultMessage: 'Ungroup',\n description: 'Label for the button to ungroup shapes',\n id: 'paint.paintEditor.ungroup'\n },\n undo: {\n defaultMessage: 'Undo',\n description: 'Alt to image for the button to undo an action',\n id: 'paint.paintEditor.undo'\n },\n redo: {\n defaultMessage: 'Redo',\n description: 'Alt to image for the button to redo an action',\n id: 'paint.paintEditor.redo'\n },\n forward: {\n defaultMessage: 'Forward',\n description: 'Label for the `Send forward on canvas` button',\n id: 'paint.paintEditor.forward'\n },\n backward: {\n defaultMessage: 'Backward',\n description: 'Label for the `Send backward on canvas` button',\n id: 'paint.paintEditor.backward'\n },\n front: {\n defaultMessage: 'Front',\n description: 'Label for the `Send to front of canvas` button',\n id: 'paint.paintEditor.front'\n },\n back: {\n defaultMessage: 'Back',\n description: 'Label for the `Send to back of canvas` button',\n id: 'paint.paintEditor.back'\n },\n more: {\n defaultMessage: 'More',\n description: 'Label for dropdown to access more action buttons',\n id: 'paint.paintEditor.more'\n }\n});\n\nconst FixedToolsComponent = props => {\n const redoDisabled = !props.canRedo();\n const undoDisabled = !props.canUndo();\n const intl = useIntl();\n\n return (\n <div className={styles.row}>\n {/* Name field */}\n <InputGroup>\n <MediaQuery minWidth={layout.fullSizeEditorMinWidth}>\n <Label text={intl.formatMessage(messages.costume)}>\n <BufferedInput\n className={styles.costumeInput}\n type=\"text\"\n value={props.name}\n onSubmit={props.onUpdateName}\n />\n </Label>\n </MediaQuery>\n <MediaQuery maxWidth={layout.fullSizeEditorMinWidth - 1}>\n <BufferedInput\n className={styles.costumeInput}\n type=\"text\"\n value={props.name}\n onSubmit={props.onUpdateName}\n />\n </MediaQuery>\n </InputGroup>\n\n {/* Undo/Redo */}\n <InputGroup>\n <ButtonGroup>\n <Button\n className={\n classNames(\n styles.buttonGroupButton,\n {\n [styles.modNoEndBorder]: !redoDisabled\n }\n )\n }\n disabled={undoDisabled}\n onClick={props.onUndo}\n >\n <img\n alt={intl.formatMessage(messages.undo)}\n className={classNames(\n styles.buttonGroupButtonIcon,\n styles.undoIcon\n )}\n draggable={false}\n src={undoIcon}\n />\n </Button>\n <Button\n className={\n classNames(\n styles.buttonGroupButton,\n {\n [styles.modStartBorder]: !redoDisabled\n }\n )\n }\n disabled={redoDisabled}\n onClick={props.onRedo}\n >\n <img\n alt={intl.formatMessage(messages.redo)}\n className={styles.buttonGroupButtonIcon}\n draggable={false}\n src={redoIcon}\n />\n </Button>\n </ButtonGroup>\n </InputGroup>\n\n {/* Group/Ungroup */}\n {isVector(props.format) ?\n <InputGroup className={styles.modDashedBorder}>\n <LabeledIconButton\n disabled={!shouldShowGroup()}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={groupIcon}\n title={intl.formatMessage(messages.group)}\n onClick={props.onGroup}\n />\n <LabeledIconButton\n disabled={!shouldShowUngroup()}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={ungroupIcon}\n title={intl.formatMessage(messages.ungroup)}\n onClick={props.onUngroup}\n />\n </InputGroup> : null\n }\n\n {/* Forward/Backward */}\n {isVector(props.format) ?\n <InputGroup className={styles.modDashedBorder}>\n <LabeledIconButton\n disabled={!shouldShowBringForward()}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={sendForwardIcon}\n title={intl.formatMessage(messages.forward)}\n onClick={props.onSendForward}\n />\n <LabeledIconButton\n disabled={!shouldShowSendBackward()}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={sendBackwardIcon}\n title={intl.formatMessage(messages.backward)}\n onClick={props.onSendBackward}\n />\n </InputGroup> : null\n }\n\n {isVector(props.format) ?\n <MediaQuery minWidth={layout.fullSizeEditorMinWidth}>\n <InputGroup className={styles.row}>\n <LabeledIconButton\n disabled={!shouldShowBringForward()}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={sendFrontIcon}\n title={intl.formatMessage(messages.front)}\n onClick={props.onSendToFront}\n />\n <LabeledIconButton\n disabled={!shouldShowSendBackward()}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={sendBackIcon}\n title={intl.formatMessage(messages.back)}\n onClick={props.onSendToBack}\n />\n </InputGroup>\n\n {/* To be rotation point */}\n {/* <InputGroup>\n <LabeledIconButton\n imgAlt=\"Rotation Point\"\n imgSrc={rotationPointIcon}\n title=\"Rotation Point\"\n onClick={function () {}}\n />\n </InputGroup> */}\n </MediaQuery> : null\n }\n {isVector(props.format) ?\n <MediaQuery maxWidth={layout.fullSizeEditorMinWidth - 1}>\n <InputGroup>\n <Dropdown\n className={styles.modUnselect}\n enterExitTransitionDurationMs={20}\n popoverContent={\n <InputGroup\n className={styles.modContextMenu}\n rtl={props.rtl}\n >\n <Button\n className={classNames(styles.modMenuItem, {\n [styles.modDisabled]: !shouldShowBringForward()\n })}\n disabled={!shouldShowBringForward()}\n onClick={props.onSendToFront}\n >\n <img\n className={styles.menuItemIcon}\n draggable={false}\n src={sendFrontIcon}\n />\n <span>{intl.formatMessage(messages.front)}</span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem, {\n [styles.modDisabled]: !shouldShowSendBackward()\n })}\n disabled={!shouldShowSendBackward()}\n onClick={props.onSendToBack}\n >\n <img\n className={styles.menuItemIcon}\n draggable={false}\n src={sendBackIcon}\n />\n <span>{intl.formatMessage(messages.back)}</span>\n </Button>\n\n {/* To be rotation point */}\n {/* <Button\n className={classNames(styles.modMenuItem, styles.modTopDivider)}\n onClick={function () {}}\n >\n <img\n className={styles.menuItemIcon}\n draggable={false}\n src={rotationPointIcon}\n />\n <span>{'Rotation Point'}</span>\n </Button> */}\n </InputGroup>\n }\n tipSize={.01}\n >\n {intl.formatMessage(messages.more)}\n </Dropdown>\n </InputGroup>\n </MediaQuery> : null\n }\n </div>\n );\n};\n\nFixedToolsComponent.propTypes = {\n canRedo: PropTypes.func.isRequired,\n canUndo: PropTypes.func.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)),\n name: PropTypes.string,\n onGroup: PropTypes.func.isRequired,\n onRedo: PropTypes.func.isRequired,\n onSendBackward: PropTypes.func.isRequired,\n onSendForward: PropTypes.func.isRequired,\n onSendToBack: PropTypes.func.isRequired,\n onSendToFront: PropTypes.func.isRequired,\n onUndo: PropTypes.func.isRequired,\n onUngroup: PropTypes.func.isRequired,\n onUpdateName: PropTypes.func.isRequired,\n rtl: PropTypes.bool.isRequired\n};\n\nconst mapStateToProps = state => ({\n format: state.scratchPaint.format,\n rtl: state.scratchPaint.layout.rtl,\n selectedItems: state.scratchPaint.selectedItems,\n undoState: state.scratchPaint.undo\n});\n\nexport default connect(\n mapStateToProps\n)(FixedToolsComponent);\n","/* DO NOT EDIT\n@todo This file is copied from GUI and should be pulled out into a shared library.\nSee https://github.com/LLK/scratch-paint/issues/13 */\n\nimport bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\n/**\n * Higher Order Component to manage inputs that submit on blur and <enter>\n * @param {React.Component} Input text input that consumes onChange, onBlur, onKeyPress\n * @returns {React.Component} Buffered input that calls onSubmit on blur and <enter>\n */\nexport default function (Input) {\n class BufferedInput extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleChange',\n 'handleKeyPress',\n 'handleFlush'\n ]);\n this.state = {\n value: null\n };\n }\n handleKeyPress (e) {\n if (e.key === 'Enter') {\n this.handleFlush();\n e.target.blur();\n }\n }\n handleFlush () {\n const isNumeric = typeof this.props.value === 'number';\n const validatesNumeric = isNumeric ? !isNaN(this.state.value) : true;\n if (this.state.value !== null && validatesNumeric) {\n this.props.onSubmit(isNumeric ? Number(this.state.value) : this.state.value);\n }\n this.setState({value: null});\n }\n handleChange (e) {\n this.setState({value: e.target.value});\n }\n render () {\n const bufferedValue = this.state.value === null ? this.props.value : this.state.value;\n return (\n <Input\n {...this.props}\n value={bufferedValue}\n onBlur={this.handleFlush}\n onChange={this.handleChange}\n onKeyPress={this.handleKeyPress}\n />\n );\n }\n }\n\n BufferedInput.propTypes = {\n onSubmit: PropTypes.func.isRequired,\n value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])\n };\n\n return BufferedInput;\n}\n","import log from '../log/log';\n\nconst CHANGE_TEXT_EDIT_TARGET = 'scratch-paint/text-tool/CHANGE_TEXT_EDIT_TARGET';\nconst initialState = null;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_TEXT_EDIT_TARGET:\n if (typeof action.textEditTargetId === 'undefined') {\n log.warn(`Text edit target should not be set to undefined. Use null.`);\n return state;\n } else if (typeof action.textEditTargetId === 'undefined' || isNaN(action.textEditTargetId)) {\n log.warn(`Text edit target should be an item ID number. Got: ${action.textEditTargetId}`);\n return state;\n }\n return action.textEditTargetId;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n/**\n * Set the currently-being-edited text field to the given item ID\n * @param {?number} textEditTargetId The paper.Item ID of the active text field.\n * Leave empty if there is no text editing target.\n * @returns {object} Redux action to change the text edit target.\n */\nconst setTextEditTarget = function (textEditTargetId) {\n return {\n type: CHANGE_TEXT_EDIT_TARGET,\n textEditTargetId: textEditTargetId ? textEditTargetId : null\n };\n};\n\nexport {\n reducer as default,\n setTextEditTarget\n};\n","import log from '../log/log';\nconst SET_LAYOUT = 'scratch-paint/layout/SET_LAYOUT';\nconst initialState = {rtl: false};\n\nconst layouts = ['ltr', 'rtl'];\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case SET_LAYOUT:\n if (layouts.indexOf(action.layout) === -1) {\n log.warn(`Unrecognized layout provided: ${action.layout}`);\n return state;\n }\n return {rtl: action.layout === 'rtl'};\n default:\n return state;\n }\n};\n\n// Action creators ==================================\n/**\n * Change the layout to the new layout\n * @param {string} layout either 'ltr' or 'rtl'\n * @returns {object} Redux action to change the selected items.\n */\nconst setLayout = function (layout) {\n return {\n type: SET_LAYOUT,\n layout: layout\n };\n};\n\n\nexport {\n reducer as default,\n setLayout,\n SET_LAYOUT\n};\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\n\nimport FixedToolsComponent from '../components/fixed-tools/fixed-tools.jsx';\n\nimport {changeMode} from '../reducers/modes';\nimport {changeFormat} from '../reducers/format';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {deactivateEyeDropper} from '../reducers/eye-dropper';\nimport {setTextEditTarget} from '../reducers/text-edit-target';\nimport {setLayout} from '../reducers/layout';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';\nimport {groupSelection, ungroupSelection} from '../helper/group';\n\nimport Formats, {isBitmap} from '../lib/format';\nimport bindAll from 'lodash.bindall';\n\nclass FixedTools extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleSendBackward',\n 'handleSendForward',\n 'handleSendToBack',\n 'handleSendToFront',\n 'handleSetSelectedItems',\n 'handleGroup',\n 'handleUngroup'\n ]);\n }\n handleGroup () {\n groupSelection(this.props.clearSelectedItems, this.handleSetSelectedItems, this.props.onUpdateImage);\n }\n handleUngroup () {\n ungroupSelection(this.props.clearSelectedItems, this.handleSetSelectedItems, this.props.onUpdateImage);\n }\n handleSendBackward () {\n sendBackward(this.props.onUpdateImage);\n }\n handleSendForward () {\n bringForward(this.props.onUpdateImage);\n }\n handleSendToBack () {\n sendToBack(this.props.onUpdateImage);\n }\n handleSendToFront () {\n bringToFront(this.props.onUpdateImage);\n }\n handleSetSelectedItems () {\n this.props.setSelectedItems(this.props.format);\n }\n render () {\n return (\n <FixedToolsComponent\n canRedo={this.props.canRedo}\n canUndo={this.props.canUndo}\n name={this.props.name}\n onGroup={this.handleGroup}\n onRedo={this.props.onRedo}\n onSendBackward={this.handleSendBackward}\n onSendForward={this.handleSendForward}\n onSendToBack={this.handleSendToBack}\n onSendToFront={this.handleSendToFront}\n onUndo={this.props.onUndo}\n onUngroup={this.handleUngroup}\n onUpdateImage={this.props.onUpdateImage}\n onUpdateName={this.props.onUpdateName}\n />\n );\n }\n}\n\nFixedTools.propTypes = {\n canRedo: PropTypes.func.isRequired,\n canUndo: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)),\n name: PropTypes.string,\n onRedo: PropTypes.func.isRequired,\n onUndo: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n onUpdateName: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n changeColorToEyeDropper: state.scratchPaint.color.eyeDropper.callback,\n format: state.scratchPaint.format,\n isEyeDropping: state.scratchPaint.color.eyeDropper.active,\n mode: state.scratchPaint.mode,\n pasteOffset: state.scratchPaint.clipboard.pasteOffset,\n previousTool: state.scratchPaint.color.eyeDropper.previousTool,\n selectedItems: state.scratchPaint.selectedItems,\n viewBounds: state.scratchPaint.viewBounds\n});\nconst mapDispatchToProps = dispatch => ({\n changeMode: mode => {\n dispatch(changeMode(mode));\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n handleSwitchToBitmap: () => {\n dispatch(changeFormat(Formats.BITMAP));\n },\n handleSwitchToVector: () => {\n dispatch(changeFormat(Formats.VECTOR));\n },\n removeTextEditTarget: () => {\n dispatch(setTextEditTarget());\n },\n setLayout: layout => {\n dispatch(setLayout(layout));\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n },\n onDeactivateEyeDropper: () => {\n // set redux values to default for eye dropper reducer\n dispatch(deactivateEyeDropper());\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(FixedTools);\n","import paper from '@scratch/paper';\nimport bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport omit from 'lodash.omit';\nimport {connect} from 'react-redux';\n\nimport {\n clearSelection,\n getAllRootItems,\n getSelectedLeafItems,\n getSelectedRootItems\n} from '../helper/selection';\nimport {getTrimmedRaster} from '../helper/bitmap';\nimport Formats, {isBitmap} from '../lib/format';\nimport Modes from '../lib/modes';\n\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {incrementPasteOffset, setClipboardItems} from '../reducers/clipboard';\n\nconst CopyPasteHOC = function (WrappedComponent) {\n class CopyPasteWrapper extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleCopy',\n 'handlePaste'\n ]);\n }\n handleCopy () {\n let selectedItems = [];\n if (this.props.mode === Modes.RESHAPE) {\n const leafItems = getSelectedLeafItems();\n // Copy root of compound paths\n for (const item of leafItems) {\n if (item.parent && item.parent instanceof paper.CompoundPath) {\n selectedItems.push(item.parent);\n } else {\n selectedItems.push(item);\n }\n }\n } else {\n selectedItems = getSelectedRootItems();\n }\n if (selectedItems.length === 0) {\n if (isBitmap(this.props.format)) {\n const raster = getTrimmedRaster(false /* shouldInsert */);\n if (!raster) return;\n selectedItems.push(raster);\n } else {\n selectedItems = getAllRootItems();\n }\n }\n const clipboardItems = [];\n for (let i = 0; i < selectedItems.length; i++) {\n const jsonItem = selectedItems[i].exportJSON({asString: false});\n clipboardItems.push(jsonItem);\n }\n this.props.setClipboardItems(clipboardItems);\n }\n handlePaste () {\n clearSelection(this.props.clearSelectedItems);\n\n if (this.props.clipboardItems.length === 0) return;\n\n let items = [];\n for (let i = 0; i < this.props.clipboardItems.length; i++) {\n const item = paper.Base.importJSON(this.props.clipboardItems[i]);\n if (item) {\n items.push(item);\n }\n }\n if (!items.length) return;\n // If pasting a group or non-raster to bitmap, rasterize first\n if (isBitmap(this.props.format) && !(items.length === 1 && items[0] instanceof paper.Raster)) {\n const group = new paper.Group(items);\n items = [group.rasterize()];\n group.remove();\n }\n for (const item of items) {\n const placedItem = paper.project.getActiveLayer().addChild(item);\n placedItem.selected = true;\n placedItem.position.x += 10 * this.props.pasteOffset;\n placedItem.position.y += 10 * this.props.pasteOffset;\n }\n this.props.incrementPasteOffset();\n this.props.setSelectedItems(this.props.format);\n this.props.onUpdateImage();\n }\n render () {\n const componentProps = omit(this.props, [\n 'clearSelectedItems',\n 'clipboardItems',\n 'format',\n 'incrementPasteOffset',\n 'mode',\n 'pasteOffset',\n 'setClipboardItems',\n 'setSelectedItems']);\n return (\n <WrappedComponent\n onCopyToClipboard={this.handleCopy}\n onPasteFromClipboard={this.handlePaste}\n {...componentProps}\n />\n );\n }\n }\n\n CopyPasteWrapper.propTypes = {\n clearSelectedItems: PropTypes.func.isRequired,\n clipboardItems: PropTypes.arrayOf(PropTypes.array),\n format: PropTypes.oneOf(Object.keys(Formats)),\n incrementPasteOffset: PropTypes.func.isRequired,\n mode: PropTypes.oneOf(Object.keys(Modes)),\n onUpdateImage: PropTypes.func.isRequired,\n pasteOffset: PropTypes.number,\n setClipboardItems: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired\n };\n const mapStateToProps = state => ({\n clipboardItems: state.scratchPaint.clipboard.items,\n format: state.scratchPaint.format,\n mode: state.scratchPaint.mode,\n pasteOffset: state.scratchPaint.clipboard.pasteOffset\n });\n const mapDispatchToProps = dispatch => ({\n setClipboardItems: items => {\n dispatch(setClipboardItems(items));\n },\n incrementPasteOffset: () => {\n dispatch(incrementPasteOffset());\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n }\n });\n\n return connect(\n mapStateToProps,\n mapDispatchToProps\n )(CopyPasteWrapper);\n};\n\nexport default CopyPasteHOC;\n","import log from '../log/log';\n\nconst CHANGE_BRUSH_SIZE = 'scratch-paint/brush-mode/CHANGE_BRUSH_SIZE';\nconst initialState = {brushSize: 10};\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_BRUSH_SIZE:\n if (isNaN(action.brushSize)) {\n log.warn(`Invalid brush size: ${action.brushSize}`);\n return state;\n }\n return {brushSize: Math.max(1, action.brushSize)};\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeBrushSize = function (brushSize) {\n return {\n type: CHANGE_BRUSH_SIZE,\n brushSize: brushSize\n };\n};\n\nexport {\n reducer as default,\n changeBrushSize\n};\n","import log from '../log/log';\nimport {CHANGE_SELECTED_ITEMS} from './selected-items';\nimport {getColorsFromSelection} from '../helper/style-path';\n\n// Bit brush size affects bit brush width, circle/rectangle outline drawing width, and line width\n// in the bitmap paint editor.\nconst CHANGE_BIT_BRUSH_SIZE = 'scratch-paint/brush-mode/CHANGE_BIT_BRUSH_SIZE';\nconst initialState = 10;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_BIT_BRUSH_SIZE:\n if (isNaN(action.brushSize)) {\n log.warn(`Invalid brush size: ${action.brushSize}`);\n return state;\n }\n return Math.max(1, action.brushSize);\n case CHANGE_SELECTED_ITEMS:\n {\n // Don't change state if no selection\n if (!action.selectedItems || !action.selectedItems.length) {\n return state;\n }\n // Vector mode doesn't have bit width\n if (!action.bitmapMode) {\n return state;\n }\n const colorState = getColorsFromSelection(action.selectedItems, action.bitmapMode);\n if (colorState.thickness) return colorState.thickness;\n return state;\n }\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeBitBrushSize = function (brushSize) {\n return {\n type: CHANGE_BIT_BRUSH_SIZE,\n brushSize: brushSize\n };\n};\n\nexport {\n reducer as default,\n changeBitBrushSize\n};\n","import log from '../log/log';\n\nconst CHANGE_BIT_ERASER_SIZE = 'scratch-paint/eraser-mode/CHANGE_BIT_ERASER_SIZE';\nconst initialState = 40;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_BIT_ERASER_SIZE:\n if (isNaN(action.eraserSize)) {\n log.warn(`Invalid eraser size: ${action.eraserSize}`);\n return state;\n }\n return Math.max(1, action.eraserSize);\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeBitEraserSize = function (eraserSize) {\n return {\n type: CHANGE_BIT_ERASER_SIZE,\n eraserSize: eraserSize\n };\n};\n\nexport {\n reducer as default,\n changeBitEraserSize\n};\n","import paper from '@scratch/paper';\nimport {CHANGE_SELECTED_ITEMS} from './selected-items';\n\nconst SET_FILLED = 'scratch-paint/fill-bitmap-shapes/SET_FILLED';\nconst initialState = true;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case SET_FILLED:\n return action.filled;\n case CHANGE_SELECTED_ITEMS:\n if (action.bitmapMode &&\n action.selectedItems &&\n action.selectedItems[0] instanceof paper.Shape) {\n return action.selectedItems[0].strokeWidth === 0;\n }\n return state;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst setShapesFilled = function (filled) {\n return {\n type: SET_FILLED,\n filled: filled\n };\n};\n\nexport {\n reducer as default,\n setShapesFilled\n};\n","const Fonts = {\n SANS_SERIF: 'Sans Serif',\n SERIF: 'Serif',\n HANDWRITING: 'Handwriting',\n MARKER: 'Marker',\n CURLY: 'Curly',\n PIXEL: 'Pixel',\n CHINESE: '\"Microsoft YaHei\", \"微软雅黑\", STXihei, \"华文细黑\"',\n JAPANESE: '\"ヒラギノ角ゴ Pro W3\", \"Hiragino Kaku Gothic Pro\", Osaka, \"メイリオ\", Meiryo, \"MS Pゴシック\", \"MS PGothic\"',\n KOREAN: 'Malgun Gothic'\n};\n\nexport default Fonts;\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./font-dropdown.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./font-dropdown.css\";\n export default content && content.locals ? content.locals : undefined;\n","import classNames from 'classnames';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport Button from '../button/button.jsx';\nimport Dropdown from '../dropdown/dropdown.jsx';\nimport InputGroup from '../input-group/input-group.jsx';\nimport Fonts from '../../lib/fonts';\nimport styles from './font-dropdown.css';\n\nconst ModeToolsComponent = props => (\n <Dropdown\n className={classNames(styles.modUnselect, styles.fontDropdown)}\n enterExitTransitionDurationMs={60}\n popoverContent={\n <InputGroup className={styles.modContextMenu}>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverSansSerif}\n >\n <span className={styles.sansSerif}>\n {props.getFontName(Fonts.SANS_SERIF)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverSerif}\n >\n <span className={styles.serif}>\n {props.getFontName(Fonts.SERIF)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverHandwriting}\n >\n <span className={styles.handwriting}>\n {props.getFontName(Fonts.HANDWRITING)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverMarker}\n >\n <span className={styles.marker}>\n {props.getFontName(Fonts.MARKER)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverCurly}\n >\n <span className={styles.curly}>\n {props.getFontName(Fonts.CURLY)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverPixel}\n >\n <span className={styles.pixel}>\n {props.getFontName(Fonts.PIXEL)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverChinese}\n >\n <span className={styles.chinese}>\n {props.getFontName(Fonts.CHINESE)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverJapanese}\n >\n <span className={styles.japanese}>\n {props.getFontName(Fonts.JAPANESE)}\n </span>\n </Button>\n <Button\n className={classNames(styles.modMenuItem)}\n onClick={props.onChoose}\n onMouseOver={props.onHoverKorean}\n >\n <span className={styles.korean}>\n {props.getFontName(Fonts.KOREAN)}\n </span>\n </Button>\n </InputGroup>\n }\n ref={props.componentRef}\n tipSize={.01}\n onOpen={props.onOpenDropdown}\n onOuterAction={props.onClickOutsideDropdown}\n >\n <span className={classNames(props.getFontStyle(props.font), styles.displayedFontName)}>\n {props.getFontName(props.font)}\n </span>\n </Dropdown>\n);\n\nModeToolsComponent.propTypes = {\n componentRef: PropTypes.func.isRequired,\n font: PropTypes.string,\n getFontName: PropTypes.func.isRequired,\n getFontStyle: PropTypes.func.isRequired,\n onChoose: PropTypes.func.isRequired,\n onClickOutsideDropdown: PropTypes.func,\n onHoverChinese: PropTypes.func,\n onHoverCurly: PropTypes.func,\n onHoverHandwriting: PropTypes.func,\n onHoverJapanese: PropTypes.func,\n onHoverKorean: PropTypes.func,\n onHoverMarker: PropTypes.func,\n onHoverPixel: PropTypes.func,\n onHoverSansSerif: PropTypes.func,\n onHoverSerif: PropTypes.func,\n onOpenDropdown: PropTypes.func\n};\nexport default ModeToolsComponent;\n","import Fonts from '../lib/fonts';\n\nconst CHANGE_FONT = 'scratch-paint/fonts/CHANGE_FONT';\nconst initialState = Fonts.SANS_SERIF;\n\nconst reducer = function (state, action) {\n if (typeof state === 'undefined') state = initialState;\n switch (action.type) {\n case CHANGE_FONT:\n if (!action.font) return state;\n return action.font;\n default:\n return state;\n }\n};\n\n// Action creators ==================================\nconst changeFont = function (font) {\n return {\n type: CHANGE_FONT,\n font: font\n };\n};\n\nexport {\n reducer as default,\n changeFont\n};\n","import paper from '@scratch/paper';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport FontDropdownComponent from '../components/font-dropdown/font-dropdown.jsx';\nimport Fonts from '../lib/fonts';\nimport {changeFont} from '../reducers/font';\nimport {getSelectedLeafItems} from '../helper/selection';\nimport styles from '../components/font-dropdown/font-dropdown.css';\n\nclass FontDropdown extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'getFontStyle',\n 'getFontName',\n 'handleChangeFontSerif',\n 'handleChangeFontSansSerif',\n 'handleChangeFontHandwriting',\n 'handleChangeFontMarker',\n 'handleChangeFontCurly',\n 'handleChangeFontPixel',\n 'handleChangeFontChinese',\n 'handleChangeFontJapanese',\n 'handleChangeFontKorean',\n 'handleOpenDropdown',\n 'handleClickOutsideDropdown',\n 'setDropdown',\n 'handleChoose'\n ]);\n }\n getFontStyle (font) {\n switch (font) {\n case Fonts.SERIF:\n return styles.serif;\n case Fonts.SANS_SERIF:\n return styles.sansSerif;\n case Fonts.HANDWRITING:\n return styles.handwriting;\n case Fonts.MARKER:\n return styles.marker;\n case Fonts.CURLY:\n return styles.curly;\n case Fonts.PIXEL:\n return styles.pixel;\n case Fonts.CHINESE:\n return styles.chinese;\n case Fonts.JAPANESE:\n return styles.japanese;\n case Fonts.KOREAN:\n return styles.korean;\n default:\n return '';\n }\n }\n getFontName (font) {\n switch (font) {\n case Fonts.CHINESE:\n return '中文';\n case Fonts.KOREAN:\n return '한국어';\n case Fonts.JAPANESE:\n return '日本語';\n default:\n return font;\n }\n }\n handleChangeFontSansSerif () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.SANS_SERIF);\n }\n }\n handleChangeFontSerif () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.SERIF);\n }\n }\n handleChangeFontHandwriting () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.HANDWRITING);\n }\n }\n handleChangeFontMarker () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.MARKER);\n }\n }\n handleChangeFontCurly () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.CURLY);\n }\n }\n handleChangeFontPixel () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.PIXEL);\n }\n }\n handleChangeFontChinese () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.CHINESE);\n }\n }\n handleChangeFontJapanese () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.JAPANESE);\n }\n }\n handleChangeFontKorean () {\n if (this.dropDown.isOpen()) {\n this.props.changeFont(Fonts.KOREAN);\n }\n }\n handleChoose () {\n if (this.dropDown.isOpen()) {\n this.dropDown.handleClosePopover();\n this.props.onUpdateImage();\n }\n }\n handleOpenDropdown () {\n this.savedFont = this.props.font;\n this.savedSelection = getSelectedLeafItems();\n }\n handleClickOutsideDropdown (e) {\n e.stopPropagation();\n this.dropDown.handleClosePopover();\n\n // Cancel font change\n for (const item of this.savedSelection) {\n if (item instanceof paper.PointText) {\n item.font = this.savedFont;\n }\n }\n\n this.props.changeFont(this.savedFont);\n this.savedFont = null;\n this.savedSelection = null;\n }\n setDropdown (element) {\n this.dropDown = element;\n }\n render () {\n return (\n <FontDropdownComponent\n componentRef={this.setDropdown}\n font={this.props.font}\n getFontName={this.getFontName}\n getFontStyle={this.getFontStyle}\n onChoose={this.handleChoose}\n onClickOutsideDropdown={this.handleClickOutsideDropdown}\n onHoverChinese={this.handleChangeFontChinese}\n onHoverCurly={this.handleChangeFontCurly}\n onHoverHandwriting={this.handleChangeFontHandwriting}\n onHoverJapanese={this.handleChangeFontJapanese}\n onHoverKorean={this.handleChangeFontKorean}\n onHoverMarker={this.handleChangeFontMarker}\n onHoverPixel={this.handleChangeFontPixel}\n onHoverSansSerif={this.handleChangeFontSansSerif}\n onHoverSerif={this.handleChangeFontSerif}\n onOpenDropdown={this.handleOpenDropdown}\n />\n );\n }\n}\n\nFontDropdown.propTypes = {\n changeFont: PropTypes.func.isRequired,\n font: PropTypes.string,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n font: state.scratchPaint.font\n});\nconst mapDispatchToProps = dispatch => ({\n changeFont: font => {\n dispatch(changeFont(font));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(FontDropdown);\n","import bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\n/**\n * Higher Order Component to manage inputs that submit on change and <enter>\n * @param {React.Component} Input text input that consumes onChange, onBlur, onKeyPress\n * @returns {React.Component} Live input that calls onSubmit on change and <enter>\n */\nexport default function (Input) {\n class LiveInput extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleChange',\n 'handleKeyPress',\n 'handleFlush'\n ]);\n this.state = {\n value: null\n };\n }\n handleKeyPress (e) {\n if (e.key === 'Enter') {\n this.handleChange(e);\n e.target.blur();\n }\n }\n handleFlush () {\n this.setState({value: null});\n }\n handleChange (e) {\n const isNumeric = typeof this.props.value === 'number';\n const validatesNumeric = isNumeric ? !isNaN(e.target.value) : true;\n if (e.target.value !== null && validatesNumeric) {\n let val = Number(e.target.value);\n if (typeof this.props.max !== 'undefined' && val > Number(this.props.max)) {\n val = this.props.max;\n }\n if (typeof this.props.min !== 'undefined' && val < Number(this.props.min)) {\n val = this.props.min;\n }\n this.props.onSubmit(val);\n }\n this.setState({value: e.target.value});\n }\n render () {\n const liveValue = this.state.value === null ? this.props.value : this.state.value;\n return (\n <Input\n {...this.props}\n value={liveValue}\n onBlur={this.handleFlush}\n onChange={this.handleChange}\n onKeyPress={this.handleKeyPress}\n />\n );\n }\n }\n\n LiveInput.propTypes = {\n max: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n min: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n onSubmit: PropTypes.func.isRequired,\n value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])\n };\n\n return LiveInput;\n}\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./mode-tools.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./mode-tools.css\";\n export default content && content.locals ? content.locals : undefined;\n","import classNames from 'classnames';\nimport {connect} from 'react-redux';\nimport PropTypes from 'prop-types';\nimport React from 'react';\n\nimport {changeBrushSize} from '../../reducers/brush-mode';\nimport {changeBrushSize as changeEraserSize} from '../../reducers/eraser-mode';\nimport {changeBitBrushSize} from '../../reducers/bit-brush-size';\nimport {changeBitEraserSize} from '../../reducers/bit-eraser-size';\nimport {setShapesFilled} from '../../reducers/fill-bitmap-shapes';\n\nimport FontDropdown from '../../containers/font-dropdown.jsx';\nimport LiveInputHOC from '../forms/live-input-hoc.jsx';\nimport Label from '../forms/label.jsx';\nimport {defineMessages, useIntl} from 'react-intl';\nimport Input from '../forms/input.jsx';\nimport InputGroup from '../input-group/input-group.jsx';\nimport LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';\nimport Modes from '../../lib/modes';\nimport Formats, {isBitmap, isVector} from '../../lib/format';\nimport {hideLabel} from '../../lib/hide-label';\nimport styles from './mode-tools.css';\n\nimport copyIcon from './icons/copy.svg';\nimport pasteIcon from './icons/paste.svg';\nimport deleteIcon from './icons/delete.svg';\n\nimport bitBrushIcon from '../bit-brush-mode/brush.svg';\nimport bitEraserIcon from '../bit-eraser-mode/eraser.svg';\nimport bitLineIcon from '../bit-line-mode/line.svg';\nimport brushIcon from '../brush-mode/brush.svg';\nimport curvedPointIcon from './icons/curved-point.svg';\nimport eraserIcon from '../eraser-mode/eraser.svg';\nimport flipHorizontalIcon from './icons/flip-horizontal.svg';\nimport flipVerticalIcon from './icons/flip-vertical.svg';\nimport straightPointIcon from './icons/straight-point.svg';\nimport bitOvalIcon from '../bit-oval-mode/oval.svg';\nimport bitRectIcon from '../bit-rect-mode/rectangle.svg';\nimport bitOvalOutlinedIcon from '../bit-oval-mode/oval-outlined.svg';\nimport bitRectOutlinedIcon from '../bit-rect-mode/rectangle-outlined.svg';\n\nimport {MAX_STROKE_WIDTH} from '../../reducers/stroke-width';\n\nconst LiveInput = LiveInputHOC(Input);\nconst ModeToolsComponent = props => {\n const intl = useIntl();\n const messages = defineMessages({\n brushSize: {\n defaultMessage: 'Size',\n description: 'Label for the brush size input',\n id: 'paint.modeTools.brushSize'\n },\n eraserSize: {\n defaultMessage: 'Eraser size',\n description: 'Label for the eraser size input',\n id: 'paint.modeTools.eraserSize'\n },\n copy: {\n defaultMessage: 'Copy',\n description: 'Label for the copy button',\n id: 'paint.modeTools.copy'\n },\n paste: {\n defaultMessage: 'Paste',\n description: 'Label for the paste button',\n id: 'paint.modeTools.paste'\n },\n delete: {\n defaultMessage: 'Delete',\n description: 'Label for the delete button',\n id: 'paint.modeTools.delete'\n },\n curved: {\n defaultMessage: 'Curved',\n description: 'Label for the button that converts selected points to curves',\n id: 'paint.modeTools.curved'\n },\n pointed: {\n defaultMessage: 'Pointed',\n description: 'Label for the button that converts selected points to sharp points',\n id: 'paint.modeTools.pointed'\n },\n thickness: {\n defaultMessage: 'Thickness',\n description: 'Label for the number input to choose the line thickness',\n id: 'paint.modeTools.thickness'\n },\n flipHorizontal: {\n defaultMessage: 'Flip Horizontal',\n description: 'Label for the button to flip the image horizontally',\n id: 'paint.modeTools.flipHorizontal'\n },\n flipVertical: {\n defaultMessage: 'Flip Vertical',\n description: 'Label for the button to flip the image vertically',\n id: 'paint.modeTools.flipVertical'\n },\n filled: {\n defaultMessage: 'Filled',\n description: 'Label for the button that sets the bitmap rectangle/oval mode to draw outlines',\n id: 'paint.modeTools.filled'\n },\n outlined: {\n defaultMessage: 'Outlined',\n description: 'Label for the button that sets the bitmap rectangle/oval mode to draw filled-in shapes',\n id: 'paint.modeTools.outlined'\n }\n });\n\n switch (props.mode) {\n case Modes.BRUSH:\n /* falls through */\n case Modes.BIT_BRUSH:\n /* falls through */\n case Modes.BIT_LINE:\n {\n const currentIcon = isVector(props.format) ? brushIcon :\n props.mode === Modes.BIT_LINE ? bitLineIcon : bitBrushIcon;\n const currentBrushValue = isBitmap(props.format) ? props.bitBrushSize : props.brushValue;\n const changeFunction = isBitmap(props.format) ? props.onBitBrushSliderChange : props.onBrushSliderChange;\n const currentMessage = props.mode === Modes.BIT_LINE ? messages.thickness : messages.brushSize;\n return (\n <div className={classNames(props.className, styles.modeTools)}>\n <div>\n <img\n alt={intl.formatMessage(currentMessage)}\n className={styles.modeToolsIcon}\n draggable={false}\n src={currentIcon}\n />\n </div>\n <LiveInput\n range\n small\n max={MAX_STROKE_WIDTH}\n min=\"1\"\n type=\"number\"\n value={currentBrushValue}\n onSubmit={changeFunction}\n />\n </div>\n );\n }\n case Modes.BIT_ERASER:\n /* falls through */\n case Modes.ERASER:\n {\n const currentIcon = isVector(props.format) ? eraserIcon : bitEraserIcon;\n const currentEraserValue = isBitmap(props.format) ? props.bitEraserSize : props.eraserValue;\n const changeFunction = isBitmap(props.format) ? props.onBitEraserSliderChange : props.onEraserSliderChange;\n return (\n <div className={classNames(props.className, styles.modeTools)}>\n <div>\n <img\n alt={intl.formatMessage(messages.eraserSize)}\n className={styles.modeToolsIcon}\n draggable={false}\n src={currentIcon}\n />\n </div>\n <LiveInput\n range\n small\n max={MAX_STROKE_WIDTH}\n min=\"1\"\n type=\"number\"\n value={currentEraserValue}\n onSubmit={changeFunction}\n />\n </div>\n );\n }\n case Modes.RESHAPE:\n return (\n <div className={classNames(props.className, styles.modeTools)}>\n <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}>\n <LabeledIconButton\n disabled={!props.hasSelectedUncurvedPoints}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={curvedPointIcon}\n title={intl.formatMessage(messages.curved)}\n onClick={props.onCurvePoints}\n />\n <LabeledIconButton\n disabled={!props.hasSelectedUnpointedPoints}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={straightPointIcon}\n title={intl.formatMessage(messages.pointed)}\n onClick={props.onPointPoints}\n />\n </InputGroup>\n <InputGroup className={classNames(styles.modLabeledIconHeight)}>\n <LabeledIconButton\n hideLabel={hideLabel(intl.locale)}\n imgSrc={deleteIcon}\n title={intl.formatMessage(messages.delete)}\n onClick={props.onDelete}\n />\n </InputGroup>\n </div>\n );\n case Modes.BIT_SELECT:\n /* falls through */\n case Modes.SELECT:\n return (\n <div className={classNames(props.className, styles.modeTools)}>\n <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}>\n <LabeledIconButton\n hideLabel={hideLabel(intl.locale)}\n imgSrc={copyIcon}\n title={intl.formatMessage(messages.copy)}\n onClick={props.onCopyToClipboard}\n />\n <LabeledIconButton\n disabled={!(props.clipboardItems.length > 0)}\n hideLabel={hideLabel(intl.locale)}\n imgSrc={pasteIcon}\n title={intl.formatMessage(messages.paste)}\n onClick={props.onPasteFromClipboard}\n />\n </InputGroup>\n <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}>\n <LabeledIconButton\n hideLabel={hideLabel(intl.locale)}\n imgSrc={deleteIcon}\n title={intl.formatMessage(messages.delete)}\n onClick={props.onDelete}\n />\n </InputGroup>\n <InputGroup className={classNames(styles.modLabeledIconHeight)}>\n <LabeledIconButton\n hideLabel={intl.locale !== 'en'}\n imgSrc={flipHorizontalIcon}\n title={intl.formatMessage(messages.flipHorizontal)}\n onClick={props.onFlipHorizontal}\n />\n <LabeledIconButton\n hideLabel={intl.locale !== 'en'}\n imgSrc={flipVerticalIcon}\n title={intl.formatMessage(messages.flipVertical)}\n onClick={props.onFlipVertical}\n />\n </InputGroup>\n </div>\n );\n case Modes.BIT_TEXT:\n /* falls through */\n case Modes.TEXT:\n return (\n <div className={classNames(props.className, styles.modeTools)}>\n <InputGroup>\n <FontDropdown\n onUpdateImage={props.onUpdateImage}\n />\n </InputGroup>\n </div>\n );\n case Modes.BIT_RECT:\n /* falls through */\n case Modes.BIT_OVAL:\n {\n const fillIcon = props.mode === Modes.BIT_RECT ? bitRectIcon : bitOvalIcon;\n const outlineIcon = props.mode === Modes.BIT_RECT ? bitRectOutlinedIcon : bitOvalOutlinedIcon;\n return (\n <div className={classNames(props.className, styles.modeTools)}>\n <InputGroup>\n <LabeledIconButton\n highlighted={props.fillBitmapShapes}\n imgSrc={fillIcon}\n title={intl.formatMessage(messages.filled)}\n onClick={props.onFillShapes}\n />\n </InputGroup>\n <InputGroup>\n <LabeledIconButton\n highlighted={!props.fillBitmapShapes}\n imgSrc={outlineIcon}\n title={intl.formatMessage(messages.outlined)}\n onClick={props.onOutlineShapes}\n />\n </InputGroup>\n {props.fillBitmapShapes ? null : (\n <InputGroup>\n <Label text={intl.formatMessage(messages.thickness)}>\n <LiveInput\n range\n small\n max={MAX_STROKE_WIDTH}\n min=\"1\"\n type=\"number\"\n value={props.bitBrushSize}\n onSubmit={props.onBitBrushSliderChange}\n />\n </Label>\n </InputGroup>)\n }\n </div>\n );\n }\n default:\n // Leave empty for now, if mode not supported\n return (\n <div className={classNames(props.className, styles.modeTools)} />\n );\n }\n};\n\nModeToolsComponent.propTypes = {\n bitBrushSize: PropTypes.number,\n bitEraserSize: PropTypes.number,\n brushValue: PropTypes.number,\n className: PropTypes.string,\n clipboardItems: PropTypes.arrayOf(PropTypes.array),\n eraserValue: PropTypes.number,\n fillBitmapShapes: PropTypes.bool,\n format: PropTypes.oneOf(Object.keys(Formats)),\n hasSelectedUncurvedPoints: PropTypes.bool,\n hasSelectedUnpointedPoints: PropTypes.bool,\n mode: PropTypes.string.isRequired,\n onBitBrushSliderChange: PropTypes.func.isRequired,\n onBitEraserSliderChange: PropTypes.func.isRequired,\n onBrushSliderChange: PropTypes.func.isRequired,\n onCopyToClipboard: PropTypes.func.isRequired,\n onCurvePoints: PropTypes.func.isRequired,\n onDelete: PropTypes.func.isRequired,\n onEraserSliderChange: PropTypes.func,\n onFillShapes: PropTypes.func.isRequired,\n onFlipHorizontal: PropTypes.func.isRequired,\n onFlipVertical: PropTypes.func.isRequired,\n onOutlineShapes: PropTypes.func.isRequired,\n onPasteFromClipboard: PropTypes.func.isRequired,\n onPointPoints: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n mode: state.scratchPaint.mode,\n format: state.scratchPaint.format,\n fillBitmapShapes: state.scratchPaint.fillBitmapShapes,\n bitBrushSize: state.scratchPaint.bitBrushSize,\n bitEraserSize: state.scratchPaint.bitEraserSize,\n brushValue: state.scratchPaint.brushMode.brushSize,\n clipboardItems: state.scratchPaint.clipboard.items,\n eraserValue: state.scratchPaint.eraserMode.brushSize\n});\nconst mapDispatchToProps = dispatch => ({\n onBrushSliderChange: brushSize => {\n dispatch(changeBrushSize(brushSize));\n },\n onBitBrushSliderChange: bitBrushSize => {\n dispatch(changeBitBrushSize(bitBrushSize));\n },\n onBitEraserSliderChange: eraserSize => {\n dispatch(changeBitEraserSize(eraserSize));\n },\n onEraserSliderChange: eraserSize => {\n dispatch(changeEraserSize(eraserSize));\n },\n onFillShapes: () => {\n dispatch(setShapesFilled(true));\n },\n onOutlineShapes: () => {\n dispatch(setShapesFilled(false));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(ModeToolsComponent);\n","import paper from '@scratch/paper';\nimport React from 'react';\nimport PropTypes from 'prop-types';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\n\nimport CopyPasteHOC from '../hocs/copy-paste-hoc.jsx';\nimport ModeToolsComponent from '../components/mode-tools/mode-tools.jsx';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {\n deleteSelection,\n getSelectedLeafItems,\n getSelectedRootItems,\n getAllRootItems,\n selectAllItems,\n selectAllSegments\n} from '../helper/selection';\nimport {HANDLE_RATIO, ensureClockwise} from '../helper/math';\nimport {getRaster} from '../helper/layer';\nimport {flipBitmapHorizontal, flipBitmapVertical, selectAllBitmap} from '../helper/bitmap';\nimport Formats, {isBitmap} from '../lib/format';\nimport Modes from '../lib/modes';\n\nclass ModeTools extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n '_getSelectedUncurvedPoints',\n '_getSelectedUnpointedPoints',\n 'hasSelectedUncurvedPoints',\n 'hasSelectedUnpointedPoints',\n 'handleCurvePoints',\n 'handleFlipHorizontal',\n 'handleFlipVertical',\n 'handleDelete',\n 'handlePasteFromClipboard',\n 'handlePointPoints'\n ]);\n }\n _getSelectedUncurvedPoints () {\n const items = [];\n const selectedItems = getSelectedLeafItems();\n for (const item of selectedItems) {\n if (!item.segments) continue;\n for (const seg of item.segments) {\n if (seg.selected) {\n const prev = seg.getPrevious();\n const next = seg.getNext();\n const isCurved =\n (!prev || seg.handleIn.length > 0) &&\n (!next || seg.handleOut.length > 0) &&\n (prev && next ? seg.handleOut.isColinear(seg.handleIn) : true);\n if (!isCurved) items.push(seg);\n }\n }\n }\n return items;\n }\n _getSelectedUnpointedPoints () {\n const points = [];\n const selectedItems = getSelectedLeafItems();\n for (const item of selectedItems) {\n if (!item.segments) continue;\n for (const seg of item.segments) {\n if (seg.selected) {\n if (seg.handleIn.length > 0 || seg.handleOut.length > 0) {\n points.push(seg);\n }\n }\n }\n }\n return points;\n }\n hasSelectedUncurvedPoints () {\n const points = this._getSelectedUncurvedPoints();\n return points.length > 0;\n }\n hasSelectedUnpointedPoints () {\n const points = this._getSelectedUnpointedPoints();\n return points.length > 0;\n }\n handleCurvePoints () {\n let changed;\n const points = this._getSelectedUncurvedPoints();\n for (const point of points) {\n const prev = point.getPrevious();\n const next = point.getNext();\n const noHandles = point.handleIn.length === 0 && point.handleOut.length === 0;\n if (!prev && !next) {\n continue;\n } else if (prev && next && noHandles) {\n // Handles are parallel to the line from prev to next\n point.handleIn = prev.point.subtract(next.point)\n .normalize()\n .multiply(prev.getCurve().length * HANDLE_RATIO);\n } else if (prev && !next && point.handleIn.length === 0) {\n // Point is end point\n // Direction is average of normal at the point and direction to prev point, using the\n // normal that points out from the convex side\n // Length is curve length * HANDLE_RATIO\n const convexity = prev.getCurve().getCurvatureAtTime(.5) < 0 ? -1 : 1;\n point.handleIn = (prev.getCurve().getNormalAtTime(1)\n .multiply(convexity)\n .add(prev.point.subtract(point.point).normalize()))\n .normalize()\n .multiply(prev.getCurve().length * HANDLE_RATIO);\n } else if (next && !prev && point.handleOut.length === 0) {\n // Point is start point\n // Direction is average of normal at the point and direction to prev point, using the\n // normal that points out from the convex side\n // Length is curve length * HANDLE_RATIO\n const convexity = point.getCurve().getCurvatureAtTime(.5) < 0 ? -1 : 1;\n point.handleOut = (point.getCurve().getNormalAtTime(0)\n .multiply(convexity)\n .add(next.point.subtract(point.point).normalize()))\n .normalize()\n .multiply(point.getCurve().length * HANDLE_RATIO);\n }\n\n // Point guaranteed to have a handle now. Make the second handle match the length and direction of first.\n // This defines a curved point.\n if (point.handleIn.length > 0 && next) {\n point.handleOut = point.handleIn.multiply(-1);\n } else if (point.handleOut.length > 0 && prev) {\n point.handleIn = point.handleOut.multiply(-1);\n }\n changed = true;\n }\n if (changed) {\n this.props.setSelectedItems(this.props.format);\n this.props.onUpdateImage();\n }\n }\n handlePointPoints () {\n let changed;\n const points = this._getSelectedUnpointedPoints();\n for (const point of points) {\n const noHandles = point.handleIn.length === 0 && point.handleOut.length === 0;\n if (!noHandles) {\n point.handleIn = null;\n point.handleOut = null;\n changed = true;\n }\n }\n if (changed) {\n this.props.setSelectedItems(this.props.format);\n this.props.onUpdateImage();\n }\n }\n _handleFlip (horizontalScale, verticalScale, selectedItems) {\n if (selectedItems.length === 0) {\n // If nothing is selected, select everything\n selectedItems = getAllRootItems();\n }\n // Record old indices\n for (const item of selectedItems) {\n item.data.index = item.index;\n }\n\n // Group items so that they flip as a unit\n const itemGroup = new paper.Group(selectedItems);\n // Flip\n itemGroup.scale(horizontalScale, verticalScale);\n ensureClockwise(itemGroup);\n\n // Remove flipped item from group and insert at old index. Must insert from bottom index up.\n for (let i = 0; i < selectedItems.length; i++) {\n itemGroup.layer.insertChild(selectedItems[i].data.index, selectedItems[i]);\n selectedItems[i].data.index = null;\n }\n itemGroup.remove();\n\n this.props.onUpdateImage();\n }\n handleFlipHorizontal () {\n const selectedItems = getSelectedRootItems();\n if (isBitmap(this.props.format) && selectedItems.length === 0) {\n getRaster().canvas = flipBitmapHorizontal(getRaster().canvas);\n this.props.onUpdateImage();\n } else {\n this._handleFlip(-1, 1, selectedItems);\n }\n }\n handleFlipVertical () {\n const selectedItems = getSelectedRootItems();\n if (isBitmap(this.props.format) && selectedItems.length === 0) {\n getRaster().canvas = flipBitmapVertical(getRaster().canvas);\n this.props.onUpdateImage();\n } else {\n this._handleFlip(1, -1, selectedItems);\n }\n }\n handlePasteFromClipboard () {\n if (this.props.onPasteFromClipboard()) {\n this.props.onUpdateImage();\n }\n }\n handleDelete () {\n if (!this.props.selectedItems.length) {\n if (isBitmap(this.props.format)) {\n selectAllBitmap(this.props.clearSelectedItems);\n } else if (this.props.mode === Modes.RESHAPE) {\n selectAllSegments();\n } else {\n selectAllItems();\n }\n }\n if (deleteSelection(this.props.mode, this.props.onUpdateImage)) {\n this.props.setSelectedItems(this.props.format);\n }\n }\n render () {\n return (\n <ModeToolsComponent\n hasSelectedUncurvedPoints={this.hasSelectedUncurvedPoints()}\n hasSelectedUnpointedPoints={this.hasSelectedUnpointedPoints()}\n onCopyToClipboard={this.props.onCopyToClipboard}\n onCurvePoints={this.handleCurvePoints}\n onDelete={this.handleDelete}\n onFlipHorizontal={this.handleFlipHorizontal}\n onFlipVertical={this.handleFlipVertical}\n onPasteFromClipboard={this.handlePasteFromClipboard}\n onPointPoints={this.handlePointPoints}\n onUpdateImage={this.props.onUpdateImage}\n />\n );\n }\n}\n\nModeTools.propTypes = {\n clearSelectedItems: PropTypes.func.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)),\n mode: PropTypes.oneOf(Object.keys(Modes)),\n onCopyToClipboard: PropTypes.func.isRequired,\n onPasteFromClipboard: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n // Listen on selected items to update hasSelectedPoints\n selectedItems:\n PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setSelectedItems: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n format: state.scratchPaint.format,\n mode: state.scratchPaint.mode,\n selectedItems: state.scratchPaint.selectedItems\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n }\n});\n\nexport default CopyPasteHOC(connect(\n mapStateToProps,\n mapDispatchToProps\n)(ModeTools));\n","import paper from '@scratch/paper';\nimport Modes from '../../lib/modes';\nimport {styleShape} from '../style-path';\nimport {clearSelection} from '../selection';\nimport {getSquareDimensions} from '../math';\nimport BoundingBoxTool from '../selection-tools/bounding-box-tool';\nimport NudgeTool from '../selection-tools/nudge-tool';\n\n/**\n * Tool for drawing ovals.\n */\nclass OvalTool extends paper.Tool {\n static get TOLERANCE () {\n return 2;\n }\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, setCursor, onUpdateImage) {\n super();\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool = new BoundingBoxTool(\n Modes.OVAL,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage\n );\n const nudgeTool = new NudgeTool(Modes.OVAL, this.boundingBoxTool, onUpdateImage);\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = nudgeTool.onKeyUp;\n this.onKeyDown = nudgeTool.onKeyDown;\n\n this.oval = null;\n this.colorState = null;\n this.isBoundingBoxMode = null;\n this.active = false;\n }\n getHitOptions () {\n return {\n segments: true,\n stroke: true,\n curves: true,\n fill: true,\n guide: false,\n match: hitResult =>\n (hitResult.item.data && (hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle)) ||\n hitResult.item.selected, // Allow hits on bounding box and selected only\n tolerance: OvalTool.TOLERANCE / paper.view.zoom\n };\n }\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n }\n setColorState (colorState) {\n this.colorState = colorState;\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (this.boundingBoxTool.onMouseDown(\n event, false /* clone */, false /* multiselect */, false /* doubleClicked */, this.getHitOptions())) {\n this.isBoundingBoxMode = true;\n } else {\n this.isBoundingBoxMode = false;\n clearSelection(this.clearSelectedItems);\n this.oval = new paper.Shape.Ellipse({\n point: event.downPoint,\n size: 0\n });\n styleShape(this.oval, this.colorState);\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseDrag(event);\n return;\n }\n\n const downPoint = new paper.Point(event.downPoint.x, event.downPoint.y);\n const point = new paper.Point(event.point.x, event.point.y);\n const squareDimensions = getSquareDimensions(event.downPoint, event.point);\n if (event.modifiers.shift) {\n this.oval.size = squareDimensions.size.abs();\n } else {\n this.oval.size = downPoint.subtract(point);\n }\n\n if (event.modifiers.alt) {\n this.oval.position = downPoint;\n } else if (event.modifiers.shift) {\n this.oval.position = squareDimensions.position;\n } else {\n this.oval.position = downPoint.subtract(this.oval.size.multiply(0.5));\n }\n\n styleShape(this.oval, this.colorState);\n }\n handleMouseMove (event) {\n this.boundingBoxTool.onMouseMove(event, this.getHitOptions());\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseUp(event);\n this.isBoundingBoxMode = null;\n return;\n }\n\n if (this.oval) {\n if (Math.abs(this.oval.size.width * this.oval.size.height) < OvalTool.TOLERANCE / paper.view.zoom) {\n // Tiny oval created unintentionally?\n this.oval.remove();\n this.oval = null;\n } else {\n const ovalPath = this.oval.toPath(true /* insert */);\n this.oval.remove();\n this.oval = null;\n\n ovalPath.selected = true;\n this.setSelectedItems();\n this.onUpdateImage();\n }\n }\n this.active = false;\n }\n deactivateTool () {\n this.boundingBoxTool.deactivateTool();\n }\n}\n\nexport default OvalTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport ovalIcon from './oval.svg';\n\nconst OvalModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.oval}\n imgSrc={ovalIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nOvalModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default OvalModeComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport {MIXED} from '../helper/style-path';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport GradientTypes from '../lib/gradient-types';\n\nimport {changeFillColor, clearFillGradient, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeStrokeColor, clearStrokeGradient} from '../reducers/stroke-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {clearSelection, getSelectedLeafItems} from '../helper/selection';\nimport OvalTool from '../helper/tools/oval-tool';\nimport OvalModeComponent from '../components/oval-mode/oval-mode.jsx';\n\nclass OvalMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool',\n 'validateColorState'\n ]);\n }\n componentDidMount () {\n if (this.props.isOvalModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.colorState !== this.props.colorState) {\n this.tool.setColorState(nextProps.colorState);\n }\n if (this.tool && nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n\n if (nextProps.isOvalModeActive && !this.props.isOvalModeActive) {\n this.activateTool();\n } else if (!nextProps.isOvalModeActive && this.props.isOvalModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isOvalModeActive !== this.props.isOvalModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n this.validateColorState();\n\n this.tool = new OvalTool(\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage\n );\n this.tool.setColorState(this.props.colorState);\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n validateColorState () {\n // Make sure that at least one of fill/stroke is set, and that MIXED is not one of the colors.\n // If fill and stroke color are both missing, set fill to default and stroke to transparent.\n // If exactly one of fill or stroke color is set, set the other one to transparent.\n const {strokeWidth} = this.props.colorState;\n const fillColor1 = this.props.colorState.fillColor.primary;\n let fillColor2 = this.props.colorState.fillColor.secondary;\n let fillGradient = this.props.colorState.fillColor.gradientType;\n const strokeColor1 = this.props.colorState.strokeColor.primary;\n let strokeColor2 = this.props.colorState.strokeColor.secondary;\n let strokeGradient = this.props.colorState.strokeColor.gradientType;\n\n if (fillColor2 === MIXED) {\n this.props.clearFillGradient();\n fillColor2 = null;\n fillGradient = GradientTypes.SOLID;\n }\n if (strokeColor2 === MIXED) {\n this.props.clearStrokeGradient();\n strokeColor2 = null;\n strokeGradient = GradientTypes.SOLID;\n }\n\n const fillColorMissing = fillColor1 === MIXED ||\n (fillGradient === GradientTypes.SOLID && fillColor1 === null) ||\n (fillGradient !== GradientTypes.SOLID && fillColor1 === null && fillColor2 === null);\n const strokeColorMissing = strokeColor1 === MIXED ||\n strokeWidth === null ||\n strokeWidth === 0 ||\n (strokeGradient === GradientTypes.SOLID && strokeColor1 === null) ||\n (strokeGradient !== GradientTypes.SOLID && strokeColor1 === null && strokeColor2 === null);\n\n if (fillColorMissing && strokeColorMissing) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n this.props.clearFillGradient();\n this.props.onChangeStrokeColor(null);\n this.props.clearStrokeGradient();\n } else if (fillColorMissing && !strokeColorMissing) {\n this.props.onChangeFillColor(null);\n this.props.clearFillGradient();\n } else if (!fillColorMissing && strokeColorMissing) {\n this.props.onChangeStrokeColor(null);\n this.props.clearStrokeGradient();\n }\n }\n render () {\n return (\n <OvalModeComponent\n isSelected={this.props.isOvalModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nOvalMode.propTypes = {\n clearFillGradient: PropTypes.func.isRequired,\n clearStrokeGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n colorState: PropTypes.shape({\n fillColor: ColorStyleProptype,\n strokeColor: ColorStyleProptype,\n strokeWidth: PropTypes.number\n }).isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n isOvalModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onChangeStrokeColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n colorState: state.scratchPaint.color,\n isOvalModeActive: state.scratchPaint.mode === Modes.OVAL,\n selectedItems: state.scratchPaint.selectedItems\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearFillGradient: () => {\n dispatch(clearFillGradient());\n },\n clearStrokeGradient: () => {\n dispatch(clearStrokeGradient());\n },\n setCursor: cursorString => {\n dispatch(setCursor(cursorString));\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), false /* bitmapMode */));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.OVAL));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n },\n onChangeStrokeColor: strokeColor => {\n dispatch(changeStrokeColor(strokeColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(OvalMode);\n","import paper from '@scratch/paper';\nimport Modes from '../../lib/modes';\nimport {styleShape} from '../style-path';\nimport {clearSelection} from '../selection';\nimport {getSquareDimensions} from '../math';\nimport BoundingBoxTool from '../selection-tools/bounding-box-tool';\nimport NudgeTool from '../selection-tools/nudge-tool';\n\n/**\n * Tool for drawing rectangles.\n */\nclass RectTool extends paper.Tool {\n static get TOLERANCE () {\n return 2;\n }\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, setCursor, onUpdateImage) {\n super();\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool = new BoundingBoxTool(\n Modes.RECT,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage\n );\n const nudgeTool = new NudgeTool(Modes.RECT, this.boundingBoxTool, onUpdateImage);\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = nudgeTool.onKeyUp;\n this.onKeyDown = nudgeTool.onKeyDown;\n\n this.rect = null;\n this.colorState = null;\n this.isBoundingBoxMode = null;\n this.active = false;\n }\n getHitOptions () {\n return {\n segments: true,\n stroke: true,\n curves: true,\n fill: true,\n guide: false,\n match: hitResult =>\n (hitResult.item.data && (hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle)) ||\n hitResult.item.selected, // Allow hits on bounding box and selected only\n tolerance: RectTool.TOLERANCE / paper.view.zoom\n };\n }\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n }\n setColorState (colorState) {\n this.colorState = colorState;\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n if (this.boundingBoxTool.onMouseDown(\n event, false /* clone */, false /* multiselect */, false /* doubleClicked */, this.getHitOptions())) {\n this.isBoundingBoxMode = true;\n } else {\n this.isBoundingBoxMode = false;\n clearSelection(this.clearSelectedItems);\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseDrag(event);\n return;\n }\n\n if (this.rect) {\n this.rect.remove();\n }\n\n const rect = new paper.Rectangle(event.downPoint, event.point);\n const squareDimensions = getSquareDimensions(event.downPoint, event.point);\n if (event.modifiers.shift) {\n rect.size = squareDimensions.size.abs();\n }\n\n this.rect = new paper.Path.Rectangle(rect);\n if (event.modifiers.alt) {\n this.rect.position = event.downPoint;\n } else if (event.modifiers.shift) {\n this.rect.position = squareDimensions.position;\n } else {\n const dimensions = event.point.subtract(event.downPoint);\n this.rect.position = event.downPoint.add(dimensions.multiply(0.5));\n }\n\n styleShape(this.rect, this.colorState);\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.isBoundingBoxMode) {\n this.boundingBoxTool.onMouseUp(event);\n this.isBoundingBoxMode = null;\n return;\n }\n\n if (this.rect) {\n if (this.rect.area < RectTool.TOLERANCE / paper.view.zoom) {\n // Tiny rectangle created unintentionally?\n this.rect.remove();\n this.rect = null;\n } else {\n this.rect.selected = true;\n this.setSelectedItems();\n this.onUpdateImage();\n this.rect = null;\n }\n }\n this.active = false;\n }\n handleMouseMove (event) {\n this.boundingBoxTool.onMouseMove(event, this.getHitOptions());\n }\n deactivateTool () {\n this.boundingBoxTool.deactivateTool();\n }\n}\n\nexport default RectTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport rectIcon from './rectangle.svg';\n\nconst RectModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.rect}\n imgSrc={rectIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nRectModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default RectModeComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\nimport {MIXED} from '../helper/style-path';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport GradientTypes from '../lib/gradient-types';\n\nimport {changeFillColor, clearFillGradient, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeStrokeColor, clearStrokeGradient} from '../reducers/stroke-style';\nimport {changeMode} from '../reducers/modes';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {clearSelection, getSelectedLeafItems} from '../helper/selection';\nimport RectTool from '../helper/tools/rect-tool';\nimport RectModeComponent from '../components/rect-mode/rect-mode.jsx';\n\nclass RectMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool',\n 'validateColorState'\n ]);\n }\n componentDidMount () {\n if (this.props.isRectModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.colorState !== this.props.colorState) {\n this.tool.setColorState(nextProps.colorState);\n }\n if (this.tool && nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n\n if (nextProps.isRectModeActive && !this.props.isRectModeActive) {\n this.activateTool();\n } else if (!nextProps.isRectModeActive && this.props.isRectModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isRectModeActive !== this.props.isRectModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n clearSelection(this.props.clearSelectedItems);\n this.validateColorState();\n\n this.tool = new RectTool(\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage\n );\n this.tool.setColorState(this.props.colorState);\n this.tool.activate();\n }\n validateColorState () { // TODO move to shared class\n // Make sure that at least one of fill/stroke is set, and that MIXED is not one of the colors.\n // If fill and stroke color are both missing, set fill to default and stroke to transparent.\n // If exactly one of fill or stroke color is set, set the other one to transparent.\n const {strokeWidth} = this.props.colorState;\n const fillColor1 = this.props.colorState.fillColor.primary;\n let fillColor2 = this.props.colorState.fillColor.secondary;\n let fillGradient = this.props.colorState.fillColor.gradientType;\n const strokeColor1 = this.props.colorState.strokeColor.primary;\n let strokeColor2 = this.props.colorState.strokeColor.secondary;\n let strokeGradient = this.props.colorState.strokeColor.gradientType;\n\n if (fillColor2 === MIXED) {\n this.props.clearFillGradient();\n fillColor2 = null;\n fillGradient = GradientTypes.SOLID;\n }\n if (strokeColor2 === MIXED) {\n this.props.clearStrokeGradient();\n strokeColor2 = null;\n strokeGradient = GradientTypes.SOLID;\n }\n\n const fillColorMissing = fillColor1 === MIXED ||\n (fillGradient === GradientTypes.SOLID && fillColor1 === null) ||\n (fillGradient !== GradientTypes.SOLID && fillColor1 === null && fillColor2 === null);\n const strokeColorMissing = strokeColor1 === MIXED ||\n strokeWidth === null ||\n strokeWidth === 0 ||\n (strokeGradient === GradientTypes.SOLID && strokeColor1 === null) ||\n (strokeGradient !== GradientTypes.SOLID && strokeColor1 === null && strokeColor2 === null);\n\n if (fillColorMissing && strokeColorMissing) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n this.props.clearFillGradient();\n this.props.onChangeStrokeColor(null);\n this.props.clearStrokeGradient();\n } else if (fillColorMissing && !strokeColorMissing) {\n this.props.onChangeFillColor(null);\n this.props.clearFillGradient();\n } else if (!fillColorMissing && strokeColorMissing) {\n this.props.onChangeStrokeColor(null);\n this.props.clearStrokeGradient();\n }\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <RectModeComponent\n isSelected={this.props.isRectModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nRectMode.propTypes = {\n clearFillGradient: PropTypes.func.isRequired,\n clearStrokeGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n colorState: PropTypes.shape({\n fillColor: ColorStyleProptype,\n strokeColor: ColorStyleProptype,\n strokeWidth: PropTypes.number\n }).isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n isRectModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onChangeStrokeColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n colorState: state.scratchPaint.color,\n isRectModeActive: state.scratchPaint.mode === Modes.RECT,\n selectedItems: state.scratchPaint.selectedItems\n});\nconst mapDispatchToProps = dispatch => ({\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearFillGradient: () => {\n dispatch(clearFillGradient());\n },\n clearStrokeGradient: () => {\n dispatch(clearStrokeGradient());\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), false /* bitmapMode */));\n },\n setCursor: cursorString => {\n dispatch(setCursor(cursorString));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.RECT));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n },\n onChangeStrokeColor: strokeColor => {\n dispatch(changeStrokeColor(strokeColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(RectMode);\n","import {HANDLE_RATIO, snapDeltaToAngle} from '../math';\nimport {getActionBounds} from '../view';\nimport {clearSelection, getSelectedLeafItems, getSelectedSegments} from '../selection';\n\n/** Subtool of ReshapeTool for moving control points. */\nclass PointTool {\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, onUpdateImage) {\n /**\n * Deselection often does not happen until mouse up. If the mouse is dragged before\n * mouse up, deselection is cancelled. This variable keeps track of which paper.Item to deselect.\n */\n this.deselectOnMouseUp = null;\n /**\n * Delete control point does not happen until mouse up. If the mouse is dragged before\n * mouse up, delete is cancelled. This variable keeps track of the hitResult that triggers delete.\n */\n this.deleteOnMouseUp = null;\n /**\n * There are 2 cases for deselection: Deselect this, or deselect everything but this.\n * When invert deselect is true, deselect everything but the item in deselectOnMouseUp.\n */\n this.invertDeselect = false;\n this.selectedItems = null;\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.lastPoint = null;\n this.onUpdateImage = onUpdateImage;\n }\n\n /**\n * @param {!object} hitProperties Describes the mouse event\n * @param {!paper.HitResult} hitProperties.hitResult Data about the location of the mouse click\n * @param {?boolean} hitProperties.multiselect Whether to multiselect on mouse down (e.g. shift key held)\n * @param {?boolean} hitProperties.doubleClicked Whether this is the second click in a short time\n */\n onMouseDown (hitProperties) {\n // Remove point\n if (hitProperties.doubleClicked) {\n this.deleteOnMouseUp = hitProperties.hitResult;\n }\n if (hitProperties.hitResult.segment.selected) {\n // selected points with no handles get handles if selected again\n if (hitProperties.multiselect) {\n this.deselectOnMouseUp = hitProperties.hitResult.segment;\n } else {\n this.deselectOnMouseUp = hitProperties.hitResult.segment;\n this.invertDeselect = true;\n hitProperties.hitResult.segment.selected = true;\n }\n } else {\n if (!hitProperties.multiselect) {\n clearSelection(this.clearSelectedItems);\n }\n hitProperties.hitResult.segment.selected = true;\n }\n\n this.selectedItems = getSelectedLeafItems();\n }\n /**\n * @param {!object} hitProperties Describes the mouse event\n * @param {!paper.HitResult} hitProperties.hitResult Data about the location of the mouse click\n * @param {?boolean} hitProperties.multiselect Whether to multiselect on mouse down (e.g. shift key held)\n */\n addPoint (hitProperties) {\n const newSegment = hitProperties.hitResult.item.divideAt(hitProperties.hitResult.location);\n\n // If we're adding a point in the middle of a straight line, it won't be smooth by default, so smooth it\n if (!newSegment.hasHandles()) newSegment.smooth();\n\n hitProperties.hitResult.segment = newSegment;\n if (!hitProperties.multiselect) {\n clearSelection(this.clearSelectedItems);\n }\n newSegment.selected = true;\n }\n removePoint (hitResult) {\n const index = hitResult.segment.index;\n hitResult.item.removeSegment(index);\n\n // Adjust handles of curve before and curve after to account for new curve length\n const beforeSegment = hitResult.item.segments[index - 1];\n const afterSegment = hitResult.item.segments[index];\n const curveLength = beforeSegment ? beforeSegment.curve ? beforeSegment.curve.length : null : null;\n if (beforeSegment && beforeSegment.handleOut) {\n if (afterSegment) {\n beforeSegment.handleOut =\n beforeSegment.handleOut.multiply(curveLength * HANDLE_RATIO / beforeSegment.handleOut.length);\n } else {\n beforeSegment.handleOut = null;\n }\n }\n if (afterSegment && afterSegment.handleIn) {\n if (beforeSegment) {\n afterSegment.handleIn =\n afterSegment.handleIn.multiply(curveLength * HANDLE_RATIO / afterSegment.handleIn.length);\n } else {\n afterSegment.handleIn = null;\n }\n }\n }\n onMouseDrag (event) {\n // A click will deselect, but a drag will not\n this.deselectOnMouseUp = null;\n this.invertDeselect = false;\n this.deleteOnMouseUp = null;\n\n const point = event.point;\n const bounds = getActionBounds();\n point.x = Math.max(bounds.left, Math.min(point.x, bounds.right));\n point.y = Math.max(bounds.top, Math.min(point.y, bounds.bottom));\n\n if (!this.lastPoint) this.lastPoint = event.lastPoint;\n const dragVector = point.subtract(event.downPoint);\n const delta = point.subtract(this.lastPoint);\n this.lastPoint = point;\n\n const selectedSegments = getSelectedSegments();\n for (const seg of selectedSegments) {\n // add the point of the segment before the drag started\n // for later use in the snap calculation\n if (!seg.origPoint) {\n seg.origPoint = seg.point.clone();\n }\n\n if (event.modifiers.shift) {\n seg.point = seg.origPoint.add(snapDeltaToAngle(dragVector, Math.PI / 4));\n } else {\n seg.point = seg.point.add(delta);\n }\n }\n }\n onMouseUp () {\n this.lastPoint = null;\n\n // resetting the items and segments origin points for the next usage\n let moved = false;\n const selectedSegments = getSelectedSegments();\n for (const seg of selectedSegments) {\n if (seg.origPoint && !seg.equals(seg.origPoint)) {\n moved = true;\n }\n seg.origPoint = null;\n }\n\n // If no drag occurred between mouse down and mouse up, then we can go through with deselect\n // and delete\n if (this.deselectOnMouseUp) {\n if (this.invertDeselect) {\n clearSelection(this.clearSelectedItems);\n this.deselectOnMouseUp.selected = true;\n } else {\n this.deselectOnMouseUp.selected = false;\n }\n this.deselectOnMouseUp = null;\n this.invertDeselect = false;\n }\n if (this.deleteOnMouseUp) {\n this.removePoint(this.deleteOnMouseUp);\n }\n this.selectedItems = null;\n this.setSelectedItems();\n if (moved || this.deleteOnMouseUp) {\n this.deleteOnMouseUp = null;\n this.onUpdateImage();\n }\n }\n}\n\nexport default PointTool;\n","import {clearSelection, getSelectedLeafItems} from '../selection';\n\n/** Sub tool of the Reshape tool for moving handles, which adjust bezier curves. */\nclass HandleTool {\n /**\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n */\n constructor (setSelectedItems, clearSelectedItems, onUpdateImage) {\n this.hitType = null;\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.onUpdateImage = onUpdateImage;\n this.selectedItems = [];\n }\n /**\n * @param {!object} hitProperties Describes the mouse event\n * @param {?boolean} hitProperties.multiselect Whether to multiselect on mouse down (e.g. shift key held)\n * select the whole group.\n */\n onMouseDown (hitProperties) {\n if (!hitProperties.multiselect) {\n clearSelection(this.clearSelectedItems);\n }\n\n hitProperties.hitResult.segment.handleIn.selected = true;\n hitProperties.hitResult.segment.handleOut.selected = true;\n this.hitType = hitProperties.hitResult.type;\n }\n onMouseDrag (event) {\n this.selectedItems = getSelectedLeafItems();\n\n for (const item of this.selectedItems) {\n for (const seg of item.segments) {\n // add the point of the segment before the drag started\n // for later use in the snap calculation\n if (!seg.origPoint) {\n seg.origPoint = seg.point.clone();\n }\n\n if (seg.handleOut.selected && this.hitType === 'handle-out'){\n // if option is pressed or handles have been split,\n // they're no longer parallel and move independently\n if (event.modifiers.option ||\n !seg.handleOut.isColinear(seg.handleIn)) {\n seg.handleOut = seg.handleOut.add(event.delta);\n } else {\n seg.handleOut = seg.handleOut.add(event.delta);\n seg.handleIn = seg.handleOut.multiply(-seg.handleIn.length / seg.handleOut.length);\n }\n } else if (seg.handleIn.selected && this.hitType === 'handle-in') {\n // if option is pressed or handles have been split,\n // they're no longer parallel and move independently\n if (event.modifiers.option ||\n !seg.handleOut.isColinear(seg.handleIn)) {\n seg.handleIn = seg.handleIn.add(event.delta);\n\n } else {\n seg.handleIn = seg.handleIn.add(event.delta);\n seg.handleOut = seg.handleIn.multiply(-seg.handleOut.length / seg.handleIn.length);\n }\n }\n }\n }\n }\n onMouseUp () {\n // resetting the items and segments origin points for the next usage\n let moved = false;\n for (const item of this.selectedItems) {\n if (!item.segments) {\n return;\n }\n for (const seg of item.segments) {\n if (seg.origPoint && !seg.equals(seg.origPoint)) {\n moved = true;\n }\n seg.origPoint = null;\n }\n }\n if (moved) {\n this.setSelectedItems();\n this.onUpdateImage();\n }\n this.selectedItems = [];\n }\n}\n\nexport default HandleTool;\n","import paper from '@scratch/paper';\nimport log from '../../log/log';\nimport keyMirror from 'keymirror';\n\nimport Modes from '../../lib/modes';\nimport {isBoundsItem} from '../item';\nimport {hoverBounds, hoverItem} from '../guides';\nimport {sortItemsByZIndex} from '../math';\nimport {getSelectedLeafItems, getSelectedSegments} from '../selection';\nimport MoveTool from './move-tool';\nimport PointTool from './point-tool';\nimport HandleTool from './handle-tool';\nimport SelectionBoxTool from './selection-box-tool';\n\n/** Modes of the reshape tool, which can do many things depending on how it's used. */\nconst ReshapeModes = keyMirror({\n FILL: null,\n POINT: null,\n HANDLE: null,\n SELECTION_BOX: null\n});\n\n/**\n * paper.Tool to handle reshape mode, which allows manipulation of control points and\n * handles of path items. Can be used to select items within groups and points within items.\n * Reshape is made up of 4 tools:\n * - Selection box tool, which is activated by clicking an empty area. Draws a box and selects\n * points and curves inside it\n * - Move tool, which translates items\n * - Point tool, which translates, adds and removes points\n * - Handle tool, which translates handles, changing the shape of curves\n */\nclass ReshapeTool extends paper.Tool {\n /** Distance within which mouse is considered to be hitting an item */\n static get TOLERANCE () {\n return ReshapeTool.HANDLE_RADIUS + ReshapeTool.HANDLE_PADDING;\n }\n /**\n * Units of padding around the visible handle area that will still register clicks as \"touching the handle\"\n */\n static get HANDLE_PADDING () {\n return 1;\n }\n /**\n * Handles' radius, including the stroke\n */\n static get HANDLE_RADIUS () {\n return 5.25;\n }\n /** Clicks registered within this amount of time are registered as double clicks */\n static get DOUBLE_CLICK_MILLIS () {\n return 250;\n }\n /**\n * @param {function} setHoveredItem Callback to set the hovered item\n * @param {function} clearHoveredItem Callback to clear the hovered item\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n * @param {!Function} switchToTextTool A callback to call to switch to the text tool\n */\n constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateImage,\n switchToTextTool) {\n super();\n this.setHoveredItem = setHoveredItem;\n this.clearHoveredItem = clearHoveredItem;\n this.onUpdateImage = onUpdateImage;\n this.prevHoveredItemId = null;\n this.lastEvent = null;\n this.active = false;\n this.mode = ReshapeModes.SELECTION_BOX;\n this._modeMap = {};\n this._modeMap[ReshapeModes.FILL] =\n new MoveTool(Modes.RESHAPE, setSelectedItems, clearSelectedItems, onUpdateImage, switchToTextTool);\n this._modeMap[ReshapeModes.POINT] = new PointTool(setSelectedItems, clearSelectedItems, onUpdateImage);\n this._modeMap[ReshapeModes.HANDLE] = new HandleTool(setSelectedItems, clearSelectedItems, onUpdateImage);\n this._modeMap[ReshapeModes.SELECTION_BOX] =\n new SelectionBoxTool(Modes.RESHAPE, setSelectedItems, clearSelectedItems);\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = this.handleKeyUp;\n this.onKeyDown = this.handleKeyDown;\n\n // A handle's size is given in diameter, and each handle has a 2.5-pixel stroke that isn't part of its size:\n // https://github.com/LLK/paper.js/blob/a187e4c81cc63f3d48c5097b9a9fbddde9f057da/src/item/Item.js#L4480\n // Size the handles such that clicking on either the stroke or the handle itself will be registered as a drag\n paper.settings.handleSize = (ReshapeTool.HANDLE_RADIUS * 2) - 2.5;\n }\n /**\n * Returns the hit options for segments to use when conducting hit tests. Segments are only visible\n * when the shape is selected. Segments take precedence, since they are always over curves and need\n * to be grabbable. (Segments are the little circles)\n * @returns {object} See paper.Item.hitTest for definition of options\n */\n getSelectedSegmentHitOptions () {\n const hitOptions = {\n segments: true,\n tolerance: ReshapeTool.TOLERANCE / paper.view.zoom,\n match: hitResult => {\n if (hitResult.type !== 'segment') return false;\n if (hitResult.item.data && hitResult.item.data.noHover) return false;\n if (!hitResult.item.selected) return false;\n return true;\n }\n };\n return hitOptions;\n }\n /**\n * Returns the hit options for handles to use when conducting hit tests. Handles need to be done\n * separately because we want to ignore hidden handles, but we don't want hidden handles to negate\n * legitimate hits on other things (like if the handle is over part of the fill). (Handles are the diamonds)\n * @returns {object} See paper.Item.hitTest for definition of options\n */\n getHandleHitOptions () {\n const hitOptions = {\n handles: true,\n tolerance: ReshapeTool.TOLERANCE / paper.view.zoom,\n match: hitResult => {\n if (hitResult.item.data && hitResult.item.data.noHover) return false;\n // Only hit test against handles that are visible, that is,\n // their segment is selected\n if (!hitResult.segment || !hitResult.segment.selected) return false;\n // If the entire shape is selected, handles are hidden\n if (hitResult.item.fullySelected) return false;\n return true;\n }\n };\n return hitOptions;\n }\n /**\n * Returns the hit options for curves of selected objects, which take precedence over\n * unselected things and fills.\n * @returns {object} See paper.Item.hitTest for definition of options\n */\n getSelectedStrokeHitOptions () {\n const hitOptions = {\n segments: false,\n stroke: false,\n curves: true,\n handles: false,\n fill: false,\n guide: false,\n tolerance: ReshapeTool.TOLERANCE / paper.view.zoom,\n match: hitResult => {\n if (hitResult.type !== 'curve') return false;\n if (!hitResult.item.selected) return false;\n if (hitResult.item.data && hitResult.item.data.noHover) return false;\n return true;\n }\n };\n return hitOptions;\n }\n /**\n * Returns the hit options for fills and unselected strokes/curves to use when conducting hit tests.\n * @param {boolean} preselectedOnly True if we should only return results that are already\n * selected.\n * @returns {object} See paper.Item.hitTest for definition of options\n */\n getUnselectedAndFillHitOptions () {\n const hitOptions = {\n fill: true,\n stroke: true,\n curves: true,\n tolerance: ReshapeTool.TOLERANCE / paper.view.zoom,\n match: hitResult => {\n if (hitResult.item.data && hitResult.item.data.noHover) return false;\n return true;\n }\n };\n return hitOptions;\n }\n /**\n * To be called when the hovered item changes. When the select tool hovers over a\n * new item, it compares against this to see if a hover item change event needs to\n * be fired.\n * @param {paper.Item} prevHoveredItemId ID of the highlight item that indicates the mouse is\n * over a given item currently\n */\n setPrevHoveredItemId (prevHoveredItemId) {\n this.prevHoveredItemId = prevHoveredItemId;\n }\n /**\n * Given the point at which the mouse is, return the prioritized hit result, or null if nothing was hit.\n * @param {paper.Point} point Point to hit test on canvas\n * @returns {?paper.HitResult} hitResult\n */\n getHitResult (point) {\n // Prefer hits on segments to other types of hits, since segments always overlap curves.\n let hitResults =\n paper.project.hitTestAll(point, this.getSelectedSegmentHitOptions());\n if (!hitResults.length) {\n hitResults = paper.project.hitTestAll(point, this.getHandleHitOptions());\n }\n if (!hitResults.length) {\n hitResults = paper.project.hitTestAll(point, this.getSelectedStrokeHitOptions());\n }\n if (!hitResults.length) {\n hitResults = paper.project.hitTestAll(point, this.getUnselectedAndFillHitOptions());\n }\n if (!hitResults.length) {\n return null;\n }\n\n // Get highest z-index result\n let hitResult;\n for (const result of hitResults) {\n if (!hitResult || sortItemsByZIndex(hitResult.item, result.item) < 0) {\n hitResult = result;\n }\n }\n return hitResult;\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n this.clearHoveredItem();\n\n // Check if double clicked\n let doubleClicked = false;\n if (this.lastEvent) {\n if ((event.event.timeStamp - this.lastEvent.event.timeStamp) < ReshapeTool.DOUBLE_CLICK_MILLIS) {\n doubleClicked = true;\n } else {\n doubleClicked = false;\n }\n }\n this.lastEvent = event;\n\n const hitResult = this.getHitResult(event.point);\n if (!hitResult) {\n this._modeMap[ReshapeModes.SELECTION_BOX].onMouseDown(event.modifiers.shift);\n return;\n }\n\n const hitProperties = {\n hitResult: hitResult,\n clone: event.modifiers.alt,\n multiselect: event.modifiers.shift,\n doubleClicked: doubleClicked,\n subselect: true\n };\n\n // If item is not yet selected, don't behave differently depending on if they clicked a segment\n // (since those were invisible), just select the whole thing as if they clicked the fill.\n if (!hitResult.item.selected ||\n hitResult.type === 'fill' ||\n hitResult.type === 'stroke' ||\n (hitResult.type !== 'segment' && doubleClicked)) {\n this.mode = ReshapeModes.FILL;\n this._modeMap[this.mode].onMouseDown(hitProperties);\n } else if (hitResult.type === 'segment') {\n this.mode = ReshapeModes.POINT;\n this._modeMap[this.mode].onMouseDown(hitProperties);\n } else if (\n hitResult.type === 'curve') {\n this.mode = ReshapeModes.POINT;\n this._modeMap[this.mode].addPoint(hitProperties);\n this.onUpdateImage();\n this._modeMap[this.mode].onMouseDown(hitProperties);\n } else if (\n hitResult.type === 'handle-in' ||\n hitResult.type === 'handle-out') {\n this.mode = ReshapeModes.HANDLE;\n this._modeMap[this.mode].onMouseDown(hitProperties);\n } else {\n log.warn(`Unhandled hit result type: ${hitResult.type}`);\n this.mode = ReshapeModes.FILL;\n this._modeMap[this.mode].onMouseDown(hitProperties);\n }\n }\n handleMouseMove (event) {\n const hitResult = this.getHitResult(event.point);\n let hoveredItem;\n\n if (hitResult) {\n const item = hitResult.item;\n if (item.selected) {\n hoveredItem = null;\n } else if (isBoundsItem(item)) {\n hoveredItem = hoverBounds(item);\n } else {\n hoveredItem = hoverItem(item);\n }\n }\n\n if ((!hoveredItem && this.prevHoveredItemId) || // There is no longer a hovered item\n (hoveredItem && !this.prevHoveredItemId) || // There is now a hovered item\n (hoveredItem && this.prevHoveredItemId &&\n hoveredItem.id !== this.prevHoveredItemId)) { // hovered item changed\n this.setHoveredItem(hoveredItem ? hoveredItem.id : null);\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n this._modeMap[this.mode].onMouseDrag(event);\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n if (this.mode === ReshapeModes.SELECTION_BOX) {\n this._modeMap[this.mode].onMouseUpVector(event);\n } else {\n this._modeMap[this.mode].onMouseUp(event);\n }\n this.mode = ReshapeModes.SELECTION_BOX;\n this.active = false;\n }\n handleKeyDown (event) {\n if (event.event.target instanceof HTMLInputElement) {\n // Ignore nudge if a text input field is focused\n return;\n }\n\n const nudgeAmount = 1 / paper.view.zoom;\n const selected = getSelectedLeafItems();\n if (selected.length === 0) return;\n\n let translation;\n if (event.key === 'up') {\n translation = new paper.Point(0, -nudgeAmount);\n } else if (event.key === 'down') {\n translation = new paper.Point(0, nudgeAmount);\n } else if (event.key === 'left') {\n translation = new paper.Point(-nudgeAmount, 0);\n } else if (event.key === 'right') {\n translation = new paper.Point(nudgeAmount, 0);\n }\n\n if (translation) {\n const segments = getSelectedSegments();\n // If no segments are selected, translate selected paths\n if (segments.length === 0) {\n for (const item of selected) {\n item.translate(translation);\n }\n } else { // Translate segments\n for (const seg of segments) {\n seg.point = seg.point.add(translation);\n }\n }\n }\n }\n handleKeyUp (event) {\n const selected = getSelectedLeafItems();\n if (selected.length === 0) return;\n\n if (event.key === 'up' || event.key === 'down' || event.key === 'left' || event.key === 'right') {\n this.onUpdateImage();\n }\n }\n deactivateTool () {\n paper.settings.handleSize = 0;\n this.clearHoveredItem();\n this.setHoveredItem = null;\n this.clearHoveredItem = null;\n this.onUpdateImage = null;\n this.lastEvent = null;\n }\n}\n\nexport default ReshapeTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport messages from '../../lib/messages.js';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\n\nimport reshapeIcon from './reshape.svg';\n\nconst ReshapeModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.reshape}\n imgSrc={reshapeIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nReshapeModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default ReshapeModeComponent;\n","import PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\n\nimport {changeMode} from '../reducers/modes';\nimport {clearHoveredItem, setHoveredItem} from '../reducers/hover';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {getSelectedLeafItems} from '../helper/selection';\n\nimport ReshapeTool from '../helper/selection-tools/reshape-tool';\nimport ReshapeModeComponent from '../components/reshape-mode/reshape-mode.jsx';\n\nclass ReshapeMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isReshapeModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {\n this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);\n }\n\n if (nextProps.isReshapeModeActive && !this.props.isReshapeModeActive) {\n this.activateTool();\n } else if (!nextProps.isReshapeModeActive && this.props.isReshapeModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isReshapeModeActive !== this.props.isReshapeModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n this.tool = new ReshapeTool(\n this.props.setHoveredItem,\n this.props.clearHoveredItem,\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.onUpdateImage,\n this.props.switchToTextTool\n );\n this.tool.setPrevHoveredItemId(this.props.hoveredItemId);\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n this.hitResult = null;\n }\n render () {\n return (\n <ReshapeModeComponent\n isSelected={this.props.isReshapeModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nReshapeMode.propTypes = {\n clearHoveredItem: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n hoveredItemId: PropTypes.number,\n isReshapeModeActive: PropTypes.bool.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n setHoveredItem: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n switchToTextTool: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n isReshapeModeActive: state.scratchPaint.mode === Modes.RESHAPE,\n hoveredItemId: state.scratchPaint.hoveredItemId\n});\nconst mapDispatchToProps = dispatch => ({\n setHoveredItem: hoveredItemId => {\n dispatch(setHoveredItem(hoveredItemId));\n },\n clearHoveredItem: () => {\n dispatch(clearHoveredItem());\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), false /* bitmapMode */));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.RESHAPE));\n },\n switchToTextTool: () => {\n dispatch(changeMode(Modes.TEXT));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(ReshapeMode);\n","import Modes from '../../lib/modes';\n\nimport {getHoveredItem} from '../hover';\nimport {selectRootItem} from '../selection';\nimport BoundingBoxTool from './bounding-box-tool';\nimport NudgeTool from './nudge-tool';\nimport SelectionBoxTool from './selection-box-tool';\nimport paper from '@scratch/paper';\n\n/**\n * paper.Tool that handles select mode. This is made up of 2 subtools.\n * - The selection box tool is active when the user clicks an empty space and drags.\n * It selects all items in the rectangle.\n * - The bounding box tool is active if the user clicks on a non-empty space. It handles\n * reshaping the item that was clicked.\n */\nclass SelectTool extends paper.Tool {\n /** The distance within which mouse events count as a hit against an item */\n static get TOLERANCE () {\n return 2;\n }\n /** Clicks registered within this amount of time are registered as double clicks */\n static get DOUBLE_CLICK_MILLIS () {\n return 250;\n }\n /**\n * @param {function} setHoveredItem Callback to set the hovered item\n * @param {function} clearHoveredItem Callback to clear the hovered item\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n * @param {!Function} switchToTextTool A callback to call to switch to the text tool\n */\n constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, setCursor, onUpdateImage,\n switchToTextTool) {\n super();\n this.setHoveredItem = setHoveredItem;\n this.clearHoveredItem = clearHoveredItem;\n this.onUpdateImage = onUpdateImage;\n this.boundingBoxTool = new BoundingBoxTool(\n Modes.SELECT,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage,\n switchToTextTool\n );\n const nudgeTool = new NudgeTool(Modes.SELECT, this.boundingBoxTool, onUpdateImage);\n this.selectionBoxTool = new SelectionBoxTool(Modes.SELECT, setSelectedItems, clearSelectedItems);\n this.selectionBoxMode = false;\n this.prevHoveredItemId = null;\n this.active = false;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseMove = this.handleMouseMove;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseUp = this.handleMouseUp;\n this.onKeyUp = nudgeTool.onKeyUp;\n this.onKeyDown = nudgeTool.onKeyDown;\n\n selectRootItem();\n setSelectedItems();\n this.boundingBoxTool.setSelectionBounds();\n }\n /**\n * To be called when the hovered item changes. When the select tool hovers over a\n * new item, it compares against this to see if a hover item change event needs to\n * be fired.\n * @param {paper.Item} prevHoveredItemId ID of the highlight item that indicates the mouse is\n * over a given item currently\n */\n setPrevHoveredItemId (prevHoveredItemId) {\n this.prevHoveredItemId = prevHoveredItemId;\n }\n /**\n * Should be called if the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n }\n /**\n * Returns the hit options to use when conducting hit tests.\n * @param {boolean} preselectedOnly True if we should only return results that are already\n * selected.\n * @returns {object} See paper.Item.hitTest for definition of options\n */\n getHitOptions (preselectedOnly) {\n // Tolerance needs to be scaled when the view is zoomed in in order to represent the same\n // distance for the user to move the mouse.\n const hitOptions = {\n segments: true,\n stroke: true,\n curves: true,\n fill: true,\n guide: false,\n tolerance: SelectTool.TOLERANCE / paper.view.zoom,\n match: hitResult => {\n // Don't match helper items, unless they are handles.\n if (!hitResult.item.data || !hitResult.item.data.isHelperItem) return true;\n return hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle;\n }\n };\n if (preselectedOnly) {\n hitOptions.selected = true;\n }\n return hitOptions;\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n this.clearHoveredItem();\n\n // Check if double clicked\n let doubleClicked = false;\n if (this.lastEvent) {\n if ((event.event.timeStamp - this.lastEvent.event.timeStamp) < SelectTool.DOUBLE_CLICK_MILLIS) {\n doubleClicked = true;\n } else {\n doubleClicked = false;\n }\n }\n this.lastEvent = event;\n\n // If bounding box tool does not find an item that was hit, use selection box tool.\n if (!this.boundingBoxTool\n .onMouseDown(\n event,\n event.modifiers.alt,\n event.modifiers.shift,\n doubleClicked,\n this.getHitOptions(false /* preseelectedOnly */))) {\n this.selectionBoxMode = true;\n this.selectionBoxTool.onMouseDown(event.modifiers.shift);\n }\n }\n handleMouseMove (event) {\n const hoveredItem = getHoveredItem(event, this.getHitOptions());\n if ((!hoveredItem && this.prevHoveredItemId) || // There is no longer a hovered item\n (hoveredItem && !this.prevHoveredItemId) || // There is now a hovered item\n (hoveredItem && this.prevHoveredItemId &&\n hoveredItem.id !== this.prevHoveredItemId)) { // hovered item changed\n this.setHoveredItem(hoveredItem ? hoveredItem.id : null);\n }\n\n if (!this.selectionBoxMode) {\n this.boundingBoxTool.onMouseMove(event, this.getHitOptions(false));\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.selectionBoxMode) {\n this.selectionBoxTool.onMouseDrag(event);\n } else {\n this.boundingBoxTool.onMouseDrag(event);\n }\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.selectionBoxMode) {\n this.selectionBoxTool.onMouseUpVector(event);\n } else {\n this.boundingBoxTool.onMouseUp(event, this.getHitOptions(false));\n }\n this.selectionBoxMode = false;\n this.active = false;\n }\n deactivateTool () {\n this.clearHoveredItem();\n this.boundingBoxTool.deactivateTool();\n this.setHoveredItem = null;\n this.clearHoveredItem = null;\n this.onUpdateImage = null;\n this.boundingBoxTool = null;\n this.selectionBoxTool = null;\n }\n}\n\nexport default SelectTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport messages from '../../lib/messages.js';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\n\nimport selectIcon from './select.svg';\n\nconst SelectModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.select}\n imgSrc={selectIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nSelectModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default SelectModeComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Modes from '../lib/modes';\n\nimport {changeMode} from '../reducers/modes';\nimport {clearHoveredItem, setHoveredItem} from '../reducers/hover';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport SelectTool from '../helper/selection-tools/select-tool';\nimport SelectModeComponent from '../components/select-mode/select-mode.jsx';\n\nclass SelectMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isSelectModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {\n this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);\n }\n if (this.tool && nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n\n if (nextProps.isSelectModeActive && !this.props.isSelectModeActive) {\n this.activateTool();\n } else if (!nextProps.isSelectModeActive && this.props.isSelectModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isSelectModeActive !== this.props.isSelectModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool () {\n this.tool = new SelectTool(\n this.props.setHoveredItem,\n this.props.clearHoveredItem,\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage,\n this.props.switchToTextTool\n );\n this.tool.activate();\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n <SelectModeComponent\n isSelected={this.props.isSelectModeActive}\n onMouseDown={this.props.handleMouseDown}\n />\n );\n }\n}\n\nSelectMode.propTypes = {\n clearHoveredItem: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n handleMouseDown: PropTypes.func.isRequired,\n hoveredItemId: PropTypes.number,\n isSelectModeActive: PropTypes.bool.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setHoveredItem: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n switchToTextTool: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = state => ({\n isSelectModeActive: state.scratchPaint.mode === Modes.SELECT,\n hoveredItemId: state.scratchPaint.hoveredItemId,\n selectedItems: state.scratchPaint.selectedItems\n});\nconst mapDispatchToProps = dispatch => ({\n setHoveredItem: hoveredItemId => {\n dispatch(setHoveredItem(hoveredItemId));\n },\n clearHoveredItem: () => {\n dispatch(clearHoveredItem());\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), false /* bitmapMode */));\n },\n setCursor: cursorString => {\n dispatch(setCursor(cursorString));\n },\n handleMouseDown: () => {\n dispatch(changeMode(Modes.SELECT));\n },\n switchToTextTool: () => {\n dispatch(changeMode(Modes.TEXT));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(SelectMode);\n","import {connect} from 'react-redux';\nimport {defineMessages} from 'react-intl';\n\nimport {changeColorIndex} from '../reducers/color-index';\nimport {changeStrokeColor, changeStrokeColor2, changeStrokeGradientType} from '../reducers/stroke-style';\nimport {changeStrokeWidth} from '../reducers/stroke-width';\nimport {openStrokeColor, closeStrokeColor} from '../reducers/modals';\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {setSelectedItems} from '../reducers/selected-items';\nimport Modes, {GradientToolsModes} from '../lib/modes';\nimport {isBitmap} from '../lib/format';\n\nimport makeColorIndicator from './color-indicator.jsx';\n\nconst messages = defineMessages({\n label: {\n id: 'paint.paintEditor.stroke',\n description: 'Label for the color picker for the outline color',\n defaultMessage: 'Outline'\n }\n});\n\nconst StrokeColorIndicator = makeColorIndicator(messages.label, true);\n\nconst mapStateToProps = state => ({\n colorIndex: state.scratchPaint.fillMode.colorIndex,\n disabled: state.scratchPaint.mode === Modes.BRUSH ||\n state.scratchPaint.mode === Modes.TEXT ||\n state.scratchPaint.mode === Modes.FILL,\n color: state.scratchPaint.color.strokeColor.primary,\n color2: state.scratchPaint.color.strokeColor.secondary,\n fillBitmapShapes: state.scratchPaint.fillBitmapShapes,\n colorModalVisible: state.scratchPaint.modals.strokeColor,\n format: state.scratchPaint.format,\n gradientType: state.scratchPaint.color.strokeColor.gradientType,\n isEyeDropping: state.scratchPaint.color.eyeDropper.active,\n mode: state.scratchPaint.mode,\n shouldShowGradientTools: state.scratchPaint.mode in GradientToolsModes,\n textEditTarget: state.scratchPaint.textEditTarget\n});\n\nconst mapDispatchToProps = dispatch => ({\n onChangeColorIndex: index => {\n dispatch(changeColorIndex(index));\n },\n onChangeColor: (strokeColor, index) => {\n if (index === 0) {\n dispatch(changeStrokeColor(strokeColor));\n } else if (index === 1) {\n dispatch(changeStrokeColor2(strokeColor));\n }\n },\n onChangeStrokeWidth: strokeWidth => {\n dispatch(changeStrokeWidth(strokeWidth));\n },\n onOpenColor: () => {\n dispatch(openStrokeColor());\n },\n onCloseColor: () => {\n dispatch(closeStrokeColor());\n },\n onChangeGradientType: gradientType => {\n dispatch(changeStrokeGradientType(gradientType));\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(StrokeColorIndicator);\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport Input from './forms/input.jsx';\nimport InputGroup from './input-group/input-group.jsx';\nimport LiveInputHOC from './forms/live-input-hoc.jsx';\n\nimport {MAX_STROKE_WIDTH} from '../reducers/stroke-width';\n\nconst LiveInput = LiveInputHOC(Input);\nconst StrokeWidthIndicatorComponent = props => (\n <InputGroup disabled={props.disabled}>\n <LiveInput\n range\n small\n disabled={props.disabled}\n max={MAX_STROKE_WIDTH}\n min=\"0\"\n type=\"number\"\n value={props.strokeWidth ? props.strokeWidth : 0}\n onSubmit={props.onChangeStrokeWidth}\n />\n </InputGroup>\n);\n\nStrokeWidthIndicatorComponent.propTypes = {\n disabled: PropTypes.bool.isRequired,\n onChangeStrokeWidth: PropTypes.func.isRequired,\n strokeWidth: PropTypes.number\n};\n\nexport default StrokeWidthIndicatorComponent;\n","import {connect} from 'react-redux';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport bindAll from 'lodash.bindall';\nimport parseColor from 'parse-color';\nimport {changeStrokeColor, changeStrokeColor2, changeStrokeGradientType} from '../reducers/stroke-style';\nimport {changeStrokeWidth} from '../reducers/stroke-width';\nimport StrokeWidthIndicatorComponent from '../components/stroke-width-indicator.jsx';\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {\n applyColorToSelection, applyStrokeWidthToSelection, getColorsFromSelection, MIXED\n} from '../helper/style-path';\nimport GradientTypes from '../lib/gradient-types';\nimport Modes from '../lib/modes';\nimport Formats, {isBitmap} from '../lib/format';\n\nclass StrokeWidthIndicator extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleChangeStrokeWidth'\n ]);\n }\n handleChangeStrokeWidth (newWidth) {\n let changed = applyStrokeWidthToSelection(newWidth, this.props.textEditTarget);\n if ((!this.props.strokeWidth || this.props.strokeWidth === 0) && newWidth > 0) {\n const currentColorState = getColorsFromSelection(getSelectedLeafItems(), isBitmap(this.props.format));\n\n // Color counts as null if either both colors are null or the primary color is null and it's solid\n // TODO: consolidate this check in one place\n const wasNull = currentColorState.strokeColor === null &&\n (currentColorState.strokeColor2 === null ||\n currentColorState.strokeGradientType === GradientTypes.SOLID);\n\n if (wasNull) {\n changed = applyColorToSelection(\n '#000',\n 0, // colorIndex,\n true, // isSolidGradient\n true, // applyToStroke\n this.props.textEditTarget) ||\n changed;\n // If there's no previous stroke color, default to solid black\n this.props.onChangeStrokeGradientType(GradientTypes.SOLID);\n this.props.onChangeStrokeColor('#000');\n } else if (currentColorState.strokeColor !== MIXED) {\n // Set color state from the selected item's stroke color\n this.props.onChangeStrokeGradientType(currentColorState.strokeGradientType);\n this.props.onChangeStrokeColor(parseColor(currentColorState.strokeColor).hex);\n this.props.onChangeStrokeColor2(parseColor(currentColorState.strokeColor2).hex);\n }\n }\n this.props.onChangeStrokeWidth(newWidth);\n if (changed) this.props.onUpdateImage();\n }\n render () {\n return (\n <StrokeWidthIndicatorComponent\n disabled={this.props.disabled}\n strokeWidth={this.props.strokeWidth}\n onChangeStrokeWidth={this.handleChangeStrokeWidth}\n />\n );\n }\n}\n\nconst mapStateToProps = state => ({\n disabled: state.scratchPaint.mode === Modes.BRUSH ||\n state.scratchPaint.mode === Modes.TEXT ||\n state.scratchPaint.mode === Modes.FILL,\n format: state.scratchPaint.format,\n strokeWidth: state.scratchPaint.color.strokeWidth,\n textEditTarget: state.scratchPaint.textEditTarget\n});\nconst mapDispatchToProps = dispatch => ({\n onChangeStrokeColor: strokeColor => {\n dispatch(changeStrokeColor(strokeColor));\n },\n onChangeStrokeColor2: strokeColor => {\n dispatch(changeStrokeColor2(strokeColor));\n },\n onChangeStrokeGradientType: strokeColor => {\n dispatch(changeStrokeGradientType(strokeColor));\n },\n onChangeStrokeWidth: strokeWidth => {\n dispatch(changeStrokeWidth(strokeWidth));\n }\n});\n\nStrokeWidthIndicator.propTypes = {\n disabled: PropTypes.bool.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)),\n onChangeStrokeColor: PropTypes.func.isRequired,\n onChangeStrokeColor2: PropTypes.func.isRequired,\n onChangeStrokeGradientType: PropTypes.func.isRequired,\n onChangeStrokeWidth: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n strokeWidth: PropTypes.number,\n textEditTarget: PropTypes.number\n};\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(StrokeWidthIndicator);\n","import paper from '@scratch/paper';\nimport Modes from '../../lib/modes';\nimport {clearSelection, getSelectedLeafItems} from '../selection';\nimport BoundingBoxTool from '../selection-tools/bounding-box-tool';\nimport NudgeTool from '../selection-tools/nudge-tool';\nimport {hoverBounds} from '../guides';\nimport {getRaster} from '../layer';\n\n/**\n * Tool for adding text. Text elements have limited editability; they can't be reshaped,\n * drawn on or erased. This way they can preserve their ability to have the text edited.\n */\nclass TextTool extends paper.Tool {\n static get TOLERANCE () {\n return 2;\n }\n static get TEXT_EDIT_MODE () {\n return 'TEXT_EDIT_MODE';\n }\n static get SELECT_MODE () {\n return 'SELECT_MODE';\n }\n /** Clicks registered within this amount of time are registered as double clicks */\n static get DOUBLE_CLICK_MILLIS () {\n return 250;\n }\n /** Typing with no pauses longer than this amount of type will count as 1 action */\n static get TYPING_TIMEOUT_MILLIS () {\n return 1000;\n }\n static get TEXT_PADDING () {\n return 8;\n }\n /**\n * @param {HTMLTextAreaElement} textAreaElement dom element for the editable text field\n * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state\n * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state\n * @param {function} setCursor Callback to set the visible mouse cursor\n * @param {!Function} onUpdateImage A callback to call when the image visibly changes\n * @param {!Function} setTextEditTarget Call to set text editing target whenever text editing is active\n * @param {!Function} changeFont Call to change the font in the dropdown\n * @param {?boolean} isBitmap True if text should be rasterized once it's deselected\n */\n constructor (textAreaElement, setSelectedItems, clearSelectedItems, setCursor, onUpdateImage, setTextEditTarget,\n changeFont, isBitmap) {\n super();\n this.element = textAreaElement;\n this.setSelectedItems = setSelectedItems;\n this.clearSelectedItems = clearSelectedItems;\n this.onUpdateImage = onUpdateImage;\n this.setTextEditTarget = setTextEditTarget;\n this.changeFont = changeFont;\n const paintMode = isBitmap ? Modes.BIT_TEXT : Modes.TEXT;\n this.boundingBoxTool = new BoundingBoxTool(\n paintMode,\n setSelectedItems,\n clearSelectedItems,\n setCursor,\n onUpdateImage\n );\n this.nudgeTool = new NudgeTool(paintMode, this.boundingBoxTool, onUpdateImage);\n this.isBitmap = isBitmap;\n\n // We have to set these functions instead of just declaring them because\n // paper.js tools hook up the listeners in the setter functions.\n this.onMouseDown = this.handleMouseDown;\n this.onMouseDrag = this.handleMouseDrag;\n this.onMouseUp = this.handleMouseUp;\n this.onMouseMove = this.handleMouseMove;\n this.onKeyUp = this.handleKeyUp;\n this.onKeyDown = this.handleKeyDown;\n\n this.textBox = null;\n this.guide = null;\n this.colorState = null;\n this.mode = null;\n this.active = false;\n this.lastTypeEvent = null;\n this.lastEvent = null;\n\n // If text selected and then activate this tool, switch to text edit mode for that text\n // If double click on text while in select mode, does mode change to text mode? Text fully selected by default\n }\n getBoundingBoxHitOptions () {\n return {\n segments: true,\n stroke: true,\n curves: true,\n fill: true,\n guide: false,\n match: hitResult =>\n (hitResult.item.data && (hitResult.item.data.isScaleHandle || hitResult.item.data.isRotHandle)) ||\n hitResult.item.selected, // Allow hits on bounding box and selected only\n tolerance: TextTool.TOLERANCE / paper.view.zoom\n };\n }\n getTextEditHitOptions () {\n return {\n class: paper.PointText,\n segments: true,\n stroke: true,\n curves: true,\n fill: true,\n guide: false,\n match: hitResult => hitResult.item &&\n !(hitResult.item.data && hitResult.item.data.isHelperItem) &&\n !hitResult.item.selected, // Unselected only\n tolerance: TextTool.TOLERANCE / paper.view.zoom\n };\n }\n /**\n * Called when the selection changes to update the bounds of the bounding box.\n * @param {Array<paper.Item>} selectedItems Array of selected items.\n */\n onSelectionChanged (selectedItems) {\n this.boundingBoxTool.onSelectionChanged(selectedItems);\n if ((!this.textBox || !this.textBox.parent) &&\n selectedItems && selectedItems.length === 1 && selectedItems[0] instanceof paper.PointText) {\n // Infer that an undo occurred and get back the active text\n this.textBox = selectedItems[0];\n this.mode = TextTool.SELECT_MODE;\n }\n }\n setFont (font) {\n this.font = font;\n if (this.textBox) {\n this.textBox.font = font;\n }\n const selected = getSelectedLeafItems();\n for (const item of selected) {\n if (item instanceof paper.PointText) {\n item.font = font;\n }\n }\n this.element.style.fontFamily = font;\n this.setSelectedItems();\n }\n // Allow other tools to cancel text edit mode\n onTextEditCancelled () {\n if (this.mode !== TextTool.TEXT_EDIT_MODE) {\n return;\n }\n this.endTextEdit();\n this.beginSelect();\n }\n /**\n * Called when the view matrix changes\n * @param {paper.Matrix} viewMtx applied to paper.view\n */\n onViewBoundsChanged (viewMtx) {\n if (this.mode !== TextTool.TEXT_EDIT_MODE) {\n return;\n }\n this.calculateMatrix(viewMtx);\n }\n calculateMatrix (viewMtx) {\n const textBoxMtx = this.textBox.matrix;\n const calculated = new paper.Matrix();\n\n // In RTL, the element is moved relative to its parent's right edge instead of its left\n // edge. We need to correct for this in order for the element to overlap the object in paper.\n let tx = 0;\n if (this.rtl && this.element.parentElement) {\n tx = -this.element.parentElement.clientWidth;\n }\n // The transform origin in paper is x at justification side, y at the baseline of the text.\n // The offset from (0, 0) to the upper left corner is recorded by internalBounds\n // (so this.textBox.internalBounds.y is negative).\n // Move the transform origin down to the text baseline to match paper\n this.element.style.transformOrigin = `${-this.textBox.internalBounds.x}px ${-this.textBox.internalBounds.y}px`;\n // Start by translating the element up so that its (0, 0) is now at the text baseline, like in paper\n calculated.translate(tx, this.textBox.internalBounds.y);\n calculated.append(viewMtx);\n calculated.append(textBoxMtx);\n this.element.style.transform = `matrix(${calculated.a}, ${calculated.b}, ${calculated.c}, ${calculated.d},\n ${calculated.tx}, ${calculated.ty})`;\n }\n setColorState (colorState) {\n this.colorState = colorState;\n }\n /** @param {boolean} isRtl True if paint editor is in right-to-left layout (e.g. Hebrew language) */\n setRtl (isRtl) {\n this.rtl = isRtl;\n }\n handleMouseMove (event) {\n const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions());\n if (hitResults.length) {\n document.body.style.cursor = 'text';\n } else {\n document.body.style.cursor = 'auto';\n }\n this.boundingBoxTool.onMouseMove(event, this.getBoundingBoxHitOptions());\n }\n handleMouseDown (event) {\n if (event.event.button > 0) return; // only first mouse button\n this.active = true;\n\n // Check if double clicked\n const doubleClicked = this.lastEvent &&\n (event.event.timeStamp - this.lastEvent.event.timeStamp) < TextTool.DOUBLE_CLICK_MILLIS;\n this.lastEvent = event;\n if (doubleClicked &&\n this.mode === TextTool.SELECT_MODE &&\n this.textBox.hitTest(event.point)) {\n // Double click in select mode moves you to text edit mode\n this.endSelect();\n this.beginTextEdit(this.textBox);\n this.element.select();\n return;\n }\n\n // In select mode staying in select mode\n if (this.boundingBoxTool.onMouseDown(\n event, false /* clone */, false /* multiselect */, false /* doubleClicked */,\n this.getBoundingBoxHitOptions())) {\n return;\n }\n\n // We clicked away from the item, so end the current mode\n const lastMode = this.mode;\n if (this.mode === TextTool.SELECT_MODE) {\n this.endSelect();\n if (this.isBitmap) {\n this.commitText();\n }\n } else if (this.mode === TextTool.TEXT_EDIT_MODE) {\n this.endTextEdit();\n }\n\n const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions());\n if (hitResults.length) {\n // Clicking a different text item to begin text edit mode on that item\n this.beginTextEdit(hitResults[0].item);\n } else if (lastMode === TextTool.TEXT_EDIT_MODE) {\n // In text mode clicking away to begin select mode\n this.beginSelect();\n } else {\n // In no mode or select mode clicking away to begin text edit mode\n this.textBox = new paper.PointText({\n point: event.point,\n content: '',\n font: this.font,\n fontSize: 40,\n // TODO: style using gradient\n // https://github.com/LLK/scratch-paint/issues/1164\n fillColor: this.colorState.fillColor.primary,\n // Default leading for both the HTML text area and paper.PointText\n // is 120%, but for some reason they are slightly off from each other.\n // This value was obtained experimentally.\n leading: 46.15\n });\n this.beginTextEdit(this.textBox);\n }\n }\n handleMouseDrag (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.mode === TextTool.SELECT_MODE) {\n this.boundingBoxTool.onMouseDrag(event);\n return;\n }\n }\n handleMouseUp (event) {\n if (event.event.button > 0 || !this.active) return; // only first mouse button\n\n if (this.mode === TextTool.SELECT_MODE) {\n this.boundingBoxTool.onMouseUp(event);\n this.isBoundingBoxMode = null;\n return;\n }\n\n this.active = false;\n }\n handleKeyUp (event) {\n if (event.event.target instanceof HTMLInputElement) {\n // Ignore nudge if a text input field is focused\n return;\n }\n\n if (this.mode === TextTool.SELECT_MODE) {\n this.nudgeTool.onKeyUp(event);\n }\n }\n handleKeyDown (event) {\n if (event.event.target instanceof HTMLInputElement) {\n // Ignore nudge if a text input field is focused\n return;\n }\n if (this.mode === TextTool.TEXT_EDIT_MODE && event.key === 'escape') {\n this.endTextEdit();\n }\n if (this.mode === TextTool.SELECT_MODE) {\n this.nudgeTool.onKeyDown(event);\n }\n }\n handleTextInput (event) {\n // Save undo state if you paused typing for long enough.\n if (this.lastTypeEvent && event.timeStamp - this.lastTypeEvent.timeStamp > TextTool.TYPING_TIMEOUT_MILLIS) {\n // Select the textbox so that it will be selected if the user performs undo.\n this.textBox.selected = true;\n this.onUpdateImage();\n this.textBox.selected = false;\n }\n this.lastTypeEvent = event;\n if (this.mode === TextTool.TEXT_EDIT_MODE) {\n this.textBox.content = this.element.value;\n }\n this.resizeGuide();\n }\n resizeGuide () {\n if (this.guide) this.guide.remove();\n this.guide = hoverBounds(this.textBox, TextTool.TEXT_PADDING);\n this.guide.dashArray = [4, 4];\n // Prevent line from wrapping\n this.element.style.width = `${this.textBox.internalBounds.width + 1}px`;\n this.element.style.height = `${this.textBox.internalBounds.height}px`;\n // The transform origin needs to be updated in RTL because this.textBox.internalBounds.x\n // changes as you type\n if (this.rtl) {\n this.element.style.transformOrigin =\n `${-this.textBox.internalBounds.x}px ${-this.textBox.internalBounds.y}px`;\n }\n }\n beginSelect () {\n if (this.textBox) {\n this.mode = TextTool.SELECT_MODE;\n this.textBox.selected = true;\n this.setSelectedItems();\n }\n }\n endSelect () {\n clearSelection(this.clearSelectedItems);\n this.mode = null;\n }\n /**\n * @param {paper.PointText} textBox Text object to begin text edit on\n */\n beginTextEdit (textBox) {\n this.textBox = textBox;\n this.mode = TextTool.TEXT_EDIT_MODE;\n this.setTextEditTarget(this.textBox.id);\n if (this.font !== this.textBox.font) {\n this.changeFont(this.textBox.font);\n }\n this.element.style.fontSize = `${this.textBox.fontSize}px`;\n this.element.style.lineHeight = this.textBox.leading / this.textBox.fontSize;\n\n this.element.style.display = 'initial';\n this.element.value = textBox.content ? textBox.content : '';\n this.calculateMatrix(paper.view.matrix);\n\n if (this.rtl) {\n // make both the textbox and the textarea element grow to the left\n this.textBox.justification = 'right';\n } else {\n this.textBox.justification = 'left';\n }\n\n this.element.focus({preventScroll: true});\n this.eventListener = this.handleTextInput.bind(this);\n this.element.addEventListener('input', this.eventListener);\n this.resizeGuide();\n }\n endTextEdit () {\n if (this.mode !== TextTool.TEXT_EDIT_MODE) {\n return;\n }\n this.mode = null;\n\n // Remove invisible textboxes\n if (this.textBox && this.textBox.content.trim() === '') {\n this.textBox.remove();\n this.textBox = null;\n }\n\n // Remove guide\n if (this.guide) {\n this.guide.remove();\n this.guide = null;\n this.setTextEditTarget();\n }\n this.element.style.display = 'none';\n if (this.eventListener) {\n this.element.removeEventListener('input', this.eventListener);\n this.eventListener = null;\n }\n if (this.textBox && this.lastTypeEvent) {\n // Finished editing a textbox, save undo state\n // Select the textbox so that it will be selected if the user performs undo.\n this.textBox.selected = true;\n this.onUpdateImage();\n this.textBox.selected = false;\n this.lastTypeEvent = null;\n }\n }\n commitText () {\n if (!this.textBox || !this.textBox.parent) return;\n\n // @todo get crisp text https://github.com/LLK/scratch-paint/issues/508\n const textRaster = this.textBox.rasterize(72, false /* insert */, this.textBox.drawnBounds);\n this.textBox.remove();\n this.textBox = null;\n getRaster().drawImage(\n textRaster.canvas,\n new paper.Point(Math.floor(textRaster.bounds.x), Math.floor(textRaster.bounds.y))\n );\n this.onUpdateImage();\n }\n deactivateTool () {\n if (this.textBox && this.textBox.content.trim() === '') {\n this.textBox.remove();\n this.textBox = null;\n }\n this.endTextEdit();\n if (this.isBitmap) {\n this.commitText();\n }\n this.boundingBoxTool.deactivateTool();\n }\n}\n\nexport default TextTool;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport messages from '../../lib/messages.js';\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\n\nimport textIcon from './text.svg';\n\nconst TextModeComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.text}\n imgSrc={textIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nTextModeComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default TextModeComponent;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';\nimport messages from '../../lib/messages.js';\nimport textIcon from './text.svg';\n\nconst BitTextComponent = props => (\n <ToolSelectComponent\n imgDescriptor={messages.text}\n imgSrc={textIcon}\n isSelected={props.isSelected}\n onMouseDown={props.onMouseDown}\n />\n);\n\nBitTextComponent.propTypes = {\n isSelected: PropTypes.bool.isRequired,\n onMouseDown: PropTypes.func.isRequired\n};\n\nexport default BitTextComponent;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\nimport Fonts from '../lib/fonts';\nimport Modes from '../lib/modes';\nimport ColorStyleProptype from '../lib/color-style-proptype';\nimport {MIXED} from '../helper/style-path';\n\nimport {changeFont} from '../reducers/font';\nimport {changeFillColor, clearFillGradient, DEFAULT_COLOR} from '../reducers/fill-style';\nimport {changeStrokeColor} from '../reducers/stroke-style';\nimport {changeMode} from '../reducers/modes';\nimport {setTextEditTarget} from '../reducers/text-edit-target';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {setCursor} from '../reducers/cursor';\n\nimport {clearSelection, getSelectedLeafItems} from '../helper/selection';\nimport TextTool from '../helper/tools/text-tool';\nimport TextModeComponent from '../components/text-mode/text-mode.jsx';\nimport BitTextModeComponent from '../components/bit-text-mode/bit-text-mode.jsx';\n\nclass TextMode extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'activateTool',\n 'deactivateTool'\n ]);\n }\n componentDidMount () {\n if (this.props.isTextModeActive) {\n this.activateTool(this.props);\n }\n }\n componentWillReceiveProps (nextProps) {\n if (this.tool) {\n if (nextProps.colorState !== this.props.colorState) {\n this.tool.setColorState(nextProps.colorState);\n }\n if (nextProps.selectedItems !== this.props.selectedItems) {\n this.tool.onSelectionChanged(nextProps.selectedItems);\n }\n if (!nextProps.textEditTarget && this.props.textEditTarget) {\n this.tool.onTextEditCancelled();\n }\n if (!nextProps.viewBounds.equals(this.props.viewBounds)) {\n this.tool.onViewBoundsChanged(nextProps.viewBounds);\n }\n if (nextProps.font !== this.props.font) {\n this.tool.setFont(nextProps.font);\n }\n if (nextProps.rtl !== this.props.rtl) {\n this.tool.setRtl(nextProps.rtl);\n }\n }\n\n if (nextProps.isTextModeActive && !this.props.isTextModeActive) {\n this.activateTool(nextProps);\n } else if (!nextProps.isTextModeActive && this.props.isTextModeActive) {\n this.deactivateTool();\n }\n }\n shouldComponentUpdate (nextProps) {\n return nextProps.isTextModeActive !== this.props.isTextModeActive;\n }\n componentWillUnmount () {\n if (this.tool) {\n this.deactivateTool();\n }\n }\n activateTool (nextProps) {\n const selected = getSelectedLeafItems();\n let textBoxToStartEditing = null;\n if (selected.length === 1 && selected[0] instanceof paper.PointText) {\n textBoxToStartEditing = selected[0];\n }\n clearSelection(this.props.clearSelectedItems);\n this.props.clearGradient();\n\n // If fill and stroke color are both mixed/transparent/absent, set fill to default and stroke to transparent.\n // If exactly one of fill or stroke color is set, set the other one to transparent.\n // This way the tool won't draw an invisible state, or be unclear about what will be drawn.\n const {strokeWidth} = nextProps.colorState;\n const fillColor = nextProps.colorState.fillColor.primary;\n const strokeColor = nextProps.colorState.strokeColor.primary;\n const fillColorPresent = fillColor !== MIXED && fillColor !== null;\n const strokeColorPresent = nextProps.isBitmap ? false :\n strokeColor !== MIXED && strokeColor !== null && strokeWidth !== null && strokeWidth !== 0;\n if (!fillColorPresent && !strokeColorPresent) {\n this.props.onChangeFillColor(DEFAULT_COLOR);\n this.props.onChangeStrokeColor(null);\n } else if (!fillColorPresent && strokeColorPresent) {\n this.props.onChangeFillColor(null);\n } else if (fillColorPresent && !strokeColorPresent) {\n this.props.onChangeStrokeColor(null);\n }\n if (!nextProps.font || Object.keys(Fonts).map(key => Fonts[key])\n .indexOf(nextProps.font) < 0) {\n this.props.changeFont(Fonts.SANS_SERIF);\n }\n\n this.tool = new TextTool(\n this.props.textArea,\n this.props.setSelectedItems,\n this.props.clearSelectedItems,\n this.props.setCursor,\n this.props.onUpdateImage,\n this.props.setTextEditTarget,\n this.props.changeFont,\n nextProps.isBitmap\n );\n this.tool.setRtl(this.props.rtl);\n this.tool.setColorState(nextProps.colorState);\n this.tool.setFont(nextProps.font);\n this.tool.activate();\n if (textBoxToStartEditing) {\n this.tool.beginTextEdit(textBoxToStartEditing);\n this.props.textArea.select();\n }\n }\n deactivateTool () {\n this.tool.deactivateTool();\n this.tool.remove();\n this.tool = null;\n }\n render () {\n return (\n this.props.isBitmap ?\n <BitTextModeComponent\n isSelected={this.props.isTextModeActive}\n onMouseDown={this.props.handleChangeModeBitText}\n /> :\n <TextModeComponent\n isSelected={this.props.isTextModeActive}\n onMouseDown={this.props.handleChangeModeText}\n />\n );\n }\n}\n\nTextMode.propTypes = {\n changeFont: PropTypes.func.isRequired,\n clearGradient: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n colorState: PropTypes.shape({\n fillColor: ColorStyleProptype,\n strokeColor: ColorStyleProptype,\n strokeWidth: PropTypes.number\n }).isRequired,\n font: PropTypes.string,\n handleChangeModeBitText: PropTypes.func.isRequired,\n handleChangeModeText: PropTypes.func.isRequired,\n isBitmap: PropTypes.bool,\n isTextModeActive: PropTypes.bool.isRequired,\n onChangeFillColor: PropTypes.func.isRequired,\n onChangeStrokeColor: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n rtl: PropTypes.bool,\n selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),\n setCursor: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n setTextEditTarget: PropTypes.func.isRequired,\n textArea: PropTypes.instanceOf(Element),\n textEditTarget: PropTypes.number,\n viewBounds: PropTypes.instanceOf(paper.Matrix).isRequired\n};\n\nconst mapStateToProps = (state, ownProps) => ({\n colorState: state.scratchPaint.color,\n font: state.scratchPaint.font,\n isTextModeActive: ownProps.isBitmap ?\n state.scratchPaint.mode === Modes.BIT_TEXT :\n state.scratchPaint.mode === Modes.TEXT,\n rtl: state.scratchPaint.layout.rtl,\n selectedItems: state.scratchPaint.selectedItems,\n textEditTarget: state.scratchPaint.textEditTarget,\n viewBounds: state.scratchPaint.viewBounds\n});\nconst mapDispatchToProps = (dispatch, ownProps) => ({\n changeFont: font => {\n dispatch(changeFont(font));\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n clearGradient: () => {\n dispatch(clearFillGradient());\n },\n handleChangeModeBitText: () => {\n dispatch(changeMode(Modes.BIT_TEXT));\n },\n handleChangeModeText: () => {\n dispatch(changeMode(Modes.TEXT));\n },\n setCursor: cursorString => {\n dispatch(setCursor(cursorString));\n },\n setSelectedItems: () => {\n dispatch(setSelectedItems(getSelectedLeafItems(), ownProps.isBitmap));\n },\n setTextEditTarget: targetId => {\n dispatch(setTextEditTarget(targetId));\n },\n onChangeFillColor: fillColor => {\n dispatch(changeFillColor(fillColor));\n },\n onChangeStrokeColor: strokeColor => {\n dispatch(changeStrokeColor(strokeColor));\n }\n});\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps\n)(TextMode);\n","\n import API from \"!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../../../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../../../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../../../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../../../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./paint-editor.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[7].use[2]!./paint-editor.css\";\n export default content && content.locals ? content.locals : undefined;\n","import paper from '@scratch/paper';\nimport classNames from 'classnames';\nimport {defineMessages, useIntl} from 'react-intl';\nimport React from 'react';\nimport PropTypes from 'prop-types';\n\nimport PaperCanvas from '../../containers/paper-canvas.jsx';\nimport ScrollableCanvas from '../../containers/scrollable-canvas.jsx';\n\nimport BitBrushMode from '../../containers/bit-brush-mode.jsx';\nimport BitLineMode from '../../containers/bit-line-mode.jsx';\nimport BitOvalMode from '../../containers/bit-oval-mode.jsx';\nimport BitRectMode from '../../containers/bit-rect-mode.jsx';\nimport BitFillMode from '../../containers/bit-fill-mode.jsx';\nimport BitEraserMode from '../../containers/bit-eraser-mode.jsx';\nimport BitSelectMode from '../../containers/bit-select-mode.jsx';\nimport Box from '../box/box.jsx';\nimport Button from '../button/button.jsx';\nimport ButtonGroup from '../button-group/button-group.jsx';\nimport BrushMode from '../../containers/brush-mode.jsx';\nimport EraserMode from '../../containers/eraser-mode.jsx';\nimport FillColorIndicatorComponent from '../../containers/fill-color-indicator.jsx';\nimport FillMode from '../../containers/fill-mode.jsx';\nimport InputGroup from '../input-group/input-group.jsx';\nimport LineMode from '../../containers/line-mode.jsx';\nimport Loupe from '../loupe/loupe.jsx';\nimport FixedToolsContainer from '../../containers/fixed-tools.jsx';\nimport ModeToolsContainer from '../../containers/mode-tools.jsx';\nimport OvalMode from '../../containers/oval-mode.jsx';\nimport RectMode from '../../containers/rect-mode.jsx';\nimport ReshapeMode from '../../containers/reshape-mode.jsx';\nimport SelectMode from '../../containers/select-mode.jsx';\nimport StrokeColorIndicatorComponent from '../../containers/stroke-color-indicator.jsx';\nimport StrokeWidthIndicatorComponent from '../../containers/stroke-width-indicator.jsx';\nimport TextMode from '../../containers/text-mode.jsx';\n\nimport Formats, {isBitmap, isVector} from '../../lib/format';\nimport styles from './paint-editor.css';\n\nimport bitmapIcon from './icons/bitmap.svg';\nimport zoomInIcon from './icons/zoom-in.svg';\nimport zoomOutIcon from './icons/zoom-out.svg';\nimport zoomResetIcon from './icons/zoom-reset.svg';\n\nconst messages = defineMessages({\n bitmap: {\n defaultMessage: 'Convert to Bitmap',\n description: 'Label for button that converts the paint editor to bitmap mode',\n id: 'paint.paintEditor.bitmap'\n },\n vector: {\n defaultMessage: 'Convert to Vector',\n description: 'Label for button that converts the paint editor to vector mode',\n id: 'paint.paintEditor.vector'\n }\n});\n\nconst PaintEditorComponent = props => {\n const intl = useIntl();\n return (\n <div\n className={styles.editorContainer}\n dir={props.rtl ? 'rtl' : 'ltr'}\n >\n {props.canvas !== null ? ( // eslint-disable-line no-negated-condition\n <div className={styles.editorContainerTop}>\n {/* First row */}\n <div className={styles.row}>\n <FixedToolsContainer\n canRedo={props.canRedo}\n canUndo={props.canUndo}\n name={props.name}\n onRedo={props.onRedo}\n onUndo={props.onUndo}\n onUpdateImage={props.onUpdateImage}\n onUpdateName={props.onUpdateName}\n />\n </div>\n {/* Second Row */}\n {isVector(props.format) ?\n <div className={styles.row}>\n <InputGroup\n className={classNames(\n styles.row,\n styles.modDashedBorder,\n styles.modLabeledIconHeight\n )}\n >\n {/* fill */}\n <FillColorIndicatorComponent\n className={styles.modMarginAfter}\n onUpdateImage={props.onUpdateImage}\n />\n {/* stroke */}\n <StrokeColorIndicatorComponent\n onUpdateImage={props.onUpdateImage}\n />\n {/* stroke width */}\n <StrokeWidthIndicatorComponent\n onUpdateImage={props.onUpdateImage}\n />\n </InputGroup>\n <InputGroup className={styles.modModeTools}>\n <ModeToolsContainer\n onUpdateImage={props.onUpdateImage}\n />\n </InputGroup>\n </div> :\n isBitmap(props.format) ?\n <div className={styles.row}>\n <InputGroup\n className={classNames(\n styles.row,\n styles.modDashedBorder,\n styles.modLabeledIconHeight\n )}\n >\n {/* fill */}\n <FillColorIndicatorComponent\n className={styles.modMarginAfter}\n onUpdateImage={props.onUpdateImage}\n />\n </InputGroup>\n <InputGroup className={styles.modModeTools}>\n <ModeToolsContainer\n onUpdateImage={props.onUpdateImage}\n />\n </InputGroup>\n </div> : null\n }\n </div>\n ) : null}\n\n <div className={styles.topAlignRow}>\n {/* Modes */}\n {props.canvas !== null && isVector(props.format) ? (\n <div className={styles.modeSelector}>\n <SelectMode\n onUpdateImage={props.onUpdateImage}\n />\n <ReshapeMode\n onUpdateImage={props.onUpdateImage}\n />\n <BrushMode\n onUpdateImage={props.onUpdateImage}\n />\n <EraserMode\n onUpdateImage={props.onUpdateImage}\n />\n <FillMode\n onUpdateImage={props.onUpdateImage}\n />\n <TextMode\n textArea={props.textArea}\n onUpdateImage={props.onUpdateImage}\n />\n <LineMode\n onUpdateImage={props.onUpdateImage}\n />\n <OvalMode\n onUpdateImage={props.onUpdateImage}\n />\n <RectMode\n onUpdateImage={props.onUpdateImage}\n />\n </div>\n ) : null}\n\n {props.canvas !== null && isBitmap(props.format) ? (\n <div className={styles.modeSelector}>\n <BitBrushMode\n onUpdateImage={props.onUpdateImage}\n />\n <BitLineMode\n onUpdateImage={props.onUpdateImage}\n />\n <BitOvalMode\n onUpdateImage={props.onUpdateImage}\n />\n <BitRectMode\n onUpdateImage={props.onUpdateImage}\n />\n <TextMode\n isBitmap\n textArea={props.textArea}\n onUpdateImage={props.onUpdateImage}\n />\n <BitFillMode\n onUpdateImage={props.onUpdateImage}\n />\n <BitEraserMode\n onUpdateImage={props.onUpdateImage}\n />\n <BitSelectMode\n onUpdateImage={props.onUpdateImage}\n />\n </div>\n ) : null}\n\n <div className={styles.controlsContainer}>\n {/* Canvas */}\n <ScrollableCanvas\n canvas={props.canvas}\n hideScrollbars={props.isEyeDropping}\n style={styles.canvasContainer}\n >\n <PaperCanvas\n canvasRef={props.setCanvas}\n image={props.image}\n imageFormat={props.imageFormat}\n imageId={props.imageId}\n rotationCenterX={props.rotationCenterX}\n rotationCenterY={props.rotationCenterY}\n zoomLevelId={props.zoomLevelId}\n onUpdateImage={props.onUpdateImage}\n />\n <textarea\n className={styles.textArea}\n ref={props.setTextArea}\n spellCheck={false}\n />\n {props.isEyeDropping &&\n props.colorInfo !== null &&\n !props.colorInfo.hideLoupe ? (\n <Box className={styles.colorPickerWrapper}>\n <Loupe\n colorInfo={props.colorInfo}\n pixelRatio={paper.project.view.pixelRatio}\n />\n </Box>\n ) : null\n }\n </ScrollableCanvas>\n <div className={styles.canvasControls}>\n {isVector(props.format) ?\n <Button\n className={styles.bitmapButton}\n onClick={props.onSwitchToBitmap}\n >\n <img\n className={styles.bitmapButtonIcon}\n draggable={false}\n src={bitmapIcon}\n />\n <span className={styles.buttonText}>\n {intl.formatMessage(messages.bitmap)}\n </span>\n </Button> :\n isBitmap(props.format) ?\n <Button\n className={styles.bitmapButton}\n onClick={props.onSwitchToVector}\n >\n <img\n className={styles.bitmapButtonIcon}\n draggable={false}\n src={bitmapIcon}\n />\n <span className={styles.buttonText}>\n {intl.formatMessage(messages.vector)}\n </span>\n </Button> : null\n }\n {/* Zoom controls */}\n <InputGroup className={styles.zoomControls}>\n <ButtonGroup>\n <Button\n className={styles.buttonGroupButton}\n onClick={props.onZoomOut}\n >\n <img\n alt=\"Zoom Out\"\n className={styles.buttonGroupButtonIcon}\n draggable={false}\n src={zoomOutIcon}\n />\n </Button>\n <Button\n className={styles.buttonGroupButton}\n onClick={props.onZoomReset}\n >\n <img\n alt=\"Zoom Reset\"\n className={styles.buttonGroupButtonIcon}\n draggable={false}\n src={zoomResetIcon}\n />\n </Button>\n <Button\n className={styles.buttonGroupButton}\n onClick={props.onZoomIn}\n >\n <img\n alt=\"Zoom In\"\n className={styles.buttonGroupButtonIcon}\n draggable={false}\n src={zoomInIcon}\n />\n </Button>\n </ButtonGroup>\n </InputGroup>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nPaintEditorComponent.propTypes = {\n canRedo: PropTypes.func.isRequired,\n canUndo: PropTypes.func.isRequired,\n canvas: PropTypes.instanceOf(Element),\n colorInfo: Loupe.propTypes.colorInfo,\n format: PropTypes.oneOf(Object.keys(Formats)),\n image: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.instanceOf(HTMLImageElement)\n ]),\n imageFormat: PropTypes.string,\n imageId: PropTypes.string,\n isEyeDropping: PropTypes.bool,\n name: PropTypes.string,\n onRedo: PropTypes.func.isRequired,\n onSwitchToBitmap: PropTypes.func.isRequired,\n onSwitchToVector: PropTypes.func.isRequired,\n onUndo: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n onUpdateName: PropTypes.func.isRequired,\n onZoomIn: PropTypes.func.isRequired,\n onZoomOut: PropTypes.func.isRequired,\n onZoomReset: PropTypes.func.isRequired,\n rotationCenterX: PropTypes.number,\n rotationCenterY: PropTypes.number,\n rtl: PropTypes.bool,\n setCanvas: PropTypes.func.isRequired,\n setTextArea: PropTypes.func.isRequired,\n textArea: PropTypes.instanceOf(Element),\n zoomLevelId: PropTypes.string\n};\n\nexport default PaintEditorComponent;\n","import bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport omit from 'lodash.omit';\nimport {connect} from 'react-redux';\n\nimport CopyPasteHOC from './copy-paste-hoc.jsx';\n\nimport {selectAllBitmap} from '../helper/bitmap';\nimport {clearSelection, deleteSelection, getSelectedLeafItems,\n selectAllItems, selectAllSegments, getSelectedRootItems} from '../helper/selection';\nimport {groupSelection, shouldShowGroup, ungroupSelection, shouldShowUngroup} from '../helper/group';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {changeMode} from '../reducers/modes';\n\nimport Formats, {isBitmap} from '../lib/format';\nimport Modes from '../lib/modes';\n\nconst KeyboardShortcutsHOC = function (WrappedComponent) {\n class KeyboardShortcutsWrapper extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleKeyPress',\n 'changeToASelectMode',\n 'selectAll'\n ]);\n }\n handleKeyPress (event) {\n if (event.target instanceof HTMLInputElement) {\n // Ignore keyboard shortcuts if a text input field is focused\n return;\n }\n // Don't activate keyboard shortcuts during text editing\n if (this.props.textEditing) return;\n\n if (event.key === 'Escape') {\n event.preventDefault();\n clearSelection(this.props.clearSelectedItems);\n } else if (event.key === 'Delete' || event.key === 'Backspace') {\n event.preventDefault();\n if (deleteSelection(this.props.mode, this.props.onUpdateImage)) {\n this.props.setSelectedItems(this.props.format);\n }\n } else if (event.metaKey || event.ctrlKey) {\n if (event.shiftKey && event.key.toLowerCase() === 'z') {\n this.props.onRedo();\n } else if (event.key === 'z') {\n this.props.onUndo();\n } else if (event.shiftKey && event.key.toLowerCase() === 'g') {\n if (shouldShowUngroup()) {\n ungroupSelection(clearSelectedItems, setSelectedItems, this.props.onUpdateImage);\n }\n event.preventDefault();\n } else if (event.key === 'g') {\n if (shouldShowGroup()) {\n groupSelection(clearSelectedItems, setSelectedItems, this.props.onUpdateImage);\n }\n event.preventDefault();\n } else if (event.key === 'c') {\n this.props.onCopyToClipboard();\n } else if (event.key === 'v') {\n this.changeToASelectMode();\n this.props.onPasteFromClipboard();\n } else if (event.key === 'x') {\n const selectedItems = getSelectedRootItems();\n if (selectedItems.length > 0) {\n this.props.onCopyToClipboard();\n if (deleteSelection(this.props.mode, this.props.onUpdateImage)) {\n this.props.setSelectedItems(this.props.format);\n }\n }\n event.preventDefault();\n } else if (event.key === 'a') {\n this.changeToASelectMode();\n event.preventDefault();\n this.selectAll();\n }\n }\n }\n changeToASelectMode () {\n if (isBitmap(this.props.format)) {\n if (this.props.mode !== Modes.BIT_SELECT) {\n this.props.changeMode(Modes.BIT_SELECT);\n }\n } else if (this.props.mode !== Modes.SELECT && this.props.mode !== Modes.RESHAPE) {\n this.props.changeMode(Modes.SELECT);\n }\n }\n selectAll () {\n if (isBitmap(this.props.format)) {\n selectAllBitmap(this.props.clearSelectedItems);\n this.props.setSelectedItems(this.props.format);\n } else if (this.props.mode === Modes.RESHAPE) {\n if (selectAllSegments()) this.props.setSelectedItems(this.props.format);\n } else if (selectAllItems()) {\n this.props.setSelectedItems(this.props.format);\n }\n }\n render () {\n const componentProps = omit(this.props, [\n 'changeMode',\n 'clearSelectedItems',\n 'format',\n 'mode',\n 'onCopyToClipboard',\n 'onPasteFromClipboard',\n 'setSelectedItems',\n 'textEditing']);\n return (\n <WrappedComponent\n onKeyPress={this.handleKeyPress}\n {...componentProps}\n />\n );\n }\n }\n\n KeyboardShortcutsWrapper.propTypes = {\n changeMode: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)),\n mode: PropTypes.oneOf(Object.keys(Modes)).isRequired,\n onCopyToClipboard: PropTypes.func.isRequired,\n onPasteFromClipboard: PropTypes.func.isRequired,\n onRedo: PropTypes.func.isRequired,\n onUndo: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n textEditing: PropTypes.bool.isRequired\n };\n\n const mapStateToProps = state => ({\n mode: state.scratchPaint.mode,\n format: state.scratchPaint.format,\n textEditing: state.scratchPaint.textEditTarget !== null\n });\n const mapDispatchToProps = dispatch => ({\n changeMode: mode => {\n dispatch(changeMode(mode));\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n }\n });\n\n return CopyPasteHOC(connect(\n mapStateToProps,\n mapDispatchToProps\n )(KeyboardShortcutsWrapper));\n};\n\nexport default KeyboardShortcutsHOC;\n","import paper from '@scratch/paper';\n\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport {connect} from 'react-redux';\nimport bindAll from 'lodash.bindall';\n\nconst SelectionHOC = function (WrappedComponent) {\n class SelectionComponent extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'removeItemById'\n ]);\n }\n componentDidUpdate (prevProps) {\n // Hovered item has changed\n if ((this.props.hoveredItemId && this.props.hoveredItemId !== prevProps.hoveredItemId) ||\n (!this.props.hoveredItemId && prevProps.hoveredItemId)) {\n // Remove the old hover item if any\n this.removeItemById(prevProps.hoveredItemId);\n }\n }\n removeItemById (itemId) {\n if (itemId) {\n const match = paper.project.getItem({\n match: item => (item.id === itemId)\n });\n if (match) {\n match.remove();\n }\n }\n }\n render () {\n const {\n hoveredItemId, // eslint-disable-line no-unused-vars\n ...props\n } = this.props;\n return (\n <WrappedComponent {...props} />\n );\n }\n }\n SelectionComponent.propTypes = {\n hoveredItemId: PropTypes.number\n };\n\n const mapStateToProps = state => ({\n hoveredItemId: state.scratchPaint.hoveredItemId\n });\n return connect(\n mapStateToProps\n )(SelectionComponent);\n};\n\nexport default SelectionHOC;\n","import bindAll from 'lodash.bindall';\nimport PropTypes from 'prop-types';\nimport React from 'react';\nimport omit from 'lodash.omit';\nimport {connect} from 'react-redux';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {setSelectedItems} from '../reducers/selected-items';\nimport {\n performUndo, performRedo, shouldShowUndo, shouldShowRedo\n} from '../helper/undo';\nimport {undo, redo} from '../reducers/undo';\n\nimport Formats, {isBitmap} from '../lib/format';\n\nconst UndoHOC = function (WrappedComponent) {\n class UndoWrapper extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleUndo',\n 'handleRedo',\n 'handleSetSelectedItems',\n 'shouldShowUndo',\n 'shouldShowRedo'\n ]);\n }\n handleUndo () {\n performUndo(this.props.undoState, this.props.onUndo, this.handleSetSelectedItems, this.props.onUpdateImage);\n }\n handleRedo () {\n performRedo(this.props.undoState, this.props.onRedo, this.handleSetSelectedItems, this.props.onUpdateImage);\n }\n handleSetSelectedItems () {\n this.props.setSelectedItems(this.props.format);\n }\n shouldShowUndo () {\n return shouldShowUndo(this.props.undoState);\n }\n shouldShowRedo () {\n return shouldShowRedo(this.props.undoState);\n }\n render () {\n const componentProps = omit(this.props, [\n 'format',\n 'onUndo',\n 'onRedo',\n 'setSelectedItems',\n 'undoState']);\n return (\n <WrappedComponent\n shouldShowRedo={this.shouldShowRedo}\n shouldShowUndo={this.shouldShowUndo}\n onRedo={this.handleRedo}\n onUndo={this.handleUndo}\n {...componentProps}\n />\n );\n }\n }\n\n UndoWrapper.propTypes = {\n format: PropTypes.oneOf(Object.keys(Formats)),\n onRedo: PropTypes.func.isRequired,\n onUndo: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n undoState: PropTypes.shape({\n stack: PropTypes.arrayOf(PropTypes.object).isRequired,\n pointer: PropTypes.number.isRequired\n })\n };\n\n const mapStateToProps = state => ({\n format: state.scratchPaint.format,\n undoState: state.scratchPaint.undo\n });\n const mapDispatchToProps = dispatch => ({\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n },\n onUndo: format => {\n dispatch(undo(format));\n },\n onRedo: format => {\n dispatch(redo(format));\n }\n });\n\n return connect(\n mapStateToProps,\n mapDispatchToProps\n )(UndoWrapper);\n};\n\nexport default UndoHOC;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport log from '../log/log';\nimport bindAll from 'lodash.bindall';\nimport React from 'react';\nimport omit from 'lodash.omit';\nimport {connect} from 'react-redux';\n\nimport {undoSnapshot} from '../reducers/undo';\nimport {setSelectedItems} from '../reducers/selected-items';\nimport {updateViewBounds} from '../reducers/view-bounds';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {getRaster, hideGuideLayers, showGuideLayers} from '../helper/layer';\nimport {commitRectToBitmap, commitOvalToBitmap, commitSelectionToBitmap, getHitBounds} from '../helper/bitmap';\nimport {performSnapshot} from '../helper/undo';\nimport {scaleWithStrokes} from '../helper/math';\n\nimport {\n ART_BOARD_WIDTH, ART_BOARD_HEIGHT, SVG_ART_BOARD_WIDTH, SVG_ART_BOARD_HEIGHT,\n setWorkspaceBounds\n} from '../helper/view';\n\nimport Modes, {BitmapModes} from '../lib/modes';\nimport Formats, {isBitmap, isVector} from '../lib/format';\n\nconst UpdateImageHOC = function (WrappedComponent) {\n class UpdateImageWrapper extends React.Component {\n constructor (props) {\n super(props);\n bindAll(this, [\n 'handleUpdateImage',\n 'handleUpdateBitmap',\n 'handleUpdateVector'\n ]);\n }\n /**\n * @param {?boolean} skipSnapshot True if the call to update image should not trigger saving\n * an undo state. For instance after calling undo.\n * @param {?Formats} formatOverride Normally the mode is used to determine the format of the image,\n * but the format used can be overridden here. In particular when converting between formats,\n * the does not accurately represent the format.\n */\n handleUpdateImage (skipSnapshot, formatOverride) {\n // If in the middle of switching formats, rely on the current mode instead of format.\n const actualFormat = formatOverride ? formatOverride :\n BitmapModes[this.props.mode] ? Formats.BITMAP : Formats.VECTOR;\n if (isBitmap(actualFormat)) {\n this.handleUpdateBitmap(skipSnapshot);\n } else if (isVector(actualFormat)) {\n this.handleUpdateVector(skipSnapshot);\n }\n // Any time an image update is made, recalculate the bounds of the artwork\n setWorkspaceBounds();\n this.props.updateViewBounds(paper.view.matrix);\n }\n handleUpdateBitmap (skipSnapshot) {\n if (!getRaster().loaded) {\n // In general, callers of updateImage should wait for getRaster().loaded = true before\n // calling updateImage.\n // However, this may happen if the user is rapidly undoing/redoing. In this case it's safe\n // to skip the update.\n log.warn('Bitmap layer should be loaded before calling updateImage.');\n return;\n }\n // Anything that is selected is on the vector layer waiting to be committed to the bitmap layer.\n // Plaster the selection onto the raster layer before exporting, if there is a selection.\n const plasteredRaster = getRaster().getSubRaster(getRaster().bounds); // Clone the raster layer\n plasteredRaster.remove(); // Don't insert\n const selectedItems = getSelectedLeafItems();\n if (selectedItems.length === 1) {\n const item = selectedItems[0];\n if (item instanceof paper.Raster) {\n if (!item.loaded ||\n (item.data &&\n item.data.expanded &&\n !item.data.expanded.loaded)) {\n // This may get logged when rapidly undoing/redoing or changing costumes,\n // in which case the warning is not relevant.\n log.warn('Bitmap layer should be loaded before calling updateImage.');\n return;\n }\n commitSelectionToBitmap(item, plasteredRaster);\n } else if (item instanceof paper.Shape && item.type === 'rectangle') {\n commitRectToBitmap(item, plasteredRaster);\n } else if (item instanceof paper.Shape && item.type === 'ellipse') {\n commitOvalToBitmap(item, plasteredRaster);\n } else if (item instanceof paper.PointText) {\n const bounds = item.drawnBounds;\n const textRaster = item.rasterize(72, false /* insert */, bounds);\n plasteredRaster.drawImage(\n textRaster.canvas,\n new paper.Point(Math.floor(bounds.x), Math.floor(bounds.y))\n );\n }\n }\n const rect = getHitBounds(plasteredRaster);\n\n // Use 1x1 instead of 0x0 for getting imageData since paper.js automagically\n // returns the full artboard in the case of getImageData(0x0).\n // Bitmaps need a non-zero width/height in order to be saved as PNG.\n if (rect.width === 0 || rect.height === 0) {\n rect.width = rect.height = 1;\n }\n\n const imageData = plasteredRaster.getImageData(rect);\n\n this.props.onUpdateImage(\n false /* isVector */,\n imageData,\n (ART_BOARD_WIDTH / 2) - rect.x,\n (ART_BOARD_HEIGHT / 2) - rect.y);\n\n if (!skipSnapshot) {\n performSnapshot(this.props.undoSnapshot, Formats.BITMAP);\n }\n }\n handleUpdateVector (skipSnapshot) {\n // Remove viewbox (this would make it export at MAX_WORKSPACE_BOUNDS)\n let workspaceMask;\n if (paper.project.activeLayer.clipped) {\n for (const child of paper.project.activeLayer.children) {\n if (child.isClipMask()) {\n workspaceMask = child;\n break;\n }\n }\n paper.project.activeLayer.clipped = false;\n workspaceMask.remove();\n }\n const guideLayers = hideGuideLayers(true /* includeRaster */);\n\n // Export at 0.5x\n scaleWithStrokes(paper.project.activeLayer, .5, new paper.Point());\n\n const bounds = paper.project.activeLayer.drawnBounds;\n\n // `bounds.x` and `bounds.y` are relative to the top left corner,\n // but if there is no content in the active layer, they default to 0,\n // making the \"Scratch space\" rotation center ((SVG_ART_BOARD_WIDTH / 2), (SVG_ART_BOARD_HEIGHT / 2)),\n // aka the upper left corner. Special-case this to be (0, 0), which is the center of the art board.\n const centerX = bounds.width === 0 ? 0 : (SVG_ART_BOARD_WIDTH / 2) - bounds.x;\n const centerY = bounds.height === 0 ? 0 : (SVG_ART_BOARD_HEIGHT / 2) - bounds.y;\n\n this.props.onUpdateImage(\n true /* isVector */,\n paper.project.exportSVG({\n asString: true,\n bounds: 'content',\n matrix: new paper.Matrix().translate(-bounds.x, -bounds.y)\n }),\n centerX,\n centerY);\n scaleWithStrokes(paper.project.activeLayer, 2, new paper.Point());\n paper.project.activeLayer.applyMatrix = true;\n\n showGuideLayers(guideLayers);\n\n // Add back viewbox\n if (workspaceMask) {\n paper.project.activeLayer.addChild(workspaceMask);\n workspaceMask.clipMask = true;\n }\n\n if (!skipSnapshot) {\n performSnapshot(this.props.undoSnapshot, Formats.VECTOR);\n }\n }\n render () {\n const componentProps = omit(this.props, [\n 'format',\n 'onUpdateImage',\n 'undoSnapshot'\n ]);\n return (\n <WrappedComponent\n onUpdateImage={this.handleUpdateImage}\n {...componentProps}\n />\n );\n }\n }\n\n UpdateImageWrapper.propTypes = {\n format: PropTypes.oneOf(Object.keys(Formats)),\n mode: PropTypes.oneOf(Object.keys(Modes)).isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n undoSnapshot: PropTypes.func.isRequired,\n updateViewBounds: PropTypes.func.isRequired\n };\n\n const mapStateToProps = state => ({\n format: state.scratchPaint.format,\n mode: state.scratchPaint.mode,\n undoState: state.scratchPaint.undo\n });\n const mapDispatchToProps = dispatch => ({\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n },\n undoSnapshot: snapshot => {\n dispatch(undoSnapshot(snapshot));\n },\n updateViewBounds: matrix => {\n dispatch(updateViewBounds(matrix));\n }\n });\n\n return connect(\n mapStateToProps,\n mapDispatchToProps\n )(UpdateImageWrapper);\n};\n\nexport default UpdateImageHOC;\n","import paper from '@scratch/paper';\nimport PropTypes from 'prop-types';\nimport log from '../log/log';\nimport React from 'react';\nimport {connect} from 'react-redux';\n\nimport PaintEditorComponent from '../components/paint-editor/paint-editor.jsx';\nimport KeyboardShortcutsHOC from '../hocs/keyboard-shortcuts-hoc.jsx';\nimport SelectionHOC from '../hocs/selection-hoc.jsx';\nimport UndoHOC from '../hocs/undo-hoc.jsx';\nimport UpdateImageHOC from '../hocs/update-image-hoc.jsx';\n\nimport {changeMode} from '../reducers/modes';\nimport {changeFormat} from '../reducers/format';\nimport {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';\nimport {deactivateEyeDropper} from '../reducers/eye-dropper';\nimport {setTextEditTarget} from '../reducers/text-edit-target';\nimport {updateViewBounds} from '../reducers/view-bounds';\nimport {setLayout} from '../reducers/layout';\n\nimport {getSelectedLeafItems} from '../helper/selection';\nimport {convertToBitmap, convertToVector} from '../helper/bitmap';\nimport {resetZoom, zoomOnSelection, OUTERMOST_ZOOM_LEVEL} from '../helper/view';\nimport EyeDropperTool from '../helper/tools/eye-dropper';\n\nimport Modes, {BitmapModes, VectorModes} from '../lib/modes';\nimport Formats, {isBitmap, isVector} from '../lib/format';\nimport bindAll from 'lodash.bindall';\n\n/**\n * The top-level paint editor component. See README for more details on usage.\n *\n * <PaintEditor\n * image={optionalImage}\n * imageId={optionalId}\n * imageFormat='svg'\n * rotationCenterX={optionalCenterPointX}\n * rotationCenterY={optionalCenterPointY}\n * rtl={true|false}\n * onUpdateImage={handleUpdateImageFunction}\n * zoomLevelId={optionalZoomLevelId}\n * />\n *\n * `image`: may either be nothing, an SVG string or a base64 data URI)\n * SVGs of up to size 480 x 360 will fit into the view window of the paint editor,\n * while bitmaps of size up to 960 x 720 will fit into the paint editor. One unit\n * of an SVG will appear twice as tall and wide as one unit of a bitmap. This quirky\n * import behavior comes from needing to support legacy projects in Scratch.\n *\n * `imageId`: If this parameter changes, then the paint editor will be cleared, the\n * undo stack reset, and the image re-imported.\n *\n * `imageFormat`: 'svg', 'png', or 'jpg'. Other formats are currently not supported.\n *\n * `rotationCenterX`: x coordinate relative to the top left corner of the sprite of\n * the point that should be centered.\n *\n * `rotationCenterY`: y coordinate relative to the top left corner of the sprite of\n * the point that should be centered.\n *\n * `rtl`: True if the paint editor should be laid out right to left (meant for right\n * to left languages)\n *\n * `onUpdateImage`: A handler called with the new image (either an SVG string or an\n * ImageData) each time the drawing is edited.\n *\n * `zoomLevelId`: All costumes with the same zoom level ID will share the same saved\n * zoom level. When a new zoom level ID is encountered, the paint editor will zoom to\n * fit the current costume comfortably. Leave undefined to perform no zoom to fit.\n */\nclass PaintEditor extends React.Component {\n static get ZOOM_INCREMENT () {\n return 0.5;\n }\n constructor (props) {\n super(props);\n bindAll(this, [\n 'switchModeForFormat',\n 'onMouseDown',\n 'onMouseUp',\n 'setCanvas',\n 'setTextArea',\n 'startEyeDroppingLoop',\n 'stopEyeDroppingLoop',\n 'handleSetSelectedItems',\n 'handleZoomIn',\n 'handleZoomOut',\n 'handleZoomReset'\n ]);\n this.state = {\n canvas: null,\n colorInfo: null\n };\n this.props.setLayout(this.props.rtl ? 'rtl' : 'ltr');\n }\n componentDidMount () {\n document.addEventListener('keydown', this.props.onKeyPress);\n\n // document listeners used to detect if a mouse is down outside of the\n // canvas, and should therefore stop the eye dropper\n document.addEventListener('mousedown', this.onMouseDown);\n document.addEventListener('touchstart', this.onMouseDown);\n document.addEventListener('mouseup', this.onMouseUp);\n document.addEventListener('touchend', this.onMouseUp);\n }\n componentWillReceiveProps (newProps) {\n if (!isBitmap(this.props.format) && isBitmap(newProps.format)) {\n this.switchModeForFormat(Formats.BITMAP);\n } else if (!isVector(this.props.format) && isVector(newProps.format)) {\n this.switchModeForFormat(Formats.VECTOR);\n }\n if (newProps.rtl !== this.props.rtl) {\n this.props.setLayout(newProps.rtl ? 'rtl' : 'ltr');\n }\n }\n componentDidUpdate (prevProps) {\n if (this.props.isEyeDropping && !prevProps.isEyeDropping) {\n this.startEyeDroppingLoop();\n } else if (!this.props.isEyeDropping && prevProps.isEyeDropping) {\n this.stopEyeDroppingLoop();\n } else if (this.props.isEyeDropping && this.props.viewBounds !== prevProps.viewBounds) {\n if (this.props.previousTool) this.props.previousTool.activate();\n this.props.onDeactivateEyeDropper();\n this.stopEyeDroppingLoop();\n }\n\n if (this.props.format === Formats.VECTOR && isBitmap(prevProps.format)) {\n convertToVector(this.props.clearSelectedItems, this.props.onUpdateImage);\n } else if (isVector(prevProps.format) && this.props.format === Formats.BITMAP) {\n convertToBitmap(this.props.clearSelectedItems, this.props.onUpdateImage, this.props.fontInlineFn);\n }\n }\n componentWillUnmount () {\n document.removeEventListener('keydown', this.props.onKeyPress);\n this.stopEyeDroppingLoop();\n document.removeEventListener('mousedown', this.onMouseDown);\n document.removeEventListener('touchstart', this.onMouseDown);\n document.removeEventListener('mouseup', this.onMouseUp);\n document.removeEventListener('touchend', this.onMouseUp);\n }\n switchModeForFormat (newFormat) {\n if ((isVector(newFormat) && (this.props.mode in VectorModes)) ||\n (isBitmap(newFormat) && (this.props.mode in BitmapModes))) {\n // Format didn't change; no mode change needed\n return;\n }\n if (isVector(newFormat)) {\n switch (this.props.mode) {\n case Modes.BIT_BRUSH:\n this.props.changeMode(Modes.BRUSH);\n break;\n case Modes.BIT_LINE:\n this.props.changeMode(Modes.LINE);\n break;\n case Modes.BIT_OVAL:\n this.props.changeMode(Modes.OVAL);\n break;\n case Modes.BIT_RECT:\n this.props.changeMode(Modes.RECT);\n break;\n case Modes.BIT_TEXT:\n this.props.changeMode(Modes.TEXT);\n break;\n case Modes.BIT_FILL:\n this.props.changeMode(Modes.FILL);\n break;\n case Modes.BIT_ERASER:\n this.props.changeMode(Modes.ERASER);\n break;\n case Modes.BIT_SELECT:\n this.props.changeMode(Modes.SELECT);\n break;\n default:\n log.error(`Mode not handled: ${this.props.mode}`);\n this.props.changeMode(Modes.BRUSH);\n }\n } else if (isBitmap(newFormat)) {\n switch (this.props.mode) {\n case Modes.BRUSH:\n this.props.changeMode(Modes.BIT_BRUSH);\n break;\n case Modes.LINE:\n this.props.changeMode(Modes.BIT_LINE);\n break;\n case Modes.OVAL:\n this.props.changeMode(Modes.BIT_OVAL);\n break;\n case Modes.RECT:\n this.props.changeMode(Modes.BIT_RECT);\n break;\n case Modes.TEXT:\n this.props.changeMode(Modes.BIT_TEXT);\n break;\n case Modes.FILL:\n this.props.changeMode(Modes.BIT_FILL);\n break;\n case Modes.ERASER:\n this.props.changeMode(Modes.BIT_ERASER);\n break;\n case Modes.RESHAPE:\n /* falls through */\n case Modes.SELECT:\n this.props.changeMode(Modes.BIT_SELECT);\n break;\n default:\n log.error(`Mode not handled: ${this.props.mode}`);\n this.props.changeMode(Modes.BIT_BRUSH);\n }\n }\n }\n handleZoomIn () {\n // Make the \"next step\" after the outermost zoom level be the default\n // zoom level (0.5)\n let zoomIncrement = PaintEditor.ZOOM_INCREMENT;\n if (paper.view.zoom === OUTERMOST_ZOOM_LEVEL) {\n zoomIncrement = 0.5 - OUTERMOST_ZOOM_LEVEL;\n }\n zoomOnSelection(zoomIncrement);\n this.props.updateViewBounds(paper.view.matrix);\n this.handleSetSelectedItems();\n }\n handleZoomOut () {\n zoomOnSelection(-PaintEditor.ZOOM_INCREMENT);\n this.props.updateViewBounds(paper.view.matrix);\n this.handleSetSelectedItems();\n }\n handleZoomReset () {\n resetZoom();\n this.props.updateViewBounds(paper.view.matrix);\n this.handleSetSelectedItems();\n }\n handleSetSelectedItems () {\n this.props.setSelectedItems(this.props.format);\n }\n setCanvas (canvas) {\n this.setState({canvas: canvas});\n this.canvas = canvas;\n }\n setTextArea (element) {\n this.setState({textArea: element});\n }\n onMouseDown (event) {\n if (event.target === paper.view.element &&\n document.activeElement instanceof HTMLInputElement) {\n document.activeElement.blur();\n }\n\n if (event.target !== paper.view.element && event.target !== this.state.textArea) {\n // Exit text edit mode if you click anywhere outside of canvas\n this.props.removeTextEditTarget();\n }\n }\n onMouseUp () {\n if (this.props.isEyeDropping) {\n const colorString = this.eyeDropper.colorString;\n const callback = this.props.changeColorToEyeDropper;\n\n this.eyeDropper.remove();\n if (!this.eyeDropper.hideLoupe) {\n // If not hide loupe, that means the click is inside the canvas,\n // so apply the new color\n callback(colorString);\n }\n if (this.props.previousTool) this.props.previousTool.activate();\n this.props.onDeactivateEyeDropper();\n this.stopEyeDroppingLoop();\n }\n }\n startEyeDroppingLoop () {\n this.eyeDropper = new EyeDropperTool(\n this.canvas,\n paper.project.view.bounds.width,\n paper.project.view.bounds.height,\n paper.project.view.pixelRatio,\n paper.view.zoom,\n paper.project.view.bounds.x,\n paper.project.view.bounds.y,\n isBitmap(this.props.format)\n );\n this.eyeDropper.pickX = -1;\n this.eyeDropper.pickY = -1;\n this.eyeDropper.activate();\n\n this.intervalId = setInterval(() => {\n const colorInfo = this.eyeDropper.getColorInfo(\n this.eyeDropper.pickX,\n this.eyeDropper.pickY,\n this.eyeDropper.hideLoupe\n );\n if (!colorInfo) return;\n if (\n this.state.colorInfo === null ||\n this.state.colorInfo.x !== colorInfo.x ||\n this.state.colorInfo.y !== colorInfo.y\n ) {\n this.setState({\n colorInfo: colorInfo\n });\n }\n }, 30);\n }\n stopEyeDroppingLoop () {\n clearInterval(this.intervalId);\n this.setState({colorInfo: null});\n }\n render () {\n return (\n <PaintEditorComponent\n canRedo={this.props.shouldShowRedo}\n canUndo={this.props.shouldShowUndo}\n canvas={this.state.canvas}\n colorInfo={this.state.colorInfo}\n format={this.props.format}\n image={this.props.image}\n imageFormat={this.props.imageFormat}\n imageId={this.props.imageId}\n isEyeDropping={this.props.isEyeDropping}\n name={this.props.name}\n rotationCenterX={this.props.rotationCenterX}\n rotationCenterY={this.props.rotationCenterY}\n rtl={this.props.rtl}\n setCanvas={this.setCanvas}\n setTextArea={this.setTextArea}\n textArea={this.state.textArea}\n zoomLevelId={this.props.zoomLevelId}\n onRedo={this.props.onRedo}\n onSwitchToBitmap={this.props.handleSwitchToBitmap}\n onSwitchToVector={this.props.handleSwitchToVector}\n onUndo={this.props.onUndo}\n onUpdateImage={this.props.onUpdateImage}\n onUpdateName={this.props.onUpdateName}\n onZoomIn={this.handleZoomIn}\n onZoomOut={this.handleZoomOut}\n onZoomReset={this.handleZoomReset}\n />\n );\n }\n}\n\nPaintEditor.propTypes = {\n changeColorToEyeDropper: PropTypes.func,\n changeMode: PropTypes.func.isRequired,\n clearSelectedItems: PropTypes.func.isRequired,\n format: PropTypes.oneOf(Object.keys(Formats)), // Internal, up-to-date data format\n fontInlineFn: PropTypes.func,\n handleSwitchToBitmap: PropTypes.func.isRequired,\n handleSwitchToVector: PropTypes.func.isRequired,\n image: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.instanceOf(HTMLImageElement)\n ]),\n imageFormat: PropTypes.string, // The incoming image's data format, used during import\n imageId: PropTypes.string,\n isEyeDropping: PropTypes.bool,\n mode: PropTypes.oneOf(Object.keys(Modes)).isRequired,\n name: PropTypes.string,\n onDeactivateEyeDropper: PropTypes.func.isRequired,\n onKeyPress: PropTypes.func.isRequired,\n onRedo: PropTypes.func.isRequired,\n onUndo: PropTypes.func.isRequired,\n onUpdateImage: PropTypes.func.isRequired,\n onUpdateName: PropTypes.func.isRequired,\n previousTool: PropTypes.shape({ // paper.Tool\n activate: PropTypes.func.isRequired,\n remove: PropTypes.func.isRequired\n }),\n removeTextEditTarget: PropTypes.func.isRequired,\n rotationCenterX: PropTypes.number,\n rotationCenterY: PropTypes.number,\n rtl: PropTypes.bool,\n setLayout: PropTypes.func.isRequired,\n setSelectedItems: PropTypes.func.isRequired,\n shouldShowRedo: PropTypes.func.isRequired,\n shouldShowUndo: PropTypes.func.isRequired,\n updateViewBounds: PropTypes.func.isRequired,\n viewBounds: PropTypes.instanceOf(paper.Matrix).isRequired,\n zoomLevelId: PropTypes.string\n};\n\nconst mapStateToProps = state => ({\n changeColorToEyeDropper: state.scratchPaint.color.eyeDropper.callback,\n format: state.scratchPaint.format,\n isEyeDropping: state.scratchPaint.color.eyeDropper.active,\n mode: state.scratchPaint.mode,\n previousTool: state.scratchPaint.color.eyeDropper.previousTool,\n viewBounds: state.scratchPaint.viewBounds\n});\nconst mapDispatchToProps = dispatch => ({\n changeMode: mode => {\n dispatch(changeMode(mode));\n },\n clearSelectedItems: () => {\n dispatch(clearSelectedItems());\n },\n handleSwitchToBitmap: () => {\n dispatch(changeFormat(Formats.BITMAP));\n },\n handleSwitchToVector: () => {\n dispatch(changeFormat(Formats.VECTOR));\n },\n removeTextEditTarget: () => {\n dispatch(setTextEditTarget());\n },\n setLayout: layout => {\n dispatch(setLayout(layout));\n },\n setSelectedItems: format => {\n dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));\n },\n onDeactivateEyeDropper: () => {\n // set redux values to default for eye dropper reducer\n dispatch(deactivateEyeDropper());\n },\n updateViewBounds: matrix => {\n dispatch(updateViewBounds(matrix));\n }\n});\n\nexport default UpdateImageHOC(SelectionHOC(UndoHOC(KeyboardShortcutsHOC(connect(\n mapStateToProps,\n mapDispatchToProps\n)(PaintEditor)))));\n","import {combineReducers} from 'redux';\nimport eyeDropperReducer from './eye-dropper';\nimport fillColorReducer from './fill-style';\nimport strokeColorReducer from './stroke-style';\nimport strokeWidthReducer from './stroke-width';\n\nexport default combineReducers({\n eyeDropper: eyeDropperReducer,\n fillColor: fillColorReducer,\n strokeColor: strokeColorReducer,\n strokeWidth: strokeWidthReducer\n});\n","import {combineReducers} from 'redux';\nimport fillModeGradientTypeReducer from './fill-mode-gradient-type';\nimport colorIndexReducer from './color-index';\n\nexport default combineReducers({\n gradientType: fillModeGradientTypeReducer,\n colorIndex: colorIndexReducer\n});\n","import {combineReducers} from 'redux';\nimport modeReducer from './modes';\nimport bitBrushSizeReducer from './bit-brush-size';\nimport bitEraserSizeReducer from './bit-eraser-size';\nimport brushModeReducer from './brush-mode';\nimport eraserModeReducer from './eraser-mode';\nimport colorReducer from './color';\nimport clipboardReducer from './clipboard';\nimport cursorReducer from './cursor';\nimport fillBitmapShapesReducer from './fill-bitmap-shapes';\nimport fillModeReducer from './fill-mode';\nimport fontReducer from './font';\nimport formatReducer from './format';\nimport hoverReducer from './hover';\nimport layoutReducer from './layout';\nimport modalsReducer from './modals';\nimport selectedItemReducer from './selected-items';\nimport textEditTargetReducer from './text-edit-target';\nimport viewBoundsReducer from './view-bounds';\nimport undoReducer from './undo';\nimport zoomLevelsReducer from './zoom-levels';\n\nexport default combineReducers({\n mode: modeReducer,\n bitBrushSize: bitBrushSizeReducer,\n bitEraserSize: bitEraserSizeReducer,\n brushMode: brushModeReducer,\n color: colorReducer,\n clipboard: clipboardReducer,\n cursor: cursorReducer,\n eraserMode: eraserModeReducer,\n fillBitmapShapes: fillBitmapShapesReducer,\n fillMode: fillModeReducer,\n font: fontReducer,\n format: formatReducer,\n hoveredItemId: hoverReducer,\n layout: layoutReducer,\n modals: modalsReducer,\n selectedItems: selectedItemReducer,\n textEditTarget: textEditTargetReducer,\n undo: undoReducer,\n viewBounds: viewBoundsReducer,\n zoomLevels: zoomLevelsReducer\n});\n"],"names":[],"sourceRoot":""}
|