class Inkcite::Renderer::Td

Constants

CLOSE_TD
LEFT
OUTLOOK_BG

Boolean attribute triggering automatic outlook background integration in the TD.

Public Instance Methods

render(tag, opt, ctx) click to toggle source
# File lib/inkcite/renderer/td.rb, line 5
def render tag, opt, ctx

  html = ''

  # Tracks the depth of currently open TD elements.
  tag_stack = ctx.tag_stack(:td)

  # Grab the attributes of the parent table so that the TD can inherit
  # specific values like padding, valign, responsiveness, etc.
  table_opt = ctx.parent_opts(:table)
  table_mobile = table_opt[:mobile]

  # Check to see if the parent table was set to fluid-drop which causes
  # the table cells to be wrapped in <div> elements and floated to
  # cause them to display more responsively on Android Mail and Gmail apps.
  #
  # Fluid-Hybrid TD courtesy of @moonstrips and our friends at Campaign Monitor
  # https://www.campaignmonitor.com/blog/email-marketing/2014/07/creating-a-centred-responsive-design-without-media-queries/
  is_fluid_drop = is_fluid_drop?(table_mobile)

  if tag == CLOSE_TD

    # Retrieve the opts that were used to open this TD.
    open_opt = tag_stack.pop

    # Normal HTML produced by the Helper to close the cell.
    html << '</td>'

    # If the td was originally opened with fluid-drop, we need to do a fair
    # bit of cleanup...
    if is_fluid_drop

      # Close the
      html << '</tr></table>'

      # Close the floating, responsive div.
      html << '{/div}'

      # Close the conditional cell
      html << if_mso('</td>')

    end

  else

    # Push this tag onto the stack so that child elements (e.g. links)
    # can have access to its attributes.
    tag_stack << opt

    td = Element.new('td')

    # Check to see if a width has been specified for this element.  The
    # width is critical to Fluid-Hybrid drop.
    width = opt[:width].to_i

    # Check for vertical alignment applied to either the TD or to the
    # parent Table.
    valign = detect(opt[:valign], table_opt[:valign])
    td[:valign] = valign unless valign.blank?

    # It is a best-practice to declare the same padding on all cells in a
    # table.  Check to see if padding was declared on the parent.
    padding = get_padding(table_opt)
    td.style[:padding] = px(padding) if padding > 0

    # Apply the no-wrap attribute if provided.
    td[:nowrap] = true if opt[:nowrap]

    mobile = opt[:mobile]

    # If the table defines mobile-padding, then apply the correct mobile
    # style to this td - and its !important if there is padding on
    # the td already.
    unless mobile == HIDE
      mix_mobile_padding td, table_opt, ctx
      mix_mobile_padding td, opt, ctx
    end

    # Need to handle Fluid-Drop HTML injection here before the rest of the
    # TD is formalized.  Fluid-Drop removes the width attribute of the cell
    # as it is wrapped in a 100%-width table.
    if is_fluid_drop

      # Width must be specified for Fluid-Drop cells.  Vertical-alignment is
      # also important but should have been preset by the Table Helper if it
      # was omitted by the designer.
      ctx.error("Width is a required attribute when #{table_mobile} is specified", opt) unless width > 0
      ctx.error("Vertical alignment should be specified when #{table_mobile} is specified", opt) if valign.blank?

      # Conditional Outlook cell to prevent the 100%-wide table within from
      # stretching beyond the max-width.  Also, valign necessary to get float
      # elements to align properly.
      html << if_mso(Element.new('td', :width => width, :valign => valign))

      # Per @campaignmonitor, the secret to the Fluid-Drop trick is to wrap the
      # floating table in a div with "display: inline-block" - which means that
      # they'll obey the text-align property on the parent cell (text-align affects
      # all inline or inline-block elements in a container).
      # https://www.campaignmonitor.com/blog/email-marketing/2014/07/creating-a-centred-responsive-design-without-media-queries/

      div_mobile = mobile == HIDE ? HIDE : FILL
      html << %Q({div width=#{width} display=inline-block valign=#{valign} mobile="#{div_mobile}"})

      # One last wrapper table within the div.  This 100%-wide table is also where any
      # padding applied to the elements belongs.
      html << Element.new('table', :cellpadding => padding, :cellspacing => 0, :border => 0, :width => '100%').to_s
      html << '<tr>'

      # Remove the width attribute from the TDs declaration.
      opt.delete(:width)

      # The TD nested within the floating div and additional table will inherit center-aligned
      # text which means fluid-drop cells would have a default layout inconsistent with a regular
      # TD - which will typically be left-aligned.  So, unless otherwise specified, presume that
      # the TD should have left-aligned text.
      opt[:align] = 'left' if opt[:align].blank?

      mobile = ''

    end

    # Inherit base cell attributes - border, background color and image, etc.
    mix_all td, opt, ctx

    # Force the td to collapse to a single pixel to support images that
    # are less than 15 pixels.
    opt.merge!({
            :font => NONE,
            :color => NONE,
            FONT_SIZE => 1,
            LINE_HEIGHT => 1
        }) if opt[:flush]

    # Custom handling for text align on TDs rather than Base's mix_text_align
    # because if possible, using align= rather than a style keeps emails
    # smaller.  But for left-aligned text, you gotta use a style because
    # you know, Outlook.
    align = opt[:align]
    unless align.blank?

      # If the alignment is justified, force the text alignment attribute to
      # be left aligned so Outlook doesn't center the text.
      td[:align] = align == JUSTIFY ? LEFT : align

      # Must use style to reinforce left-align text in certain email clients.
      # All other alignments are accepted naturally.
      td.style[TEXT_ALIGN] = align if align == LEFT || align == JUSTIFY

      mix_text_justify(td, opt, ctx) if align === JUSTIFY

    end

    # Support custom alignment on mobile devices
    mix_mobile_text_align td, opt, ctx

    rowspan = opt[:rowspan].to_i
    td[:rowspan] = rowspan if rowspan > 0

    mix_font td, opt, ctx, table_opt

    # In Fluid-Drop, the font-size is set to zero to overcome Outlook rendering
    # problems so it is important to warn the designer that they need to set
    # it back to a reasonable size on the TD element.
    # TODO [JDH 11/14/2015] Decide if the warning re: font-size should ever
    # be restored based on whether or not users are finding it confusing.

    if mobile.blank?

      # If the cell doesn't define it's own responsive behavior, check to
      # see if it inherits from its parent table.  DROP and SWITCH declared
      # at the table-level descend to their tds.
      pm = table_opt[:mobile]
      mobile = pm if pm == DROP || pm == SWITCH

    end

    mix_responsive td, opt, ctx, mobile

    html << td.to_s

  end

  html
end