263 lines
4.5 KiB
Crystal
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
|