[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"es6\": true\n  },\n  \"extends\": [\"eslint:recommended\", \"plugin:prettier/recommended\"],\n  // \"plugins\": [\"prettier\"],\n  \"parserOptions\": {\n    \"sourceType\": \"module\"\n  },\n  \"ignorePatterns\": [\"node_modules/\", \"dist/\"]\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.idea\n.vscode/\nnode_modules/\ndist\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"trailingComma\": \"all\",\n  \"tabWidth\": 2,\n  \"semi\": false,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2018 Ulrich-Matthias\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# svg.draggable.js\n\nA plugin for the [svgdotjs.github.io](https://svgdotjs.github.io/) library to make elements draggable.\n\nsvg.draggable.js is licensed under the terms of the MIT License.\n\n## Usage\n\nInstall the plugin:\n\n```sh\nnpm install @svgdotjs/svg.js @svgdotjs/svg.draggable.js\n```\n\nInclude this plugin after including the svg.js library in your html document.\n\n```html\n<script src=\"node_modules/@svgdotjs/svg.js/dist/svg.js\"></script>\n<script src=\"node_modules/@svgdotjs/svg.draggable.js/dist/svg.draggable.js\"></script>\n```\n\nOr for esm just require it:\n\n```js\nimport { SVG } from '@svgdotjs/svg.js'\nimport '@svgdotjs/svg.draggable.js'\n```\n\nTo make an element draggable just call `draggable()` on the element\n\n```javascript\nvar draw = SVG().addTo('#canvas').size(400, 400)\nvar rect = draw.rect(100, 100)\n\nrect.draggable()\n```\n\nYes indeed, that's it! Now the `rect` is draggable.\n\n## Events\n\nThe Plugin fires 4 different events\n\n- beforedrag (cancelable)\n- dragstart\n- dragmove (cancelable)\n- dragend\n\nYou can bind/unbind listeners to this events:\n\n```javascript\n// bind\nrect.on('dragstart.namespace', function (event) {\n  // event.detail.event hold the given data explained below\n  // this == rect\n})\n\n// unbind\nrect.off('dragstart.namespace')\n```\n\n### event.detail\n\n`beforedrag`, `dragstart`, `dragmove` and `dragend` gives you the mouse / touch `event` and the `handler` which calculates the drag. The `dragmove` event also gives you the `dx` and `dy` values for your convenience.\nExcept for `beforedrag` the events also give you `detail.box` which holds the initial or new bbox of the element before or after the drag.\n\nYou can use this property to implement custom drag behavior as seen below.\n\nPlease note that the bounding box is not what you would expect for nested svgs because those calculate their bbox based on their content and not their x, y, width and height values. Therefore stuff like constraints needs to be implemented a bit differently.\n\n### Cancelable Events\n\nYou can prevent the default action of `beforedrag` and `dragmove` with a call to `event.preventDefault()` in the callback function.\nThe shape won't be dragged in this case. That is helpfull if you want to implement your own drag handling.\n\n```javascript\nrect.draggable().on('beforedrag', (e) => {\n  e.preventDefault()\n  // no other events are bound\n  // drag was completely prevented\n})\n\nrect.draggable().on('dragmove', (e) => {\n  e.preventDefault()\n  e.detail.handler.move(100, 200)\n  // events are still bound e.g. dragend will fire anyway\n})\n```\n\n### Custom Drag Behavior\n\n#### Constraints\n\n```js\n// Some constraints (x, y, width, height)\nconst constraints = new SVG.Box(100, 100, 400, 400)\n\nrect.on('dragmove.namespace', (e) => {\n  const { handler, box } = e.detail\n  e.preventDefault()\n\n  let { x, y } = box\n\n  // In case your dragged element is a nested element,\n  // you are better off using the rbox() instead of bbox()\n\n  if (x < constraints.x) {\n    x = constraints.x\n  }\n\n  if (y < constraints.y) {\n    y = constraints.y\n  }\n\n  if (box.x2 > constraints.x2) {\n    x = constraints.x2 - box.w\n  }\n\n  if (box.y2 > constraints.y2) {\n    y = constraints.y2 - box.h\n  }\n\n  handler.move(x - (x % 50), y - (y % 50))\n})\n```\n\n#### Snap to grid\n\n```js\nrect.on('dragmove.namespace', (e) => {\n  const { handler, box } = e.detail\n  e.preventDefault()\n\n  handler.move(box.x - (box.x % 50), box.y - (box.y % 50))\n})\n```\n\n## Remove\n\nThe draggable functionality can be removed calling draggable again with false as argument:\n\n```javascript\nrect.draggable(false)\n```\n\n## Restrictions\n\n- If your root-svg is transformed this plugin won't work properly in Firefox. Viewbox however is not affected.\n\n## Dependencies\n\nThis module requires svg.js >= v3.0.10\n"
  },
  {
    "path": "manual-test.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n    <title>svg.js</title>\n    <style type=\"text/css\">\n      html,\n      body,\n      #drawing {\n        width: 100%;\n        height: 100%;\n        margin: 0;\n        border: 1px solid black;\n      }\n    </style>\n  </head>\n\n  <body>\n    <div id=\"drawing\"></div>\n\n    <script src=\"https://unpkg.com/@svgdotjs/svg.js\"></script>\n    <script src=\"dist/svg.draggable.js\"></script>\n\n    <script>\n      var e,\n        constrainingShape,\n        ghostShape,\n        draw = SVG()\n          .addTo('#drawing')\n          .size('100%', '400')\n          .attr({ 'font-size': 10 })\n          .fill('#f06')\n\n      /* plain draggable */\n      draw\n        .rect(100, 100)\n        .center(150, 150)\n        .draggable()\n\n      draw.plain('just plain draggable').center(150, 210)\n\n      /* grouped draggable */\n      var g = draw.group().draggable()\n      g.rect(100, 100).center(400, 150)\n      g.plain('grouped draggable').center(400, 210)\n\n      /* constrained with object */\n      constrainedWithObject = draw\n        .rect(100, 100)\n        .center(650, 150)\n        .draggable()\n        .on('dragstart', function () {\n          ghostShape = draw.put(constrainedWithObject.clone().opacity(0.2))\n\n          constrainingShape = draw\n            .rect(400, 350)\n            .move(400, 50)\n            .fill('none')\n            .stroke('#0fa')\n        })\n        .on('dragmove', e => {\n          e.preventDefault()\n\n          const { handler, box } = e.detail\n          let { x, y } = box\n\n          const constraints = constrainingShape.bbox()\n\n          if (x < constraints.x) {\n            x = constraints.x\n          }\n\n          if (y < constraints.y) {\n            y = constraints.y\n          }\n\n          if (box.x2 > constraints.x2) {\n            x = constraints.x2 - box.w\n          }\n\n          if (box.y2 > constraints.y2) {\n            y = constraints.y2 - box.h\n          }\n\n          handler.move(x, y)\n          ghostShape.animate(300, '>').move(x, y)\n        })\n        .on('dragend', function () {\n          constrainingShape.remove()\n          ghostShape.remove()\n        })\n      draw.plain('constrained with object and ghost').center(650, 210)\n\n      /* constraind with function */\n      // Some constraints (x, y, width, height)\n      const constraints = new SVG.Box(750, 0, 300, 300)\n\n      draw\n        .rect(100, 100)\n        .center(900, 150)\n        .draggable()\n        .on('dragmove', e => {\n          const { handler, box } = e.detail\n          e.preventDefault()\n\n          let { x, y } = box\n\n          // In case your dragged element is a nested element,\n          // you are better off using the rbox() instead of bbox()\n\n          if (x < constraints.x) {\n            x = constraints.x\n          }\n\n          if (y < constraints.y) {\n            y = constraints.y\n          }\n\n          if (box.x2 > constraints.x2) {\n            x = constraints.x2 - box.w\n          }\n\n          if (box.y2 > constraints.y2) {\n            y = constraints.y2 - box.h\n          }\n\n          handler.move(x, y)\n        })\n\n      draw.plain('constraint with function').center(900, 210)\n\n      /* group with multiple levels of draggables (dragging a part doesn't drag the group) */\n      var g2 = draw.group().draggable()\n      for (var i = 0; i < 4; i++) {\n        var cx = i & 1 ? -25 : 25\n        var cy = i & 2 ? -25 : 25\n        g2.rect(50, 50)\n          .center(cx, cy)\n          .draggable()\n      }\n      g2.plain('grouped with multiple levels of draggable').center(0, 70)\n      g2.move(1150, 150)\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@svgdotjs/svg.draggable.js\",\n  \"version\": \"3.0.6\",\n  \"description\": \"An extension for svg.js which allows to drag elements with your mouse\",\n  \"type\": \"module\",\n  \"main\": \"dist/svg.draggable.js\",\n  \"module\": \"src/svg.draggable.js\",\n  \"exports\": {\n    \".\": {\n      \"import\": {\n        \"types\": \"./svg.draggable.js.d.ts\",\n        \"default\": \"./src/svg.draggable.js\"\n      },\n      \"require\": {\n        \"types\": \"./svg.draggable.js.d.cts\",\n        \"default\": \"./src/svg.draggable.js\"\n      },\n      \"browser\": {\n        \"types\": \"./svg.draggable.js.d.ts\",\n        \"default\": \"./src/svg.draggable.js\"\n      }\n    }\n  },\n  \"unpkg\": \"dist/svg.draggable.js\",\n  \"jsdelivr\": \"dist/svg.draggable.js\",\n  \"files\": [\n    \"/dist\",\n    \"/src\",\n    \"/svg.draggable.js.d.ts\",\n    \"/svg.draggable.js.d.cts\"\n  ],\n  \"keywords\": [\n    \"svg.js\",\n    \"draggable\",\n    \"mouse\"\n  ],\n  \"bugs\": \"https://github.com/svgdotjs/svg.draggable.js/issues\",\n  \"license\": \"MIT\",\n  \"typings\": \"./svg.draggable.js.d.ts\",\n  \"author\": {\n    \"name\": \"Wout Fierens\"\n  },\n  \"contributors\": [\n    {\n      \"name\": \"Wout Fierens\"\n    },\n    {\n      \"name\": \"Ulrich-Matthias Schäfer\"\n    }\n  ],\n  \"homepage\": \"https://github.com/svgdotjs/svg.draggable.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/svgdotjs/svg.draggable.js.git\"\n  },\n  \"scripts\": {\n    \"build\": \"npm run fix && vite build\",\n    \"fix\": \"npx eslint --fix\",\n    \"prepublishOnly\": \"rm -rf ./dist && npm run build\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.36.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"eslint-config-prettier\": \"^8.8.0\",\n    \"prettier\": \"^2.8.5\",\n    \"typescript\": \"^5.0.2\",\n    \"vite\": \"^4.2.1\"\n  },\n  \"peerDependencies\": {\n    \"@svgdotjs/svg.js\": \"^3.2.4\"\n  }\n}\n"
  },
  {
    "path": "src/svg.draggable.js",
    "content": "import { Box, Element, G, extend, off, on } from '@svgdotjs/svg.js'\n\nconst getCoordsFromEvent = (ev) => {\n  if (ev.changedTouches) {\n    ev = ev.changedTouches[0]\n  }\n  return { x: ev.clientX, y: ev.clientY }\n}\n\n// Creates handler, saves it\nclass DragHandler {\n  constructor(el) {\n    el.remember('_draggable', this)\n    this.el = el\n\n    this.drag = this.drag.bind(this)\n    this.startDrag = this.startDrag.bind(this)\n    this.endDrag = this.endDrag.bind(this)\n  }\n\n  // Enables or disabled drag based on input\n  init(enabled) {\n    if (enabled) {\n      this.el.on('mousedown.drag', this.startDrag)\n      this.el.on('touchstart.drag', this.startDrag, { passive: false })\n    } else {\n      this.el.off('mousedown.drag')\n      this.el.off('touchstart.drag')\n    }\n  }\n\n  // Start dragging\n  startDrag(ev) {\n    const isMouse = !ev.type.indexOf('mouse')\n\n    // Check for left button\n    if (isMouse && ev.which !== 1 && ev.buttons !== 0) {\n      return\n    }\n\n    // Fire beforedrag event\n    if (\n      this.el.dispatch('beforedrag', { event: ev, handler: this })\n        .defaultPrevented\n    ) {\n      return\n    }\n\n    // Prevent browser drag behavior as soon as possible\n    ev.preventDefault()\n\n    // Prevent propagation to a parent that might also have dragging enabled\n    ev.stopPropagation()\n\n    // Make sure that start events are unbound so that one element\n    // is only dragged by one input only\n    this.init(false)\n\n    this.box = this.el.bbox()\n    this.lastClick = this.el.point(getCoordsFromEvent(ev))\n\n    const eventMove = (isMouse ? 'mousemove' : 'touchmove') + '.drag'\n    const eventEnd = (isMouse ? 'mouseup' : 'touchend') + '.drag'\n\n    // Bind drag and end events to window\n    on(window, eventMove, this.drag, this, { passive: false })\n    on(window, eventEnd, this.endDrag, this, { passive: false })\n\n    // Fire dragstart event\n    this.el.fire('dragstart', { event: ev, handler: this, box: this.box })\n  }\n\n  // While dragging\n  drag(ev) {\n    const { box, lastClick } = this\n\n    const currentClick = this.el.point(getCoordsFromEvent(ev))\n    const dx = currentClick.x - lastClick.x\n    const dy = currentClick.y - lastClick.y\n\n    if (!dx && !dy) return box\n\n    const x = box.x + dx\n    const y = box.y + dy\n    this.box = new Box(x, y, box.w, box.h)\n    this.lastClick = currentClick\n\n    if (\n      this.el.dispatch('dragmove', {\n        event: ev,\n        handler: this,\n        box: this.box,\n        dx,\n        dy,\n      }).defaultPrevented\n    ) {\n      return\n    }\n\n    this.move(x, y)\n  }\n\n  move(x, y) {\n    // Svg elements bbox depends on their content even though they have\n    // x, y, width and height - strange!\n    // Thats why we handle them the same as groups\n    if (this.el.type === 'svg') {\n      G.prototype.move.call(this.el, x, y)\n    } else {\n      this.el.move(x, y)\n    }\n  }\n\n  endDrag(ev) {\n    // final drag\n    this.drag(ev)\n\n    // fire dragend event\n    this.el.fire('dragend', { event: ev, handler: this, box: this.box })\n\n    // unbind events\n    off(window, 'mousemove.drag')\n    off(window, 'touchmove.drag')\n    off(window, 'mouseup.drag')\n    off(window, 'touchend.drag')\n\n    // Rebind initial Events\n    this.init(true)\n  }\n}\n\nextend(Element, {\n  draggable(enable = true) {\n    const dragHandler = this.remember('_draggable') || new DragHandler(this)\n    dragHandler.init(enable)\n    return this\n  },\n})\n"
  },
  {
    "path": "svg.draggable.js.d.cts",
    "content": "import { Element } from '@svgdotjs/svg.js'\n\ndeclare module '@svgdotjs/svg.js' {\n  interface Element {\n    draggable(enable?: boolean): this\n  }\n}\n"
  },
  {
    "path": "svg.draggable.js.d.ts",
    "content": "import { Element } from '@svgdotjs/svg.js'\n\ndeclare module '@svgdotjs/svg.js' {\n  interface Element {\n    draggable(enable?: boolean): this\n  }\n}\n"
  },
  {
    "path": "vite.config.ts",
    "content": "import { defineConfig } from 'vite'\n\nimport pkg from './package.json'\nconst buildDate = Date()\n\nconst headerLong = `/*!\n* ${pkg.name} - ${pkg.description}\n* @version ${pkg.version}\n* ${pkg.homepage}\n*\n* @copyright ${pkg.author.name}\n* @license ${pkg.license}\n*\n* BUILT: ${buildDate}\n*/;`\n\nexport default defineConfig({\n  build: {\n    sourcemap: true,\n    lib: {\n      entry: 'src/svg.draggable.js',\n      name: 'SVG',\n      formats: ['umd'],\n      fileName: () => `svg.draggable.js`,\n    },\n    rollupOptions: {\n      output: {\n        globals: {\n          '@svgdotjs/svg.js': 'SVG',\n        },\n        banner: headerLong,\n      },\n      external: ['@svgdotjs/svg.js'],\n    },\n  },\n})\n"
  }
]