|
@@ -7,6 +7,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
const saveForm = document.getElementById('saveForm')
|
|
const saveForm = document.getElementById('saveForm')
|
|
|
const htmlInput = document.getElementById('htmlInput')
|
|
const htmlInput = document.getElementById('htmlInput')
|
|
|
const pageContainer = document.getElementById('page-container')
|
|
const pageContainer = document.getElementById('page-container')
|
|
|
|
|
+ let host = null
|
|
|
|
|
|
|
|
let mode = null
|
|
let mode = null
|
|
|
const toggleMode = newMode => {
|
|
const toggleMode = newMode => {
|
|
@@ -29,16 +30,60 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
const range = document.createRange()
|
|
const range = document.createRange()
|
|
|
range.selectNode(image)
|
|
range.selectNode(image)
|
|
|
selection.addRange(range)
|
|
selection.addRange(range)
|
|
|
- console.log('selected?')
|
|
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
saveButton.addEventListener('click', () => {
|
|
saveButton.addEventListener('click', () => {
|
|
|
|
|
+ htmlInput.value = ""
|
|
|
htmlInput.value = document.documentElement.outerHTML
|
|
htmlInput.value = document.documentElement.outerHTML
|
|
|
saveForm.submit()
|
|
saveForm.submit()
|
|
|
|
|
+ htmlInput.value = ""
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const save = () => new Promise((resolve, reject) => {
|
|
|
|
|
+ document.documentElement.classList.add('saving')
|
|
|
|
|
+ const req = new XMLHttpRequest()
|
|
|
|
|
+ req.open('POST', 'save', true)
|
|
|
|
|
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
+ req.responseType = 'arraybuffer'
|
|
|
|
|
+ htmlInput.value = ""
|
|
|
|
|
+ const body = `html=${encodeURIComponent(document.documentElement.outerHTML)}`
|
|
|
|
|
+
|
|
|
|
|
+ req.onload = () => {
|
|
|
|
|
+ resolve(req.response)
|
|
|
|
|
+ //const reader = new FileReader()
|
|
|
|
|
+ // reader.readAsDataURL(req.response)
|
|
|
|
|
+ // reader.addEventListener('load', () => {
|
|
|
|
|
+ // resolve(reader.result)
|
|
|
|
|
+ // })
|
|
|
|
|
+ // reader.addEventListener('error', (ev) => {
|
|
|
|
|
+ // reject(ev.error)
|
|
|
|
|
+ // })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ req.onerror = (ev) => {
|
|
|
|
|
+ reject(ev.error)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ req.send(body)
|
|
|
|
|
+
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ document.documentElement.classList.remove('saving')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Get screen to print ratio
|
|
// Get screen to print ratio
|
|
|
|
|
+ /*
|
|
|
|
|
+ The editing process deals with documents in several different dimensions and units of distance:
|
|
|
|
|
+ - Paper size (A4, Legal, etc)
|
|
|
|
|
+ - Print size (Font size in points, applied using the '@media print' rule)
|
|
|
|
|
+ - Screen size (Font size in pixels, applied using the '@media screen' rule)
|
|
|
|
|
+ - Zoom (Display transformation of the document preview, applied using transform-matrix)
|
|
|
|
|
+ - Browser size (1:1 ratio for the browser itself)
|
|
|
|
|
+
|
|
|
|
|
+ The following code reads evidence of these different values from the CSS, and determines how to transform from
|
|
|
|
|
+ browser vector space, to preview vector space, to print vector space, so our edits and the preview are accurately
|
|
|
|
|
+ represented in the resulting PDF.
|
|
|
|
|
+ */
|
|
|
const localStyleSheets = Array.from(document.styleSheets).filter(x => !x.href)
|
|
const localStyleSheets = Array.from(document.styleSheets).filter(x => !x.href)
|
|
|
const pageOnePixelWidth = localStyleSheets.map(ss =>
|
|
const pageOnePixelWidth = localStyleSheets.map(ss =>
|
|
|
Array.from(ss.rules)
|
|
Array.from(ss.rules)
|
|
@@ -58,6 +103,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
const screenToPrintRatio = pageOnePrintPixelWidth / pageOnePixelWidth
|
|
const screenToPrintRatio = pageOnePrintPixelWidth / pageOnePixelWidth
|
|
|
const printToScreenRatio = pageOnePixelWidth / pageOnePrintPixelWidth
|
|
const printToScreenRatio = pageOnePixelWidth / pageOnePrintPixelWidth
|
|
|
|
|
|
|
|
|
|
+ // Replacements are constructed in print vector space, so they must be scaled down in screen.
|
|
|
const myStyle = document.createElement('style')
|
|
const myStyle = document.createElement('style')
|
|
|
myStyle.type = 'text/css'
|
|
myStyle.type = 'text/css'
|
|
|
myStyle.innerHTML = `
|
|
myStyle.innerHTML = `
|
|
@@ -130,6 +176,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
r2.top > r1.bottom ||
|
|
r2.top > r1.bottom ||
|
|
|
r2.bottom < r1.top)
|
|
r2.bottom < r1.top)
|
|
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
|
+ The common whiteout approach of drawing an opaque white box over sensitive content is quite ineffective.
|
|
|
|
|
+ A user can simply select the text from behind the whiteout, and copy & paste it into another program.
|
|
|
|
|
+ This method finds every text character within the selected rectangle, and replaces it with a placeholder.
|
|
|
|
|
+ It also modifies the background image by drawing a white box, replacing the pixels within the rectangle.
|
|
|
|
|
+ */
|
|
|
const whiteout = rect => {
|
|
const whiteout = rect => {
|
|
|
const elements = []
|
|
const elements = []
|
|
|
const walk = (element) => {
|
|
const walk = (element) => {
|
|
@@ -187,6 +239,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
element.src = canvas.toDataURL()
|
|
element.src = canvas.toDataURL()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
+ window.editor.changed = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Edit Text
|
|
// Edit Text
|
|
@@ -198,6 +251,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
document.querySelectorAll('.pc').forEach(page => {
|
|
document.querySelectorAll('.pc').forEach(page => {
|
|
|
page.setAttribute('contenteditable', 'true')
|
|
page.setAttribute('contenteditable', 'true')
|
|
|
})
|
|
})
|
|
|
|
|
+ window.editor.changed = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const disableEditText = () => {
|
|
const disableEditText = () => {
|
|
@@ -207,4 +261,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
page.setAttribute('contenteditable', 'false')
|
|
page.setAttribute('contenteditable', 'false')
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ const attach = hostController => {
|
|
|
|
|
+ host = hostController
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.editor = {
|
|
|
|
|
+ attach,
|
|
|
|
|
+ save,
|
|
|
|
|
+ changed: false
|
|
|
|
|
+ }
|
|
|
})
|
|
})
|