# frozen_string_literal: true
module Jekyll
class SeoTag
class Drop < Jekyll::Drops::Drop
include Jekyll::SeoTag::UrlHelper
TITLE_SEPARATOR = " | "
FORMAT_STRING_METHODS = [
:markdownify, :strip_html, :normalize_whitespace, :escape_once,
].freeze
HOMEPAGE_OR_ABOUT_REGEX = %r!^/(about/)?(index.html?)?$!.freeze
EMPTY_READ_ONLY_HASH = {}.freeze
private_constant :EMPTY_READ_ONLY_HASH
def initialize(text, context)
@obj = EMPTY_READ_ONLY_HASH
@mutations = {}
@text = text
@context = context
end
def version
Jekyll::SeoTag::VERSION
end
# Should the `
` tag be generated for this page?
def title?
return false unless title
return @display_title if defined?(@display_title)
@display_title = (@text !~ %r!title=false!i)
end
def site_title
@site_title ||= format_string(site["title"] || site["name"])
end
def site_tagline
@site_tagline ||= format_string site["tagline"]
end
def site_description
@site_description ||= format_string site["description"]
end
# Page title without site title or description appended
def page_title
@page_title ||= format_string(page["title"]) || site_title
end
def site_tagline_or_description
site_tagline || site_description
end
# Page title with site title or description appended
# rubocop:disable Metrics/CyclomaticComplexity
def title
@title ||= begin
if site_title && page_title != site_title
page_title + TITLE_SEPARATOR + site_title
elsif site_description && site_title
site_title + TITLE_SEPARATOR + site_tagline_or_description
else
page_title || site_title
end
end
return page_number + @title if page_number
@title
end
# rubocop:enable Metrics/CyclomaticComplexity
def name
return @name if defined?(@name)
@name = if seo_name
seo_name
elsif !homepage_or_about?
nil
elsif site_social["name"]
format_string site_social["name"]
elsif site_title
site_title
end
end
def description
@description ||= begin
format_string(page["description"] || page["excerpt"]) || site_description
end
end
# A drop representing the page author
def author
@author ||= AuthorDrop.new(:page => page, :site => site)
end
# A drop representing the JSON-LD output
def json_ld
@json_ld ||= JSONLDDrop.new(self)
end
# Returns a Drop representing the page's image
# Returns nil if the image has no path, to preserve backwards compatability
def image
@image ||= ImageDrop.new(:page => page, :context => @context)
@image if @image.path
end
def date_modified
@date_modified ||= begin
date = page_seo["date_modified"] || page["last_modified_at"].to_liquid || page["date"]
filters.date_to_xmlschema(date) if date
end
end
def date_published
@date_published ||= filters.date_to_xmlschema(page["date"]) if page["date"]
end
def type
@type ||= begin
if page_seo["type"]
page_seo["type"]
elsif homepage_or_about?
"WebSite"
elsif page["date"]
"BlogPosting"
else
"WebPage"
end
end
end
def links
@links ||= begin
if page_seo["links"]
page_seo["links"]
elsif homepage_or_about? && site_social["links"]
site_social["links"]
end
end
end
def logo
@logo ||= begin
return unless site["logo"]
if absolute_url? site["logo"]
filters.uri_escape site["logo"]
else
filters.uri_escape filters.absolute_url site["logo"]
end
end
end
def page_lang
@page_lang ||= page["lang"] || site["lang"] || "en_US"
end
def page_locale
@page_locale ||= (page["locale"] || site["locale"] || page_lang).tr("-", "_")
end
def canonical_url
@canonical_url ||= begin
if page["canonical_url"].to_s.empty?
filters.absolute_url(page["url"]).to_s.gsub(%r!/index\.html$!, "/")
else
page["canonical_url"]
end
end
end
def soopr
@soopr ||= SooprDrop.new(:page => page, :site => site)
@soopr if @soopr.publish_token
end
private
def filters
@filters ||= Jekyll::SeoTag::Filters.new(@context)
end
def page
@page ||= @context.registers[:page].to_liquid
end
def site
@site ||= @context.registers[:site].site_payload["site"].to_liquid
end
def homepage_or_about?
page["url"] =~ HOMEPAGE_OR_ABOUT_REGEX
end
def page_number
return unless @context["paginator"] && @context["paginator"]["page"]
current = @context["paginator"]["page"]
total = @context["paginator"]["total_pages"]
paginator_message = site["seo_paginator_message"] || "Page %s of %s for "
format(paginator_message, :current => current, :total => total) if current > 1
end
attr_reader :context
def fallback_data
@fallback_data ||= {}
end
def format_string(string)
string = FORMAT_STRING_METHODS.reduce(string) do |memo, method|
filters.public_send(method, memo)
end
string unless string.empty?
end
def seo_name
@seo_name ||= format_string(page_seo["name"]) if page_seo["name"]
end
def page_seo
@page_seo ||= sub_hash(page, "seo")
end
def site_social
@site_social ||= sub_hash(site, "social")
end
# Safely returns a sub hash
#
# hash - the parent hash
# key - the key in the parent hash
#
# Returns the sub hash or an empty hash, if it does not exist
def sub_hash(hash, key)
if hash[key].is_a?(Hash)
hash[key]
else
EMPTY_READ_ONLY_HASH
end
end
end
end
end