scribe/src/models/nodes.cr

263 lines
4.5 KiB
Crystal

module Nodes
alias Embedded = EmbeddedLink | EmbeddedContent | GithubGist
alias Leaf = Text | Image | Embedded
alias Child = Container | Leaf | Empty
alias Children = Array(Child)
class Container
getter children : Children
def initialize(@children : Children)
end
def ==(other : Container)
other.children == children
end
def empty?
children.empty? || children.each(&.empty?)
end
end
class Empty
def empty?
true
end
end
class BlockQuote < Container
end
class Code < Container
end
class Emphasis < Container
end
class Figure < Container
end
class FigureCaption < Container
end
class Heading1 < Container
getter identifier : String
def initialize(@children : Children, @identifier : String)
end
end
class Heading2 < Container
getter identifier : String
def initialize(@children : Children, @identifier : String)
end
end
class Heading3 < Container
getter identifier : String
def initialize(@children : Children, @identifier : String)
end
end
class ListItem < Container
end
class MixtapeEmbed < Container
end
class OrderedList < Container
end
class Paragraph < Container
end
class Preformatted < Container
end
class Strong < Container
end
class UnorderedList < Container
end
class Text
getter content : String
def initialize(@content : String)
end
def ==(other : Text)
other.content == content
end
def empty?
content.empty?
end
end
class Image
IMAGE_HOST = "https://cdn-images-1.medium.com/fit/c"
MAX_WIDTH = 800
FALLBACK_HEIGHT = 600
getter originalHeight : Int32
getter originalWidth : Int32
def initialize(
@src : String,
originalWidth : Int32?,
originalHeight : Int32?
)
@originalWidth = originalWidth || MAX_WIDTH
@originalHeight = originalHeight || FALLBACK_HEIGHT
end
def ==(other : Image)
other.src == src
end
def src
[IMAGE_HOST, width, height, @src].join("/")
end
def width
[originalWidth, MAX_WIDTH].min.to_s
end
def height
if originalWidth > MAX_WIDTH
(originalHeight * ratio).round.to_i.to_s
else
originalHeight.to_s
end
end
private def ratio
MAX_WIDTH / originalWidth
end
def empty?
false
end
end
class EmbeddedContent
MAX_WIDTH = 800
getter src : String
getter caption : FigureCaption?
def initialize(
@src : String,
@originalWidth : Int32,
@originalHeight : Int32,
@caption : FigureCaption? = nil
)
end
def width
[@originalWidth, MAX_WIDTH].min.to_s
end
def height
if @originalWidth > MAX_WIDTH
(@originalHeight * ratio).round.to_i.to_s
else
@originalHeight.to_s
end
end
private def ratio
MAX_WIDTH / @originalWidth
end
def ==(other : EmbeddedContent)
other.src == src &&
other.width == width &&
other.height == height &&
other.caption == caption
end
def empty?
false
end
end
class EmbeddedLink
getter href : String
def initialize(@href : String)
end
def domain
URI.parse(href).host
end
def ==(other : EmbeddedLink)
other.href == href
end
def empty?
false
end
end
class Anchor < Container
getter href : String
def initialize(@children : Children, @href : String)
end
def ==(other : Anchor)
other.children == children && other.href == href
end
def empty?
false
end
end
class UserAnchor < Container
USER_BASE_URL = "https://medium.com/u/"
getter href : String
def initialize(@children : Children, user_id : String)
@href = USER_BASE_URL + user_id
end
def ==(other : UserAnchor)
other.children == children && other.href == href
end
def empty?
false
end
end
class GithubGist
getter gist_store : GistStore | RateLimitedGistStore
def initialize(@href : String, @gist_store : GistStore | RateLimitedGistStore)
end
def files : Array(GistFile) | Array(MissingGistFile) | Array(RateLimitedGistFile)
gist_store.get_gist_files(params.id, params.filename)
end
private def params
GistParams.extract_from_url(@href)
end
def ==(other : GithubGist)
other.gist_store == gist_store
end
def empty?
false
end
end
end