     dimensions.width = parseInt(width, 10) if width
     dimensions.height = parseInt(height, 10) if height
     dimensions
+
+  # Element inspection
+
+  isBlockElement = (element) ->
+    return unless element?.nodeType is Node.ELEMENT_NODE
+    return if findClosestElementFromNode(element, matchingSelector: "td")
+    tagName(element) in getBlockTagNames() or window.getComputedStyle(element).display is "block"
+
+  isInsignificantTextNode = (node) ->
+    return unless node?.nodeType is Node.TEXT_NODE
+    return if /\S/.test(node.data)
+    return if elementCanDisplayPreformattedText(node.parentNode)
+    not node.previousSibling or isBlockElement(node.previousSibling) or not node.nextSibling or isBlockElement(node.nextSibling)
+
+  isExtraBR = (element) ->
+    tagName(element) is "br" and
+      isBlockElement(element.parentNode) and
+      element.parentNode.lastChild is element
+
+  elementCanDisplayPreformattedText = (element) ->
+    {whiteSpace} = window.getComputedStyle(element)
+    whiteSpace in ["pre", "pre-wrap", "pre-line"]
+
+  # Margin translation
+
+  translateBlockElementMarginsToNewlines: ->
+    defaultMargin = @getMarginOfDefaultBlockElement()
+
+    for block, index in @blocks when margin = @getMarginOfBlockElementAtIndex(index)
+      if margin.top > defaultMargin.top * 2
+        @prependStringToTextAtIndex("\n", index)
+
+      if margin.bottom > defaultMargin.bottom * 2
+        @appendStringToTextAtIndex("\n", index)
+
+  getMarginOfBlockElementAtIndex: (index) ->
+    if element = @blockElements[index]
+      unless tagName(element) in getBlockTagNames() or element in @processedElements
+        getBlockElementMargin(element)
+
+  getMarginOfDefaultBlockElement: ->
+    element = makeElement(Trix.config.blockAttributes.default.tagName)
+    @containerElement.appendChild(element)
+    getBlockElementMargin(element)
+
+  getBlockElementMargin = (element) ->
+    style = window.getComputedStyle(element)
+    if style.display is "block"
+      top: parseInt(style.marginTop), bottom: parseInt(style.marginBottom)
+
+  # Helpers
+
+  squishWhitespace = (string) ->
+    string
+      # Replace all breaking whitespace characters with a space
+      .replace(///[^\S#{Trix.NON_BREAKING_SPACE}]///g, " ")
+      # Replace two or more spaces with a single space
+      .replace(/\ {2,}/g, " ")

      element = element.parentNode
    ancestors

  getMarginOfBlockElementAtIndex: (index) ->
    if element = @blockElements[index]
      unless tagName(element) in getBlockTagNames() or element in @processedElements
        getBlockElementMargin(element)

  getMarginOfDefaultBlockElement: ->
    element = makeElement(Trix.config.blockAttributes.default.tagName)
    @containerElement.appendChild(element)
    getBlockElementMargin(element)

  translateBlockElementMarginsToNewlines: ->
    defaultMargin = @getMarginOfDefaultBlockElement()

    for block, index in @blocks when margin = @getMarginOfBlockElementAtIndex(index)
      if margin.top > defaultMargin.top * 2
        @prependStringToTextAtIndex("\n", index)

      if margin.bottom > defaultMargin.bottom * 2
        @appendStringToTextAtIndex("\n", index)

  pieceForString = (string, attributes = {}) ->
    type = "string"
    string = normalizeSpaces(string)
    {string, attributes, type}

  pieceForAttachment = (attachment, attributes = {}) ->
    type = "attachment"
    {attachment, attributes, type}

  blockForAttributes = (attributes = {}) ->
    text = []
    {text, attributes}

  getAttachmentAttributes = (element) ->
    JSON.parse(element.dataset.trixAttachment)

  sanitizeHTML = (html) ->
    doc = document.implementation.createHTMLDocument("")
    doc.documentElement.innerHTML = html
    {body, head} = doc

    for style in head.querySelectorAll("style")
      body.appendChild(style)

    nodesToRemove = []
    walker = walkTree(body)

    while walker.nextNode()
      node = walker.currentNode
      switch node.nodeType
        when Node.ELEMENT_NODE
          if tagName(node) is "script"
            nodesToRemove.push(node)
          else
            for {name} in [node.attributes...]
              unless name in allowedAttributes or name.indexOf("data-trix") is 0
                node.removeAttribute(name)
        when Node.COMMENT_NODE
          nodesToRemove.push(node)

    for node in nodesToRemove
      node.parentNode.removeChild(node)

    body.innerHTML

  squishWhitespace = (string) ->
    string
      # Replace all breaking whitespace characters with a space
      .replace(///[^\S#{Trix.NON_BREAKING_SPACE}]///g, " ")
      # Replace two or more spaces with a single space
      .replace(/\ {2,}/g, " ")

  isBlockElement = (element) ->
    return unless element?.nodeType is Node.ELEMENT_NODE
    return if findClosestElementFromNode(element, matchingSelector: "td")
    tagName(element) in getBlockTagNames() or window.getComputedStyle(element).display is "block"

  isInsignificantTextNode = (node) ->
    return unless node?.nodeType is Node.TEXT_NODE
    return if /\S/.test(node.data)
    return if elementCanDisplayPreformattedText(node.parentNode)
    not node.previousSibling or isBlockElement(node.previousSibling) or not node.nextSibling or isBlockElement(node.nextSibling)

  isExtraBR = (element) ->
    tagName(element) is "br" and
      isBlockElement(element.parentNode) and
      element.parentNode.lastChild is element

  elementCanDisplayPreformattedText = (element) ->
    {whiteSpace} = window.getComputedStyle(element)
    whiteSpace in ["pre", "pre-wrap", "pre-line"]

  getBlockElementMargin = (element) ->
    style = window.getComputedStyle(element)
    if style.display is "block"
      top: parseInt(style.marginTop), bottom: parseInt(style.marginBottom)

  getImageDimensions = (element) ->
    width = element.getAttribute("width")


  removeHiddenContainer: ->
    @containerElement.parentNode.removeChild(@containerElement)

  sanitizeHTML = (html) ->
    doc = document.implementation.createHTMLDocument("")
    doc.documentElement.innerHTML = html
    {body, head} = doc

    for style in head.querySelectorAll("style")
      body.appendChild(style)

    nodesToRemove = []
    walker = walkTree(body)

    while walker.nextNode()
      node = walker.currentNode
      switch node.nodeType
        when Node.ELEMENT_NODE
          if tagName(node) is "script"
            nodesToRemove.push(node)
          else
            for {name} in [node.attributes...]
              unless name in allowedAttributes or name.indexOf("data-trix") is 0
                node.removeAttribute(name)
        when Node.COMMENT_NODE
          nodesToRemove.push(node)

    for node in nodesToRemove
      node.parentNode.removeChild(node)

    body.innerHTML

  nodeFilter = (node) ->
    if tagName(node) is "style"
      NodeFilter.FILTER_REJECT
    else
      NodeFilter.FILTER_ACCEPT

  processNode: (node) ->
    switch node.nodeType
