Skip to content

Commit

Permalink
Merge pull request #331 from crabbly/custom_clone
Browse files Browse the repository at this point in the history
Custom html deep clone
  • Loading branch information
crabbly authored May 25, 2019
2 parents 1fe527a + c0168f9 commit e943e27
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 84 deletions.
29 changes: 19 additions & 10 deletions src/js/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function loopNodesCollectStyles (elements, params) {
currentElement.setAttribute('style', collectStyles(currentElement, params))
}

// Check if more elements in tree
// Check if we have more elements in the tree
let children = currentElement.children

if (children && children.length) {
Expand All @@ -89,18 +89,27 @@ export function loopNodesCollectStyles (elements, params) {
}
}

export function addHeader (printElement, header, headerStyle) {
// Create header element
let headerElement = document.createElement('h1')
export function addHeader (printElement, params) {
// Create the header container div
let headerContainer = document.createElement('div')

// Create header text node
let headerNode = document.createTextNode(header)
// Check if the header is text or raw html
if (isRawHTML(params.header)) {
headerContainer.innerHTML = params.header
} else {
// Create header element
let headerElement = document.createElement('h1')

// Build and style
headerElement.appendChild(headerNode)
headerElement.setAttribute('style', headerStyle)
// Create header text node
let headerNode = document.createTextNode(params.header)

printElement.insertBefore(headerElement, printElement.childNodes[0])
// Build and style
headerElement.appendChild(headerNode)
headerElement.setAttribute('style', params.headerStyle)
headerContainer.appendChild(headerElement)
}

printElement.insertBefore(headerContainer, printElement.childNodes[0])
}

export function cleanUp (params) {
Expand Down
79 changes: 39 additions & 40 deletions src/js/html.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,59 @@
import { collectStyles, loopNodesCollectStyles, addWrapper, addHeader } from './functions'
import { addHeader } from './functions'
import Print from './print'

export default {
print: (params, printFrame) => {
// Get HTML printable element
// Get the DOM printable element
let printElement = document.getElementById(params.printable)

// Check if element exists
// Check if the element exists
if (!printElement) {
window.console.error('Invalid HTML element id: ' + params.printable)

return false
return
}

// Make a copy of the printElement to prevent DOM changes
let printableElement = document.createElement('div')
printableElement.appendChild(printElement.cloneNode(true))

// Add cloned element to DOM, to have DOM element methods available. It will also be easier to colect styles
printableElement.setAttribute('style', 'height:0; overflow:hidden;')
printableElement.setAttribute('id', 'printJS-html')
printElement.parentNode.appendChild(printableElement)

// Update printableElement variable with newly created DOM element
printableElement = document.getElementById('printJS-html')

// Process html styles
if (params.scanStyles === true) {
// Optional - include margin and padding
if (params.honorMarginPadding) params.targetStyles.push('margin', 'padding')

// Optional - include color
if (params.honorColor) params.targetStyles.push('color')
// Clone the target element including its children (if available)
params.printableElement = cloneElement(printElement, params)

// Get main element styling
printableElement.setAttribute('style', collectStyles(printableElement, params) + 'margin:0 !important;')
// Add header
if (params.header) {
addHeader(params.printableElement, params)
}

// Get all children elements
let elements = printableElement.children
// Print html element contents
Print.send(params, printFrame)
}
}

// Get styles for all children elements
loopNodesCollectStyles(elements, params)
}
function cloneElement (element, params) {
// Clone the main node (if not already inside the recursion process)
const clone = element.cloneNode()

// Add header
if (params.header) {
addHeader(printableElement, params.header, params.headerStyle)
// Loop over and process the children elements / nodes (including text nodes)
for (let child of element.childNodes) {
// Check if we are skiping the current element
if (params.ignoreElements.indexOf(child.id) !== -1) {
continue
}

// Remove DOM printableElement
printableElement.parentNode.removeChild(printableElement)
// Clone the child element
const clonedChild = cloneElement(child, params)

// Store html data
params.htmlData = addWrapper(printableElement.innerHTML, params)
// Attach the cloned child to the cloned parent node
clone.appendChild(clonedChild)
}

// Print html element contents
Print.send(params, printFrame)
// Check if the element needs any state processing (copy user input data)
switch (element.tagName) {
case 'SELECT':
// Copy the current selection value to its clone
clone.value = element.value
break
case 'CANVAS':
// Copy the canvas content to its clone
clone.getContext('2d').drawImage(element, 0, 0)
break
}

return clone
}
12 changes: 4 additions & 8 deletions src/js/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,29 @@ export default {
}

// Create printable element (container)
let printableElement = document.createElement('div')
printableElement.setAttribute('style', 'width:100%')
params.printableElement = document.createElement('div')

// Create all image elements and append them to the printable container
params.printable.forEach(src => {
// Create the image element
let img = document.createElement('img')
img.setAttribute('style', params.imageStyle)

// Set image src with the file url
img.src = src

// Create the image wrapper
let imageWrapper = document.createElement('div')
imageWrapper.setAttribute('style', params.imageStyle)

// Append image to the wrapper element
imageWrapper.appendChild(img)

// Append wrapper to the printable element
printableElement.appendChild(imageWrapper)
params.printableElement.appendChild(imageWrapper)
})

// Check if we are adding a print header
if (params.header) addHeader(printableElement, params.header, params.headerStyle)

// Store html data
params.htmlData = printableElement.outerHTML
if (params.header) addHeader(params.printableElement, params)

// Print image
Print.send(params, printFrame)
Expand Down
4 changes: 2 additions & 2 deletions src/js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export default {
onBrowserIncompatible: () => true,
modalMessage: 'Retrieving Document...',
frameId: 'printJS',
htmlData: '',
printableElement: null,
documentTitle: 'Document',
targetStyle: ['clear', 'display', 'width', 'min-width', 'height', 'min-height', 'max-height'],
targetStyles: ['border', 'box', 'break', 'text-decoration'],
ignoreElements: [],
imageStyle: 'width:100%;',
imageStyle: 'max-width: 100%;',
repeatTableHeader: true,
css: null,
style: null,
Expand Down
21 changes: 6 additions & 15 deletions src/js/json.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
addWrapper,
capitalizePrint,
isRawHTML
} from './functions'
import { capitalizePrint, addHeader } from './functions'
import Print from './print'

export default {
Expand Down Expand Up @@ -31,21 +27,16 @@ export default {
}
})

// Variable to hold the html string
let htmlData = ''
// Create a print container element
params.printableElement = document.createElement('div')

// Check if there is a header on top of the table
// Check if we are adding a print header
if (params.header) {
htmlData += isRawHTML(params.header)
? params.header
: '<h1 style="' + params.headerStyle + '">' + params.header + '</h1>'
addHeader(params.printableElement, params)
}

// Build the printable html data
htmlData += jsonToHTML(params)

// Store the data
params.htmlData = addWrapper(htmlData, params)
params.printableElement.innerHTML += jsonToHTML(params)

// Print the json data
Print.send(params, printFrame)
Expand Down
6 changes: 3 additions & 3 deletions src/js/print.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ const Print = {
let printDocument = (iframeElement.contentWindow || iframeElement.contentDocument)
if (printDocument.document) printDocument = printDocument.document

// Inject printable html into iframe body
printDocument.body.innerHTML = params.htmlData
// Append printable element to the iframe body
printDocument.body.appendChild(params.printableElement)

// Add custom style
if (params.type !== 'pdf' && params.style !== null) {
if (params.type !== 'pdf' && params.style) {
// Create style element
const style = document.createElement('style')
style.innerHTML = params.style
Expand Down
9 changes: 6 additions & 3 deletions src/js/raw-html.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { addWrapper } from './functions'
import Print from './print'

export default {
print: (params, printFrame) => {
// Store html data
params.htmlData = addWrapper(params.printable, params)
// Create printable element (container)
params.printableElement = document.createElement('div')
params.printableElement.setAttribute('style', 'width:100%')

// Set our raw html as the printable element inner html content
params.printableElement.innerHTML = params.printable

// Print html contents
Print.send(params, printFrame)
Expand Down
38 changes: 35 additions & 3 deletions test/manual/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@
columnSize: 4
}
],
type: 'json'
type: 'json',
header: 'JSON Print Test'
})
}

Expand Down Expand Up @@ -214,15 +215,14 @@
'https://printjs.crabbly.com/images/print-03-highres.jpg'
],
type: 'image',
style: 'img { max-width: 300px; margin: 30px; }',
showModal: true,
modalMessage: 'Printing...'
})
}
</script>
<body>
<section id="test" class="test">
<h1></h1>
<h1>Print.js Test Page</h1>
<p>
<button onClick='printPdf();'>
Print PDF
Expand Down Expand Up @@ -282,6 +282,38 @@ <h1></h1>
Print Multiple External Images
</button>
</p>
<div>
<h2>Form Elements</h2>
<div>
<input type="checkbox" /> Checkbox Example
</div>
<div>
<input type="text" value="Styled text input content..." style="color: red; min-width: 300px;" />
</div>
<div>
<textarea>Text area content...</textarea>
</div>
<div>
<canvas id="myCanvas" width="200" height="50"
style="border:2px solid blue;">
Your browser does not support the canvas element.
</canvas>

<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,25);
</script>
</div>
<div>
<select>
<option value="super" selected>Super</option>
<option value="test">Test</option>
<option value="printjs">Print.js</option>
</select>
</div>
</div>
</section>
</body>
</html>

0 comments on commit e943e27

Please sign in to comment.