Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop blaze #1995

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion IHP/AutoRefresh/View.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module IHP.AutoRefresh.View where
import IHP.Prelude
import IHP.AutoRefresh.Types
import IHP.HSX.QQ (hsx)
import qualified Text.Blaze.Html5 as Html5
import qualified IHP.HSX.Html as Html5
import IHP.Controller.Context

autoRefreshMeta :: (?context :: ControllerContext) => Html5.Html
Expand Down
4 changes: 2 additions & 2 deletions IHP/Breadcrumb/Types.hs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module IHP.Breadcrumb.Types where

import IHP.Prelude
import Text.Blaze.Html (Html)
import Text.Blaze.Html.Renderer.String (renderHtml)
import IHP.HSX.Html (Html)
import IHP.HSX.Html (renderHtml)
import ClassyPrelude

data BreadcrumbItem =
Expand Down
2 changes: 1 addition & 1 deletion IHP/Breadcrumb/ViewFunctions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import IHP.Breadcrumb.Types

import IHP.ControllerSupport

import Text.Blaze.Html (Html)
import IHP.HSX.Html (Html)

import IHP.View.Types (BreadcrumbsView(..), styledBreadcrumb, styledBreadcrumbItem)
import IHP.ViewSupport (theCSSFramework)
Expand Down
4 changes: 2 additions & 2 deletions IHP/Controller/AccessDenied.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import IHP.HSX.QQ (hsx)
import qualified System.Directory as Directory
import IHP.Controller.Context
import IHP.Controller.Response (respondAndExit)

import qualified IHP.HSX.Html as HSX

-- | Stops the action execution with an access denied message (403) when the access condition is True.
--
Expand Down Expand Up @@ -63,7 +63,7 @@ buildAccessDeniedResponse = do

-- | The default IHP 403 not found page
defaultAccessDeniedResponse :: Response
defaultAccessDeniedResponse = responseBuilder status403 [(hContentType, "text/html")] $ Blaze.renderHtmlBuilder [hsx|
defaultAccessDeniedResponse = responseBuilder status403 [(hContentType, "text/html")] $ HSX.renderHtmlBuilder [hsx|
<!DOCTYPE html>
<html lang="en">
<head>
Expand Down
3 changes: 2 additions & 1 deletion IHP/Controller/NotFound.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import IHP.HSX.QQ (hsx)
import qualified System.Directory as Directory
import IHP.Controller.Context
import IHP.Controller.Response (respondAndExit)
import qualified IHP.HSX.Html as HSX


-- | Stops the action execution with a not found message (404) when the access condition is True.
Expand Down Expand Up @@ -64,7 +65,7 @@ buildNotFoundResponse = do

-- | The default IHP 404 not found page
defaultNotFoundResponse :: Response
defaultNotFoundResponse = responseBuilder status404 [(hContentType, "text/html")] $ Blaze.renderHtmlBuilder [hsx|
defaultNotFoundResponse = responseBuilder status404 [(hContentType, "text/html")] $ HSX.renderHtmlBuilder [hsx|
<!DOCTYPE html>
<html lang="en">
<head>
Expand Down
4 changes: 2 additions & 2 deletions IHP/Controller/Render.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import IHP.ControllerSupport
import qualified Network.HTTP.Media as Accept
import qualified Data.List as List

import qualified Text.Blaze.Html.Renderer.Utf8 as Blaze
import Text.Blaze.Html (Html)
import qualified IHP.HSX.Html as Blaze
import IHP.HSX.Html (Html)
import qualified IHP.Controller.Context as Context
import IHP.Controller.Layout
import qualified IHP.FrameworkConfig as FrameworkConfig
Expand Down
6 changes: 3 additions & 3 deletions IHP/ErrorController.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import Network.HTTP.Types (status500, status400)
import Network.Wai
import Network.HTTP.Types.Header

import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html.Renderer.Utf8 as Blaze
import qualified IHP.HSX.Html as H
import qualified IHP.HSX.Html as Blaze
import qualified Database.PostgreSQL.Simple as PG
import qualified Data.ByteString.Char8 as ByteString

Expand Down Expand Up @@ -401,7 +401,7 @@ handleRouterException exception request respond =
<h2>Possible Solutions</h2>
<p>Are you trying to do a DELETE action, but your link is missing class="js-delete"?</p>
|]
let title = H.text "Routing failed"
let title = "Routing failed"
respond $ responseBuilder status500 [(hContentType, "text/html")] (Blaze.renderHtmlBuilder (renderError title errorMessage))


Expand Down
2 changes: 1 addition & 1 deletion IHP/FlashMessages/ViewFunctions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Copyright: (c) digitally induced GmbH, 2020
module IHP.FlashMessages.ViewFunctions where

import IHP.FlashMessages.Types
import qualified Text.Blaze.Html5 as Html5
import qualified IHP.HSX.Html as Html5
import IHP.ViewSupport
import IHP.View.Types
import IHP.Controller.Context
Expand Down
2 changes: 1 addition & 1 deletion IHP/Modal/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module IHP.Modal.Types
) where

import IHP.Prelude
import Text.Blaze.Html5 (Html)
import IHP.HSX.Html (Html)

data Modal = Modal
{ modalContent :: Html
Expand Down
2 changes: 1 addition & 1 deletion IHP/Modal/ViewFunctions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import IHP.Prelude
import IHP.Controller.Context
import IHP.HSX.QQ (hsx)
import IHP.Modal.Types
import Text.Blaze.Html5 (Html)
import IHP.HSX.Html (Html)

renderModal modal = renderModal' modal True
renderModal' Modal { .. } show = [hsx|
Expand Down
2 changes: 1 addition & 1 deletion IHP/PageHead/ViewFunctions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import IHP.PageHead.Types
import IHP.Controller.Context
import IHP.PageHead.ControllerFunctions
import IHP.HSX.QQ (hsx)
import Text.Blaze.Html5 (Html)
import IHP.HSX.Html (Html)

-- | Returns the current page title. The title can be set using @setTitle "my title"@ from the action.
--
Expand Down
2 changes: 1 addition & 1 deletion IHP/Pagination/ViewFunctions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import IHP.Pagination.Helpers

import IHP.ControllerSupport

import Text.Blaze.Html (Html)
import IHP.HSX.Html (Html)
import IHP.HSX.QQ (hsx)

import IHP.Controller.Param (paramOrNothing)
Expand Down
8 changes: 5 additions & 3 deletions IHP/RouterSupport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import qualified Data.ByteString.Char8 as ByteString
import qualified Data.Char as Char
import Control.Monad.Fail
import Data.String.Conversions (ConvertibleStrings (convertString), cs)
import qualified IHP.HSX.ToHtml as HSX
import qualified IHP.HSX.Attribute as HSX
import qualified Text.Blaze.Html5 as Html5
import qualified IHP.ErrorController as ErrorController
import qualified Control.Exception as Exception
Expand Down Expand Up @@ -879,9 +881,9 @@ catchAll action = do
{-# INLINABLE catchAll #-}

-- | This instances makes it possible to write @<a href={MyAction}/>@ in HSX
instance {-# OVERLAPPABLE #-} (HasPath action) => ConvertibleStrings action Html5.AttributeValue where
convertString action = Html5.textValue (pathTo action)
{-# INLINE convertString #-}
instance {-# OVERLAPPABLE #-} (HasPath action) => HSX.AttributeConverter action where
attributeValueToText name action = HSX.attributeValueToText name (pathTo action)
{-# INLINE attributeValueToText #-}

-- | Parses and returns an UUID
parseUUID :: Parser UUID
Expand Down
2 changes: 1 addition & 1 deletion IHP/View/CSSFramework.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module IHP.View.CSSFramework where

import IHP.Prelude
import IHP.FlashMessages.Types
import qualified Text.Blaze.Html5 as Blaze
import qualified IHP.HSX.Html as Blaze
import IHP.HSX.QQ (hsx)
import IHP.HSX.ToHtml ()
import IHP.View.Types
Expand Down
4 changes: 2 additions & 2 deletions IHP/View/Form.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import IHP.ValidationSupport
import IHP.HSX.ConvertibleStrings ()
import IHP.ViewErrorMessages ()
import IHP.ViewSupport
import qualified Text.Blaze.Html5 as Html5
import qualified IHP.HSX.Html as Html5
import IHP.HSX.ToHtml
import GHC.Types
import IHP.ModelSupport (getModelName, inputValue, isNew, Id', InputValue, didTouchField)
Expand Down Expand Up @@ -312,7 +312,7 @@ submitButton =
buttonText = modelName |> humanize -- We do this to turn 'Create ProjectTask' into 'Create Project Task'
isNew = IHP.ModelSupport.isNew (model ?formContext)
in SubmitButton
{ label = cs $ (if isNew then "Create " else "Save ") <> buttonText
{ label = Html5.textToHtml $ (if isNew then "Create " else "Save ") <> buttonText
, buttonClass = mempty
, buttonDisabled = False
, cssFramework = ?formContext.cssFramework
Expand Down
2 changes: 1 addition & 1 deletion IHP/View/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module IHP.View.Types
where

import IHP.Prelude hiding (div)
import qualified Text.Blaze.Html5 as Blaze
import qualified IHP.HSX.Html as Blaze
import IHP.FlashMessages.Types
import IHP.ModelSupport (Violation)
import IHP.Breadcrumb.Types
Expand Down
3 changes: 1 addition & 2 deletions IHP/ViewPrelude.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ module IHP.ViewPrelude (
hsx,
toHtml,
preEscapedToHtml,
preEscapedTextValue,
module IHP.ValidationSupport,
pathTo,
urlTo,
Expand Down Expand Up @@ -42,7 +41,7 @@ import IHP.Prelude
import IHP.ViewErrorMessages ()
import IHP.ViewSupport
import Text.Blaze (stringValue, (!))
import Text.Blaze.Html5 (preEscapedToHtml, preEscapedTextValue)
import IHP.HSX.Html (preEscapedToHtml)
import IHP.View.Form
import IHP.HSX.QQ (hsx)
import IHP.HSX.ToHtml
Expand Down
29 changes: 5 additions & 24 deletions IHP/ViewSupport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ module IHP.ViewSupport
, onLoad
, theRequest
, viewContext
, addStyle
, ViewFetchHelpMessage
, param
, fetch
Expand All @@ -34,7 +33,7 @@ module IHP.ViewSupport
) where

import IHP.Prelude
import qualified Text.Blaze.Html5 as Html5
import qualified IHP.HSX.Html as Html5
import IHP.ControllerSupport
import IHP.ModelSupport
import qualified Data.Aeson as JSON
Expand All @@ -56,6 +55,7 @@ import IHP.View.Types
import qualified IHP.FrameworkConfig as FrameworkConfig
import IHP.Controller.Context
import qualified IHP.HSX.Attribute as HSX
import qualified IHP.HSX.Html as HSX

class View theView where
-- | Hook which is called before the render is called
Expand Down Expand Up @@ -200,25 +200,6 @@ viewContext :: (?context :: ControllerContext) => ControllerContext
viewContext = ?context
{-# INLINE viewContext #-}

-- | Adds an inline style element to the html.
--
-- This helps to work around the issue, that our HSX parser cannot deal with CSS yet.
--
-- __Example:__
--
-- > myStyle = addStyle "#my-div { color: blue; }"
-- > [hsx|{myStyle}<div id="my-div">Hello World</div>|]
--
-- This will render like:
--
-- > <style>
-- > #my-div { color: blue; }
-- > </style>
-- > <div id="my-div">Hello World</div>
addStyle :: (ConvertibleStrings string Text) => string -> Html5.Markup
addStyle style = Html5.style (Html5.preEscapedText (cs style))
{-# INLINE addStyle #-}

-- | This class provides helpful compile-time error messages when you use common
-- controller functions inside of your views.
class ViewParamHelpMessage where
Expand Down Expand Up @@ -253,7 +234,7 @@ nl2br :: (Sequences.Textual text, ToHtml text) => text -> Html5.Html
nl2br content = content
|> Sequences.lines
|> map (\line -> [hsx|{line}<br/>|])
|> mconcat
|> HSX.concat

type Html = HtmlWithContext ControllerContext

Expand All @@ -263,5 +244,5 @@ liveReloadWebsocketUrl = ?context.frameworkConfig.ideBaseUrl
|> Text.replace "http://" "ws://"
|> Text.replace "https://" "wss://"

instance InputValue (PrimaryKey table) => HSX.ApplyAttribute (Id' table) where
applyAttribute attr attr' value h = HSX.applyAttribute attr attr' (inputValue value) h
instance InputValue (PrimaryKey table) => HSX.AttributeConverter (Id' table) where
attributeValueToText name value = HSX.attributeValueToText name (inputValue value)
2 changes: 1 addition & 1 deletion IHP/Welcome/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ renderLayout view = [hsx|
bodyStyle :: Text
bodyStyle = "margin: 0; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Helvetica Neue\", Arial, sans-serif;"

icon = preEscapedToHtml [plain|
icon = preEscapedToHtml [trimming|
<svg id="ab0c8d57-73b5-4efc-a896-a2305a232bd8" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1027.4958" height="696.05876" viewBox="0 0 1027.4958 696.05876"><title>experience design</title><circle cx="696.2479" cy="355.70168" r="76.7521" fill="#657b83" opacity="0.4"/><rect x="270" y="461.90193" width="428" height="116" fill="#3f3d56"/><path d="M756.83869,276.47654l-17.08989,8.08834,15.1253-11.3099a85.35244,85.35244,0,0,0-8.25839-10.86725L672.499,280.80982l66.94832-25.53848a85.33888,85.33888,0,0,0-9.72181-7.41015L616.789,266.0523a84.63924,84.63924,0,0,0-18.53688,52.82025h170A84.59367,84.59367,0,0,0,756.83869,276.47654Z" transform="translate(-86.2521 -101.97062)" fill="#f2f2f2"/><path d="M770.37527,152.166A495.97179,495.97179,0,0,0,552.2521,101.97062c-153.82965,0-291.36139,69.75269-382.71033,179.34869C325.96817,247.04484,598.319,187.71378,770.37527,152.166Z" transform="translate(-86.2521 -101.97062)" fill="#f2f2f2"/><rect y="693.90193" width="1020" height="2" fill="#2f2e41"/><path d="M682.11649,318.87255,624.2521,232.92193s87.63109,30.59259,77.68592,85.95062Z" transform="translate(-86.2521 -101.97062)" fill="#3f3d56"/><path d="M684.38771,318.87255l57.86439-85.95062s-87.63109,30.59259-77.68591,85.95062Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><path d="M784.7521,797.87255h-429v-640h429Zm-427-2h425v-636h-425Z" transform="translate(-86.2521 -101.97062)" fill="#2f2e41"/><rect x="271" y="71.08308" width="427" height="2" fill="#2f2e41"/><circle cx="282" cy="63.90193" r="4" fill="#657b83"/><circle cx="293" cy="63.90193" r="4" fill="#657b83"/><circle cx="304" cy="63.90193" r="4" fill="#657b83"/><rect x="286" y="84.90193" width="18" height="10" fill="#657b83"/><rect x="286" y="163.90193" width="88" height="10" fill="#657b83"/><rect x="685.2521" y="186.87255" width="88" height="10" transform="translate(1372.2521 281.77448) rotate(-180)" fill="#f2f2f2"/><rect x="286" y="180.90193" width="148" height="6" fill="#f2f2f2"/><rect x="286" y="193.90193" width="140" height="6" fill="#f2f2f2"/><rect x="286" y="206.90193" width="102" height="6" fill="#f2f2f2"/><rect x="320" y="384.90193" width="58" height="6" fill="#f2f2f2"/><rect x="320" y="397.90193" width="54.86486" height="6" fill="#f2f2f2"/><rect x="320" y="410.90193" width="39.97297" height="6" fill="#f2f2f2"/><rect x="455" y="384.90193" width="58" height="6" fill="#f2f2f2"/><rect x="455" y="397.90193" width="54.86486" height="6" fill="#f2f2f2"/><rect x="455" y="410.90193" width="39.97297" height="6" fill="#f2f2f2"/><rect x="595" y="384.90193" width="58" height="6" fill="#f2f2f2"/><rect x="595" y="397.90193" width="54.86486" height="6" fill="#f2f2f2"/><rect x="595" y="410.90193" width="39.97297" height="6" fill="#f2f2f2"/><rect x="410" y="611.90193" width="88" height="10" fill="#657b83"/><rect x="410" y="628.90193" width="148" height="6" fill="#f2f2f2"/><rect x="410" y="641.90193" width="140" height="6" fill="#f2f2f2"/><rect x="410" y="654.90193" width="102" height="6" fill="#f2f2f2"/><rect x="312.5" y="271.90193" width="348" height="2" fill="#657b83" opacity="0.3"/><rect x="312" y="306.90193" width="74" height="66" fill="#657b83" opacity="0.3"/><rect x="447" y="306.90193" width="74" height="66" fill="#3f3d56"/><rect x="587" y="306.90193" width="74" height="66" fill="#3f3d56"/><path d="M560.61073,474.59512l-.97627-.01907c.021-1.08114.63114-26.56133,10.66219-33.604l.56059.79893C561.23091,448.52955,560.6155,474.33532,560.61073,474.59512Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><circle cx="489.20618" cy="333.79786" r="4.88134" fill="#657b83"/><path d="M574.47138,453.00423a15.19766,15.19766,0,0,1-7.84149.33859,13.85019,13.85019,0,0,1,14.23985-4.88441A15.19761,15.19761,0,0,1,574.47138,453.00423Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><path d="M700.61073,474.59512l-.97627-.01907c.021-1.08114.63114-26.56133,10.66219-33.604l.56059.79893C701.23091,448.52955,700.6155,474.33532,700.61073,474.59512Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><circle cx="629.20618" cy="333.79786" r="4.88134" fill="#657b83"/><path d="M714.47138,453.00423a15.19766,15.19766,0,0,1-7.84149.33859,13.85019,13.85019,0,0,1,14.23985-4.88441A15.19761,15.19761,0,0,1,714.47138,453.00423Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><rect x="286" y="498.90193" width="88" height="10" fill="#657b83"/><rect x="286" y="515.90193" width="148" height="6" fill="#f2f2f2"/><rect x="286" y="528.90193" width="140" height="6" fill="#f2f2f2"/><rect x="286" y="541.90193" width="102" height="6" fill="#f2f2f2"/><circle cx="611.7521" cy="512.70168" r="31.2479" fill="#657b83" opacity="0.4"/><circle cx="553.92017" cy="527.62605" r="21.92017" fill="#657b83" opacity="0.4"/><circle cx="167.5" cy="582.40193" r="79" fill="#2f2e41"/><rect x="131.5" y="644.40193" width="24" height="43" fill="#2f2e41"/><rect x="179.5" y="644.40193" width="24" height="43" fill="#2f2e41"/><ellipse cx="151.5" cy="687.90193" rx="20" ry="7.5" fill="#2f2e41"/><ellipse cx="199.5" cy="686.90193" rx="20" ry="7.5" fill="#2f2e41"/><circle cx="169.5" cy="562.40193" r="27" fill="#fff"/><circle cx="169.5" cy="550.40193" r="9" fill="#3f3d56"/><path d="M177.11943,610.90483c-6.37889-28.56758,14.01185-57.43392,45.544-64.47477s62.2651,10.41,68.644,38.9776-14.51861,39.10379-46.05075,46.14464S183.49832,639.4724,177.11943,610.90483Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><ellipse cx="313.52848" cy="596.95692" rx="39.5" ry="12.40027" transform="translate(-446.20984 527.75787) rotate(-65.07683)" fill="#2f2e41"/><ellipse cx="168.52848" cy="620.95692" rx="12.40027" ry="39.5" transform="translate(-357.17251 49.01394) rotate(-27.89329)" fill="#2f2e41"/><ellipse cx="906.25518" cy="625.62283" rx="12.40027" ry="39.5" transform="translate(-250.61562 265.81898) rotate(-21.07832)" fill="#2f2e41"/><circle cx="893.18955" cy="583.10381" r="79" fill="#2f2e41"/><rect x="991.67544" y="747.00687" width="24" height="43" transform="matrix(-1, 0.0028, -0.0028, -1, 1923.24743, 1432.22882)" fill="#2f2e41"/><rect x="943.67563" y="747.14132" width="24" height="43" transform="translate(1827.24837 1432.63217) rotate(179.83951)" fill="#2f2e41"/><ellipse cx="995.7371" cy="790.5292" rx="20" ry="7.5" transform="translate(-88.46246 -99.17847) rotate(-0.16049)" fill="#2f2e41"/><ellipse cx="947.73448" cy="789.66365" rx="20" ry="7.5" transform="translate(-88.46023 -99.31292) rotate(-0.16049)" fill="#2f2e41"/><circle cx="891.13354" cy="563.10949" r="27" fill="#fff"/><circle cx="891.13354" cy="563.10949" r="9" fill="#3f3d56"/><path d="M1055.86824,611.39234c6.29884-28.58533-14.17267-57.39444-45.72441-64.34694s-62.23569,10.58438-68.53454,39.16971,14.62808,39.063,46.17982,46.01548S1049.56939,639.97768,1055.86824,611.39234Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><ellipse cx="1068.31896" cy="648.16898" rx="39.5" ry="12.40027" transform="translate(-261.30169 421.84772) rotate(-25.53475)" fill="#2f2e41"/><path d="M1018.32588,713.46562c.02166,7.732-19.84411,23.05568-41.93541,23.11755s-42.86536-21.648-42.887-29.38,20.71728,1.442,42.80858,1.3801S1018.30423,705.73367,1018.32588,713.46562Z" transform="translate(-86.2521 -101.97062)" fill="#fff"/><circle cx="917.2479" cy="468.70168" r="110.2479" fill="#657b83" opacity="0.4"/><circle cx="190.40483" cy="348.59022" r="79" fill="#2f2e41"/><rect x="226.16663" y="506.95016" width="24" height="43" transform="translate(11.69174 -135.94422) rotate(10.26106)" fill="#2f2e41"/><rect x="273.39893" y="515.50057" width="24" height="43" transform="translate(13.97028 -144.22112) rotate(10.26106)" fill="#2f2e41"/><ellipse cx="285.68749" cy="563.4754" rx="7.5" ry="20" transform="translate(-415.18867 315.35713) rotate(-49.73894)" fill="#2f2e41"/><ellipse cx="239.4392" cy="555.10313" rx="7.5" ry="20" transform="translate(-425.15907 277.10318) rotate(-49.73894)" fill="#2f2e41"/><circle cx="195.93551" cy="329.26636" r="27" fill="#fff"/><circle cx="207.03243" cy="319.5883" r="9" fill="#3f3d56"/><path d="M214.337,364.61728c-1.188-29.247,24.01865-54.01935,56.30068-55.33066s59.41489,21.335,60.60291,50.582-21.2521,35.89214-53.53414,37.20345S215.525,393.86426,214.337,364.61728Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><ellipse cx="349.6025" cy="436.16834" rx="39.5" ry="12.40027" transform="translate(-174.86437 -12.8362) rotate(-12.9101)" fill="#2f2e41"/><ellipse cx="194.52848" cy="461.95692" rx="39.5" ry="12.40027" transform="translate(-184.5461 -46.83125) rotate(-12.9101)" fill="#2f2e41"/><path d="M254.95954,481.69373a18,18,0,0,0,35.42423,6.41281c1.77085-9.78213-5.79238-13.24363-15.57451-15.01448S256.73039,471.9116,254.95954,481.69373Z" transform="translate(-86.2521 -101.97062)" fill="#fff"/><rect x="380.7521" y="374.37255" width="74" height="66" transform="translate(48.33035 -204.77859) rotate(16.68876)" fill="#3f3d56"/><path d="M399.11981,435.94807l-.92967-.29863c.33056-1.02957,8.23224-25.26129,19.86323-29.12666l.30755.92627C407.19918,411.15851,399.199,435.70058,399.11981,435.94807Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/><circle cx="338.23982" cy="301.05004" r="4.88134" fill="#657b83"/><path d="M418.59694,419.247a15.19762,15.19762,0,0,1-7.60842-1.92753,13.85017,13.85017,0,0,1,15.04271-.58938A15.19758,15.19758,0,0,1,418.59694,419.247Z" transform="translate(-86.2521 -101.97062)" fill="#657b83"/></svg>
|]
Loading