LinkedIn.purs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. module LinkedIn where
  2. import Prelude
  3. import Yoga.Tree
  4. import Data.List.NonEmpty as NEL
  5. import Data.List.Types (NonEmptyList)
  6. import Data.Maybe (Maybe(..))
  7. import Data.Traversable (sequence)
  8. import Effect (Effect)
  9. import Partial.Unsafe (unsafePartial)
  10. import Web.DOM (Document, Node)
  11. import Web.DOM.Document as D
  12. import Web.DOM.Element as E
  13. import Web.DOM.Node (nodeName, nodeType, textContent)
  14. import Web.DOM.Node as N
  15. import Web.DOM.NodeList as NL
  16. import Web.DOM.NodeType (NodeType(..))
  17. import Web.DOM.ParentNode (QuerySelector(..), querySelector, querySelectorAll)
  18. -- A light abstraction layer above the DOM manipulation API
  19. fromDocument ∷ Document → Node
  20. fromDocument doc = D.toNode doc
  21. queryOne :: String -> Document -> Effect (Maybe Node)
  22. queryOne selector doc = do
  23. found <- querySelector (QuerySelector selector) $ D.toParentNode doc
  24. pure case found of
  25. Nothing -> Nothing
  26. Just el -> Just $ E.toNode el
  27. queryAll :: String -> Document -> Effect (Maybe (NonEmptyList Node))
  28. queryAll selector doc = do
  29. found <- querySelectorAll (QuerySelector selector) $ D.toParentNode doc
  30. liftA1 NEL.fromFoldable $ NL.toArray found
  31. -- First pass of naming ; from here we know what we are looking for
  32. data LinkedInUIElementType = LinkedInUIArtDecoCard | LinkedInUIArtDecoTab
  33. instance Show LinkedInUIElementType where
  34. show LinkedInUIArtDecoCard = "ArtDecoCard"
  35. show LinkedInUIArtDecoTab = "ArtDecoTab"
  36. data LinkedInUIElement = LinkedInUIElement LinkedInUIElementType Node
  37. instance Show LinkedInUIElement where
  38. show (LinkedInUIElement typ n) = "LinkedInUIElement(" <> show typ <> ", " <> nodeName n <> ")"
  39. getArtDecoCards ∷ Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  40. getArtDecoCards = queryAll' LinkedInUIArtDecoCard "section.artdeco-card > div ~ div > div > div > ul > li"
  41. getArtDecoTabs ∷ Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  42. getArtDecoTabs = queryAll' LinkedInUIArtDecoTab "div.artdeco-tabs > div > div > div > div > ul > li"
  43. queryAll' ∷ LinkedInUIElementType → String → Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  44. queryAll' constructor selector doc = do
  45. nodes <- queryAll selector doc
  46. pure case nodes of
  47. Nothing -> Nothing
  48. Just cards -> Just $ map (LinkedInUIElement constructor) cards
  49. data DetachedNode =
  50. DetachedElement Node
  51. | DetachedComment String
  52. | DetachedText String
  53. instance Show DetachedNode where
  54. show (DetachedElement n) = "DetachedElement(" <> nodeName n <> ")"
  55. show (DetachedComment c) = "DetachedComment(" <> c <> ")"
  56. show (DetachedText t) = "DetachedText(" <> t <> ")"
  57. asTree :: LinkedInUIElement -> Effect (Tree DetachedNode)
  58. asTree (LinkedInUIElement _ n) = asTree' n
  59. asTree' :: Node -> Effect (Tree DetachedNode)
  60. asTree' n = do
  61. children <- N.childNodes n
  62. childArray <- NL.toArray children :: Effect (Array Node)
  63. detached <- toDetached n
  64. case childArray of
  65. [] -> pure $ leaf detached
  66. arr -> do
  67. a <- sequence (map asTree' arr) :: Effect (Array (Tree DetachedNode))
  68. pure $ mkTree detached a
  69. toDetached :: Node -> Effect DetachedNode
  70. toDetached node = unsafePartial $ toDetached' (nodeType node) node where
  71. toDetached' :: Partial => NodeType -> Node -> Effect DetachedNode
  72. toDetached' ElementNode n = pure $ DetachedElement n
  73. toDetached' CommentNode n = do
  74. txt <- textContent n
  75. pure $ DetachedComment txt
  76. toDetached' TextNode n = do
  77. txt <- textContent n
  78. pure $ DetachedText txt