Skip to content

Commit 14baf43

Browse files
committed
Initial commit
0 parents  commit 14baf43

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.vscode/

manifest.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"manifest_version": 2,
3+
4+
"name": "Orpets",
5+
"version": "0.1.1",
6+
"description": "Easily see if Fido can stay at your Orbitz.com(R) Hotel",
7+
"permissions": ["storage"],
8+
"content_scripts": [
9+
{
10+
"js": ["orpets.js"],
11+
"matches": ["https://www.orbitz.com/Hotel-Search*"]
12+
}
13+
]
14+
}

orpets.js

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
const storageHours = 48;
2+
var processedNodes = [];
3+
const listings = document.getElementsByClassName("listing");
4+
5+
clearLocalStorage();
6+
window.onload = doOnScroll;
7+
window.onscroll = doOnScroll;
8+
9+
/**
10+
* Fires on scroll
11+
*/
12+
async function doOnScroll() {
13+
for (var i = listings.length - 1; i >= 0; i--) {
14+
// for (var i = 1; i >= 0; i--) {
15+
var listing = listings[i];
16+
17+
if (processedNodes.includes(listing) === false) {
18+
if (isScrolledIntoView(listing) === true) {
19+
processedNodes.push(listing);
20+
21+
let listUrl = getLink(listing);
22+
23+
let petText = await getPetText(listUrl);
24+
25+
appendPetText(listing, petText);
26+
}
27+
}
28+
}
29+
}
30+
31+
/**
32+
* Determines whether given element is visible in the viewscreen
33+
*
34+
* @param {element} el DOM Element
35+
* @returns bool
36+
*/
37+
function isScrolledIntoView(el) {
38+
let elTop = el.getBoundingClientRect().top;
39+
let elBottom = el.getBoundingClientRect().bottom;
40+
41+
let isVisible = elTop >= 0 && elBottom <= window.innerHeight;
42+
43+
return isVisible;
44+
}
45+
46+
/**
47+
* Pauses code execution for desired amount.
48+
*
49+
* @param {int} milliseconds Time to wait
50+
* @returns promise
51+
*/
52+
function sleep(milliseconds) {
53+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
54+
}
55+
56+
/**
57+
* Get the link from listing element
58+
*
59+
* @param {html} listing
60+
* @returns string
61+
*/
62+
function getLink(listing) {
63+
const listingLink = listing.getElementsByClassName("listing__link");
64+
const href = listingLink[0].href;
65+
const baseURL = href.substring(0, href.indexOf("?"));
66+
return baseURL;
67+
}
68+
69+
/**
70+
* Get the Pet Text for a given url
71+
* Checks local storage first, and if not found, fetches the text from the given
72+
* URL and stores it for the future.
73+
*
74+
* @param {string} url
75+
* @returns string
76+
*/
77+
async function getPetText(url) {
78+
let petText = getWithExpiry(url);
79+
80+
if (petText !== null) {
81+
return petText;
82+
} else {
83+
let expires = storageHours * 60 * 60 * 1000;
84+
85+
petText = await scrapePetText(url);
86+
87+
setWithExpiry(url, petText, expires);
88+
89+
return petText;
90+
}
91+
}
92+
93+
/**
94+
* Sets a localstorage item with expiration date
95+
*
96+
* @param {string} key Key name
97+
* @param {string} value Value content
98+
* @param {int} exp Expiration time (in milliseconds)
99+
*/
100+
function setWithExpiry(key, value, exp) {
101+
const now = new Date().getTime();
102+
103+
const item = {
104+
origin: "orpets",
105+
value: value,
106+
expiry: now + exp,
107+
};
108+
localStorage.setItem(key, JSON.stringify(item));
109+
}
110+
111+
/**
112+
* Get the Pet Text from local storage and check whether it has expired. Deletes
113+
* the key/value if expired and returns null instead.
114+
*
115+
* @param {string} key Key to search for
116+
* @returns Value or Null
117+
*/
118+
function getWithExpiry(key) {
119+
const value = localStorage.getItem(key);
120+
121+
if (!value) {
122+
return null;
123+
}
124+
125+
const item = JSON.parse(value);
126+
const now = new Date().getTime();
127+
128+
if (now > item.expiry) {
129+
localStorage.removeItem(key);
130+
return null;
131+
}
132+
return item.value;
133+
}
134+
135+
/**
136+
*
137+
* @param {string} url URL of hotel general info to scrape
138+
* @returns {string} Pet text in <li> form
139+
*/
140+
async function scrapePetText(url) {
141+
console.log("Scraping"); // DEBUG
142+
143+
// Limit the rate of hits to be polite.
144+
await sleep(Math.random() * 3000 + 1000); // wait 1 to 3 seconds
145+
146+
let response = await fetch(url);
147+
148+
if (!response.ok) {
149+
throw new Error(`HTTP error! status: ${response.status}`);
150+
}
151+
152+
var parsedResponse = new window.DOMParser().parseFromString(await response.text(), "text/html");
153+
154+
var petText = parsedResponse.querySelector("[itemprop=petsAllowed]").innerHTML;
155+
156+
return petText;
157+
}
158+
159+
/**
160+
*
161+
* @param {element} listing HTML Element
162+
* @param {string} petText <li> listings of pet text info
163+
*/
164+
function appendPetText(listing, petText) {
165+
const petContent = document.createElement("ul");
166+
petContent.innerHTML = petText;
167+
listing.appendChild(petContent);
168+
}
169+
170+
/**
171+
* Houseclaning function to clear all Orpets data that is expired on each load
172+
*/
173+
function clearLocalStorage() {
174+
const now = new Date().getTime();
175+
176+
for (let i = 0; i < localStorage.length; i++) {
177+
const key = localStorage.key(i);
178+
179+
let item;
180+
try {
181+
item = JSON.parse(localStorage.getItem(key));
182+
} catch (e) {
183+
item = {};
184+
}
185+
186+
if (item.origin === "orpets" && item.exipiry < now) {
187+
localStorage.removeItem(key);
188+
}
189+
190+
// Nuclear Option
191+
// if (item.origin === "orpets") {
192+
// localStorage.removeItem(key);
193+
// }
194+
}
195+
}

0 commit comments

Comments
 (0)