ArtDecoCard.purs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. module LinkedIn.ArtDecoCard where
  2. import Prelude
  3. import Data.Either (Either(..), hush)
  4. import Data.Generic.Rep (class Generic)
  5. import Data.List.NonEmpty (NonEmptyList)
  6. import Data.List.NonEmpty as NEL
  7. import Data.Maybe (Maybe(..), fromJust)
  8. import Data.Show.Generic (genericShow)
  9. import Data.Traversable (sequence)
  10. import Effect (Effect)
  11. import LinkedIn (DetachedNode, toDetached)
  12. import Partial.Unsafe (unsafePartial)
  13. import Web.DOM (Node, ParentNode)
  14. import Web.DOM.Element as E
  15. import Web.DOM.NodeList as NL
  16. import Web.DOM.ParentNode (QuerySelector(..), querySelector, querySelectorAll)
  17. data ParseError =
  18. NodeNotFoundError String
  19. | NodeListNotFoundError String
  20. derive instance Generic ParseError _
  21. instance Show ParseError where
  22. show = genericShow
  23. type Parser a = Node → Effect (Either ParseError a)
  24. data ArtDecoPvsEntitySubComponent = ArtDecoPvsEntitySubComponent DetachedNode
  25. derive instance Generic ArtDecoPvsEntitySubComponent _
  26. instance Show ArtDecoPvsEntitySubComponent where
  27. show = genericShow
  28. parseArtDecoPvsEntitySubComponent ∷ Parser ArtDecoPvsEntitySubComponent
  29. parseArtDecoPvsEntitySubComponent n = do
  30. content <- queryAndDetachOne "span[aria-hidden=true]" n
  31. pure $ ado
  32. c <- content
  33. in ArtDecoPvsEntitySubComponent c
  34. data ArtDecoCenterContent = ArtDecoCenterContent (NonEmptyList ArtDecoPvsEntitySubComponent)
  35. derive instance Generic ArtDecoCenterContent _
  36. instance Show ArtDecoCenterContent where
  37. show = genericShow
  38. parseArtDecoCenterContent ∷ Parser ArtDecoCenterContent
  39. parseArtDecoCenterContent n = do
  40. list <- queryManyAndParse ":scope > ul > li" parseArtDecoPvsEntitySubComponent n
  41. pure $ ado
  42. l <- list
  43. in ArtDecoCenterContent l
  44. data ArtDecoCenterHeader = ArtDecoCenterHeader {
  45. bold :: DetachedNode,
  46. normal :: Maybe DetachedNode,
  47. light :: Maybe (NonEmptyList DetachedNode)
  48. }
  49. derive instance Generic ArtDecoCenterHeader _
  50. instance Show ArtDecoCenterHeader where
  51. show = genericShow
  52. parseArtDecoCenterHeader :: Parser ArtDecoCenterHeader
  53. parseArtDecoCenterHeader n = do
  54. bold <- queryAndDetachOne ":scope div.t-bold > span[aria-hidden=true]" n
  55. normal <- queryAndDetachOne ":scope span.t-normal:not(t-black--light) > span[aria-hidden=true]" n
  56. light <- queryAndDetachMany ":scope span.t-black--light > span[aria-hidden=true]" n
  57. pure $ ado
  58. b <- bold
  59. in ArtDecoCenterHeader {bold: b, normal: hush normal, light: hush light}
  60. data ArtDecoCenter = ArtDecoCenter {
  61. header :: ArtDecoCenterHeader,
  62. content :: ArtDecoCenterContent
  63. }
  64. derive instance Generic ArtDecoCenter _
  65. instance Show ArtDecoCenter where
  66. show = genericShow
  67. parseArtDecoCenter :: Parser ArtDecoCenter
  68. parseArtDecoCenter n = do
  69. header <- queryOneAndParse ":scope > div" parseArtDecoCenterHeader n
  70. content <- queryOneAndParse ":scope > div ~ div" parseArtDecoCenterContent n
  71. pure $ ado
  72. h <- header
  73. c <- content
  74. in ArtDecoCenter {header: h, content: c}
  75. data ArtDecoPvsEntity = ArtDecoPvsEntity {
  76. side :: Unit,
  77. center :: ArtDecoCenter
  78. }
  79. derive instance Generic ArtDecoPvsEntity _
  80. instance Show ArtDecoPvsEntity where
  81. show = genericShow
  82. parseArtDecoPvsEntity :: Parser ArtDecoPvsEntity
  83. parseArtDecoPvsEntity n = do
  84. center <- queryOneAndParse ":scope > div.display-flex" parseArtDecoCenter n
  85. pure $ ado
  86. c <- center
  87. in ArtDecoPvsEntity {side: unit, center: c}
  88. data ArtDecoCardElement = ArtDecoCardElement {
  89. pvs_entity :: ArtDecoPvsEntity
  90. }
  91. derive instance Generic ArtDecoCardElement _
  92. instance Show ArtDecoCardElement where
  93. show = genericShow
  94. parseArtDecoCard :: Parser ArtDecoCardElement
  95. parseArtDecoCard n = do
  96. pvs <- queryOneAndParse ":scope div.pvs-entity--padded" parseArtDecoPvsEntity n
  97. pure $ ado
  98. p <- pvs
  99. in ArtDecoCardElement {pvs_entity: p}
  100. toParentNode' :: Node -> ParentNode
  101. toParentNode' n =
  102. unsafePartial $ fromJust $ intoParentNode' n where
  103. intoParentNode' :: Node -> Maybe ParentNode
  104. intoParentNode' node = do
  105. he <- E.fromNode node
  106. pure $ E.toParentNode he
  107. queryOne :: String -> Node -> Effect (Maybe Node)
  108. queryOne selector n = do
  109. found <- querySelector (QuerySelector selector) $ toParentNode' n
  110. pure case found of
  111. Nothing -> Nothing
  112. Just el -> Just $ E.toNode el
  113. queryAll :: String -> Node -> Effect (Maybe (NonEmptyList Node))
  114. queryAll selector n = do
  115. found <- querySelectorAll (QuerySelector selector) $ toParentNode' n
  116. liftA1 NEL.fromFoldable $ NL.toArray found
  117. parseDetachedNode :: Parser DetachedNode
  118. parseDetachedNode node = do
  119. node' <- toDetached node
  120. pure $ Right node'
  121. queryAndDetachOne ∷ String -> Parser DetachedNode
  122. queryAndDetachOne selector n = queryOneAndParse selector parseDetachedNode n
  123. queryAndDetachMany ∷ String -> Parser (NonEmptyList DetachedNode)
  124. queryAndDetachMany selector n = queryManyAndParse selector parseDetachedNode n
  125. queryOneAndParse ∷ ∀ a. String → Parser a → Parser a
  126. queryOneAndParse selector parser n = do
  127. selected <- queryOne selector n
  128. case selected of
  129. Nothing -> pure $ Left $ NodeNotFoundError selector
  130. Just node -> parser node
  131. queryManyAndParse ∷ ∀ a. String → Parser a → Parser (NonEmptyList a)
  132. queryManyAndParse selector parser n = do
  133. selected <- queryAll selector n
  134. case selected of
  135. Nothing -> pure $ Left $ NodeListNotFoundError selector
  136. Just nodes -> do
  137. nodes' <- sequence $ map parser nodes :: Effect (NonEmptyList((Either ParseError a)))
  138. pure $ sequence nodes'