Skip to content
This repository has been archived by the owner on Apr 22, 2021. It is now read-only.

Commit

Permalink
initial reddit behavior implementation fixes #24
Browse files Browse the repository at this point in the history
  • Loading branch information
N0taN3rd committed Jun 25, 2019
1 parent 2487c39 commit ba4a15d
Show file tree
Hide file tree
Showing 12 changed files with 446 additions and 90 deletions.
17 changes: 17 additions & 0 deletions behaviors/reddit/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as lib from '../../lib';

export const ReditContainerSelector = 'div[id*="container" i]';
export const CloseButtonSelector = 'button[aria-label="close" i] > span';
export const APost = 'div[class*="post" i]';
export const APostVideo = 'video';
export const APostTitleAndViewClicker = 'a[data-click-id="body"]';
export const APostMediaEmbed = 'iframe[class*="media-element" i]';
export const MaybePromotedSpan = 'div > span';
export const MoreCommentsDivP = 'div[id*="moreComments-" i] > * > p';
export const ViewedPostOverlayScrollContainerId = 'overlayScrollContainer';

export function createSubPartsSelector() {
const theSub = lib.substringFromIndexOf(location.pathname, '/r/');
return `div[class="SubredditVars-r-${theSub.replace('/', '')}" i]`;
}

127 changes: 127 additions & 0 deletions behaviors/reddit/shared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import * as lib from '../../lib';
import * as selectors from './selectors';

/**
*
* @return {SubStructure}
*/
export function getSubStructure() {
let header;
let body;
let bottomMostSubVar;
if (location.pathname.startsWith('/r/')) {
const parts = lib.qsa(selectors.createSubPartsSelector());
header = parts[0];
body = parts[1];
bottomMostSubVar = parts[2];
} else {
const container = lib.chainFistChildElemOf(
lib.qs(selectors.ReditContainerSelector),
3
);
for (let i = 0; i < container.children.length; i++) {
const child = container.children[i];
if (child instanceof HTMLDivElement) {
if (header == null) header = child;
else if (body == null) body = child;
else if (bottomMostSubVar == null) bottomMostSubVar = child;
}
}
}
// loader > div > div > div.Post
const postList = lib.getNthParentElement(lib.qs(selectors.APost, body), 3);
return {
header,
body,
postList,
bottomMostSubVar,
};
}

/**
*
* @param {?Element} postContainer
* @return {boolean}
*/
export function isNotPromotedOrAddPost(postContainer) {
const maybePromotedSpan = lib.qs(selectors.MaybePromotedSpan, postContainer);
if (maybePromotedSpan) {
const promoted = lib.elementTextContains(
maybePromotedSpan,
'promoted',
true
);
if (promoted) lib.scrollIntoView(postContainer);
return !promoted;
}
return true;
}

/**
* @param {Element} postContainer
* @return {?Element}
*/
export function selectPost(postContainer) {
return lib.qs(selectors.APost, postContainer);
}

/**
* @param {Element} postContainer
* @return {{postContainer: Element, post: Element}}
*/
export function selectPostWithContainer(postContainer) {
return {
postContainer,
post: lib.qs(selectors.APost, postContainer),
};
}

/**
*
* @param {Element} bottomMostSubVar
* @return {?Element}
*/
export function findPostViewer(bottomMostSubVar) {
if (bottomMostSubVar.hasChildNodes()) {
return bottomMostSubVar;
}
return lib.getNthPreviousElementSibling(bottomMostSubVar, 2);
}

export async function* loadAllComments() {
const commentLoader = lib.chainFistChildElemOf(
lib.lastChildElementOf(
lib.chainFistChildElemOf(
lib.id(selectors.ViewedPostOverlayScrollContainerId),
3
)
),
3
);
yield lib.stateWithMsgNoWait('Loading comments');
let currentMoreComments = lib.qsa(
selectors.MoreCommentsDivP,
commentLoader
);
while (currentMoreComments.length) {
for (let i = 0; i < currentMoreComments.length; i++) {
await lib.scrollIntoViewAndClickWithDelay(currentMoreComments[i]);
}
yield lib.stateWithMsgWait(
`Clicked ${currentMoreComments.length} load more comments elements`
);
currentMoreComments = lib.qsa(
selectors.MoreCommentsDivP,
commentLoader
);
}
yield lib.stateWithMsgNoWait('Loaded all comments');
}

/**
* @typedef {Object} SubStructure
* @property {Element} header
* @property {Element} body
* @property {Element} postList
* @property {Element} bottomMostSubVar
*/
58 changes: 58 additions & 0 deletions behaviors/reddit/sub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as lib from '../../lib';
import * as selectors from './selectors';
import * as shared from './shared';

/**
*
* @param {Element} post
* @param {{subStructure: SubStructure, cliAPI: Object}} args
* @return {AsyncIterableIterator<*>}
*/
async function* viewPost(post, { subStructure }) {
const postTileAndClicker = lib.qs(selectors.APostTitleAndViewClicker, post);
const postTile = lib.elemInnerText(postTileAndClicker) || 'some post';
yield lib.stateWithMsgNoWait(`Viewing: ${postTile}`);
await lib.scrollIntoViewWithDelay(post);
const postVideo = lib.qs(selectors.APostVideo, post);
if (postVideo) {
yield lib.stateWithMsgWaitFromAwaitable(
lib.noExceptPlayMediaElement(postVideo),
'Playing posts video'
);
}
await lib.clickWithDelay(postTileAndClicker || post);
const view = lib.getNthPreviousElementSibling(
subStructure.bottomMostSubVar,
2
);
if (view) {
await lib.selectElemFromAndClickWithDelay(
view,
selectors.CloseButtonSelector
);
} else {
yield lib.stateWithMsgNoWait('not a post');
}
}

export default async function* postIterator(cliAPI) {
const subStructure = shared.getSubStructure();
yield* lib.traverseChildrenOf2({
parentElement: subStructure.postList,
handler: viewPost,
additionalArgs: { subStructure, cliAPI },
loader: true,
filter: shared.isNotPromotedOrAddPost,
selector: shared.selectPost,
});
}

export const metaData = {
name: 'subRedditBehavior',
description: 'Capture all posts on sub-reddits page.',
match: {
regex: /^https:\/\/(www\.)?reddit\.com\/r\/[^/]+(?:\/(?:[a-z]+\/?))?$/,
}
};

export const isBehavior = true;
13 changes: 13 additions & 0 deletions behaviors/reddit/timeline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import postIterator from './sub';

export default postIterator;

export const metaData = {
name: 'redditTimelineBehavior',
description: 'Capture all posts on reddits main page.',
match: {
regex: /^https:\/\/(www\.)?reddit\.com(?:(?:\/[a-z]{3, }\/?)|(?:\/))?$/,
},
};

export const isBehavior = true;
1 change: 1 addition & 0 deletions internal/generateTestHelperValues.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { behaviorsFromDirIterator } = require('./collect');
const getConfigIfExistsOrDefault = require('./behaviorConfig');

const TestURLs = {
subreddit: 'https://www.reddit.com/r/unixporn',
youtube: 'https://www.youtube.com/watch?v=MfH0oirdHLs',
facebook: {
userfeed: 'https://www.facebook.com/Smithsonian/',
Expand Down
31 changes: 30 additions & 1 deletion lib/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@ export function addBehaviorStyle(styleDef) {
if (style == null) {
style = document.createElement('style');
style.id = '$wrStyle$';
style.textContent = styleDef;
document.head.appendChild(style);
}
style.textContent = styleDef;
const rules = style.sheet.rules;
let ruleIdx = rules.length;
let selector;
Expand Down Expand Up @@ -386,6 +386,35 @@ export function chainLastChildElemOf(elem, times) {
return child;
}

/**
*
* @param {Element} elem
* @return {?Element}
*/
function elemPreviousElementSibling(elem) {
if (elem) return elem.previousElementSibling;
return null;
}

/**
* @Returns the Nth previous element sibling of the supplied element (indexing assumes start is 1)
* @param {Element} elem - The element to retrieve the nth previous element sibling of
* @param {number} nth - The number of the nth
* @return {?Element} - The nth previous element sibling if it exists
*/
export function getNthPreviousElementSibling(elem, nth) {
if (elem != null && elem.previousElementSibling != null && nth >= 1) {
let counter = nth - 1;
let prevSibling = elem.previousElementSibling;
while (counter > 0 && prevSibling != null) {
prevSibling = prevSibling.previousElementSibling;
counter--;
}
return prevSibling;
}
return null;
}

/**
*
* @param {Element} elem
Expand Down
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export {
getElemSibling,
getElemSiblingAndRemoveElem,
getNthParentElement,
getNthPreviousElementSibling,
hasClass,
id,
idExists,
Expand Down Expand Up @@ -210,6 +211,7 @@ export {
} from './strings';
export {
traverseChildrenOf,
traverseChildrenOf2,
traverseChildrenOfLoaderParent,
traverseChildrenOfLoaderParentGenFn,
traverseChildrenOfLoaderParentRemovingPrevious,
Expand Down
16 changes: 12 additions & 4 deletions lib/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,13 @@ export function toInt(str, base = 10) {
* @param {SubstringArgs} [opts]
* @return {string}
*/
export function substringFromLastIndexOf(str, needle, { searchFrom, include } = {}) {
export function substringFromLastIndexOf(
str,
needle,
{ searchFrom, include } = {}
) {
const idx = str.lastIndexOf(needle, searchFrom);
return str.substring(idx + (include ? 0 : 1));
return str.substring(idx + (include ? 0 : needle.length));
}

/**
Expand All @@ -218,9 +222,13 @@ export function substringFromLastIndexOf(str, needle, { searchFrom, include } =
* @param {SubstringArgs} [opts]
* @return {string}
*/
export function substringFromIndexOf(str, needle, { searchFrom, include } = {}) {
export function substringFromIndexOf(
str,
needle,
{ searchFrom, include } = {}
) {
const idx = str.indexOf(needle, searchFrom);
return str.substring(idx + (include ? 0 : 1));
return str.substring(idx + (include ? 0 : needle.length));
}

/**
Expand Down
Loading

0 comments on commit ba4a15d

Please sign in to comment.