]> _ Git - fluidbook-toolbox.git/commitdiff
wip #4214 @5
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 22 Sep 2022 14:59:20 +0000 (16:59 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 22 Sep 2022 14:59:20 +0000 (16:59 +0200)
16 files changed:
package-lock.json
public/packages/fluidbook/toolbox/css/linkeditor.css [deleted file]
public/packages/fluidbook/toolbox/css/linkeditor.css.map [deleted file]
public/packages/fluidbook/toolbox/css/linkeditor.less [deleted file]
public/packages/fluidbook/toolbox/js/linkeditor.js [deleted file]
resources/linkeditor/js/linkeditor.js
resources/linkeditor/js/linkeditor.links.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.loader.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.resize.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.rulers.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.save.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.toolbar.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.utils.js [new file with mode: 0644]
resources/linkeditor/js/linkeditor.zoom.js [new file with mode: 0644]
resources/linkeditor/style/links.sass
resources/views/fluidbook_publication/link_editor.blade.php

index 6d2a75db389d9a5542cdbe7c11ee2b64a442e1fa..3db959012f688c08a6f52fae72817978c5ae8081 100644 (file)
@@ -7,12 +7,14 @@
             "dependencies": {
                 "command-line-args": "^5.2.1",
                 "jquery.scrollto": "^2.1.3",
+                "keymaster": "^1.6.2",
                 "lz4js": "^0.2.0",
                 "pako": "^2.0.4",
                 "pdfjs-dist": "^2.14.305",
                 "pipwerks-scorm-api-wrapper": "^0.1.2",
                 "plyr": "^3.7.2",
-                "puppeteer": "^17.0.0"
+                "puppeteer": "^17.0.0",
+                "tippy.js": "^6.3.7"
             },
             "devDependencies": {
                 "axios": "^0.19",
                 "node": ">= 8"
             }
         },
+        "node_modules/@popperjs/core": {
+            "version": "2.11.6",
+            "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+            "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/popperjs"
+            }
+        },
         "node_modules/@types/glob": {
             "version": "7.2.0",
             "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
                 "graceful-fs": "^4.1.6"
             }
         },
+        "node_modules/keymaster": {
+            "version": "1.6.2",
+            "resolved": "https://registry.npmjs.org/keymaster/-/keymaster-1.6.2.tgz",
+            "integrity": "sha512-OvA/AALN8IDKKkTk2Z+bDrzs/SQao4lo/QPbwSdDvm+frxfiYiYCSn1aHFUypJY3SruAO1y/c771agBmTXqUtg=="
+        },
         "node_modules/killable": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
             "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==",
             "dev": true
         },
+        "node_modules/tippy.js": {
+            "version": "6.3.7",
+            "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
+            "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+            "dependencies": {
+                "@popperjs/core": "^2.9.0"
+            }
+        },
         "node_modules/to-arraybuffer": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
                 "fastq": "^1.6.0"
             }
         },
+        "@popperjs/core": {
+            "version": "2.11.6",
+            "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+            "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
+        },
         "@types/glob": {
             "version": "7.2.0",
             "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
                 "graceful-fs": "^4.1.6"
             }
         },
+        "keymaster": {
+            "version": "1.6.2",
+            "resolved": "https://registry.npmjs.org/keymaster/-/keymaster-1.6.2.tgz",
+            "integrity": "sha512-OvA/AALN8IDKKkTk2Z+bDrzs/SQao4lo/QPbwSdDvm+frxfiYiYCSn1aHFUypJY3SruAO1y/c771agBmTXqUtg=="
+        },
         "killable": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
             "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==",
             "dev": true
         },
+        "tippy.js": {
+            "version": "6.3.7",
+            "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
+            "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+            "requires": {
+                "@popperjs/core": "^2.9.0"
+            }
+        },
         "to-arraybuffer": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
diff --git a/public/packages/fluidbook/toolbox/css/linkeditor.css b/public/packages/fluidbook/toolbox/css/linkeditor.css
deleted file mode 100644 (file)
index 03c0032..0000000
+++ /dev/null
@@ -1,553 +0,0 @@
-* {
-  margin: 0;
-  padding: 0;
-  box-sizing: border-box;
-}
-body {
-  background-color: #ebecee;
-}
-@media (prefers-color-scheme: dark) {
-  body {
-    background-color: #333;
-  }
-}
-img,
-.division,
-.info {
-  user-select: none;
-  user-drag: none;
-}
-body,
-#linkeditor,
-html {
-  height: 100%;
-  width: 100%;
-  overflow: hidden;
-}
-#linkeditor {
-  white-space: nowrap;
-  font-size: 0;
-  overflow: hidden;
-}
-#linkeditor aside,
-#linkeditor #linkeditor-main {
-  display: inline-block;
-  height: 100%;
-  vertical-align: top;
-  text-align: left;
-}
-#linkeditor aside {
-  width: 40px;
-  background-color: #EBECEE;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor aside {
-    background-color: #333;
-  }
-}
-#linkeditor #linkeditor-main {
-  width: calc(100% - 40px - 40px);
-}
-#linkeditor #linkeditor-main.grab {
-  cursor: grab;
-}
-#linkeditor #linkeditor-main.grabbing {
-  cursor: grabbing;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar {
-  background-color: #dbdddf;
-  color: #5d5d5d;
-  height: 40px;
-  padding: 5px;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-toolbar {
-    background-color: #444;
-  }
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-toolbar {
-    color: #bbb;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar nav {
-  padding: 2px;
-  display: inline-block;
-  height: 26px;
-  vertical-align: top;
-  width: 33%;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar nav#linkeditor-toolbar-center {
-  text-align: center;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar div {
-  display: inline-block;
-  font-size: 16px;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar .separator {
-  width: 0;
-  height: 26px;
-  margin: 0 2px;
-  border-left: 1px solid #5d5d5d;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field {
-  padding: 2px 10px;
-  background-color: #fff;
-  border: 1px solid #ccc;
-  border-radius: 5px;
-  cursor: text;
-  font-size: 13px;
-  position: relative;
-  top: -9px;
-  user-select: none;
-  margin: 0 8px;
-  font-weight: 600;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field {
-    background-color: #000;
-    border-color: #333;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field input {
-  vertical-align: top;
-  text-align: right;
-  border: 0;
-  background-color: transparent;
-  width: 20px;
-  color: #5d5d5d;
-  font-size: 13px;
-  appearance: textfield;
-  font-weight: 600;
-  position: relative;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field input {
-    color: #bbb;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field input::-webkit-outer-spin-button,
-#linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field input::-webkit-inner-spin-button {
-  -webkit-appearance: none;
-  margin: 0;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field input:hover,
-#linkeditor #linkeditor-main #linkeditor-toolbar #linkeditor-page-field input:focus {
-  outline: 0;
-  border: 0;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar [data-icon] {
-  display: inline-block;
-  vertical-align: top;
-  height: 26px;
-  min-width: 26px;
-  padding: 3px;
-  border-radius: 5px;
-  margin: 0 3px;
-  text-align: center;
-  color: #5d5d5d;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-toolbar [data-icon] {
-    color: #bbb;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar [data-icon]:hover,
-#linkeditor #linkeditor-main #linkeditor-toolbar [data-icon].hover {
-  background-color: #fff;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-toolbar [data-icon]:hover,
-  #linkeditor #linkeditor-main #linkeditor-toolbar [data-icon].hover {
-    background-color: #000;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar [data-icon].arrow {
-  padding: 1px;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar [data-icon].arrow svg {
-  height: 16px;
-}
-#linkeditor #linkeditor-main #linkeditor-toolbar [data-icon] svg {
-  position: relative;
-  top: 1px;
-  height: 18px;
-  width: auto;
-}
-#linkeditor #linkeditor-main #linkeditor-editor {
-  position: relative;
-  height: calc( 100% - 40px);
-  width: 100%;
-  overflow: hidden;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links {
-  position: relative;
-  z-index: 500;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link {
-  position: absolute;
-  outline: 1px solid currentColor;
-  cursor: cell;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link.selected .corners {
-  visibility: visible;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners {
-  visibility: hidden;
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div {
-  position: absolute;
-  outline: 1px solid currentColor;
-  background-color: #fff;
-  width: 8px;
-  height: 8px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.nw {
-  cursor: nw-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.n {
-  cursor: n-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.ne {
-  cursor: ne-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.e {
-  cursor: e-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.se {
-  cursor: se-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.s {
-  cursor: s-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.sw {
-  cursor: sw-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.w {
-  cursor: w-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.n,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.nw,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.ne {
-  top: -4px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.e,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.w {
-  top: calc(50% - 4px);
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.sw,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.s,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.se {
-  bottom: -4px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.nw,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.w,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.sw {
-  left: -4px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.ne,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.e,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.se {
-  right: -4px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.n,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-links .link .corners > div.s {
-  left: calc(50% - 4px);
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler {
-  position: absolute;
-  top: 0;
-  left: 0;
-  z-index: 600;
-  border-width: 0;
-  border-color: #0f0;
-  border-style: solid;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler:after {
-  position: absolute;
-  content: "";
-  display: block;
-  height: 100%;
-  width: 100%;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler.pending-delete {
-  border-color: #f00 !important;
-  z-index: 1100;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler:hover {
-  border-color: #0ff;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler[data-axis="x"] {
-  width: 0px;
-  height: calc( 100% - 16px);
-  border-left-width: 1px;
-  cursor: col-resize;
-  top: 16px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler[data-axis="x"]:after {
-  left: -2px;
-  width: 4px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler[data-axis="y"] {
-  border-bottom-width: 1px;
-  height: 0px;
-  width: calc( 100% - 16px);
-  left: 16px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor .ruler[data-axis="y"]:after {
-  top: -2px;
-  height: 4px;
-  cursor: row-resize;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers {
-  color: #333;
-  position: absolute;
-  top: 0px;
-  left: 0px;
-  width: 100%;
-  height: 100%;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers {
-    color: #eee;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-corner {
-  position: absolute;
-  top: 0px;
-  left: 0px;
-  width: 16px;
-  height: 16px;
-  z-index: 5;
-  background-color: #aaa;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-corner {
-    background-color: #666;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar {
-  overflow: hidden;
-  position: absolute;
-  left: 0;
-  top: 0;
-  z-index: 1000;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .info {
-  position: absolute;
-  top: 0;
-  left: 0;
-  z-index: 3;
-  font-size: 12px;
-  line-height: 8px;
-  display: none;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .info span {
-  display: block;
-  position: absolute;
-  top: 0;
-  left: 0;
-  background-color: #fff;
-  color: #000;
-  padding: 3px;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .info span {
-    background-color: #000;
-    color: #fff;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .division {
-  pointer-events: none;
-  position: absolute;
-  background: #fff;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .division {
-    background-color: #000;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .division .value {
-  position: absolute;
-  font-size: 12px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers .ruler-bar .division .subdivision {
-  position: absolute;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x {
-  height: 16px;
-  width: 100%;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .info {
-  height: 16px;
-  border-left: 1px dotted #333;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .info {
-    border-color: #eee;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .division,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .subdivision {
-  width: 0px;
-  border-left: 1px solid #333;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .division,
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .subdivision {
-    border-color: #eee;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .division {
-  height: 16px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .division .value {
-  bottom: 0px;
-  left: 3px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .subdivision {
-  bottom: 0;
-  height: 2px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-x .subdivision.middle {
-  height: 5px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y {
-  width: 16px;
-  height: 100%;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .info {
-  width: 16px;
-  border-bottom: 1px dotted #333;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .info {
-    border-color: #eee;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .info span {
-  transform-origin: 0 0;
-  transform: rotate(270deg);
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .division,
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .subdivision {
-  height: 0px;
-  border-bottom: 1px solid #333;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .division,
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .subdivision {
-    border-color: #eee;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .division {
-  width: 16px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .division .value {
-  text-align: center;
-  max-width: 16px;
-  word-wrap: break-word;
-  white-space: normal;
-  line-height: 10px;
-  letter-spacing: 30px;
-  top: 3px;
-  left: 3px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .division .subdivision {
-  right: 0;
-  width: 2px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-rulers #linkeditor-ruler-y .division .subdivision.middle {
-  width: 5px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-zoom {
-  width: 100%;
-  height: 100%;
-  max-width: 100%;
-  max-height: 100%;
-  min-height: 100%;
-  min-width: 100%;
-  transform-origin: 0 0;
-  overflow: hidden;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas {
-  background-color: #505050;
-  position: relative;
-  z-index: 1;
-  top: 16px;
-  left: 16px;
-  height: calc( 100% - 16px);
-  max-height: calc( 100% - 16px);
-  width: calc( 100% - 16px);
-  max-width: calc( 100% - 16px);
-  overflow: auto;
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas {
-    background-color: #222;
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas::-webkit-scrollbar {
-  width: 6px;
-  height: 6px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas::-webkit-scrollbar-track {
-  background: transparent;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas::-webkit-scrollbar-track:hover {
-  background-color: #000;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas::-webkit-scrollbar-thumb {
-  background-color: #aaa;
-  border: 1px solid #333;
-  border-radius: 20px;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook {
-  transform-origin: 0 0;
-  position: absolute;
-  top: 0;
-  left: 0;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook .linkeditor-page {
-  position: absolute;
-  top: 0px;
-  left: 0px;
-  background-color: rgba(255, 255, 255, 0.2);
-}
-@media (prefers-color-scheme: dark) {
-  #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook .linkeditor-page {
-    background-color: rgba(0, 0, 0, 0.2);
-  }
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook .linkeditor-page .contents {
-  background-color: #fff;
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook .linkeditor-page .contents img {
-  display: block;
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: auto;
-  z-index: 1;
-}
-#linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook .linkeditor-page .contents img.texts {
-  z-index: 2;
-}
-.simple #linkeditor #linkeditor-main #linkeditor-editor #linkeditor-canvas #linkeditor-fluidbook .linkeditor-page#linkeditor-page-right {
-  display: none;
-}
-#linkeditor-preload {
-  display: none;
-}
-/*# sourceMappingURL=linkeditor.css.map */
\ No newline at end of file
diff --git a/public/packages/fluidbook/toolbox/css/linkeditor.css.map b/public/packages/fluidbook/toolbox/css/linkeditor.css.map
deleted file mode 100644 (file)
index e8ef77f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["linkeditor.less"],"names":[],"mappings":"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","file":"linkeditor.css"}
\ No newline at end of file
diff --git a/public/packages/fluidbook/toolbox/css/linkeditor.less b/public/packages/fluidbook/toolbox/css/linkeditor.less
deleted file mode 100644 (file)
index f21e168..0000000
+++ /dev/null
@@ -1,615 +0,0 @@
-* {
-    margin: 0;
-    padding: 0;
-    box-sizing: border-box;
-}
-
-@font-size: 16px;
-
-body {
-    background-color: #ebecee;
-    @media (prefers-color-scheme: dark) {
-        background-color: #333;
-    }
-}
-
-img, .division, .info {
-    user-select: none;
-    user-drag: none;
-}
-
-body, #linkeditor, html {
-    height: 100%;
-    width: 100%;
-    overflow: hidden;
-}
-
-#linkeditor {
-    @sidebar-width: 40px;
-    @rulers-size: 16px;
-    white-space: nowrap;
-    font-size: 0;
-    overflow: hidden;
-
-    aside, #linkeditor-main {
-        display: inline-block;
-        height: 100%;
-        vertical-align: top;
-        text-align: left;
-    }
-
-    aside {
-        width: @sidebar-width;
-        background-color: #EBECEE;
-        @media (prefers-color-scheme: dark) {
-            background-color: #333;
-        }
-    }
-
-    #linkeditor-main {
-        @toolbar-height: 40px;
-
-        width: ~"calc(100% - @{sidebar-width} - @{sidebar-width})";
-
-        &.grab {
-            cursor: grab;
-        }
-
-        &.grabbing {
-            cursor: grabbing;
-        }
-
-        #linkeditor-toolbar {
-
-            @toolbar-color: #5d5d5d;
-            @toolbar-color-dark: #bbb;
-            background-color: #dbdddf;
-
-            @media (prefers-color-scheme: dark) {
-                background-color: #444;
-            }
-            color: @toolbar-color;
-            @media (prefers-color-scheme: dark) {
-                color: @toolbar-color-dark;
-            }
-            height: @toolbar-height;
-            padding: 5px;
-
-            nav {
-                padding: 2px;
-                display: inline-block;
-                height: 26px;
-                vertical-align: top;
-                width: 33%;
-
-                &#linkeditor-toolbar-center {
-                    text-align: center;
-                }
-
-                &#linkeditor-toolbar-right {
-
-                }
-            }
-
-            div {
-                display: inline-block;
-                font-size: @font-size;
-            }
-
-            .separator {
-                width: 0;
-                height: 26px;
-                margin: 0 2px;
-                border-left: 1px solid @toolbar-color;
-            }
-
-            #linkeditor-page-field {
-                padding: 2px 10px;
-                background-color: #fff;
-                @media (prefers-color-scheme: dark) {
-                    background-color: #000;
-                    border-color: #333;
-                }
-                border: 1px solid #ccc;
-                border-radius: 5px;
-                cursor: text;
-                font-size: 13px;
-                position: relative;
-                top: -9px;
-                user-select: none;
-                margin: 0 8px;
-                font-weight: 600;
-
-                
-                input {
-                    vertical-align: top;
-                    text-align: right;
-                    border: 0;
-                    background-color: transparent;
-                    width: 20px;
-                    color: @toolbar-color;
-                    @media (prefers-color-scheme: dark) {
-                        color: @toolbar-color-dark;
-                    }
-                    font-size: 13px;
-                    appearance: textfield;
-
-                    &::-webkit-outer-spin-button, &::-webkit-inner-spin-button {
-                        -webkit-appearance: none;
-                        margin: 0;
-                    }
-
-                    font-weight: 600;
-                    position: relative;
-
-
-                    &:hover, &:focus {
-                        outline: 0;
-                        border: 0;
-                    }
-                }
-            }
-
-            [data-icon] {
-                display: inline-block;
-                vertical-align: top;
-                height: 26px;
-                min-width: 26px;
-                padding: 3px;
-                border-radius: 5px;
-                margin: 0 3px;
-                text-align: center;
-                color: @toolbar-color;
-                @media (prefers-color-scheme: dark) {
-                    color: @toolbar-color-dark;
-                }
-
-                &:hover, &.hover {
-                    background-color: #fff;
-                    @media (prefers-color-scheme: dark) {
-                        background-color: #000;
-                    }
-                }
-
-                &.arrow {
-                    padding: 1px;
-
-                    svg {
-                        height: 16px;
-                    }
-                }
-
-                svg {
-                    position: relative;
-                    top: 1px;
-                    height: 18px;
-                    width: auto;
-                }
-            }
-        }
-
-        #linkeditor-editor {
-            position: relative;
-            height: ~"calc( 100% - @{toolbar-height})";
-            width: 100%;
-            overflow: hidden;
-
-            @ruler-margin: 2px;
-
-            #linkeditor-links {
-                .link {
-                    position: absolute;
-                    outline: 1px solid currentColor;
-                    cursor: cell;
-
-                    &.selected {
-                        .corners {
-                            visibility: visible;
-                        }
-                    }
-
-                    .corners {
-                        visibility: hidden;
-                        position: absolute;
-                        top: 0;
-                        left: 0;
-                        width: 100%;
-                        height: 100%;
-
-                        > div {
-                            position: absolute;
-                            outline: 1px solid currentColor;
-                            background-color: #fff;
-                            width: 8px;
-                            height: 8px;
-
-                            &.nw {
-                                cursor: nw-resize;
-                            }
-
-                            &.n {
-                                cursor: n-resize;
-                            }
-
-                            &.ne {
-                                cursor: ne-resize;
-                            }
-
-                            &.e {
-                                cursor: e-resize;
-                            }
-
-                            &.se {
-                                cursor: se-resize;
-                            }
-
-                            &.s {
-                                cursor: s-resize;
-                            }
-
-                            &.sw {
-                                cursor: sw-resize;
-                            }
-
-                            &.w {
-                                cursor: w-resize;
-                            }
-
-                            &.n, &.nw, &.ne {
-                                top: -4px;
-                            }
-
-                            &.e, &.w {
-                                top: calc(50% - 4px);
-                            }
-
-                            &.sw, &.s, &.se {
-                                bottom: -4px;
-                            }
-
-                            &.nw, &.w, &.sw {
-                                left: -4px;
-                            }
-
-                            &.ne, &.e, &.se {
-                                right: -4px;
-                            }
-
-                            &.n, &.s {
-                                left: calc(50% - 4px);
-                            }
-                        }
-                    }
-                }
-
-                position: relative;
-                z-index: 500;
-            }
-
-            .ruler {
-                position: absolute;
-                top: 0;
-                left: 0;
-                z-index: 600;
-                border-width: 0;
-                border-color: #0f0;
-                border-style: solid;
-
-                &:after {
-                    position: absolute;
-                    content: "";
-                    display: block;
-                    height: 100%;
-                    width: 100%;
-                }
-
-                &.pending-delete {
-                    border-color: #f00 !important;
-                    z-index: 1100;
-                }
-
-                &:hover {
-                    border-color: #0ff;
-                }
-
-                &[data-axis="x"] {
-                    width: 0px;
-                    height: ~"calc( 100% - @{rulers-size})";
-                    border-left-width: 1px;
-                    cursor: col-resize;
-                    top: @rulers-size;
-
-                    &:after {
-                        left: @ruler-margin*-1;
-                        width: @ruler-margin*2;
-                    }
-                }
-
-                &[data-axis="y"] {
-                    border-bottom-width: 1px;
-                    height: 0px;
-                    width: ~"calc( 100% - @{rulers-size})";
-                    left: @rulers-size;
-
-                    &:after {
-                        top: @ruler-margin*-1;
-                        height: @ruler-margin*2;
-                        cursor: row-resize;
-                    }
-                }
-            }
-
-
-            #linkeditor-rulers {
-                @rulers-color: #333;
-                @rulers-color-dark: #eee;
-
-                @media (prefers-color-scheme: dark) {
-                    color: @rulers-color-dark;
-                }
-
-                color: @rulers-color;
-                position: absolute;
-                top: 0px;
-                left: 0px;
-                width: 100%;
-                height: 100%;
-
-                #linkeditor-ruler-corner {
-                    position: absolute;
-                    top: 0px;
-                    left: 0px;
-                    width: @rulers-size;
-                    height: @rulers-size;
-                    z-index: 5;
-                    background-color: #aaa;
-                    @media (prefers-color-scheme: dark) {
-                        background-color: #666;
-                    }
-
-                }
-
-                .ruler-bar {
-                    overflow: hidden;
-                    position: absolute;
-                    left: 0;
-                    top: 0;
-                    z-index: 1000;
-
-                    .info {
-                        position: absolute;
-                        top: 0;
-                        left: 0;
-                        z-index: 3;
-                        font-size: 12px;
-                        line-height: 8px;
-                        display: none;
-
-                        span {
-                            display: block;
-                            position: absolute;
-                            top: 0;
-                            left: 0;
-                            background-color: #fff;
-                            color: #000;
-                            @media (prefers-color-scheme: dark) {
-                                background-color: #000;
-                                color: #fff;
-                            }
-                            padding: 3px;
-                        }
-                    }
-
-                    .division {
-                        pointer-events: none;
-                        position: absolute;
-                        background: #fff;
-                        @media (prefers-color-scheme: dark) {
-                            background-color: #000;
-                        }
-
-                        .value {
-                            position: absolute;
-                            font-size: 12px;
-                        }
-
-                        .subdivision {
-                            position: absolute;
-                        }
-                    }
-                }
-
-                #linkeditor-ruler-x {
-                    height: @rulers-size;
-                    width: 100%;
-
-                    .info {
-                        height: @rulers-size;
-                        border-left: 1px dotted @rulers-color;
-                        @media (prefers-color-scheme: dark) {
-                            border-color: @rulers-color-dark;
-                        }
-                    }
-
-                    .division, .subdivision {
-                        width: 0px;
-                        border-left: 1px solid @rulers-color;
-                        @media (prefers-color-scheme: dark) {
-                            border-color: @rulers-color-dark;
-                        }
-                    }
-
-                    .division {
-                        height: @rulers-size;
-
-                        .value {
-                            bottom: 0px;
-                            left: 3px;
-                        }
-                    }
-
-                    .subdivision {
-                        bottom: 0;
-                        height: 2px;
-
-                        &.middle {
-                            height: 5px;
-                        }
-                    }
-                }
-
-                #linkeditor-ruler-y {
-                    width: @rulers-size;
-                    height: 100%;
-
-                    .info {
-                        width: @rulers-size;
-                        border-bottom: 1px dotted @rulers-color;
-                        @media (prefers-color-scheme: dark) {
-                            border-color: @rulers-color-dark;
-                        }
-
-                        span {
-                            transform-origin: 0 0;
-                            transform: rotate(270deg);
-                        }
-                    }
-
-                    .division, .subdivision {
-                        height: 0px;
-                        border-bottom: 1px solid @rulers-color;
-                        @media (prefers-color-scheme: dark) {
-                            border-color: @rulers-color-dark;
-                        }
-                    }
-
-                    .division {
-                        width: @rulers-size;
-
-                        .value {
-                            text-align: center;
-                            max-width: @rulers-size;
-                            word-wrap: break-word;
-                            white-space: normal;
-                            line-height: 10px;
-                            letter-spacing: 30px;
-                            top: 3px;
-                            left: 3px;
-                        }
-
-                        .subdivision {
-                            right: 0;
-                            width: 2px;
-
-                            &.middle {
-                                width: 5px;
-                            }
-                        }
-                    }
-                }
-            }
-
-            #linkeditor-zoom {
-                width: 100%;
-                height: 100%;
-                max-width: 100%;
-                max-height: 100%;
-                min-height: 100%;
-                min-width: 100%;
-                transform-origin: 0 0;
-                overflow: hidden;
-            }
-
-            #linkeditor-canvas {
-                background-color: #505050;
-                @media (prefers-color-scheme: dark) {
-                    background-color: #222;
-                }
-
-                position: relative;
-                z-index: 1;
-                top: @rulers-size;
-                left: @rulers-size;
-                height: ~"calc( 100% - @{rulers-size})";
-                max-height: ~"calc( 100% - @{rulers-size})";
-                width: ~"calc( 100% - @{rulers-size})";
-                max-width: ~"calc( 100% - @{rulers-size})";
-                overflow: auto;
-
-                &::-webkit-scrollbar {
-                    width: 6px;
-                    height: 6px;
-                }
-
-                &::-webkit-scrollbar-track {
-                    background: transparent;
-
-                    &:hover {
-                        background-color: #000;
-                    }
-                }
-
-                &::-webkit-scrollbar-thumb {
-                    background-color: #aaa;
-                    border: 1px solid #333;
-                    border-radius: 20px;
-                }
-
-
-                #linkeditor-fluidbook {
-                    transform-origin: 0 0;
-                    position: absolute;
-                    top: 0;
-                    left: 0;
-
-                    .linkeditor-page {
-                        position: absolute;
-                        top: 0px;
-                        left: 0px;
-                        background-color: rgba(255, 255, 255, 0.2);
-                        @media (prefers-color-scheme: dark) {
-                            background-color: rgba(0, 0, 0, 0.2);
-                        }
-
-
-                        .contents {
-                            background-color: #fff;
-                            position: absolute;
-                            top: 0;
-                            left: 0;
-                            width: 100%;
-                            height: 100%;
-
-                            img {
-                                display: block;
-                                position: absolute;
-                                top: 0;
-                                left: 0;
-                                width: 100%;
-                                height: auto;
-                                z-index: 1;
-
-                                &.texts {
-                                    z-index: 2;
-                                }
-                            }
-                        }
-
-
-                        &#linkeditor-page-right {
-                            .simple & {
-                                display: none;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-
-}
-
-#linkeditor-preload {
-    display: none;
-}
diff --git a/public/packages/fluidbook/toolbox/js/linkeditor.js b/public/packages/fluidbook/toolbox/js/linkeditor.js
deleted file mode 100644 (file)
index 7695d02..0000000
+++ /dev/null
@@ -1,768 +0,0 @@
-var single = ['mobilefirst', 'portrait'].indexOf(FLUIDBOOK_DATA.settings.mobileNavigationType) >= 0;
-var pw = FLUIDBOOK_DATA.settings.width;
-var ph = FLUIDBOOK_DATA.settings.height;
-var fw = pw * (single ? 1 : 2);
-var fh = ph;
-var fs = 1;
-var mx = 0, my = 0;
-var zoom = 1;
-var zoomdragging = false;
-var dividers = [1, 2, 5, 10, 20, 50, 100, 200];
-var fluidbookRect, canvasRect, editorRect;
-var currentPage;
-var movingRuler = null;
-var rasterizePages = splitPages(FLUIDBOOK_DATA.settings.rasterizePages);
-var vectorPages = splitPages(FLUIDBOOK_DATA.settings.vectorPages);
-var unsavedChanges = false;
-var automaticSaveTimeout;
-var runningAutomaticSaveTimeout = false;
-var dimensionProperties = ['left', 'top', 'width', 'height'];
-var rulersMagnetValuesX = [];
-var rulersMagnetValuesY = [];
-var currentSelection = [];
-
-$(function () {
-    init();
-});
-
-
-function init() {
-    $(window).on('hashchange', function () {
-        changePage();
-    });
-    $(window).on('resize', function () {
-        resize();
-    });
-    $(window).on('beforeunload', function () {
-        if (unsavedChanges) {
-            return TRANSLATIONS.warning_unsaved_changes;
-        }
-    })
-    // Disable scroll by spacebar
-    $(window).on('keydown', function (e) {
-        if (e.keyCode == 32) {
-            $("#linkeditor-main").addClass('grab');
-            return false;
-        }
-        moveRuler();
-    });
-    $(window).on('keyup', function (e) {
-        if (e.keyCode == 32) {
-            resetZoomDrag();
-        }
-        moveRuler();
-    });
-
-    $("#linkeditor-canvas").on('scroll', function () {
-        updateRulers();
-    });
-    $("#linkeditor-main").on('mousedown', function (e) {
-        if ($(this).hasClass('grab') && zoom > 1) {
-            zoomdragging = {
-                x: e.pageX,
-                y: e.pageY,
-                scrollX: $("#linkeditor-canvas").scrollLeft(),
-                scrollY: $("#linkeditor-canvas").scrollTop()
-            };
-            $(this).addClass('grabbing');
-        } else {
-            resetZoomDrag();
-        }
-        deselectAllLinks();
-    });
-
-    $(document).on('mousedown', '.link', function (e) {
-        if (!e.ctrlKey) {
-            deselectAllLinks();
-        }
-        selectLink($(this));
-    });
-
-    $("#linkeditor-ruler-x").on('mousedown', function (e) {
-        addRuler('y');
-    });
-
-    $("#linkeditor-ruler-y").on('mousedown', function (e) {
-        addRuler('x');
-    });
-
-    $(document).on('mousedown', ".ruler", function (e) {
-        movingRuler = $(this);
-
-    });
-
-    $(window).on('mousemove', function (e) {
-        if (!$("#linkeditor-main").hasClass('grab') || zoom === 1) {
-            resetZoomDrag();
-        }
-        if (zoomdragging !== false) {
-            moveZoomDrag(e);
-        }
-        updateMousePosition(e);
-    });
-    $(window).on('mouseup', function (e) {
-        if (zoomdragging !== false) {
-            moveZoomDrag(e);
-        }
-        stopMoveRuler();
-        resetZoomDrag();
-    });
-    $("#linkeditor-main").on('wheel', function (e) {
-        var delta = e.originalEvent.deltaY;
-        if (delta == 0) {
-            return true;
-        }
-        e.stopPropagation();
-        e.stopImmediatePropagation();
-        e.preventDefault();
-        if (delta < 0) {
-            if (setZoom(zoom + 0.25)) {
-                moveZoom(e);
-            }
-        } else {
-            setZoom(zoom - 0.25);
-        }
-        return false;
-    });
-
-    if (single) {
-        $("#linkeditor").addClass('single');
-    } else {
-        $("#linkeditor").addClass('double');
-    }
-    $('.linkeditor-page').css({
-        width: pw, height: ph
-    });
-    $("#linkeditor-page-right").css({left: pw})
-    $('#linkeditor-fluidbook').css({
-        width: fw, height: ph
-    });
-    initToolbar();
-    resize();
-    changePage();
-}
-
-function selectLink(l) {
-    if ($(l).find('.corners').length === 0) {
-        $(l).append('<div class="corners"><div class="nw"></div><div class="n"></div><div class="ne"></div><div class="e"></div><div class="se"></div><div class="s"></div><div class="sw"></div><div class="w"></div></div>')
-    }
-    $(l).addClass('selected');
-    currentSelection.push(l);
-}
-
-function deselectAllLinks() {
-    currentSelection = [];
-    $(".link.selected").removeClass('selected');
-}
-
-function initToolbar() {
-    $("#linkeditor-page-field input").on('change', function () {
-        changePage($(this).val());
-        $(this).blur();
-        return false;
-    });
-
-    $("#linkeditor-toolbar [data-icon]").each(function () {
-        $(this).append(getSpriteIcon('linkeditor-' + $(this).data('icon')));
-    });
-
-    $("[data-key]").each(function () {
-        var e = $(this);
-        key($(this).data('key'), function () {
-            $(e).addClass('hover');
-            runAction($(e).data('action'));
-            setTimeout(function () {
-                $(e).removeClass('hover')
-            }, 150);
-            return false;
-        });
-    });
-
-    $('[data-action]').click(function () {
-        runAction($(this).data('action'));
-        return false;
-    });
-
-    $('[data-tooltip]').each(function () {
-        tippy($(this).get(0), {content: $(this).data('tooltip')});
-    });
-}
-
-function runAction(act) {
-    window[act]();
-}
-
-function saveLinks(message) {
-    if (message === undefined) {
-        message = TRANSLATIONS.manual_save_message;
-    }
-    $.ajax({
-        url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/save/links', method: 'post', data: {
-            _method: 'put', 'message': message, rulers: window.RULERS, links: window.LINKS,
-        }, success: function (data) {
-            new Noty({
-                type: "success", text: TRANSLATIONS.success_save,
-            }).show();
-            clearTimeout(automaticSaveTimeout);
-            unsavedChanges = false;
-            runningAutomaticSaveTimeout = false;
-        }, error: function (jqXHR, status, error) {
-            hasChanged();
-            new Noty({
-                type: "error", text: TRANSLATIONS.error_save + ' : ' + error,
-            }).show();
-        },
-    });
-}
-
-function automaticSaveLinks() {
-    saveLinks(TRANSLATIONS.automatic_save_message);
-}
-
-function firstPage() {
-    changePage(1);
-}
-
-function nextPage() {
-    changePage(currentPage + (single ? 1 : 2));
-}
-
-function previousPage() {
-    changePage(currentPage - (single ? 1 : 2));
-}
-
-function lastPage() {
-    changePage(FLUIDBOOK_DATA.settings.pages);
-}
-
-function focusPageField() {
-    var i = $("#linkeditor-page-field input").get(0);
-    i.focus();
-    i.select();
-}
-
-function zoomReset() {
-    setZoom(1);
-    resetZoomDrag();
-}
-
-function generateUID() {
-    var length = 12;
-    var result = '';
-    var characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
-    var charactersLength = characters.length;
-    for (var i = 0; i < length; i++) {
-        result += characters.charAt(Math.floor(Math.random() * charactersLength));
-    }
-    return result;
-}
-
-function addRuler(axis, pos, uid) {
-    if (undefined === uid) {
-        uid = generateUID();
-        RULERS[uid] = {page: currentPage, type: axis, uid: uid};
-        hasChanged();
-    }
-    var ruler = $('<div class="ruler" data-uid="' + uid + '" fb-ref="editor" data-axis="' + axis + '"></div>');
-    if (pos === undefined) {
-        movingRuler = ruler;
-    } else {
-        var dim = axis == 'x' ? 'left' : 'top';
-        $(ruler).attr('fb-' + dim, pos);
-    }
-    $("#linkeditor-editor").append(ruler);
-    moveRuler();
-}
-
-function deleteRuler(ruler) {
-    if (ruler === undefined) {
-        ruler = movingRuler;
-    }
-    var uid = $(ruler).data('uid');
-    delete RULERS[uid];
-    $(ruler).remove();
-    movingRuler = null;
-    hasChanged();
-}
-
-function hasChanged() {
-    unsavedChanges = true;
-    if (runningAutomaticSaveTimeout === false) {
-        runningAutomaticSaveTimeout = true;
-        automaticSaveTimeout = setTimeout(function () {
-            automaticSaveLinks();
-        }, 1000 * 5 * 60);
-    }
-}
-
-function moveRuler() {
-    if (movingRuler === null || movingRuler === undefined) {
-        return;
-    }
-    var magnet = !key.ctrl;
-    var editorMouse = globalToEditor(mx, my);
-    var fbMouse = globalToFluidbook(mx, my, false);
-    var css = {};
-    var attrs = {};
-    var v, fbv;
-    if ($(movingRuler).data('axis') === 'x') {
-        v = editorMouse.x;
-        fbv = attrs['fb-left'] = magnet ? magnetize(fbMouse.x, rulersMagnetValuesX) : fbMouse.x;
-    } else {
-        v = editorMouse.y;
-        fbv = attrs['fb-top'] = magnet ? magnetize(fbMouse.y, rulersMagnetValuesY) : fbMouse.y;
-    }
-    if (v < 16) {
-        $(movingRuler).addClass('pending-delete');
-    } else {
-        $(movingRuler).removeClass('pending-delete');
-    }
-    $(movingRuler).css(css).attr(attrs);
-    RULERS[$(movingRuler).data('uid')].pos = fbv;
-    updateFBElements();
-}
-
-function magnetize(value, values) {
-    var sensibility = 8 / (zoom * fs);
-    var min = 100000;
-    var magnetValue;
-    if (values.length === 0) {
-        return value;
-    }
-
-    for (var i in values) {
-        var v = values[i];
-        var diff = Math.abs(v - value);
-        if (diff < min) {
-            min = diff;
-            magnetValue = v;
-        }
-    }
-
-    if (min > sensibility) {
-        return value;
-    }
-    return magnetValue;
-}
-
-function getLinksOfPage(p) {
-    let pages = [];
-    if (!single) {
-        if (p % 2 === 1) {
-            p--;
-        }
-        pages.push(p);
-        pages.push(p + 1);
-    } else {
-        pages.push(p);
-    }
-    let res = {};
-    $.each(LINKS, function (uid, link) {
-        if (pages.indexOf(parseInt(link.page)) >= 0) {
-            res[uid] = link;
-        }
-    });
-    return res;
-}
-
-function updateFBElements() {
-    $('[fb-ref]').each(function () {
-        var e = $(this);
-        var rect = $(this).attr('fb-ref');
-        var css = {};
-        $.each(dimensionProperties, function (k, dim) {
-            if ($(e).is('[fb-' + dim + ']')) {
-                var v = parseFloat($(e).attr('fb-' + dim));
-                if (dim === 'width' || dim === 'height') {
-                    css[dim] = v * (fs * zoom);
-                } else {
-                    css[dim] = fluidbookTo(v, dim, rect);
-                }
-            }
-        });
-        $(e).css(css);
-    });
-}
-
-function fluidbookTo(dim, name, rect) {
-    switch (rect) {
-        case 'editor':
-            rect = editorRect;
-            break;
-        case 'canvas':
-            rect = canvasRect;
-            break;
-    }
-    return fluidbookToGlobal(dim, name) - rect[name];
-}
-
-function stopMoveRuler() {
-    moveRuler();
-    if ($(movingRuler).hasClass('pending-delete')) {
-        deleteRuler($(movingRuler));
-    }
-    movingRuler = null;
-}
-
-function updateMousePosition(e) {
-    mx = e.pageX;
-    my = e.pageY;
-    updateMousePositionRulers();
-    moveRuler();
-}
-
-function updateMousePositionRulers() {
-    var rulersRect = $("#linkeditor-rulers").get(0).getBoundingClientRect();
-    var rx = mx - rulersRect.x;
-    var ry = my - rulersRect.y;
-    $("#linkeditor-ruler-x .info").css('left', rx);
-    $("#linkeditor-ruler-y .info").css('top', ry);
-
-    var rrect = $("#linkeditor-rulers").get(0).getBoundingClientRect();
-
-    if (mx - rrect.x < 0 || my - rrect.y < 0 || mx - rrect.x > rrect.width || my - rrect.y > rrect.height) {
-        $("#linkeditor-rulers .info").hide();
-        return;
-    }
-
-    var fb = globalToFluidbook(mx, my, true);
-    $("#linkeditor-ruler-y .info span").text(fb.y.toFixed(2));
-    $("#linkeditor-ruler-x .info span").text(fb.x.toFixed(2));
-    $("#linkeditor-rulers .info").css('display', 'inline-block');
-}
-
-function resetZoomDrag() {
-    $("#linkeditor-main").removeClass('grab').removeClass('grabbing');
-    if (zoom === 1) {
-        $("#linkeditor-canvas").scrollTo({left: 0, top: 0});
-    }
-    zoomdragging = false;
-}
-
-function moveZoomDrag(e) {
-    var deltaX = e.pageX - zoomdragging.x;
-    var deltaY = e.pageY - zoomdragging.y;
-    $("#linkeditor-canvas").scrollTo({
-        top: zoomdragging.scrollY - deltaY, left: zoomdragging.scrollX - deltaX
-    });
-    updateRulers();
-}
-
-function moveZoom(e) {
-    var rect = $("#linkeditor-zoom").get(0).getBoundingClientRect();
-    var lx = (e.originalEvent.pageX - rect.x) / rect.width;
-    var ly = (e.originalEvent.pageY - rect.y) / rect.height;
-    $("#linkeditor-canvas").scrollTo({left: pct(lx), top: pct(ly)});
-    updateRulers();
-}
-
-function pct(v) {
-    return (v * 100) + '%';
-}
-
-function setZoom(z) {
-    z = Math.max(1, Math.min(6, z));
-    if (z === zoom) {
-        return false;
-    }
-    zoom = z;
-    $("#linkeditor-canvas").attr('data-z', zoom);
-    $("#linkeditor-zoom").css({transform: 'scale(' + zoom + ')', overflow: 'visible'});
-
-    setTimeout(function () {
-        $("#linkeditor-zoom").css({overflow: 'hidden'});
-        if (zoom === 1) {
-            resetZoomDrag();
-        }
-        updateRulers();
-
-    }, 10);
-
-    return true;
-}
-
-function changePage(page) {
-    if (page === undefined) {
-        var h = window.location.hash;
-        if (h.length === 0) {
-            page = 0;
-        } else {
-            page = window.location.hash.substring(1);
-        }
-    }
-    page = parseInt(page);
-    if (page % 2 === 1 && !single) {
-        page--;
-    }
-    var normPage = Math.max(0, Math.min(page, FLUIDBOOK_DATA.settings.pages));
-    if (normPage === currentPage) {
-        return;
-    }
-    currentPage = normPage;
-
-    window.location.hash = '#' + currentPage;
-    clearLinksAndRulers();
-    loadPage(currentPage, 'left');
-    if (!single) {
-        loadPage(currentPage + 1, 'right');
-    }
-    $("#linkeditor-page-field input").val(currentPage);
-    updateRulersMagnetValues();
-    resize();
-    preloadPages();
-}
-
-function updateRulersMagnetValues() {
-    rulersMagnetValuesX = [0, pw, pw * 2];
-    rulersMagnetValuesY = [0, ph];
-    $.each(getLinksOfPage(currentPage), function (uid, link) {
-        const left = parseFloat(link.left);
-        const top = parseFloat(link.top);
-        const width = parseFloat(link.width);
-        const height = parseFloat(link.height);
-        rulersMagnetValuesX.push(left, left + width);
-        rulersMagnetValuesY.push(top, top + height);
-    });
-}
-
-// Convert global coordinates to fluidbook ones
-function globalToFluidbook(x, y, onePage) {
-    var res = _globalTo(x, y, fluidbookRect, 1 / (fs * zoom));
-    if (onePage) {
-        res.xside = 'left';
-        if (!single && res.x >= pw) {
-            res.xside = 'right';
-            res.x -= pw;
-        }
-    }
-    return res;
-}
-
-function fluidbookToGlobal(dim, name) {
-    return fluidbookRect[name] + (dim * fs * zoom);
-}
-
-function globalToCanvas(x, y) {
-    return _globalTo(x, y, canvasRect, 1);
-}
-
-function globalToEditor(x, y) {
-    return _globalTo(x, y, editorRect, 1);
-}
-
-function _globalTo(x, y, rect, multi) {
-    return {x: multi * (x - rect.x), y: multi * (y - rect.y)};
-}
-
-function loadPage(p, side) {
-    var container = $("#linkeditor-page-" + side);
-    $(container).attr('data-page', p);
-
-    if (p === 0 || p > FLUIDBOOK_DATA.settings.pages) {
-        $(container).html('');
-    } else {
-        _loadPage(p, container);
-    }
-
-    loadLinks(p, side);
-    loadRulers(p, side);
-}
-
-function preloadPages() {
-    let j = 1;
-    for (let i = Math.max(1, currentPage - 4); i <= Math.min(currentPage + 16, FLUIDBOOK_DATA.settings.pages); i++) {
-        if ($('.preload[data-page="' + i + '"]').length >= 1) {
-            continue;
-        }
-        setTimeout(function () {
-            var c = $('<div class="preload" data-page="i"></div>');
-            $("#linkeditor-preload").append(c);
-            _loadPage(i, c);
-        }, j * 500);
-        j++;
-    }
-}
-
-function _loadPage(p, container) {
-    var imageFormat = FLUIDBOOK_DATA.settings.imageFormat;
-    var c = '<div class="contents">';
-    if (rasterizePages.indexOf(p) >= 0) {
-        c += '<img draggable="false" src="raster_' + p + '.' + imageFormat + '" />';
-    } else if (vectorPages.indexOf(p) >= 0) {
-        c += '<img draggable="false" src="vector_' + p + '.svg" />';
-    } else {
-        c += '<img draggable="false" class="images" src="images_' + p + '.' + imageFormat + '" />';
-        c += '<img draggable="false" class="texts" src="texts_' + p + '.svg" />';
-    }
-    c += '</div>';
-    $(container).html(c);
-}
-
-function loadLinks(page, side) {
-    $.each(LINKS, function (uid, link) {
-        if (link.page != page) {
-            return;
-        }
-        if (side === 'right') {
-            link.left = parseInt(link.left) + pw;
-        }
-        addLink(link, side);
-    });
-}
-
-function addLink(link) {
-    if (link.uid === undefined) {
-        link.uid = generateUID();
-        LINKS[link.uid] = link;
-        updateRulersMagnetValues();
-        hasChanged();
-    }
-
-    var attrs = {};
-    $.each(link, function (k, v) {
-        if (dimensionProperties.indexOf(k) >= 0) {
-            attrs['fb-' + k] = v;
-        } else {
-            attrs['data-' + k] = v;
-        }
-    });
-
-    var e = $('<div class="link" fb-ref="editor"></div>');
-    $(e).attr(attrs);
-    $("#linkeditor-links").append(e);
-}
-
-function clearLinksAndRulers() {
-    $('#linkeditor-editor .ruler').remove();
-}
-
-function loadRulers(page, side) {
-    $.each(RULERS, function (uid, ruler) {
-        if (ruler.page != page) {
-            return;
-        }
-        addRuler(ruler.type, ruler.pos, ruler.uid);
-    });
-}
-
-
-function resize() {
-    resizeCanvas();
-    updateRulers();
-}
-
-function updateRulers() {
-    // Update rects
-    fluidbookRect = $("#linkeditor-fluidbook").get(0).getBoundingClientRect();
-
-    $("#linkeditor-ruler-y,#linkeditor-ruler-x").html('');
-    // Measure of visible fluidbook px at current zoom
-
-    var factor = fs / zoom;
-    var visible_w = (canvasRect.width / fs) / zoom;
-    var visible_h = (canvasRect.height / fs) / zoom;
-
-    // Find the best divider to have around 10 main divisions
-    var divider = 0;
-    for (var i in dividers) {
-        divider = dividers[i];
-        var v = visible_h / divider;
-        if (v <= 10) {
-            break;
-        }
-    }
-
-    var divisionSize = divider * fs * zoom;
-    // Draw vertical ruler
-    var margin = 100;
-    var nbDivisions = Math.floor(visible_h / divider);
-    var y0 = 16 + fluidbookRect.y - canvasRect.y;
-
-    var yruler = '<div class="info"><span>1234.12</span></div>';
-    for (var y = -margin; y <= nbDivisions + (margin * 2) + 1; y++) {
-        // Draw subdivision
-        var v = divider * y;
-        var ystart = y0 + (y * divisionSize);
-        if (ystart + divisionSize < 0 || ystart > canvasRect.height) {
-            continue;
-        }
-        yruler += '<div class="division" style="top:' + ystart + 'px;height:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
-        for (var i = 1; i <= 9; i++) {
-            yruler += '<div class="subdivision ' + (i === 5 ? ' middle' : '') + '" style="top:' + ((i * divisionSize) / 10) + 'px;"></div>';
-        }
-        yruler += '</div>';
-    }
-    $("#linkeditor-ruler-y").html(yruler);
-
-    // Draw horizontal ruler
-    nbDivisions = Math.floor(visible_w / divider);
-    var x0 = 16 + fluidbookRect.x - canvasRect.x;
-    var xruler = '<div class="info"><span></span></div>';
-
-    for (var x = -margin; x <= nbDivisions + (margin * 2) + 1; x++) {
-        // Draw subdivision
-        var v = divider * x;
-        var xstart = x0 + (x * divisionSize);
-        if (xstart + divisionSize < 0 || xstart > canvasRect.width) {
-            continue;
-        }
-        xruler += '<div class="division" style="left:' + xstart + 'px;width:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
-        for (var i = 1; i <= 9; i++) {
-            var cls = '';
-            if (i === 5) {
-                cls += ' middle';
-            }
-            xruler += '<div class="subdivision ' + cls + '" style="left:' + ((i * divisionSize) / 10) + 'px;"></div>';
-        }
-        xruler += '</div>';
-    }
-    // Draw right page horizontal ruler
-    if (!single) {
-        x0 = x0 + pw * fs * zoom;
-        for (var x = 0; x <= nbDivisions + margin + 1; x++) {
-            // Draw subdivision
-            var v = divider * x;
-            var xstart = x0 + (x * divisionSize);
-            if (xstart + divisionSize < 0 || xstart > canvasRect.width) {
-                continue;
-            }
-            xruler += '<div class="division" style="left:' + xstart + 'px;width:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
-            for (var i = 1; i <= 9; i++) {
-                var cls = '';
-                if (i === 5) {
-                    cls += ' middle';
-                }
-                xruler += '<div class="subdivision ' + cls + '" style="left:' + ((i * divisionSize) / 10) + 'px;"></div>';
-            }
-            xruler += '</div>';
-        }
-    }
-    $("#linkeditor-ruler-x").html(xruler);
-    updateFBElements()
-    updateMousePositionRulers();
-}
-
-function resizeCanvas() {
-    canvasRect = $("#linkeditor-canvas").get(0).getBoundingClientRect();
-    editorRect = $("#linkeditor-editor").get(0).getBoundingClientRect();
-    var aw = canvasRect.width - 30;
-    var ah = canvasRect.height - 30;
-    fs = Math.min(aw / fw, ah / fh);
-    var left = (canvasRect.width - fw * fs) / 2;
-    var top = (canvasRect.height - fh * fs) / 2;
-    $("#linkeditor-fluidbook").css({left: left, top: top, transform: 'scale(' + fs + ')'});
-}
-
-
-function splitPages(str) {
-    str = str.toString();
-    var res = [];
-    if (str == '') {
-        return res;
-    }
-    var pages = str.split(',');
-    for (var p in pages) {
-        res.push(parseInt(pages[p]));
-    }
-    return res;
-}
index 04b09d07ab794406afb62652b59e161a53b2120a..7d8ffb453cd030402f322dd9ddcc3ff892032642 100644 (file)
 window.$ = window.jQuery = require('jquery');
+window.key = require('keymaster');
 import tippy from 'tippy.js';
 import 'tippy.js/dist/tippy.css';
-import key from 'keymaster';
+
+window.tippy = tippy;
 
 require('jquery.scrollto');
 require('jquery-contextmenu');
 
-var single = ['mobilefirst', 'portrait'].indexOf(FLUIDBOOK_DATA.settings.mobileNavigationType) >= 0;
-var pw = FLUIDBOOK_DATA.settings.width;
-var ph = FLUIDBOOK_DATA.settings.height;
-var fw = pw * (single ? 1 : 2);
-var fh = ph;
-var fs = 1;
-var mx = 0, my = 0;
-var zoom = 1;
-var zoomdragging = false;
-var dividers = [1, 2, 5, 10, 20, 50, 100, 200];
-var fluidbookRect, canvasRect, editorRect;
-var currentPage;
-var movingRuler = null;
-var rasterizePages = splitPages(FLUIDBOOK_DATA.settings.rasterizePages);
-var vectorPages = splitPages(FLUIDBOOK_DATA.settings.vectorPages);
-var unsavedChanges = false;
-var automaticSaveTimeout;
-var runningAutomaticSaveTimeout = false;
-var dimensionProperties = ['left', 'top', 'width', 'height'];
-var rulersMagnetValuesX = [];
-var rulersMagnetValuesY = [];
-var currentSelection = [];
-var dragLinkPos = null;
-
-$(function () {
-    init();
-});
-
+import LinkeditorLinks from './linkeditor.links';
+import LinkeditorLoader from './linkeditor.loader';
+import LinkeditorResize from './linkeditor.resize';
+import LinkeditorRulers from './linkeditor.rulers';
+import LinkeditorToolbar from './linkeditor.toolbar';
+import LinkeditorUtils from './linkeditor.utils';
+import LinkeditorZoom from './linkeditor.zoom';
+import LinkeditorSave from './linkeditor.save';
+
+function LinkEditor() {
+    this.single = ['mobilefirst', 'portrait'].indexOf(FLUIDBOOK_DATA.settings.mobileNavigationType) >= 0;
+    this.pw = FLUIDBOOK_DATA.settings.width;
+    this.ph = FLUIDBOOK_DATA.settings.height;
+    this.fw = this.pw * (this.single ? 1 : 2);
+    this.fh = this.ph;
+    this.fs = 1;
+    this.mx = 0;
+    this.my = 0;
+
+    this.fluidbookRect = null;
+    this.canvasRect = null;
+    this.editorRect = null;
+    this.currentPage = -1;
+
+    this.dimensionProperties = ['left', 'top', 'width', 'height'];
+
+    this.init();
+}
+
+LinkEditor.prototype = {
+    init: function () {
+        this.toolbar = new LinkeditorToolbar(this);
+        this.resize = new LinkeditorResize(this);
+        this.rulers = new LinkeditorRulers(this);
+        this.zoom = new LinkeditorZoom(this);
+        this.links = new LinkeditorLinks(this);
+        this.utils = new LinkeditorUtils(this);
+        this.loader = new LinkeditorLoader(this);
+        this.save = new LinkeditorSave(this);
+
+        this.initEvents();
+    },
+
+    initEvents: function () {
+        var $this = this;
+        $(window).on('hashchange', function () {
+            $this.changePage();
+        });
 
-function init() {
-    $(window).on('hashchange', function () {
-        changePage();
-    });
-    $(window).on('resize', function () {
-        resize();
-    });
-    $(window).on('beforeunload', function () {
-        if (unsavedChanges) {
-            return TRANSLATIONS.warning_unsaved_changes;
-        }
-    })
-    // Disable scroll by spacebar
-    $(window).on('keydown', function (e) {
-        if (e.keyCode == 32) {
-            $("#linkeditor-main").addClass('grab');
-            return false;
-        }
-        moveRuler();
-    });
-    $(window).on('keyup', function (e) {
-        if (e.keyCode == 32) {
-            resetZoomDrag();
-        }
-        moveRuler();
-    });
-
-    $("#linkeditor-canvas").on('scroll', function () {
-        updateRulers();
-    });
-
-    $(document).on('mousedown', '.link', function (e) {
-        e.preventDefault();
-        e.stopPropagation();
-        if (!$(this).hasClass('selected')) {
-            if (!e.ctrlKey) {
-                deselectAllLinks();
+        // Disable scroll by spacebar
+        $(window).on('keydown', function (e) {
+            if (e.keyCode == 32) {
+                $("#linkeditor-main").addClass('grab');
+                return false;
             }
-            selectLink($(this));
-        }
-        startDragLink();
-
-        return false;
-    });
-
-    $(document).on('mousedown', "#linkeditor-main", function (e) {
-        if ($(this).hasClass('grab') && zoom > 1) {
-            zoomdragging = {
-                x: e.pageX,
-                y: e.pageY,
-                scrollX: $("#linkeditor-canvas").scrollLeft(),
-                scrollY: $("#linkeditor-canvas").scrollTop()
-            };
-            $(this).addClass('grabbing');
-        } else {
-            resetZoomDrag();
-        }
-        deselectAllLinks();
-    });
-
-
-    $("#linkeditor-ruler-x").on('mousedown', function (e) {
-        addRuler('y');
-    });
+            $this.rulers.moveRuler();
+        });
+        $(window).on('keyup', function (e) {
+            if (e.keyCode == 32) {
+                $this.zoom.resetZoomDrag();
+            }
+            $this.rulers.moveRuler();
+        });
 
-    $("#linkeditor-ruler-y").on('mousedown', function (e) {
-        addRuler('x');
-    });
+        $(document).on('mousedown', "#linkeditor-main", function (e) {
+            if ($(this).hasClass('grab') && $this.zoom.zoom > 1) {
+                $this.zoomdragging = {
+                    x: e.pageX,
+                    y: e.pageY,
+                    scrollX: $("#linkeditor-canvas").scrollLeft(),
+                    scrollY: $("#linkeditor-canvas").scrollTop()
+                };
+                $(this).addClass('grabbing');
+            } else {
+                $this.zoom.resetZoomDrag();
+            }
+            $this.links.deselectAllLinks();
+        });
 
-    $(document).on('mousedown', ".ruler", function (e) {
-        movingRuler = $(this);
+        $(window).on('mousemove', function (e) {
+            $this.updateMousePosition(e);
+        });
 
-    });
+        $(window).on('mouseup', function (e) {
+            $this.zoom.mouseUp();
+            $this.rulers.mouseUp();
+            $this.links.mouseUp();
+        });
 
-    $(window).on('mousemove', function (e) {
-        if (!$("#linkeditor-main").hasClass('grab') || zoom === 1) {
-            resetZoomDrag();
-        }
-        if (zoomdragging !== false) {
-            moveZoomDrag(e);
-        }
-        updateMousePosition(e);
-        moveDragLink();
-    });
-    $(window).on('mouseup', function (e) {
-        if (zoomdragging !== false) {
-            moveZoomDrag(e);
-        }
-        stopDragLink();
-        stopMoveRuler();
-        resetZoomDrag();
-    });
-    $("#linkeditor-main").on('wheel', function (e) {
-        var delta = e.originalEvent.deltaY;
-        if (delta == 0) {
-            return true;
-        }
-        e.stopPropagation();
-        e.stopImmediatePropagation();
-        e.preventDefault();
-        if (delta < 0) {
-            if (setZoom(zoom + 0.25)) {
-                moveZoom(e);
-            }
+        if (this.single) {
+            $("#linkeditor").addClass('single');
         } else {
-            setZoom(zoom - 0.25);
+            $("#linkeditor").addClass('double');
         }
-        return false;
-    });
-
-    if (single) {
-        $("#linkeditor").addClass('single');
-    } else {
-        $("#linkeditor").addClass('double');
-    }
-    $('.linkeditor-page').css({
-        width: pw, height: ph
-    });
-    $("#linkeditor-page-right").css({left: pw})
-    $('#linkeditor-fluidbook').css({
-        width: fw, height: ph
-    });
-
-    key('left', function () {
-        offsetSelectedLinks('left', -1);
-    });
-    key('ctrl+left', function () {
-        offsetSelectedLinks('left', -10);
-    });
-    key('right', function () {
-        offsetSelectedLinks('left', 1);
-    });
-    key('ctrl+right', function () {
-        offsetSelectedLinks('left', 10);
-    });
-    key('up', function () {
-        offsetSelectedLinks('top', -1);
-    });
-    key('ctrl+up', function () {
-        offsetSelectedLinks('top', -10);
-    });
-    key('down', function () {
-        offsetSelectedLinks('top', 1);
-    });
-    key('ctrl+down', function () {
-        offsetSelectedLinks('top', 10);
-    });
-
-
-    initToolbar();
-    resize();
-    changePage();
-}
-
-function startDragLink() {
-    dragLinkPos = {x: mx, y: my};
-    $(".link.selected").each(function () {
-        $(this).data('drag-orig-left', parseFloat($(this).attr('fb-left')));
-        $(this).data('drag-orig-top', parseFloat($(this).attr('fb-top')));
-    });
-}
-
-function stopDragLink() {
-    moveDragLink();
-    dragLinkPos = null;
-}
-
-function moveDragLink() {
-    if (dragLinkPos === null) {
-        return;
-    }
-    var f = 1 / (fs * zoom);
-    var dx = (mx - dragLinkPos.x) * f;
-    var dy = (my - dragLinkPos.y) * f;
-    $(".link.selected").each(function () {
-        $(this).attr('fb-left', $(this).data('drag-orig-left') + dx).attr('fb-top', $(this).data('drag-orig-top') + dy).attr('fb-update', '1');
-    });
-    updateFBElements(false);
-}
-
-function selectLink(l) {
-    if ($(l).find('.corners').length === 0) {
-        $(l).append('<div class="corners"><div class="nw"></div><div class="n"></div><div class="ne"></div><div class="e"></div><div class="se"></div><div class="s"></div><div class="sw"></div><div class="w"></div></div>')
-    }
-    $(l).addClass('selected');
-    currentSelection.push(l);
-}
-
-
-function deselectAllLinks() {
-    currentSelection = [];
-    $(".link.selected").removeClass('selected');
-}
-
-function offsetSelectedLinks(dim, value) {
-    console.log(dim, value);
-    $('.link.selected').each(function () {
-        var v = parseFloat($(this).attr('fb-' + dim));
-        $(this).attr('fb-' + dim, v + value).attr('fb-update', '1');
-    });
-    updateFBElements();
-}
 
-function initToolbar() {
-    $("#linkeditor-page-field input").on('change', function () {
-        changePage($(this).val());
-        $(this).blur();
-        return false;
-    });
-
-    $("#linkeditor-toolbar [data-icon]").each(function () {
-        $(this).append(getSpriteIcon('linkeditor-' + $(this).data('icon')));
-    });
-
-    $("[data-key]").each(function () {
-        var e = $(this);
-        key($(this).data('key'), function () {
-            $(e).addClass('hover');
-            runAction($(e).data('action'));
-            setTimeout(function () {
-                $(e).removeClass('hover')
-            }, 150);
-            return false;
+        $('.linkeditor-page').css({
+            width: this.pw, height: this.ph
         });
-    });
 
-    $('[data-action]').click(function () {
-        runAction($(this).data('action'));
-        return false;
-    });
-
-    $('[data-tooltip]').each(function () {
-        tippy($(this).get(0), {content: $(this).data('tooltip')});
-    });
-}
-
-function runAction(act) {
-    eval(act + '()');
-}
-
-function saveLinks(message) {
-    if (message === undefined) {
-        message = TRANSLATIONS.manual_save_message;
-    }
-    $.ajax({
-        url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/save/links', method: 'post', data: {
-            _method: 'put', 'message': message, rulers: window.RULERS, links: window.LINKS,
-        }, success: function (data) {
-            new Noty({
-                type: "success", text: TRANSLATIONS.success_save,
-            }).show();
-            clearTimeout(automaticSaveTimeout);
-            unsavedChanges = false;
-            runningAutomaticSaveTimeout = false;
-        }, error: function (jqXHR, status, error) {
-            hasChanged();
-            new Noty({
-                type: "error", text: TRANSLATIONS.error_save + ' : ' + error,
-            }).show();
-        },
-    });
-}
-
-function automaticSaveLinks() {
-    saveLinks(TRANSLATIONS.automatic_save_message);
-}
-
-function firstPage() {
-    changePage(1);
-}
-
-function nextPage() {
-    changePage(currentPage + (single ? 1 : 2));
-}
-
-function previousPage() {
-    changePage(currentPage - (single ? 1 : 2));
-}
-
-function lastPage() {
-    changePage(FLUIDBOOK_DATA.settings.pages);
-}
-
-function focusPageField() {
-    var i = $("#linkeditor-page-field input").get(0);
-    i.focus();
-    i.select();
-}
-
-function zoomReset() {
-    setZoom(1);
-    resetZoomDrag();
-}
-
-function generateUID() {
-    var length = 12;
-    var result = '';
-    var characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
-    var charactersLength = characters.length;
-    for (var i = 0; i < length; i++) {
-        result += characters.charAt(Math.floor(Math.random() * charactersLength));
-    }
-    return result;
-}
-
-function addRuler(axis, pos, uid) {
-    if (undefined === uid) {
-        uid = generateUID();
-        RULERS[uid] = {page: currentPage, type: axis, uid: uid};
-        hasChanged();
-    }
-    var ruler = $('<div class="ruler" data-uid="' + uid + '" fb-update="1" fb-ref="editor" data-axis="' + axis + '"></div>');
-    if (pos === undefined) {
-        movingRuler = ruler;
-    } else {
-        var dim = axis == 'x' ? 'left' : 'top';
-        $(ruler).attr('fb-' + dim, pos);
-    }
-    $("#linkeditor-editor").append(ruler);
-    moveRuler();
-}
-
-function deleteRuler(ruler) {
-    if (ruler === undefined) {
-        ruler = movingRuler;
-    }
-    var uid = $(ruler).data('uid');
-    delete RULERS[uid];
-    $(ruler).remove();
-    movingRuler = null;
-    hasChanged();
-}
-
-function hasChanged() {
-    unsavedChanges = true;
-    if (runningAutomaticSaveTimeout === false) {
-        runningAutomaticSaveTimeout = true;
-        automaticSaveTimeout = setTimeout(function () {
-            automaticSaveLinks();
-        }, 1000 * 5 * 60);
-    }
-}
-
-function moveRuler() {
-    if (movingRuler === null || movingRuler === undefined) {
-        return;
-    }
-    var magnet = !key.ctrl;
-    var editorMouse = globalToEditor(mx, my);
-    var fbMouse = globalToFluidbook(mx, my, false);
-    var css = {};
-    var attrs = {'fb-update': '1'};
-    var v, fbv;
-    if ($(movingRuler).data('axis') === 'x') {
-        v = editorMouse.x;
-        fbv = attrs['fb-left'] = magnet ? magnetize(fbMouse.x, rulersMagnetValuesX) : fbMouse.x;
-    } else {
-        v = editorMouse.y;
-        fbv = attrs['fb-top'] = magnet ? magnetize(fbMouse.y, rulersMagnetValuesY) : fbMouse.y;
-    }
-    if (v < 16) {
-        $(movingRuler).addClass('pending-delete');
-    } else {
-        $(movingRuler).removeClass('pending-delete');
-    }
-    $(movingRuler).css(css).attr(attrs);
-    RULERS[$(movingRuler).data('uid')].pos = fbv;
-    updateFBElements();
-}
+        $("#linkeditor-page-right").css({left: this.pw})
+        $('#linkeditor-fluidbook').css({
+            width: this.fw, height: this.ph
+        });
 
-function magnetize(value, values) {
-    var sensibility = 8 / (zoom * fs);
-    var min = 100000;
-    var magnetValue;
-    if (values.length === 0) {
-        return value;
-    }
-
-    for (var i in values) {
-        var v = values[i];
-        var diff = Math.abs(v - value);
-        if (diff < min) {
-            min = diff;
-            magnetValue = v;
+        this.resize.resize();
+        this.changePage();
+    },
+
+    runAction: function (act) {
+        var a = act.split('.');
+        var o = this;
+        let po = this;
+        for (let i in a) {
+            po = o;
+            o = o[a[i]];
         }
-    }
-
-    if (min > sensibility) {
-        return value;
-    }
-    return magnetValue;
-}
+        return o.call(po);
+    },
+
+    firstPage: function () {
+        this.changePage(1);
+    },
+
+    nextPage: function () {
+        this.changePage(this.currentPage + (this.single ? 1 : 2));
+    },
+
+    previousPage: function () {
+        this.changePage(this.currentPage - (this.single ? 1 : 2));
+    },
+
+    lastPage: function () {
+        this.changePage(FLUIDBOOK_DATA.settings.pages);
+    },
+
+    focusPageField: function () {
+        let i = $("#linkeditor-page-field input").get(0);
+        i.focus();
+        i.select();
+    },
+
+    hasChanged: function () {
+        this.save.hasChanged();
+    },
+
+    updateFBElements: function (force) {
+        let $this = this;
+        requestAnimationFrame(function () {
+            $this._updateFBElements(force);
+        });
+    },
 
-function getLinksOfPage(p) {
-    let pages = [];
-    if (!single) {
-        if (p % 2 === 1) {
-            p--;
-        }
-        pages.push(p);
-        pages.push(p + 1);
-    } else {
-        pages.push(p);
-    }
-    let res = {};
-    $.each(LINKS, function (uid, link) {
-        if (pages.indexOf(parseInt(link.page)) >= 0) {
-            res[uid] = link;
+    _updateFBElements: function (force) {
+        let $this = this;
+        let selector = '[fb-ref]';
+        if (force !== true) {
+            selector += '[fb-update="1"]';
         }
-    });
-    return res;
-}
 
-function updateFBElements(force) {
-    var selector = '[fb-ref]';
-    if (force !== true) {
-        selector += '[fb-update="1"]';
-    }
-
-    $(selector).each(function () {
-        var e = $(this);
-        var rect = $(this).attr('fb-ref');
-        var css = {};
-        $.each(dimensionProperties, function (k, dim) {
-            if ($(e).is('[fb-' + dim + ']')) {
-                var v = parseFloat($(e).attr('fb-' + dim));
-                if (dim === 'width' || dim === 'height') {
-                    css[dim] = v * (fs * zoom);
-                } else {
-                    css[dim] = fluidbookTo(v, dim, rect);
+        $(selector).each(function () {
+            let e = $(this);
+            let rect = $(this).attr('fb-ref');
+            let css = {};
+            $.each($this.dimensionProperties, function (k, dim) {
+                if ($(e).is('[fb-' + dim + ']')) {
+                    let v = parseFloat($(e).attr('fb-' + dim));
+                    if (dim === 'width' || dim === 'height') {
+                        css[dim] = v * ($this.fs * $this.zoom.zoom);
+                    } else {
+                        css[dim] = $this.fluidbookTo(v, dim, rect);
+                    }
                 }
-            }
+            });
+            $(e).css(css).attr('fb-update', '0');
         });
-        $(e).css(css).attr('fb-update', '0');
-    });
-}
-
-function fluidbookTo(dim, name, rect) {
-    switch (rect) {
-        case 'editor':
-            rect = editorRect;
-            break;
-        case 'canvas':
-            rect = canvasRect;
-            break;
-    }
-    return fluidbookToGlobal(dim, name) - rect[name];
-}
-
-function stopMoveRuler() {
-    moveRuler();
-    if ($(movingRuler).hasClass('pending-delete')) {
-        deleteRuler($(movingRuler));
-    }
-    movingRuler = null;
-}
-
-function updateMousePosition(e) {
-    mx = e.pageX;
-    my = e.pageY;
-    updateMousePositionRulers();
-    moveRuler();
-}
-
-function updateMousePositionRulers() {
-    var rulersRect = $("#linkeditor-rulers").get(0).getBoundingClientRect();
-    var rx = mx - rulersRect.x;
-    var ry = my - rulersRect.y;
-    $("#linkeditor-ruler-x .info").css('left', rx);
-    $("#linkeditor-ruler-y .info").css('top', ry);
-
-    var rrect = $("#linkeditor-rulers").get(0).getBoundingClientRect();
-
-    if (mx - rrect.x < 0 || my - rrect.y < 0 || mx - rrect.x > rrect.width || my - rrect.y > rrect.height) {
-        $("#linkeditor-rulers .info").hide();
-        return;
-    }
-
-    var fb = globalToFluidbook(mx, my, true);
-    $("#linkeditor-ruler-y .info span").text(fb.y.toFixed(2));
-    $("#linkeditor-ruler-x .info span").text(fb.x.toFixed(2));
-    $("#linkeditor-rulers .info").css('display', 'inline-block');
-}
-
-function resetZoomDrag() {
-    $("#linkeditor-main").removeClass('grab').removeClass('grabbing');
-    if (zoom === 1) {
-        $("#linkeditor-canvas").scrollTo({left: 0, top: 0});
-    }
-    zoomdragging = false;
-}
-
-function moveZoomDrag(e) {
-    var deltaX = e.pageX - zoomdragging.x;
-    var deltaY = e.pageY - zoomdragging.y;
-    $("#linkeditor-canvas").scrollTo({
-        top: zoomdragging.scrollY - deltaY, left: zoomdragging.scrollX - deltaX
-    });
-    updateRulers();
-}
-
-function moveZoom(e) {
-    var rect = $("#linkeditor-zoom").get(0).getBoundingClientRect();
-    var lx = (e.originalEvent.pageX - rect.x) / rect.width;
-    var ly = (e.originalEvent.pageY - rect.y) / rect.height;
-    $("#linkeditor-canvas").scrollTo({left: pct(lx), top: pct(ly)});
-    updateRulers();
-}
-
-function pct(v) {
-    return (v * 100) + '%';
-}
-
-function setZoom(z) {
-    z = Math.max(1, Math.min(6, z));
-    if (z === zoom) {
-        return false;
-    }
-    zoom = z;
-    $("#linkeditor-canvas").attr('data-z', zoom);
-    $("#linkeditor-zoom").css({transform: 'scale(' + zoom + ')', overflow: 'visible'});
-
-    setTimeout(function () {
-        $("#linkeditor-zoom").css({overflow: 'hidden'});
-        if (zoom === 1) {
-            resetZoomDrag();
+    },
+
+    fluidbookTo: function (dim, name, rect) {
+        switch (rect) {
+            case 'editor':
+                rect = this.editorRect;
+                break;
+            case 'canvas':
+                rect = this.canvasRect;
+                break;
         }
-        updateRulers();
-
-    }, 10);
-
-    return true;
-}
-
-function changePage(page) {
-    if (page === undefined) {
-        var h = window.location.hash;
-        if (h.length === 0) {
-            page = 0;
-        } else {
-            page = window.location.hash.substring(1);
+        return this.fluidbookToGlobal(dim, name) - rect[name];
+    },
+
+
+    updateMousePosition: function (e) {
+        this.mx = e.pageX;
+        this.my = e.pageY;
+
+        this.rulers.updateMousePositionRulers();
+        this.rulers.moveRuler();
+        this.links.moveDragLink();
+        this.links.moveResizeLink();
+        this.zoom.updateMousePosition();
+    },
+
+    changePage: function (page) {
+        if (page === undefined) {
+            let h = window.location.hash;
+            if (h.length === 0) {
+                page = 0;
+            } else {
+                page = window.location.hash.substring(1);
+            }
         }
-    }
-    page = parseInt(page);
-    if (page % 2 === 1 && !single) {
-        page--;
-    }
-    var normPage = Math.max(0, Math.min(page, FLUIDBOOK_DATA.settings.pages));
-    if (normPage === currentPage) {
-        return;
-    }
-    currentPage = normPage;
-
-    window.location.hash = '#' + currentPage;
-    clearLinksAndRulers();
-    loadPage(currentPage, 'left');
-    if (!single) {
-        loadPage(currentPage + 1, 'right');
-    }
-    $("#linkeditor-page-field input").val(currentPage);
-    updateRulersMagnetValues();
-    resize();
-    preloadPages();
-}
-
-function updateRulersMagnetValues() {
-    rulersMagnetValuesX = [0, pw, pw * 2];
-    rulersMagnetValuesY = [0, ph];
-    $.each(getLinksOfPage(currentPage), function (uid, link) {
-        const left = parseFloat(link.left);
-        const top = parseFloat(link.top);
-        const width = parseFloat(link.width);
-        const height = parseFloat(link.height);
-        rulersMagnetValuesX.push(left, left + width);
-        rulersMagnetValuesY.push(top, top + height);
-    });
-}
-
-// Convert global coordinates to fluidbook ones
-function globalToFluidbook(x, y, onePage) {
-    var res = _globalTo(x, y, fluidbookRect, 1 / (fs * zoom));
-    if (onePage) {
-        res.xside = 'left';
-        if (!single && res.x >= pw) {
-            res.xside = 'right';
-            res.x -= pw;
+        let normPage = this.utils.normalizePage(page);
+        if (normPage === this.currentPage) {
+            return;
         }
-    }
-    return res;
-}
-
-function fluidbookToGlobal(dim, name) {
-    return fluidbookRect[name] + (dim * fs * zoom);
-}
-
-function globalToCanvas(x, y) {
-    return _globalTo(x, y, canvasRect, 1);
-}
-
-function globalToEditor(x, y) {
-    return _globalTo(x, y, editorRect, 1);
-}
-
-function _globalTo(x, y, rect, multi) {
-    return {x: multi * (x - rect.x), y: multi * (y - rect.y)};
-}
-
-function loadPage(p, side) {
-    var container = $("#linkeditor-page-" + side);
-    $(container).attr('data-page', p);
-
-    if (p === 0 || p > FLUIDBOOK_DATA.settings.pages) {
-        $(container).html('');
-    } else {
-        _loadPage(p, container);
-    }
-
-    loadLinks(p, side);
-    loadRulers(p, side);
-}
+        this.currentPage = normPage;
 
-function preloadPages() {
-    let j = 1;
-    for (let i = Math.max(1, currentPage - 4); i <= Math.min(currentPage + 16, FLUIDBOOK_DATA.settings.pages); i++) {
-        if ($('.preload[data-page="' + i + '"]').length >= 1) {
-            continue;
+        window.location.hash = '#' + this.currentPage;
+        this.clearLinksAndRulers();
+        this.loader.loadPage(this.currentPage, 'left');
+        if (!this.loader.single) {
+            this.loader.loadPage(this.currentPage + 1, 'right');
         }
-        setTimeout(function () {
-            var c = $('<div class="preload" data-page="i"></div>');
-            $("#linkeditor-preload").append(c);
-            _loadPage(i, c);
-        }, j * 500);
-        j++;
-    }
-}
+        $("#linkeditor-page-field input").val(this.currentPage);
+        this.rulers.updateRulersMagnetValues();
+        this.resize.resize();
+        this.loader.preloadPages();
+    },
 
-function _loadPage(p, container) {
-    var imageFormat = FLUIDBOOK_DATA.settings.imageFormat;
-    var c = '<div class="contents">';
-    if (rasterizePages.indexOf(p) >= 0) {
-        c += '<img draggable="false" src="raster_' + p + '.' + imageFormat + '" />';
-    } else if (vectorPages.indexOf(p) >= 0) {
-        c += '<img draggable="false" src="vector_' + p + '.svg" />';
-    } else {
-        c += '<img draggable="false" class="images" src="images_' + p + '.' + imageFormat + '" />';
-        c += '<img draggable="false" class="texts" src="texts_' + p + '.svg" />';
-    }
-    c += '</div>';
-    $(container).html(c);
-}
 
-function loadLinks(page, side) {
-    $.each(LINKS, function (uid, link) {
-        if (link.page != page) {
-            return;
-        }
-        if (side === 'right') {
-            link.left = parseInt(link.left) + pw;
+// Convert global coordinates to fluidbook ones
+    globalToFluidbook: function (x, y, onePage) {
+        let res = this._globalTo(x, y, this.fluidbookRect, 1 / (this.fs * this.zoom.zoom));
+        if (onePage) {
+            res.xside = 'left';
+            if (!this.single && res.x >= this.pw) {
+                res.xside = 'right';
+                res.x -= this.pw;
+            }
         }
-        addLink(link, side);
-    });
-}
+        return res;
+    },
 
-function addLink(link) {
-    if (link.uid === undefined) {
-        link.uid = generateUID();
-        LINKS[link.uid] = link;
-        updateRulersMagnetValues();
-        hasChanged();
-    }
-
-    var attrs = {};
-    $.each(link, function (k, v) {
-        if (dimensionProperties.indexOf(k) >= 0) {
-            attrs['fb-' + k] = v;
-        } else {
-            attrs['data-' + k] = v;
-        }
-    });
+    fluidbookToGlobal: function (dim, name) {
+        return this.fluidbookRect[name] + (dim * this.fs * this.zoom.zoom);
+    },
 
-    var e = $('<div class="link" fb-ref="editor" fb-update="1"></div>');
-    $(e).attr(attrs);
-    $("#linkeditor-links").append(e);
-}
+    globalToCanvas: function (x, y) {
+        return this._globalTo(x, y, this.canvasRect, 1);
+    },
 
-function clearLinksAndRulers() {
-    $('#linkeditor-editor .ruler').remove();
-}
+    globalToEditor: function (x, y) {
+        return this._globalTo(x, y, this.editorRect, 1);
+    },
 
-function loadRulers(page, side) {
-    $.each(RULERS, function (uid, ruler) {
-        if (ruler.page != page) {
-            return;
-        }
-        addRuler(ruler.type, ruler.pos, ruler.uid);
-    });
-}
+    _globalTo: function (x, y, rect, multi) {
+        return {x: multi * (x - rect.x), y: multi * (y - rect.y)};
+    },
 
-
-function resize() {
-    resizeCanvas();
-    updateRulers();
+    clearLinksAndRulers: function () {
+        this.rulers.clear();
+        this.links.clear();
+    },
 }
 
-function updateRulers() {
-    // Update rects
-    fluidbookRect = $("#linkeditor-fluidbook").get(0).getBoundingClientRect();
-
-    $("#linkeditor-ruler-y,#linkeditor-ruler-x").html('');
-    // Measure of visible fluidbook px at current zoom
-
-    var factor = fs / zoom;
-    var visible_w = (canvasRect.width / fs) / zoom;
-    var visible_h = (canvasRect.height / fs) / zoom;
 
-    // Find the best divider to have around 10 main divisions
-    var divider = 0;
-    for (var i in dividers) {
-        divider = dividers[i];
-        var v = visible_h / divider;
-        if (v <= 10) {
-            break;
-        }
-    }
-
-    var divisionSize = divider * fs * zoom;
-    // Draw vertical ruler
-    var margin = 100;
-    var nbDivisions = Math.floor(visible_h / divider);
-    var y0 = 16 + fluidbookRect.y - canvasRect.y;
-
-    var yruler = '<div class="info"><span>1234.12</span></div>';
-    for (var y = -margin; y <= nbDivisions + (margin * 2) + 1; y++) {
-        // Draw subdivision
-        var v = divider * y;
-        var ystart = y0 + (y * divisionSize);
-        if (ystart + divisionSize < 0 || ystart > canvasRect.height) {
-            continue;
-        }
-        yruler += '<div class="division" style="top:' + ystart + 'px;height:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
-        for (var i = 1; i <= 9; i++) {
-            yruler += '<div class="subdivision ' + (i === 5 ? ' middle' : '') + '" style="top:' + ((i * divisionSize) / 10) + 'px;"></div>';
-        }
-        yruler += '</div>';
-    }
-    $("#linkeditor-ruler-y").html(yruler);
-
-    // Draw horizontal ruler
-    nbDivisions = Math.floor(visible_w / divider);
-    var x0 = 16 + fluidbookRect.x - canvasRect.x;
-    var xruler = '<div class="info"><span></span></div>';
-
-    for (var x = -margin; x <= nbDivisions + (margin * 2) + 1; x++) {
-        // Draw subdivision
-        var v = divider * x;
-        var xstart = x0 + (x * divisionSize);
-        if (xstart + divisionSize < 0 || xstart > canvasRect.width) {
-            continue;
-        }
-        xruler += '<div class="division" style="left:' + xstart + 'px;width:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
-        for (var i = 1; i <= 9; i++) {
-            var cls = '';
-            if (i === 5) {
-                cls += ' middle';
-            }
-            xruler += '<div class="subdivision ' + cls + '" style="left:' + ((i * divisionSize) / 10) + 'px;"></div>';
-        }
-        xruler += '</div>';
-    }
-    // Draw right page horizontal ruler
-    if (!single) {
-        x0 = x0 + pw * fs * zoom;
-        for (var x = 0; x <= nbDivisions + margin + 1; x++) {
-            // Draw subdivision
-            var v = divider * x;
-            var xstart = x0 + (x * divisionSize);
-            if (xstart + divisionSize < 0 || xstart > canvasRect.width) {
-                continue;
-            }
-            xruler += '<div class="division" style="left:' + xstart + 'px;width:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
-            for (var i = 1; i <= 9; i++) {
-                var cls = '';
-                if (i === 5) {
-                    cls += ' middle';
-                }
-                xruler += '<div class="subdivision ' + cls + '" style="left:' + ((i * divisionSize) / 10) + 'px;"></div>';
-            }
-            xruler += '</div>';
-        }
-    }
-    $("#linkeditor-ruler-x").html(xruler);
-    updateFBElements(true);
-    updateMousePositionRulers();
-}
+$(function () {
+    window.linkeditor = new LinkEditor();
+});
 
-function resizeCanvas() {
-    canvasRect = $("#linkeditor-canvas").get(0).getBoundingClientRect();
-    editorRect = $("#linkeditor-editor").get(0).getBoundingClientRect();
-    var aw = canvasRect.width - 30;
-    var ah = canvasRect.height - 30;
-    fs = Math.min(aw / fw, ah / fh);
-    var left = (canvasRect.width - fw * fs) / 2;
-    var top = (canvasRect.height - fh * fs) / 2;
-    $("#linkeditor-fluidbook").css({left: left, top: top, transform: 'scale(' + fs + ')'});
-}
 
 
-function splitPages(str) {
-    str = str.toString();
-    var res = [];
-    if (str == '') {
-        return res;
-    }
-    var pages = str.split(',');
-    for (var p in pages) {
-        res.push(parseInt(pages[p]));
-    }
-    return res;
-}
diff --git a/resources/linkeditor/js/linkeditor.links.js b/resources/linkeditor/js/linkeditor.links.js
new file mode 100644 (file)
index 0000000..d8b2511
--- /dev/null
@@ -0,0 +1,263 @@
+var LinkeditorLinks = function (linkeditor) {
+    this.linkeditor = linkeditor;
+
+    this.currentSelection = [];
+    this.dragLinkPos = null;
+    this.resizeLinkPos = null;
+
+    this.init();
+}
+
+LinkeditorLinks.prototype = {
+    init: function () {
+        var $this = this;
+        $(document).on('mousedown', '.link .corners div', function (e) {
+            e.preventDefault();
+            e.stopPropagation();
+            $this.deselectAllLinks();
+            let link = $(this).closest('.link');
+            $this.selectLink(link);
+            $this.startResizeLink($(this).attr('class'));
+        });
+
+        $(document).on('mousedown', '.link', function (e) {
+            e.preventDefault();
+            e.stopPropagation();
+            if (!$(this).hasClass('selected')) {
+                if (!e.ctrlKey) {
+                    $this.deselectAllLinks();
+                }
+                $this.selectLink($(this));
+            }
+            $this.startDragLink();
+            return false;
+        });
+
+        key('left', function () {
+            $this.offsetSelectedLinks('left', -1);
+        });
+        key('ctrl+left', function () {
+            $this.offsetSelectedLinks('left', -10);
+        });
+        key('right', function () {
+            $this.offsetSelectedLinks('left', 1);
+        });
+        key('ctrl+right', function () {
+            $this.offsetSelectedLinks('left', 10);
+        });
+        key('up', function () {
+            $this.offsetSelectedLinks('top', -1);
+        });
+        key('ctrl+up', function () {
+            $this.offsetSelectedLinks('top', -10);
+        });
+        key('down', function () {
+            $this.offsetSelectedLinks('top', 1);
+        });
+        key('ctrl+down', function () {
+            $this.offsetSelectedLinks('top', 10);
+        });
+    },
+
+    mouseUp: function () {
+        this.stopDragLink();
+        this.stopResizeLink();
+    },
+
+    startResizeLink: function (corner) {
+        this.resizeLinkPos = {x: this.linkeditor.mx, y: this.linkeditor.my, corner: corner};
+        $(".link.selected").each(function () {
+            $(this).data('drag-orig-left', parseFloat($(this).attr('fb-left')));
+            $(this).data('drag-orig-top', parseFloat($(this).attr('fb-top')));
+            $(this).data('drag-orig-width', parseFloat($(this).attr('fb-width')));
+            $(this).data('drag-orig-height', parseFloat($(this).attr('fb-height')));
+        });
+        console.log(this.resizeLinkPos);
+    },
+
+    stopResizeLink: function () {
+        this.moveResizeLink();
+        this.resizeLinkPos = null;
+    },
+
+    moveResizeLink: function () {
+        if (this.resizeLinkPos === null) {
+            return;
+        }
+        var f = 1 / (this.linkeditor.fs * this.linkeditor.zoom.zoom);
+        var dx = (this.linkeditor.mx - this.resizeLinkPos.x) * f;
+        var dy = (this.linkeditor.my - this.resizeLinkPos.y) * f;
+
+        var top = 0, left = 0, width = 0, height = 0;
+
+        if (['n', 'ne', 'nw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+            top = dy;
+            height = -dy;
+        } else if (['s', 'se', 'sw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+            height = dy;
+        }
+        if (['nw', 'w', 'sw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+            left = dx;
+            width = -dx;
+        } else if (['ne', 'e', 'se'].indexOf(this.resizeLinkPos.corner) >= 0) {
+            width = dx;
+        }
+
+        $(".link.selected").each(function () {
+            var newWidth = $(this).data('drag-orig-width') + width;
+            var newHeight = $(this).data('drag-orig-height') + height;
+
+            if (key.ctrl) {
+                // Keep ratio
+                var ratio = $(this).data('drag-orig-width') / $(this).data('drag-orig-height');
+                var xscale = newWidth / $(this).data('drag-orig-width');
+                var yscale = newHeight / $(this).data('drag-orig-height');
+                if (['ne', 'nw', 'se', 'sw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    if (xscale < yscale) {
+                        newHeight = newWidth / ratio;
+                    } else {
+                        newWidth = newHeight * ratio;
+                    }
+                } else if (['e', 'w'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    newHeight = newWidth / ratio;
+                } else if (['n', 's'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    newWidth = newHeight * ratio;
+                }
+
+                width = newWidth - $(this).data('drag-orig-width');
+                height = newHeight - $(this).data('drag-orig-height');
+
+                if (['ne', 'e', 'se'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    left = 0;
+                } else if (['nw', 'w', 'sw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    left = width;
+                }
+                if (['ne', 'n', 'nw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    top = height;
+                } else if (['se', 's', 'sw'].indexOf(this.resizeLinkPos.corner) >= 0) {
+                    top = 0;
+                }
+            }
+
+            var newLeft = $(this).data('drag-orig-left') + left;
+            var newTop = $(this).data('drag-orig-top') + top;
+
+            $(this).attr('fb-left', newLeft)
+                .attr('fb-top', newTop)
+                .attr('fb-width', newWidth)
+                .attr('fb-height', newHeight)
+                .attr('fb-update', '1');
+        });
+        this.linkeditor.updateFBElements(false);
+    },
+
+    startDragLink: function () {
+        this.dragLinkPos = {x: this.linkeditor.mx, y: this.linkeditor.my};
+        $(".link.selected").each(function () {
+            $(this).data('drag-orig-left', parseFloat($(this).attr('fb-left')));
+            $(this).data('drag-orig-top', parseFloat($(this).attr('fb-top')));
+        });
+    },
+
+    stopDragLink: function () {
+        this.moveDragLink();
+        this.dragLinkPos = null;
+    },
+
+    moveDragLink: function () {
+        if (this.dragLinkPos === null) {
+            return;
+        }
+        var f = 1 / (this.linkeditor.fs * this.linkeditor.zoom.zoom);
+        var dx = (this.linkeditor.mx - this.dragLinkPos.x) * f;
+        var dy = (this.linkeditor.my - this.dragLinkPos.y) * f;
+        $(".link.selected").each(function () {
+            $(this).attr('fb-left', $(this).data('drag-orig-left') + dx).attr('fb-top', $(this).data('drag-orig-top') + dy).attr('fb-update', '1');
+        });
+        this.linkeditor.updateFBElements(false);
+    },
+
+    selectLink: function (l) {
+        if ($(l).find('.corners').length === 0) {
+            $(l).append('<div class="corners"><div class="nw"></div><div class="n"></div><div class="ne"></div><div class="e"></div><div class="se"></div><div class="s"></div><div class="sw"></div><div class="w"></div></div>')
+        }
+        $(l).addClass('selected');
+        this.currentSelection.push(l);
+    },
+
+
+    deselectAllLinks: function () {
+        this.currentSelection = [];
+        $(".link.selected").removeClass('selected');
+    },
+
+    offsetSelectedLinks: function (dim, value) {
+        $('.link.selected').each(function () {
+            var v = parseFloat($(this).attr('fb-' + dim));
+            $(this).attr('fb-' + dim, v + value).attr('fb-update', '1');
+        });
+        this.linkeditor.updateFBElements();
+    },
+
+    getLinksOfPage: function (p) {
+        let pages = [];
+        if (!this.linkeditor.single) {
+            if (p % 2 === 1) {
+                p--;
+            }
+            pages.push(p);
+            pages.push(p + 1);
+        } else {
+            pages.push(p);
+        }
+        let res = {};
+        $.each(LINKS, function (uid, link) {
+            if (pages.indexOf(parseInt(link.page)) >= 0) {
+                res[uid] = link;
+            }
+        });
+        return res;
+    },
+
+    loadLinks: function (page, side) {
+        var $this = this;
+        $.each(LINKS, function (uid, link) {
+            if (link.page != page) {
+                return;
+            }
+            if (side === 'right') {
+                link.left = parseInt(link.left) + $this.linkeditor.pw;
+            }
+            $this.addLink(link, side);
+        });
+    },
+
+    addLink: function (link) {
+        var $this = this;
+        if (link.uid === undefined) {
+            link.uid = generateUID();
+            LINKS[link.uid] = link;
+            this.linkeditor.rulers.updateRulersMagnetValues();
+            this.linkeditor.hasChanged();
+        }
+
+        var attrs = {};
+        $.each(link, function (k, v) {
+            if ($this.linkeditor.dimensionProperties.indexOf(k) >= 0) {
+                attrs['fb-' + k] = v;
+            } else {
+                attrs['data-' + k] = v;
+            }
+        });
+
+        var e = $('<div class="link" fb-ref="editor" fb-update="1"></div>');
+        $(e).attr(attrs);
+        $("#linkeditor-links").append(e);
+    },
+
+
+    clear: function () {
+
+    },
+};
+module.exports = LinkeditorLinks;
diff --git a/resources/linkeditor/js/linkeditor.loader.js b/resources/linkeditor/js/linkeditor.loader.js
new file mode 100644 (file)
index 0000000..00624cd
--- /dev/null
@@ -0,0 +1,59 @@
+function LinkeditorLoader(linkeditor) {
+    this.linkeditor = linkeditor;
+    this.init();
+}
+
+LinkeditorLoader.prototype = {
+
+    init: function () {
+        this.rasterizePages = this.linkeditor.utils.splitPages(FLUIDBOOK_DATA.settings.rasterizePages);
+        this.vectorPages = this.linkeditor.utils.splitPages(FLUIDBOOK_DATA.settings.vectorPages);
+    },
+
+    loadPage: function (p, side) {
+        var container = $("#linkeditor-page-" + side);
+        $(container).attr('data-page', p);
+
+        if (p === 0 || p > FLUIDBOOK_DATA.settings.pages) {
+            $(container).html('');
+        } else {
+            this._loadPage(p, container);
+        }
+
+        this.linkeditor.links.loadLinks(p, side);
+        this.linkeditor.rulers.loadRulers(p, side);
+    },
+
+
+    preloadPages: function () {
+        let j = 1;
+        var $this = this;
+        for (let i = Math.max(1, this.linkeditor.currentPage - 4); i <= Math.min(this.linkeditor.currentPage + 16, FLUIDBOOK_DATA.settings.pages); i++) {
+            if ($('.preload[data-page="' + i + '"]').length >= 1) {
+                continue;
+            }
+            setTimeout(function () {
+                var c = $('<div class="preload" data-page="i"></div>');
+                $("#linkeditor-preload").append(c);
+                $this._loadPage(i, c);
+            }, j * 500);
+            j++;
+        }
+    },
+
+    _loadPage: function (p, container) {
+        var imageFormat = FLUIDBOOK_DATA.settings.imageFormat;
+        var c = '<div class="contents">';
+        if (this.rasterizePages.indexOf(p) >= 0) {
+            c += '<img draggable="false" src="raster_' + p + '.' + imageFormat + '" />';
+        } else if (this.vectorPages.indexOf(p) >= 0) {
+            c += '<img draggable="false" src="vector_' + p + '.svg" />';
+        } else {
+            c += '<img draggable="false" class="images" src="images_' + p + '.' + imageFormat + '" />';
+            c += '<img draggable="false" class="texts" src="texts_' + p + '.svg" />';
+        }
+        c += '</div>';
+        $(container).html(c);
+    },
+}
+module.exports = LinkeditorLoader;
diff --git a/resources/linkeditor/js/linkeditor.resize.js b/resources/linkeditor/js/linkeditor.resize.js
new file mode 100644 (file)
index 0000000..7f3aa44
--- /dev/null
@@ -0,0 +1,30 @@
+function LinkeditorResize(linkeditor) {
+    this.linkeditor = linkeditor;
+    this.init();
+}
+
+LinkeditorResize.prototype = {
+    init: function () {
+        var $this = this;
+        $(window).on('resize', function () {
+            $this.resize();
+        });
+    },
+
+    resize: function () {
+        this.resizeCanvas();
+        this.linkeditor.rulers.updateRulers();
+    },
+
+    resizeCanvas: function () {
+        this.linkeditor.canvasRect = $("#linkeditor-canvas").get(0).getBoundingClientRect();
+        this.linkeditor.editorRect = $("#linkeditor-editor").get(0).getBoundingClientRect();
+        var aw = this.linkeditor.canvasRect.width - 30;
+        var ah = this.linkeditor.canvasRect.height - 30;
+        this.linkeditor.fs = Math.min(aw / this.linkeditor.fw, ah / this.linkeditor.fh);
+        var left = (this.linkeditor.canvasRect.width - this.linkeditor.fw * this.linkeditor.fs) / 2;
+        var top = (this.linkeditor.canvasRect.height - this.linkeditor.fh * this.linkeditor.fs) / 2;
+        $("#linkeditor-fluidbook").css({left: left, top: top, transform: 'scale(' + this.linkeditor.fs + ')'});
+    },
+};
+module.exports = LinkeditorResize;
diff --git a/resources/linkeditor/js/linkeditor.rulers.js b/resources/linkeditor/js/linkeditor.rulers.js
new file mode 100644 (file)
index 0000000..014d16c
--- /dev/null
@@ -0,0 +1,230 @@
+function LinkeditorRulers(linkeditor) {
+    this.linkeditor = linkeditor;
+
+    this.movingRuler = null;
+    this.rulersMagnetValuesX = [];
+    this.rulersMagnetValuesY = [];
+    this.dividers = [1, 2, 5, 10, 20, 50, 100, 200];
+
+    this.init();
+}
+
+LinkeditorRulers.prototype = {
+    init: function () {
+        let $this = this;
+        $("#linkeditor-canvas").on('scroll', function () {
+            $this.updateRulers();
+        });
+
+        $("#linkeditor-ruler-x").on('mousedown', function (e) {
+            $this.addRuler('y');
+        });
+
+        $("#linkeditor-ruler-y").on('mousedown', function (e) {
+            $this.addRuler('x');
+        });
+
+        $(document).on('mousedown', ".ruler", function (e) {
+            $this.movingRuler = $(this);
+        });
+    },
+
+    mouseUp: function () {
+        this.stopMoveRuler();
+    },
+
+    loadRulers: function (page, side) {
+        let $this = this;
+        $.each(RULERS, function (uid, ruler) {
+            if (ruler.page != page) {
+                return;
+            }
+            $this.addRuler(ruler.type, ruler.pos, ruler.uid);
+        });
+    },
+
+
+    updateRulers: function () {
+        // Update rects
+        this.linkeditor.fluidbookRect = $("#linkeditor-fluidbook").get(0).getBoundingClientRect();
+
+        $("#linkeditor-ruler-y,#linkeditor-ruler-x").html('');
+        // Measure of visible fluidbook px at current zoom
+
+        let factor = this.linkeditor.fs / this.linkeditor.zoom.zoom;
+        let visible_w = (this.linkeditor.canvasRect.width / this.linkeditor.fs) / this.linkeditor.zoom.zoom;
+        let visible_h = (this.linkeditor.canvasRect.height / this.linkeditor.fs) / this.linkeditor.zoom.zoom;
+
+        // Find the best divider to have around 10 main divisions
+        let divider = 0;
+        for (let d in this.dividers) {
+            divider = this.dividers[d];
+            let v = visible_h / divider;
+            if (v <= 10) {
+                break;
+            }
+        }
+
+        let divisionSize = divider * this.linkeditor.fs * this.linkeditor.zoom.zoom;
+        // Draw vertical ruler
+        let margin = 100;
+        let nbDivisions = Math.floor(visible_h / divider);
+        let y0 = 16 + this.linkeditor.fluidbookRect.y - this.linkeditor.canvasRect.y;
+
+        let yruler = '<div class="info"><span>1234.12</span></div>';
+        for (let y = -margin; y <= nbDivisions + (margin * 2) + 1; y++) {
+            // Draw subdivision
+            let v = divider * y;
+            let ystart = y0 + (y * divisionSize);
+            if (ystart + divisionSize < 0 || ystart > this.linkeditor.canvasRect.height) {
+                continue;
+            }
+            yruler += '<div class="division" style="top:' + ystart + 'px;height:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
+            for (let j = 1; j <= 9; j++) {
+                yruler += '<div class="subdivision ' + (j === 5 ? ' middle' : '') + '" style="top:' + ((j * divisionSize) / 10) + 'px;"></div>';
+            }
+            yruler += '</div>';
+        }
+        $("#linkeditor-ruler-y").html(yruler);
+
+        // Draw horizontal ruler
+        nbDivisions = Math.floor(visible_w / divider);
+        let x0 = 16 + this.linkeditor.fluidbookRect.x - this.linkeditor.canvasRect.x;
+        let xruler = '<div class="info"><span></span></div>';
+
+        for (let x = -margin; x <= nbDivisions + (margin * 2) + 1; x++) {
+            // Draw subdivision
+            xruler += this._drawHorizontalSubdiv(x, x0, divider, divisionSize);
+        }
+        // Draw right page horizontal ruler
+        if (!this.linkeditor.single) {
+            x0 = x0 + this.linkeditor.pw * this.linkeditor.fs * this.linkeditor.zoom.zoom;
+            for (let x = 0; x <= nbDivisions + margin + 1; x++) {
+                // Draw subdivision
+                xruler += this._drawHorizontalSubdiv(x, x0, divider, divisionSize);
+            }
+        }
+        $("#linkeditor-ruler-x").html(xruler);
+        this.linkeditor.updateFBElements(true);
+        this.updateMousePositionRulers();
+    },
+
+    _drawHorizontalSubdiv: function (x, x0, divider, divisionSize) {
+        let v = divider * x;
+        let xstart = x0 + (x * divisionSize);
+        if (xstart + divisionSize < 0 || xstart > this.linkeditor.canvasRect.width) {
+            return '';
+        }
+        let res = '<div class="division" style="left:' + xstart + 'px;width:' + divisionSize + 'px;"><div class="value">' + Math.abs(v) + '</div>';
+        for (let i = 1; i <= 9; i++) {
+            let cls = '';
+            if (i === 5) {
+                cls += ' middle';
+            }
+            res += '<div class="subdivision ' + cls + '" style="left:' + ((i * divisionSize) / 10) + 'px;"></div>';
+        }
+        res += '</div>';
+        return res;
+    },
+
+    updateRulersMagnetValues: function () {
+        var $this = this;
+        this.rulersMagnetValuesX = [0, this.linkeditor.pw, this.linkeditor.pw * 2];
+        this.rulersMagnetValuesY = [0, this.linkeditor.ph];
+        $.each(this.linkeditor.links.getLinksOfPage(this.linkeditor.currentPage), function (uid, link) {
+            const left = parseFloat(link.left);
+            const top = parseFloat(link.top);
+            const width = parseFloat(link.width);
+            const height = parseFloat(link.height);
+            $this.rulersMagnetValuesX.push(left, left + width);
+            $this.rulersMagnetValuesY.push(top, top + height);
+        });
+    },
+
+    updateMousePositionRulers: function () {
+        let rulersRect = $("#linkeditor-rulers").get(0).getBoundingClientRect();
+        let rx = this.linkeditor.mx - rulersRect.x;
+        let ry = this.linkeditor.my - rulersRect.y;
+        $("#linkeditor-ruler-x .info").css('left', rx);
+        $("#linkeditor-ruler-y .info").css('top', ry);
+
+        let rrect = $("#linkeditor-rulers").get(0).getBoundingClientRect();
+
+        if (this.linkeditor.mx - rrect.x < 0 || this.linkeditor.my - rrect.y < 0 || this.linkeditor.mx - rrect.x > rrect.width || this.linkeditor.my - rrect.y > rrect.height) {
+            $("#linkeditor-rulers .info").hide();
+            return;
+        }
+
+        let fb = this.linkeditor.globalToFluidbook(this.linkeditor.mx, this.linkeditor.my, true);
+        $("#linkeditor-ruler-y .info span").text(fb.y.toFixed(2));
+        $("#linkeditor-ruler-x .info span").text(fb.x.toFixed(2));
+        $("#linkeditor-rulers .info").css('display', 'inline-block');
+    },
+
+    stopMoveRuler: function () {
+        this.moveRuler();
+        if ($(this.movingRuler).hasClass('pending-delete')) {
+            this.deleteRuler($(this.movingRuler));
+        }
+        this.movingRuler = null;
+    },
+
+    moveRuler: function () {
+        if (this.movingRuler === null || this.movingRuler === undefined) {
+            return;
+        }
+        let magnet = !key.ctrl;
+        let editorMouse = this.linkeditor.globalToEditor(this.linkeditor.mx, this.linkeditor.my);
+        let fbMouse = this.linkeditor.globalToFluidbook(this.linkeditor.mx, this.linkeditor.my, false);
+        let css = {};
+        let attrs = {'fb-update': '1'};
+        let v, fbv;
+        if ($(this.movingRuler).data('axis') === 'x') {
+            v = editorMouse.x;
+            fbv = attrs['fb-left'] = magnet ? this.linkeditor.utils.magnetize(fbMouse.x, this.rulersMagnetValuesX) : fbMouse.x;
+        } else {
+            v = editorMouse.y;
+            fbv = attrs['fb-top'] = magnet ? this.linkeditor.utils.magnetize(fbMouse.y, this.rulersMagnetValuesY) : fbMouse.y;
+        }
+        if (v < 16) {
+            $(this.movingRuler).addClass('pending-delete');
+        } else {
+            $(this.movingRuler).removeClass('pending-delete');
+        }
+        $(this.movingRuler).css(css).attr(attrs);
+        RULERS[$(this.movingRuler).data('uid')].pos = fbv;
+        this.linkeditor.updateFBElements();
+    },
+
+    addRuler: function (axis, pos, uid) {
+        if (undefined === uid) {
+            uid = this.linkeditor.utils.generateUID();
+            RULERS[uid] = {page: this.linkeditor.currentPage, type: axis, uid: uid};
+            this.linkeditor.hasChanged();
+        }
+        let ruler = $('<div class="ruler" data-uid="' + uid + '" fb-update="1" fb-ref="editor" data-axis="' + axis + '"></div>');
+        if (pos === undefined) {
+            this.movingRuler = ruler;
+        } else {
+            let dim = axis == 'x' ? 'left' : 'top';
+            $(ruler).attr('fb-' + dim, pos);
+        }
+        $("#linkeditor-editor").append(ruler);
+        this.moveRuler();
+    },
+
+    deleteRuler: function (ruler) {
+        if (ruler === undefined) {
+            ruler = this.movingRuler;
+        }
+        delete RULERS[$(ruler).data('uid')];
+        $(ruler).remove();
+        this.movingRuler = null;
+        this.linkeditor.hasChanged();
+    },
+
+    clear: function () {
+        $('#linkeditor-editor .ruler').remove();
+    },
+};
+module.exports = LinkeditorRulers;
diff --git a/resources/linkeditor/js/linkeditor.save.js b/resources/linkeditor/js/linkeditor.save.js
new file mode 100644 (file)
index 0000000..6ef30f9
--- /dev/null
@@ -0,0 +1,65 @@
+
+function LinkeditorSave(linkeditor) {
+    this.linkeditor = linkeditor;
+    this.init();
+}
+
+LinkeditorSave.prototype = {
+    init: function () {
+        let $this = this;
+
+        this.automaticSaveFrequency = 1000 * 5 * 60;
+        this.unsavedChanges = false;
+        this.automaticSaveTimeout = null;
+        this.runningAutomaticSaveTimeout = false;
+
+        $(window).on('beforeunload', function () {
+            if ($this.unsavedChanges) {
+                return TRANSLATIONS.warning_unsaved_changes;
+            }
+        })
+    },
+
+    hasChanged: function () {
+        let $this = this;
+        this.unsavedChanges = true;
+        if (this.runningAutomaticSaveTimeout === false) {
+            this.runningAutomaticSaveTimeout = true;
+            this.automaticSaveTimeout = setTimeout(function () {
+                $this.automaticSave();
+            }, this.automaticSaveFrequency);
+        }
+    },
+
+    save: function (message) {
+        var $this = this;
+        if (message === undefined) {
+            message = TRANSLATIONS.manual_save_message;
+        }
+        $.ajax({
+            url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/save/links', method: 'post', data: {
+                _method: 'put', 'message': message, rulers: window.RULERS, links: window.LINKS,
+            },
+            success: function (data) {
+                new Noty({
+                    type: "success", text: TRANSLATIONS.success_save,
+                }).show();
+                clearTimeout(automaticSaveTimeout);
+                $this.unsavedChanges = false;
+                $this.runningAutomaticSaveTimeout = false;
+            },
+            error: function (jqXHR, status, error) {
+                $this.linkeditor.hasChanged();
+                new Noty({
+                    type: "error", text: TRANSLATIONS.error_save + ' : ' + error,
+                }).show();
+            },
+        });
+    },
+
+    automaticSave: function () {
+        this.save(TRANSLATIONS.automatic_save_message);
+    },
+};
+
+module.exports = LinkeditorSave;
diff --git a/resources/linkeditor/js/linkeditor.toolbar.js b/resources/linkeditor/js/linkeditor.toolbar.js
new file mode 100644 (file)
index 0000000..4c4e705
--- /dev/null
@@ -0,0 +1,41 @@
+function LinkeditorToolbar(linkeditor) {
+    this.linkeditor = linkeditor;
+    this.init();
+}
+
+LinkeditorToolbar.prototype = {
+    init: function () {
+        var $this=this;
+        $("#linkeditor-page-field input").on('change', function () {
+            $this.linkeditor.changePage($(this).val());
+            $(this).blur();
+            return false;
+        });
+
+        $("#linkeditor-toolbar [data-icon]").each(function () {
+            $(this).append(getSpriteIcon('linkeditor-' + $(this).data('icon')));
+        });
+
+        $("[data-key]").each(function () {
+            let e = $(this);
+            key($(this).data('key'), function () {
+                $(e).addClass('hover');
+                $this.linkeditor.runAction($(e).data('action'));
+                setTimeout(function () {
+                    $(e).removeClass('hover')
+                }, 150);
+                return false;
+            });
+        });
+
+        $('[data-action]').click(function () {
+            $this.linkeditor.runAction($(this).data('action'));
+            return false;
+        });
+
+        $('[data-tooltip]').each(function () {
+            tippy($(this).get(0), {content: $(this).data('tooltip')});
+        });
+    },
+};
+module.exports = LinkeditorToolbar;
diff --git a/resources/linkeditor/js/linkeditor.utils.js b/resources/linkeditor/js/linkeditor.utils.js
new file mode 100644 (file)
index 0000000..f5122f7
--- /dev/null
@@ -0,0 +1,71 @@
+var LinkeditorUtils = function (linkeditor) {
+    this.linkeditor = linkeditor;
+    this.init();
+};
+
+LinkeditorUtils.prototype = {
+    init: function () {
+
+    },
+
+    normalizePage: function (page) {
+        page = parseInt(page);
+        if (page % 2 === 1 && !this.linkeditor.single) {
+            page--;
+        }
+        return Math.max(0, Math.min(page, FLUIDBOOK_DATA.settings.pages));
+    },
+
+    splitPages: function (str) {
+        str = str.toString();
+        let res = [];
+        if (str == '') {
+            return res;
+        }
+        let pages = str.split(',');
+        for (let p in pages) {
+            res.push(parseInt(pages[p]));
+        }
+        return res;
+    },
+
+    pct: function (v) {
+        return (v * 100) + '%';
+    },
+
+    generateUID: function () {
+        const length = 12;
+        let result = '';
+        const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
+        const charactersLength = characters.length;
+        for (let i = 0; i < length; i++) {
+            result += characters.charAt(Math.floor(Math.random() * charactersLength));
+        }
+        return result;
+    },
+
+    magnetize: function (value, values) {
+        let sensibility = 8 / (this.linkeditor.zoom.zoom * this.linkeditor.fs);
+        let min = 100000;
+        let magnetValue;
+        if (values.length === 0) {
+            return value;
+        }
+
+        for (let i in values) {
+            let v = values[i];
+            let diff = Math.abs(v - value);
+            if (diff < min) {
+                min = diff;
+                magnetValue = v;
+            }
+        }
+
+        if (min > sensibility) {
+            return value;
+        }
+        return magnetValue;
+    },
+};
+
+module.exports = LinkeditorUtils;
diff --git a/resources/linkeditor/js/linkeditor.zoom.js b/resources/linkeditor/js/linkeditor.zoom.js
new file mode 100644 (file)
index 0000000..8ae298d
--- /dev/null
@@ -0,0 +1,99 @@
+function LinkeditorZoom(linkeditor) {
+    this.linkeditor = linkeditor;
+    this.init();
+}
+
+LinkeditorZoom.prototype = {
+    init: function () {
+        var $this = this;
+
+        this.zoom = 1;
+        this.zoomdragging = false;
+
+        $("#linkeditor-main").on('wheel', function (e) {
+            let delta = e.originalEvent.deltaY;
+            if (delta == 0) {
+                return true;
+            }
+            e.stopPropagation();
+            e.stopImmediatePropagation();
+            e.preventDefault();
+            if (delta < 0) {
+                if ($this.setZoom($this.zoom + 0.25)) {
+                    $this.moveZoom(e);
+                }
+            } else {
+                $this.setZoom($this.zoom - 0.25);
+            }
+            return false;
+        });
+    },
+
+    mouseUp: function () {
+        if (this.zoomdragging !== false) {
+            this.moveZoomDrag(e);
+        }
+        this.resetZoomDrag();
+    },
+
+    updateMousePosition: function () {
+        if (!$("#linkeditor-main").hasClass('grab') || this.zoom === 1) {
+            this.resetZoomDrag();
+        }
+        if (this.zoomdragging !== false) {
+            this.moveZoomDrag(e);
+        }
+    },
+
+    resetZoomDrag: function () {
+        $("#linkeditor-main").removeClass('grab').removeClass('grabbing');
+        if (this.zoom === 1) {
+            $("#linkeditor-canvas").scrollTo({left: 0, top: 0});
+        }
+        this.zoomdragging = false;
+    },
+
+    moveZoomDrag: function (e) {
+        let deltaX = e.pageX - this.zoomdragging.x;
+        let deltaY = e.pageY - this.zoomdragging.y;
+        $("#linkeditor-canvas").scrollTo({
+            top: this.zoomdragging.scrollY - deltaY, left: this.zoomdragging.scrollX - deltaX
+        });
+        this.linkeditor.rulers.updateRulers();
+    },
+
+    moveZoom: function (e) {
+        let rect = $("#linkeditor-zoom").get(0).getBoundingClientRect();
+        let lx = (e.originalEvent.pageX - rect.x) / rect.width;
+        let ly = (e.originalEvent.pageY - rect.y) / rect.height;
+        $("#linkeditor-canvas").scrollTo({left: this.linkeditor.utils.pct(lx), top: this.linkeditor.utils.pct(ly)});
+        this.linkeditor.rulers.updateRulers();
+    },
+
+    setZoom: function (z) {
+        let $this = this;
+        z = Math.max(1, Math.min(6, z));
+        if (z === this.zoom) {
+            return false;
+        }
+        this.zoom = z;
+        $("#linkeditor-canvas").attr('data-z', this.zoom);
+        $("#linkeditor-zoom").css({transform: 'scale(' + this.zoom + ')', overflow: 'visible'});
+
+        setTimeout(function () {
+            $("#linkeditor-zoom").css({overflow: 'hidden'});
+            if (this.zoom === 1) {
+                $this.resetZoomDrag();
+            }
+            $this.linkeditor.rulers.updateRulers();
+
+        }, 10);
+        return true;
+    },
+
+    reset: function () {
+        setZoom(1);
+        resetZoomDrag();
+    },
+};
+module.exports = LinkeditorZoom;
index 7829cc61cd6c7438cd1093a321d6662481e6f385..1ebef6f2c79770e49901b99d4cdee8336cd2ecf0 100644 (file)
@@ -7,6 +7,7 @@
         cursor: cell
 
         &.selected
+            cursor: move
             .corners
                 visibility: visible
 
index 49f809b891213aba8b8d402e0985a2bebd350cab..869b623d0c0342a409fdc6fb3cd957b9313df9e5 100644 (file)
         <div id="linkeditor-main">
             <div draggable="false" id="linkeditor-toolbar">
                 <nav id="linkeditor-toolbar-left">
-                    <a href="#" data-icon="save" data-action="saveLinks" data-tooltip="{{__('Sauvegarder')}} (Ctrl+S)"
+                    <a href="#" data-icon="save" data-action="save.save" data-tooltip="{{__('Sauvegarder')}} (Ctrl+S)"
                        data-key="ctrl+s"></a>
                     <a href="#" data-action="openVersions" data-icon="wayback-machine"
                        data-tooltip="{{__('Restaurer une version précédente')}}"></a>
                     <div class="separator"></div>
-                    <a href="#" data-action="zoomReset" data-icon="zoom-reset"
+                    <a href="#" data-action="zoom.reset" data-icon="zoom-reset"
                        data-tooltip="{{__('Réinitialiser le zoom')}} (Esc)" data-key="esc"></a>
                     <div class="separator"></div>
                     <a href="#" data-action="importExcel" data-icon="import-links"