require "raconteur" require "kramdown" require "nokogiri" require "anecdote/engine" module Anecdote def self.markdown_and_parse(content="") ::Kramdown::Document.new( raconteur.parse(content), { input: :GFM } ).to_html.html_safe end def self.raconteur @raconteur ||= ::Raconteur.new end def self.inline_js view_context.content_tag(:script, Rails.application.assets.find_asset('anecdote/application.js').to_s.html_safe) end def self.init_raconteur raconteur.settings.setting_quotes = '$' raconteur.processors.register!('graphic', { handler: lambda do |settings| klasses = (['anecdote-graphic-dn32ja'] + module_classes(settings)) klasses << case settings[:border] when 'none' then 'v-border-none' when 'shadow' then 'v-border-shadow' when 'line' then 'v-border-line' end contents = [] contents << view_context.content_tag((settings[:href].present? ? :a : :div), (settings[:href].present? ? { href: settings[:href], title: settings[:href_title] } : {}).merge({ class: 'anecdote-intrinsic-embed-n42ha1' })) do if settings[:assets_path] content = view_context.image_tag(settings[:assets_path], alt: '') paperclip_geo = Paperclip::Geometry.from_file(Rails.root.join('app', 'assets', 'images', settings[:assets_path])) geo = { width: paperclip_geo.width, height: paperclip_geo.height } elsif settings[:image_url] content = view_context.image_tag(settings[:image_url], alt: '') dimensions = (settings[:dimensions] || settings[:image_dimensions]).split('x').map(&:to_f) geo = { width: dimensions.first, height: dimensions.last } elsif settings[:embed_code] content = settings[:embed_code].html_safe dimensions = settings[:dimensions].split('x').map(&:to_f) geo = { width: dimensions.first, height: dimensions.last } end if settings[:_scope_].present? && settings[:_scope_][:processor].tag == 'gallery' settings[:_scope_][:settings][:_graphics_] ||= [] settings[:_scope_][:settings][:_graphics_] << geo end view_context.content_tag(:div, content, class: 'inner', style: "padding-bottom: #{geo[:height] / geo[:width] * 100}%;") end if settings[:caption].present? contents << view_context.content_tag(:div, view_context.content_tag(:div, markdown_and_parse(settings[:caption]), class: 'inner anecdote-wysicontent-ndj4ab'), class: 'anecdote-caption-ajkd3b') end view_context.content_tag(:div, view_context.content_tag(:div, contents.join("\n").html_safe, class: 'inner'), class: klasses.compact.flatten.join(' ')) end }) raconteur.processors.register!('gallery', { handler: lambda do |settings| klasses = ['anecdote-gallery-dn2bak'] klasses += module_classes(settings) graphics = settings[:_yield_].html_safe # handle scaling flexes = [] if settings[:flexes] flexes = parse_custom_flexes(settings) else if settings[:scale_by] == 'relative-width-bottom-alignment' # images scaled based on their relative width with bottom alignment total_width = settings[:_graphics_].sum { |hsh| hsh[:width]} settings[:_graphics_].each do |graphic| flex = { width: graphic[:width] / total_width, graphic: graphic, ratio: graphic[:width] / graphic[:height], gfx_height_pad: graphic[:height] / graphic[:width] } flex[:width_ratio_balance] = flex[:width] / flex[:ratio] flexes << flex end tallest = flexes.sort_by { |k| k[:width_ratio_balance] }.last flexes.map do |flex| flex[:faux] = flex[:width_ratio_balance] / tallest[:width_ratio_balance] flex[:gfx_height_pad_faux] = flex[:gfx_height_pad] / flex[:faux] flex[:top_offset] = flex[:gfx_height_pad_faux] - flex[:gfx_height_pad] end index = 0 graphics.gsub!('
') do |match| flex = flexes[index] index += 1 (view_context.content_tag(:div, '', class: 'anecdote-flex-offset-a4j2aj', style: "padding-top: #{flex[:top_offset] * 100}%;").html_safe + match.html_safe).html_safe end # graphics.gsub!(/anecdote-intrinsic-embed-n42ha1.*?padding-bottom:\s*([\d|\.]*)/mi) do |match| # flex = flexes[index] # index += 1 # match.sub(/[\d|\.]*$/, (flex[:gfx_height_pad_faux] * 100).to_s).html_safe # end elsif settings[:scale_by] == 'relative-width' # images scaled based on their relative width total_width = settings[:_graphics_].sum { |hsh| hsh[:width]} settings[:_graphics_].each do |graphic| flexes << { width: graphic[:width] / total_width, graphic: graphic } end else # images scaled to equal height total_ratio = settings[:_graphics_].sum { |geo| geo[:width] / geo[:height] } settings[:_graphics_].each do |graphic| flexes << { width: (graphic[:width] / graphic[:height]) / total_ratio, graphic: graphic } end end end graphics = insert_flex_basis_styles(graphics, flexes) # build HTML output contents = [] settings[:gutter_spacing] = 'small' if settings[:gutter_spacing].blank? contents << view_context.content_tag(:div, graphics.html_safe, class: (['content'] + flex_classes(settings)).flatten.join(' ')) if settings[:caption].present? contents << view_context.content_tag(:div, view_context.content_tag(:div, markdown_and_parse(settings[:caption]), class: 'inner anecdote-wysicontent-ndj4ab'), class: 'anecdote-caption-ajkd3b') end view_context.content_tag(:div, view_context.content_tag(:div, contents.join("\n").html_safe, class: 'inner'), class: klasses.flatten.join(' ')) end }) raconteur.processors.register!('columns', { handler: lambda do |settings| klasses = ['anecdote-columns-nab3a2'] klasses += module_classes(settings) columns = insert_flex_basis_styles(settings[:_yield_].html_safe, parse_custom_flexes(settings)) view_context.content_tag(:div, view_context.content_tag(:div, columns.html_safe, class: (['inner'] + flex_classes(settings)).flatten.join(' ')), class: klasses.flatten.join(' ')) end }) raconteur.processors.register!('anecdote', { handler: lambda do |settings| klass = (['anecdote-inception-ab2a8j'] + module_classes(settings)).flatten.join(' ') view_context.content_tag(:div, view_context.content_tag(:div, markdown_and_parse(settings[:_yield_]), class: 'anecdote-wysicontent-ndj4ab inner'), class: klass) end }) raconteur.processors.register!('pull-quote', { handler: lambda do |settings| klass = (['anecdote-pull-quote-sba2ha'] + module_classes(settings)).flatten.join(' ') view_context.content_tag(:div, view_context.content_tag(:div, markdown_and_parse(settings[:text]), class: 'inner'), class: klass) end }) raconteur.processors.register!('horizontal-line', { handler: lambda do |settings| klasses = ['anecdote-horizontal-line-asj31a'] klasses += module_classes(settings) klasses << case settings[:weight] when 'light' then 'v-light' when 'notable' then 'v-notable' when 'heavy' then 'v-heavy' end view_context.content_tag(:div, view_context.content_tag(:hr), class: klasses.flatten.join(' ')) end }) raconteur.processors.register!('spacing', { handler: lambda do |settings| klasses = ['anecdote-spacing-an4a2q'] klasses << case settings[:size] when 'tiny' then 'v-tiny' when 'small' then 'v-small' when 'big' then 'v-big' when 'mega' then 'v-mega' end view_context.content_tag(:div, nil, class: klasses.flatten.join(' ')) end }) end def self.parse_custom_flexes(settings) ratios = settings[:flexes].split(':').map(&:to_i) sum = ratios.inject(&:+) flexes = ratios.map { |ratio| { width: ratio.to_f / sum } } end def self.insert_flex_basis_styles(html_content, flexes) index = 0 doc = ::Nokogiri::HTML::DocumentFragment.parse(html_content) doc.elements.each do |element| if element.attributes['class'].present? && element.attributes['class'].value.split(' ').include?('anecdote-module-3ba83n') flex = flexes[index] index += 1 styles = [] if flex[:width].present? styles << "-webkit-flex-basis:#{flex[:width] * 100}%" styles << "-moz-flex-basis:#{flex[:width] * 100}%" styles << "flex-basis:#{flex[:width] * 100}%" end element.set_attribute('style', styles.join(';')) end end doc.to_html end def self.flex_classes(settings) klasses = %w(anecdote-flex-module-a4j2aj) # custom gutter spacing klasses << case settings[:gutter_spacing] when 'small' then 'v-gutter-spacing-small' when 'large' then 'v-gutter-spacing-large' end # when to wrap klasses << case settings[:flow_from] when 'always' then 'v-flow-from-always' when 'medium-handheld' then 'v-flow-from-medium-handheld' when 'large-handheld' then 'v-flow-from-large-handheld' when 'tablet' then 'v-flow-from-tablet' when 'laptop' then 'v-flow-from-laptop' when 'large-monitor' then 'v-flow-from-large-monitor' else if settings[:size].present? case settings[:size] when 'small' then 'v-flow-from-medium-handheld' when 'medium' then 'v-flow-from-large-handheld' when 'big' then 'v-flow-from-tablet' when 'full-width' then 'v-flow-from-tablet' end else 'v-flow-from-tablet' # default end end klasses end def self.module_classes(settings) klasses = %w(anecdote-module-3ba83n) klasses << case settings[:font_family] when 'primary' then 'anecdote-primary-font-a3a8fb' when 'secondary' then 'anecdote-secondary-font-a3a8fb' end klasses << case settings[:font_size] when 'tiny' then 'anecdote-tiny-text-size-an43ja' when 'small' then 'anecdote-small-text-size-an43ja' when 'normal' then 'anecdote-normal-text-size-an43ja' when 'large' then 'anecdote-large-text-size-an43ja' end if settings[:size].present? klasses << case settings[:size] when 'small' then 'v-size-small' when 'medium' then 'v-size-medium' when 'big' then 'v-size-big' when 'full-width' then 'v-size-full-width' end end if settings[:float].present? klasses << case settings[:float] when 'right' then 'v-float-right' when 'left' then 'v-float-left' end klasses << case settings[:float_from] when 'always' then 'v-float-from-always' when 'medium-handheld' then 'v-float-from-medium-handheld' when 'large-handheld' then 'v-float-from-large-handheld' when 'tablet' then 'v-float-from-tablet' when 'laptop' then 'v-float-from-laptop' when 'large-monitor' then 'v-float-from-large-monitor' else if settings[:size].present? case settings[:size] when 'small' then 'v-float-from-large-handheld' when 'medium' then 'v-float-from-laptop' when 'big' then 'v-flow-from-large-monitor' end else 'v-float-from-laptop' # default end end end if settings[:mood].present? klasses << case settings[:mood] when 'positive' then 'v-mood-positive' when 'negative' then 'v-mood-negative' end end klasses.compact end private def self.view_context Anecdote::ApplicationController.helpers end end Anecdote.init_raconteur