LinkedIn.purs 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. module LinkedIn where
  2. import Prelude
  3. import Data.List.NonEmpty as NEL
  4. import Data.List.Types (NonEmptyList)
  5. import Data.Maybe (Maybe(..))
  6. import Data.Traversable (traverse)
  7. import Effect (Effect)
  8. import LinkedIn.DetachedNode (DetachedNode, asTree', cutBranches, filterEmpty)
  9. import Web.DOM (Document, Node)
  10. import Web.DOM.Document as D
  11. import Web.DOM.Element as E
  12. import Web.DOM.Node (nodeName)
  13. import Web.DOM.NodeList as NL
  14. import Web.DOM.ParentNode (QuerySelector(..), querySelector, querySelectorAll)
  15. import Yoga.Tree (Tree)
  16. -- A light abstraction layer above the DOM manipulation API
  17. fromDocument ∷ Document → Node
  18. fromDocument doc = D.toNode doc
  19. queryOne :: String -> Document -> Effect (Maybe Node)
  20. queryOne selector doc = do
  21. found <- querySelector (QuerySelector selector) $ D.toParentNode doc
  22. pure case found of
  23. Nothing -> Nothing
  24. Just el -> Just $ E.toNode el
  25. queryAll :: String -> Document -> Effect (Maybe (NonEmptyList Node))
  26. queryAll selector doc = do
  27. found <- querySelectorAll (QuerySelector selector) $ D.toParentNode doc
  28. liftA1 NEL.fromFoldable $ NL.toArray found
  29. -- First pass of naming ; from here we know what we are looking for
  30. data LinkedInUIElementType = LinkedInUIArtDecoCard | LinkedInUIArtDecoTab | LinkedInUIJobsUnifiedTopCard
  31. instance Show LinkedInUIElementType where
  32. show LinkedInUIArtDecoCard = "ArtDecoCard"
  33. show LinkedInUIArtDecoTab = "ArtDecoTab"
  34. show LinkedInUIJobsUnifiedTopCard = "JobsUnifiedTopCard"
  35. data LinkedInUIElement = LinkedInUIElement LinkedInUIElementType Node
  36. instance Show LinkedInUIElement where
  37. show (LinkedInUIElement typ n) = "LinkedInUIElement(" <> show typ <> ", " <> nodeName n <> ")"
  38. getArtDecoCards ∷ Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  39. getArtDecoCards = queryAll' LinkedInUIArtDecoCard "section.artdeco-card > div ~ div > div > div > ul > li"
  40. getArtDecoTabs ∷ Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  41. getArtDecoTabs = queryAll' LinkedInUIArtDecoTab "div.artdeco-tabs > div > div > div > div > ul > li"
  42. getJobsUnifiedTopCard ∷ Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  43. getJobsUnifiedTopCard = queryAll' LinkedInUIJobsUnifiedTopCard "div.jobs-unified-top-card"
  44. queryAll' ∷ LinkedInUIElementType → String → Document → Effect (Maybe (NonEmptyList LinkedInUIElement))
  45. queryAll' constructor selector doc = do
  46. nodes <- queryAll selector doc
  47. pure case nodes of
  48. Nothing -> Nothing
  49. Just cards -> Just $ map (LinkedInUIElement constructor) cards
  50. asTree :: LinkedInUIElement -> Effect (Tree DetachedNode)
  51. asTree (LinkedInUIElement _ n) = asTree' n
  52. asPrunedTrees :: Maybe (NonEmptyList LinkedInUIElement) → Effect (Maybe (NonEmptyList (Tree DetachedNode)))
  53. asPrunedTrees =
  54. case _ of
  55. Nothing -> pure Nothing
  56. Just els -> do
  57. trees <- traverse asPrunedTree els
  58. pure $ Just $ trees
  59. asPrunedTree :: LinkedInUIElement → Effect (Tree DetachedNode)
  60. asPrunedTree el = do
  61. tree <- asTree el
  62. pure $ cutBranches filterEmpty tree