diff --git a/src/fallback_extraction.js b/src/fallback_extraction.js
new file mode 100644
index 0000000..db1f6ee
--- /dev/null
+++ b/src/fallback_extraction.js
@@ -0,0 +1,88 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+* Uses CSS selectors, or failing that, Open Graph tags to extract
+* a product from its product page, where a 'product' is defined by the bundle
+* of features that makes it identifiable.
+*
+* Features: title, image, price
+*/
+
+import extractionData from 'commerce/product_extraction_data.json';
+
+const OPEN_GRAPH_PROPERTY_VALUES = {
+ title: 'og:title',
+ image: 'og:image',
+ price: 'og:price:amount',
+};
+
+/**
+ * Returns any extraction data found for the vendor based on the URL
+ * for the page.
+ */
+function getProductAttributeInfo() {
+ const hostname = new URL(window.location.href).host;
+ for (const [vendor, attributeInfo] of Object.entries(extractionData)) {
+ if (hostname.includes(vendor)) {
+ return attributeInfo;
+ }
+ }
+ return null;
+}
+
+/**
+ * Extracts and returns the string value for a given element property or attribute.
+ *
+ * @param {HTMLElement} element
+ * @param {string} extractionProperty
+ */
+function extractValueFromElement(element, extractionProperty) {
+ switch (extractionProperty) {
+ case 'content':
+ return element.getAttribute('content');
+ case 'innerText':
+ return element.innerText;
+ case 'src':
+ return element.src;
+ default:
+ throw new Error(`Unrecognized extraction property or attribute '${extractionProperty}'.`);
+ }
+}
+
+/**
+ * Returns any product information available on the page from CSS
+ * selectors if they exist, otherwise from Open Graph tags.
+ */
+export default function extractProduct() {
+ const data = {};
+ const attributeInfo = getProductAttributeInfo();
+ if (attributeInfo) {
+ for (const [productAttribute, extractor] of Object.entries(attributeInfo)) {
+ const {selectors, extractUsing} = extractor;
+ for (const selector of selectors) {
+ const element = document.querySelector(selector);
+ if (element) {
+ data[productAttribute] = extractValueFromElement(element, extractUsing);
+ if (data[productAttribute]) {
+ break;
+ } else {
+ throw new Error(`Element found did not return a valid product ${productAttribute}.`);
+ }
+ } else if (selector === selectors[selectors.length - 1]) {
+ // None of the selectors matched an element on the page
+ throw new Error(`No elements found with vendor data for product ${productAttribute}.`);
+ }
+ }
+ }
+ } else {
+ for (const [key, value] of Object.entries(OPEN_GRAPH_PROPERTY_VALUES)) {
+ const metaEle = document.querySelector(`meta[property='${value}']`);
+ if (metaEle) {
+ data[key] = metaEle.getAttribute('content');
+ }
+ }
+ }
+ return data;
+}
diff --git a/src/fathom_ruleset.js b/src/fathom_extraction.js
similarity index 70%
rename from src/fathom_ruleset.js
rename to src/fathom_extraction.js
index efc29da..ba4846e 100644
--- a/src/fathom_ruleset.js
+++ b/src/fathom_extraction.js
@@ -1,9 +1,9 @@
-/** This Source Code Form is subject to the terms of the Mozilla Public
+/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
- * Using Fathom to extract a product from its product page,
+ * Uses Fathom to extract a product from its product page,
* where a 'product' is defined by the bundle of features that
* makes it identifiable.
*
@@ -42,10 +42,10 @@ const rules = ruleset(
);
/**
- * Extracts the highest scoring element above a score threshold for a
- * given feature contained in a page's HTML document.
+ * Extracts the highest scoring element above a score threshold
+ * contained in a page's HTML document.
*/
-export default function runTuningRoutine(doc) {
+function runRuleset(doc) {
let fnodesList = rules.against(doc).get('product-price');
fnodesList = fnodesList.filter(fnode => fnode.scoreFor('priceish') >= SCORE_THRESHOLD);
// It is possible for multiple elements to have the same highest score.
@@ -54,3 +54,19 @@ export default function runTuningRoutine(doc) {
}
return null;
}
+
+/*
+ * Run the ruleset for the product features against the current window document
+ */
+export default function extractProduct(doc) {
+ const priceEle = runRuleset(doc);
+ if (priceEle) {
+ const price = (priceEle.tagName !== 'META') ? priceEle.textContent : priceEle.getAttribute('content');
+ if (price) {
+ return {
+ price,
+ };
+ }
+ }
+ return null;
+}
diff --git a/src/product_extraction_data.json b/src/product_extraction_data.json
index b17318d..9194a7b 100644
--- a/src/product_extraction_data.json
+++ b/src/product_extraction_data.json
@@ -27,6 +27,7 @@
"price": {
"selectors": [
"#priceblock_ourprice",
+ "#priceblock_dealprice",
".display-price",
".offer-price"
],
diff --git a/src/product_info.js b/src/product_info.js
index c17c28e..1852e6b 100644
--- a/src/product_info.js
+++ b/src/product_info.js
@@ -7,15 +7,9 @@
* which is after all DOM content has been loaded.
*/
-import runTuningRoutine from 'commerce/fathom_ruleset';
+import extractProductWithFathom from 'commerce/fathom_extraction';
+import extractProductWithFallback from 'commerce/fallback_extraction';
import {retry} from 'commerce/utils';
-import extractionData from 'commerce/product_extraction_data.json';
-
-const OPEN_GRAPH_PROPERTY_VALUES = {
- title: 'og:title',
- image: 'og:image',
- price: 'og:price:amount',
-};
/**
* Open a Port to the background script and wait for the background script to
@@ -55,103 +49,14 @@ async function openBackgroundPort() {
}
}());
-const fallbackExtraction = {
- /**
- * Returns any extraction data found for the vendor based on the URL
- * for the page.
- */
- getProductAttributeInfo() {
- const hostname = new URL(window.location.href).host;
- for (const [vendor, attributeInfo] of Object.entries(extractionData)) {
- if (hostname.includes(vendor)) {
- return attributeInfo;
- }
- }
- return null;
- },
-
- /**
- * Extracts and returns the string value for a given element property or attribute.
- *
- * @param {HTMLElement} element
- * @param {string} extractionProperty
- */
- extractValueFromElement(element, extractionProperty) {
- switch (extractionProperty) {
- case 'content':
- return element.getAttribute('content');
- case 'innerText':
- return element.innerText;
- case 'src':
- return element.src;
- default:
- throw new Error(`Unrecognized extraction property or attribute '${extractionProperty}'.`);
- }
- },
-
- /**
- * Returns any product information available on the page from CSS
- * selectors if they exist, otherwise from Open Graph tags.
- */
- extractProduct() {
- const data = {};
- const attributeInfo = this.getProductAttributeInfo();
- if (attributeInfo) {
- for (const [productAttribute, extractor] of Object.entries(attributeInfo)) {
- const {selectors, extractUsing} = extractor;
- for (const selector of selectors) {
- const element = document.querySelector(selector);
- if (element) {
- data[productAttribute] = this.extractValueFromElement(element, extractUsing);
- if (data[productAttribute]) {
- break;
- } else {
- throw new Error(`Element found did not return a valid product ${productAttribute}.`);
- }
- } else if (selector === selectors[selectors.length - 1]) {
- // None of the selectors matched an element on the page
- throw new Error(`No elements found with vendor data for product ${productAttribute}.`);
- }
- }
- }
- } else {
- for (const [key, value] of Object.entries(OPEN_GRAPH_PROPERTY_VALUES)) {
- const metaEle = document.querySelector(`meta[property='${value}']`);
- if (metaEle) {
- data[key] = metaEle.getAttribute('content');
- }
- }
- }
- data.url = window.document.URL;
- return data;
- },
-};
-
-const fathomExtraction = {
- /*
- * Run the ruleset for the product features against the current window document
- */
- extractProduct() {
- const priceEle = runTuningRoutine(window.document);
- if (priceEle) {
- const price = (priceEle.tagName !== 'META') ? priceEle.textContent : priceEle.getAttribute('content');
- if (price) {
- return {
- price,
- url: window.document.URL,
- };
- }
- }
- return null;
- },
-};
-
/**
* Checks to see if any product information for the page was found,
* and if so, sends it to the background script via the port.
*/
async function getProductInfo(port) {
- const extractedProduct = fathomExtraction.extractProduct() || fallbackExtraction.extractProduct();
+ const extractedProduct = (extractProductWithFathom(window.document)
+ || extractProductWithFallback());
+ extractedProduct.url = window.document.URL;
port.postMessage({
from: 'content',
subject: 'ready',