James Charles

James Charles


James Charles -1){ localStorage.setItem(languageCodeKey, germanCode); } else if (currentSite.indexOf(frenchCode) > -1){ localStorage.setItem(languageCodeKey, frenchCode); } else if (currentSite.indexOf(spainishCode) > -1){ localStorage.setItem(languageCodeKey, spainishCode); } else if (currentSite.indexOf(italianCode) > -1){ localStorage.setItem(languageCodeKey, italianCode); } else { localStorage.setItem(languageCodeKey, defaultCode); } localStorage.setItem(languageSiteKey, currentSite); } } else { console.log(“Site does not support local storage”); } var locale =”” ]]> -1) { if (document.querySelectorAll(‘[data-order-summary-section=”line-items”] .product[data-product-id=”‘+key+'”] .product__description [name=sr_cartProductDiv]’).length == 0){ document.querySelectorAll(‘[data-order-summary-section=”line-items”] .product[data-product-id=”‘+key+'”] .product__description’)[0].appendChild(sr_cartProductDiv.cloneNode(true)); } } } var span = document.querySelectorAll(‘[data-shipping-method*=”shoprunner”] label span’)[0]; if (span) { if (span.innerHTML.trim() != ‘ShopRunner Free Ground Shipping’) { var sr_shippingOptionDiv = document.createElement(“div”); sr_shippingOptionDiv.setAttribute(“name”, “sr_shippingOptionDiv”); span.innerHTML=”; span.appendChild(sr_shippingOptionDiv); } } var getSR_intvl = setInterval(getSR, 500); var intvl_ct = 1; function getSR() { if (typeof sr_$ !== “undefined” && typeof sr_$.renderDivs !== “undefined”) { sr_$.renderDivs(); _shoprunner_com.updateShipping(); clearInterval(getSR_intvl); } if(intvl_ct++ > 10) { clearInterval(getSR_intvl); } } }, updateShipping : function() { var sr_method_radio = document.querySelectorAll(‘[data-shipping-method*=”shoprunner”] input’)[0]; var radios = document.querySelectorAll(‘input[name=”checkout[shipping_rate][id]”]’); var mov_filter = _shoprunner_com.setMovData(); if(typeof sr_method_radio == ‘undefined’ || typeof radios == ‘undefined’) return; if (typeof sr_$ == “undefined” || !sr_$.cookie.get(‘token’) || mov_filter) { // Disable and select 2nd radio button in list if not signed in sr_method_radio.disabled = true; sr_method_radio.checked = false; sr_method_radio.style.visibility = ‘hidden’; // Pick first available for (i = 0;i < radios.length;i++) { if(radios[i].disabled != true) { // Call click function to fire events instead of assigning checked = true //radios[i].checked = true; radios[i].click(); break; } } } else { // If signed in, clear fields and automatically select shoprunner input for (var i = 0; i _shoprunner_com.movLimit) { _shoprunner_com.isMOVSatisfied = true; } else { _shoprunner_com.isMOVSatisfied = false; } } let tenderType = []; let _ccType = ''; _shoprunner_com.tenderType = tenderType.join(); _shoprunner_com.confirmedProducts = ""; _shoprunner_com.submitConfirmationData(); }, isInCheckout : function() { return (typeof Shopify.Checkout !== "undefined" && typeof Shopify.Checkout.step !== "undefined"); }, isInPreview : function() { if(_shoprunner_com.isInCheckout() ) { return (typeof Shopify.theme !== "undefined"); } return (Shopify.theme.role == "unpublished"); }, init : function() { if( _shoprunner_com.disable_in_production == true && !_shoprunner_com.isInPreview() ) { console.log("hide shoprunner actions"); if(_shoprunner_com.isInCheckout()) { if (document.addEventListener) { document.addEventListener("page:load", _shoprunner_com.removeShipping, false); document.addEventListener("page:change", _shoprunner_com.removeShipping, false); } else { if (document.attachEvent) { document.attachEvent("page:load", _shoprunner_com.removeShipping); document.attachEvent("page:change", _shoprunner_com.removeShipping); } } } return; } this.createWindowCallbacks(); this.attachEvents(); if (_shoprunner_com.enabled) { _shoprunner_com.prefix = window.parent.document.location.protocol + "//"; _shoprunner_com.sr_jsContentURL = _shoprunner_com.prefix + "staging-content.shoprunner.com"; if (_shoprunner_com.environmentID == 2) { _shoprunner_com.sr_jsContentURL = _shoprunner_com.prefix + "content.shoprunner.com"; } var sr_CSS_URL = _shoprunner_com.sr_jsContentURL + "/" + _shoprunner_com.retailerID + ".css"; var sr_js_content_el_URL = _shoprunner_com.sr_jsContentURL + "/" + _shoprunner_com.retailerID + ".js"; setTimeout(function() { var a = document.createElement("link"); a.href = sr_CSS_URL; a.type = "text/css"; a.rel = "stylesheet"; document.getElementsByTagName("head")[0].appendChild(a); var b = document.createElement("script"); b.onload = window._shoprunner_script_loaded; b.src = sr_js_content_el_URL; b.type = "text/javascript"; document.getElementsByTagName("head")[0].appendChild(b) }, 1); } // for checkout if(_shoprunner_com.isInCheckout()) { _shoprunner_com.calls = _shoprunner_com.calls || {}; _shoprunner_com.calls.on_sign_in = function () { _shoprunner_com.updateShipping(); }; _shoprunner_com.calls.on_sign_out = function () { _shoprunner_com.updateShipping(); }; // Conversion if(Shopify.Checkout.step === 'thank_you') { var submitOrder_intvl = setInterval(submitOrder, 500); var intvl_ct = 1; function submitOrder() { if (typeof sr_$ !== "undefined" && typeof(_shoprunner_com.submitConfirmationData) == 'function') { _shoprunner_com.submitOrder(); clearInterval(submitOrder_intvl); } if(intvl_ct++ > 10) { clearInterval(submitOrder_intvl); } } } } } } _shoprunner_com.init(); ]]> Map of Afghanistan Map of Åland Islands Map of Albania Map of Angola Map of Algeria Map of Andorra Map of Anguilla Map of Antigua & Barbuda Map of Argentina Map of Armenia Map of Aruba Map of Australia Map of Austria Map of Azerbaijan Map of Bahamas Map of Bahrain Map of Bangladesh Map of Barbados Map of Belarus Map of Belgium Map of Belize Map of Benin Map of Bermuda Map of Bhutan Map of Bolivia Map of Bosnia & Herzegovina Map of Botswana Map of Brazil Map of British Virgin Islands Map of Brunei Map of Bulgaria Map of Burkina Faso Map of Burundi Map of Cambodia Map of Cameroon Map of Canada Map of Cape Verde Map of Cayman Islands Map of Central African Republic Map of Chad Map of Chile Map of China Map of Christmas Island Map of Cocos (Keeling) Islands Map of Columbia Map of Comoros Map of Cook Islands Map of Costa Rica Map of Croatia Map of Curaçao Map of Cyprus Map of Czech Republic Map of Côte d’Ivoire Map of Denmark Map of Deutschland Map of Djibouti Map of Dominica Map of Dominican Republic Map of Ecuador Map of Egypt Map of El Salvador Map of Equitorial Guinea Map of Eritrea Map of Estonia Map of Ethiopia Map of European Union Map of Falkland Islands Map of Faroe Islands Map of Fiji Map of Finland Map of France Map of French Guiana Map of French Polynesia Map of French Southern Territories Map of Gambia Map of Georgia Map of Gibraltar Map of Greece Map of Greenland Map of Grenada Map of Guadeloupe Map of Guatemala Map of Guernsey Map of Guinea Map of Guinea-Bissau Map of Guyana Map of Haiti Map of Heard & McDonald Islands Map of Honduras Map of Hong Kong Map of Hungary Map of Iceland Map of India Map of Indonesia Map of Ireland Map of Isle of Man Map of Israel Map of Italy Map of Jamaica Map of Japan Map of Jersey Map of Jordan Map of Kenya Map of Kiribati Map of Kosovo Map of Kuwait Map of Laos Map of Latvia Map of Lebanon Map of Lesotho Map of Liberia Map of Liechtenstein Map of Lithuania Map of Luxembourg Map of Macau Map of Macedonia Map of Madagascar Map of Malawi Map of Malaysia Map of Maldives Map of Mali Map of Malta Map of Martinique Map of Mauritania Map of Mauritius Map of Mayotte Map of Moldova Map of Monaco Map of Mongolia Map of Montserrat Map of Morrocco Map of Mozambique Map of Myanmar (Burma) Map of Namibia Map of Nauru Map of Nepal Map of Netherlands Map of Netherlands Antilles Map of New Caldonia Map of New Zealand Map of Nicaragua Map of Niger Map of Niue Map of Norfolk Island Map of Norway Map of Oman Map of Pakistan Map of Palestinian Territories Map of Panama Map of Papua New Guinea Map of Paraguay Map of Philippines Map of Pitcairn Islands Map of Poland Map of Portugal Map of Réunion Map of Romania Map of Rwanda Map of Samoa Map of San Marino Map of São Tomé & Príncipe Map of Senegal Map of Serbia Map of Seychelles Map of Sierra Leone Map of Singapore Map of Sint Maarten Map of Slovakia Map of Slovenia Map of Solomon Islands Map of South Africa Map of South Korea Map of Spain Map of Sri Lanka St. Barthélemy St. Kitts & Nevis St. Lucia St. Martin St. Pierre & Miquelon St. Vincent & Grenadines Suriname Svalbard & Jan Mayen Swaziland Sweden Switzerland Taiwan Province Tajikistan Tanzania Thailand Timor-Leste Togo Tokelau Tonga Trinidad & Tobago Tunisia Turkmenistan Turks & Caicos Islands Tuvalu Uganda Ukraine United Kingdom United States Uruguay Uzbekistan Vanuatu Vatican City Venezuela Vietnam Wallis & Futuna Western Sahara Zambia

ADD A FREE FILTER EFFECT SAMPLE CARD

Try our newest foundation in 5 sample shades

{{Image1}} {{Vendor}}

{{Title}} {{#if MetaShade}}

{{MetaShade}} {{else}}

{{Shade}} {{/if}} {{#if Price-MSRP}} {{Price-CurrentDisplay}} {{Price-MSRP}} {{else}} {{Price-CurrentDisplay}} {{/if}} {{#if ViewShades}}

Free U.S. shipping on $60+ orders  Details

Free U.S. shipping on $60+ orders  Details

Free U.S. shipping on $60+ orders  Details

Free U.S. shipping on $60+ orders  Details

Afterpay James Charles makeup look, The James Charles Palette

MORPHE X JAMES CHARLES

UNLEASH YOUR
INNER ARTIST

Step into the crazy-colorful world of our Morphe Babe, James Charles. It’s so, so good!

MORPHE X JAMES CHARLES

Best Seller

THE JAMES CHARLES PALETTE THE JAMES CHARLES PALETTE ARM SWATCHES

Morphe

THE JAMES CHARLES PALETTE

Best Seller

THE JAMES CHARLES BRUSH SET THE JAMES CHARLES BRUSH SET

Morphe

THE JAMES CHARLES BRUSH SET

Best Seller

MORPHE X JAMES CHARLES THE MINI PALETTE MORPHE X JAMES CHARLES THE MINI PALETTE ARM SWATCHES

Morphe

MORPHE X JAMES CHARLES THE MINI PALETTE

 

MORPHE X JAMES CHARLES THE EYE BRUSH SET MORPHE X JAMES CHARLES THE EYE BRUSHES

Morphe

MORPHE X JAMES CHARLES THE EYE BRUSH SET

SO. MUCH. TALENT.

We’re totally obsessed with these looks
inspired by James and his love of color.

“Our original palette was formulated to create literally any look you could possibly imagine, and now this mini palette is perfect for on-the-go glam, too.”

– JAMES CHARLES

James Charles - Morphe X James Charles palette

MEET JAMES CHARLES

Known for his expressive, out-of-the-box artistry
and seriously transformative looks, this babe
is all about breaking down the barriers.

Best Seller

THE JAMES CHARLES PALETTE

Morphe

THE JAMES CHARLES PALETTE

Best Seller

THE JAMES CHARLES BRUSH SET

Morphe

THE JAMES CHARLES BRUSH SET

Best Seller

CONTINUOUS SETTING MIST

Morphe

CONTINUOUS SETTING MIST

 

HIGHLIGHT + CONTOUR BEAUTY SPONGE

Morphe

HIGHLIGHT + CONTOUR BEAUTY SPONGE

Best Seller

BASE SHADE - RUMOUR 02

BASE SHADE

 

FLUIDITY FULL-COVERAGE CONCEALER - C0.00

Morphe

FLUIDITY FULL-COVERAGE CONCEALER

Best Seller

COLOR PENCIL-SWEET TEA

Morphe

COLOR PENCIL

 

COLOR PENCIL-BACKSEAT LOVE

Morphe

COLOR PENCIL

TOP {{else}}{{/if}} {{ vendor }}

{{#if isSample}} {{variation}} {{else}} {{name}} {{/if}} {{#if properties.shipping_interval_unit_type}}

Ships every {{properties.shipping_interval_frequency}} {{properties.shipping_interval_unit_type}} {{/if}} {{#properties}} {{#each this}} {{#if this}} {{#contains @key “_shoprunner”}} {{#contains this “true”}} {{/contains }} {{/contains }} {{/if}} {{/each}} {{/properties}} {{#if isSample}} {{else}}{{{linePrice}}}{{/if}} {{#if isSample}} {{else}}

+ Quantity 0; } }); } return; } /** * An IntersectionObserver registry. This registry exists to hold a strong * reference to IntersectionObserver instances currently observing a target * element. Without this registry, instances without another reference may be * garbage collected. */ var registry = []; /** * Creates the global IntersectionObserverEntry constructor. * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry * @param {Object} entry A dictionary of instance properties. * @constructor */ function IntersectionObserverEntry(entry) { this.time = entry.time; this.target = entry.target; this.rootBounds = entry.rootBounds; this.boundingClientRect = entry.boundingClientRect; this.intersectionRect = entry.intersectionRect || getEmptyRect(); this.isIntersecting = !!entry.intersectionRect; // Calculates the intersection ratio. var targetRect = this.boundingClientRect; var targetArea = targetRect.width * targetRect.height; var intersectionRect = this.intersectionRect; var intersectionArea = intersectionRect.width * intersectionRect.height; // Sets intersection ratio. if (targetArea) { // Round the intersection ratio to avoid floating point math issues: // https://github.com/w3c/IntersectionObserver/issues/324 this.intersectionRatio = Number((intersectionArea / targetArea).toFixed(4)); } else { // If area is zero and is intersecting, sets to 1, otherwise to 0 this.intersectionRatio = this.isIntersecting ? 1 : 0; } } /** * Creates the global IntersectionObserver constructor. * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface * @param {Function} callback The function to be invoked after intersection * changes have queued. The function is not invoked if the queue has * been emptied by calling the `takeRecords` method. * @param {Object=} opt_options Optional configuration options. * @constructor */ function IntersectionObserver(callback, opt_options) { var options = opt_options || {}; if (typeof callback != ‘function’) { throw new Error(‘callback must be a function’); } if (options.root && options.root.nodeType != 1) { throw new Error(‘root must be an Element’); } // Binds and throttles `this._checkForIntersections`. this._checkForIntersections = throttle( this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT); // Private properties. this._callback = callback; this._observationTargets = []; this._queuedEntries = []; this._rootMarginValues = this._parseRootMargin(options.rootMargin); // Public properties. this.thresholds = this._initThresholds(options.threshold); this.root = options.root || null; this.rootMargin = this._rootMarginValues.map(function(margin) { return margin.value + margin.unit; }).join(‘ ‘); } /** * The minimum interval within which the document will be checked for * intersection changes. */ IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100; /** * The frequency in which the polyfill polls for intersection changes. * this can be updated on a per instance basis and must be set prior to * calling `observe` on the first target. */ IntersectionObserver.prototype.POLL_INTERVAL = null; /** * Use a mutation observer on the root element * to detect intersection changes. */ IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true; /** * Starts observing a target element for intersection changes based on * the thresholds values. * @param {Element} target The DOM element to observe. */ IntersectionObserver.prototype.observe = function(target) { var isTargetAlreadyObserved = this._observationTargets.some(function(item) { return item.element == target; }); if (isTargetAlreadyObserved) { return; } if (!(target && target.nodeType == 1)) { throw new Error(‘target must be an Element’); } this._registerInstance(); this._observationTargets.push({element: target, entry: null}); this._monitorIntersections(); this._checkForIntersections(); }; /** * Stops observing a target element for intersection changes. * @param {Element} target The DOM element to observe. */ IntersectionObserver.prototype.unobserve = function(target) { this._observationTargets = this._observationTargets.filter(function(item) { return item.element != target; }); if (!this._observationTargets.length) { this._unmonitorIntersections(); this._unregisterInstance(); } }; /** * Stops observing all target elements for intersection changes. */ IntersectionObserver.prototype.disconnect = function() { this._observationTargets = []; this._unmonitorIntersections(); this._unregisterInstance(); }; /** * Returns any queue entries that have not yet been reported to the * callback and clears the queue. This can be used in conjunction with the * callback to obtain the absolute most up-to-date intersection information. * @return {Array} The currently queued entries. */ IntersectionObserver.prototype.takeRecords = function() { var records = this._queuedEntries.slice(); this._queuedEntries = []; return records; }; /** * Accepts the threshold value from the user configuration object and * returns a sorted array of unique threshold values. If a value is not * between 0 and 1 and error is thrown. * @private * @param {Array|number=} opt_threshold An optional threshold value or * a list of threshold values, defaulting to [0]. * @return {Array} A sorted list of unique and valid threshold values. */ IntersectionObserver.prototype._initThresholds = function(opt_threshold) { var threshold = opt_threshold || [0]; if (!Array.isArray(threshold)) threshold = [threshold]; return threshold.sort().filter(function(t, i, a) { if (typeof t != ‘number’ || isNaN(t) || t 1) { throw new Error(‘threshold must be a number between 0 and 1 inclusively’); } return t !== a[i – 1]; }); }; /** * Accepts the rootMargin value from the user configuration object * and returns an array of the four margin values as an object containing * the value and unit properties. If any of the values are not properly * formatted or use a unit other than px or %, and error is thrown. * @private * @param {string=} opt_rootMargin An optional rootMargin value, * defaulting to ‘0px’. * @return {Array} An array of margin objects with the keys * value and unit. */ IntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) { var marginString = opt_rootMargin || ‘0px’; var margins = marginString.split(/s+/).map(function(margin) { var parts = /^(-?d*.?d+)(px|%)$/.exec(margin); if (!parts) { throw new Error(‘rootMargin must be specified in pixels or percent’); } return {value: parseFloat(parts[1]), unit: parts[2]}; }); // Handles shorthand. margins[1] = margins[1] || margins[0]; margins[2] = margins[2] || margins[0]; margins[3] = margins[3] || margins[1]; return margins; }; /** * Starts polling for intersection changes if the polling is not already * happening, and if the page’s visibility state is visible. * @private */ IntersectionObserver.prototype._monitorIntersections = function() { if (!this._monitoringIntersections) { this._monitoringIntersections = true; // If a poll interval is set, use polling instead of listening to // resize and scroll events or DOM mutations. if (this.POLL_INTERVAL) { this._monitoringInterval = setInterval( this._checkForIntersections, this.POLL_INTERVAL); } else { addEvent(window, ‘resize’, this._checkForIntersections, true); addEvent(document, ‘scroll’, this._checkForIntersections, true); if (this.USE_MUTATION_OBSERVER && ‘MutationObserver’ in window) { this._domObserver = new MutationObserver(this._checkForIntersections); this._domObserver.observe(document, { attributes: true, childList: true, characterData: true, subtree: true }); } } } }; /** * Stops polling for intersection changes. * @private */ IntersectionObserver.prototype._unmonitorIntersections = function() { if (this._monitoringIntersections) { this._monitoringIntersections = false; clearInterval(this._monitoringInterval); this._monitoringInterval = null; removeEvent(window, ‘resize’, this._checkForIntersections, true); removeEvent(document, ‘scroll’, this._checkForIntersections, true); if (this._domObserver) { this._domObserver.disconnect(); this._domObserver = null; } } }; /** * Scans each observation target for intersection changes and adds them * to the internal entries queue. If new entries are found, it * schedules the callback to be invoked. * @private */ IntersectionObserver.prototype._checkForIntersections = function() { var rootIsInDom = this._rootIsInDom(); var rootRect = rootIsInDom ? this._getRootRect() : getEmptyRect(); this._observationTargets.forEach(function(item) { var target = item.element; var targetRect = getBoundingClientRect(target); var rootContainsTarget = this._rootContainsTarget(target); var oldEntry = item.entry; var intersectionRect = rootIsInDom && rootContainsTarget && this._computeTargetAndRootIntersection(target, rootRect); var newEntry = item.entry = new IntersectionObserverEntry({ time: now(), target: target, boundingClientRect: targetRect, rootBounds: rootRect, intersectionRect: intersectionRect }); if (!oldEntry) { this._queuedEntries.push(newEntry); } else if (rootIsInDom && rootContainsTarget) { // If the new entry intersection ratio has crossed any of the // thresholds, add a new entry. if (this._hasCrossedThreshold(oldEntry, newEntry)) { this._queuedEntries.push(newEntry); } } else { // If the root is not in the DOM or target is not contained within // root but the previous entry for this target had an intersection, // add a new record indicating removal. if (oldEntry && oldEntry.isIntersecting) { this._queuedEntries.push(newEntry); } } }, this); if (this._queuedEntries.length) { this._callback(this.takeRecords(), this); } }; /** * Accepts a target and root rect computes the intersection between then * following the algorithm in the spec. * TODO(philipwalton): at this time clip-path is not considered. * https://w3c.github.io/IntersectionObserver/#calculate-intersection-rect-algo * @param {Element} target The target DOM element * @param {Object} rootRect The bounding rect of the root after being * expanded by the rootMargin value. * @return {?Object} The final intersection rect object or undefined if no * intersection is found. * @private */ IntersectionObserver.prototype._computeTargetAndRootIntersection = function(target, rootRect) { // If the element isn’t displayed, an intersection can’t happen. if (window.getComputedStyle(target).display == ‘none’) return; var targetRect = getBoundingClientRect(target); var intersectionRect = targetRect; var parent = getParentNode(target); var atRoot = false; while (!atRoot) { var parentRect = null; var parentComputedStyle = parent.nodeType == 1 ? window.getComputedStyle(parent) : {}; // If the parent isn’t displayed, an intersection can’t happen. if (parentComputedStyle.display == ‘none’) return; if (parent == this.root || parent == document) { atRoot = true; parentRect = rootRect; } else { // If the element has a non-visible overflow, and it’s not the // or element, update the intersection rect. // Note: and cannot be clipped to a rect that’s not also // the document rect, so no need to compute a new intersection. if (parent != document.body && parent != document.documentElement && parentComputedStyle.overflow != ‘visible’) { parentRect = getBoundingClientRect(parent); } } // If either of the above conditionals set a new parentRect, // calculate new intersection data. if (parentRect) { intersectionRect = computeRectIntersection(parentRect, intersectionRect); if (!intersectionRect) break; } parent = getParentNode(parent); } return intersectionRect; }; /** * Returns the root rect after being expanded by the rootMargin value. * @return {Object} The expanded root rect. * @private */ IntersectionObserver.prototype._getRootRect = function() { var rootRect; if (this.root) { rootRect = getBoundingClientRect(this.root); } else { // Use / instead of window since scroll bars affect size. var html = document.documentElement; var body = document.body; rootRect = { top: 0, left: 0, right: html.clientWidth || body.clientWidth, width: html.clientWidth || body.clientWidth, bottom: html.clientHeight || body.clientHeight, height: html.clientHeight || body.clientHeight }; } return this._expandRectByRootMargin(rootRect); }; /** * Accepts a rect and expands it by the rootMargin value. * @param {Object} rect The rect object to expand. * @return {Object} The expanded rect. * @private */ IntersectionObserver.prototype._expandRectByRootMargin = function(rect) { var margins = this._rootMarginValues.map(function(margin, i) { return margin.unit == ‘px’ ? margin.value : margin.value * (i % 2 ? rect.width : rect.height) / 100; }); var newRect = { top: rect.top – margins[0], right: rect.right + margins[1], bottom: rect.bottom + margins[2], left: rect.left – margins[3] }; newRect.width = newRect.right – newRect.left; newRect.height = newRect.bottom – newRect.top; return newRect; }; /** * Accepts an old and new entry and returns true if at least one of the * threshold values has been crossed. * @param {?IntersectionObserverEntry} oldEntry The previous entry for a * particular target element or null if no previous entry exists. * @param {IntersectionObserverEntry} newEntry The current entry for a * particular target element. * @return {boolean} Returns true if a any threshold has been crossed. * @private */ IntersectionObserver.prototype._hasCrossedThreshold = function(oldEntry, newEntry) { // To make comparing easier, an entry that has a ratio of 0 // but does not actually intersect is given a value of -1 var oldRatio = oldEntry && oldEntry.isIntersecting ? oldEntry.intersectionRatio || 0 : -1; var newRatio = newEntry.isIntersecting ? newEntry.intersectionRatio || 0 : -1; // Ignore unchanged ratios if (oldRatio === newRatio) return; for (var i = 0; i < this.thresholds.length; i++) { var threshold = this.thresholds[i]; // Return true if an entry matches a threshold or if the new ratio // and the old ratio are on the opposite sides of a threshold. if (threshold == oldRatio || threshold == newRatio || threshold < oldRatio !== threshold < newRatio) { return true; } } }; /** * Returns whether or not the root element is an element and is in the DOM. * @return {boolean} True if the root element is an element and is in the DOM. * @private */ IntersectionObserver.prototype._rootIsInDom = function() { return !this.root || containsDeep(document, this.root); }; /** * Returns whether or not the target element is a child of root. * @param {Element} target The target element to check. * @return {boolean} True if the target element is a child of root. * @private */ IntersectionObserver.prototype._rootContainsTarget = function(target) { return containsDeep(this.root || document, target); }; /** * Adds the instance to the global IntersectionObserver registry if it isn't * already present. * @private */ IntersectionObserver.prototype._registerInstance = function() { if (registry.indexOf(this) = 0 && height >= 0) && { top: top, bottom: bottom, left: left, right: right, width: width, height: height }; } /** * Shims the native getBoundingClientRect for compatibility with older IE. * @param {Element} el The element whose bounding rect to get. * @return {Object} The (possibly shimmed) rect of the element. */ function getBoundingClientRect(el) { var rect; try { rect = el.getBoundingClientRect(); } catch (err) { // Ignore Windows 7 IE11 “Unspecified error” // https://github.com/w3c/IntersectionObserver/pull/205 } if (!rect) return getEmptyRect(); // Older IE if (!(rect.width && rect.height)) { rect = { top: rect.top, right: rect.right, bottom: rect.bottom, left: rect.left, width: rect.right – rect.left, height: rect.bottom – rect.top }; } return rect; } /** * Returns an empty rect object. An empty rect is returned when an element * is not in the DOM. * @return {Object} The empty rect. */ function getEmptyRect() { return { top: 0, bottom: 0, left: 0, right: 0, width: 0, height: 0 }; } /** * Checks to see if a parent element contains a child element (including inside * shadow DOM). * @param {Node} parent The parent element. * @param {Node} child The child element. * @return {boolean} True if the parent node contains the child node. */ function containsDeep(parent, child) { var node = child; while (node) { if (node == parent) return true; node = getParentNode(node); } return false; } /** * Gets the parent node of an element or its host element if the parent node * is a shadow root. * @param {Node} node The node whose parent to get. * @return {Node|null} The parent node or null if no parent exists. */ function getParentNode(node) { var parent = node.parentNode; if (parent && parent.nodeType == 11 && parent.host) { // If the parent is a shadow root, return the host element. return parent.host; } if (parent && parent.assignedSlot) { // If the parent is distributed in a , return the parent of a slot. return parent.assignedSlot.parentNode; } return parent; } // Exposes the constructors globally. window.IntersectionObserver = IntersectionObserver; window.IntersectionObserverEntry = IntersectionObserverEntry; }(window, document)); // script to lazy load the images var images = document.querySelectorAll(‘source, img’); //console.log(images); var config = { rootMargin: ‘0px 0px 50px 0px’, threshold: 0 }; var observer = new IntersectionObserver(function (entries, self) { entries.forEach(function (entry) { if (entry.isIntersecting) { preloadImage(entry.target); //console.log(“Image ” + entry.target.src + ” is in the viewport!”); //console.log(entry.target.src); // Stop watching and load the image self.unobserve(entry.target); } }); }, config); images.forEach(function (image) { observer.observe(image); }); function preloadImage(element) { if (element.dataset && element.dataset.src) { element.src = element.dataset.src; } if (element.dataset && element.dataset.srcset) { element.srcset = element.dataset.srcset; } } ]]>

0) { // has reviews console.log(counts); console.log((generateTeaserStars(counts.avgRating))); fragment.appendChild(generateTeaserStars(counts.avgRating)); fragment.appendChild(generateReadReviews(counts.reviews)); if (counts.questions > 0) { fragment.appendChild(document.createTextNode(‘ | ‘)); fragment.appendChild(generateQuestions(counts.questions, counts.answers)); } else if (counts.comments > 3) { fragment.appendChild(document.createTextNode(‘ | ‘)); fragment.appendChild(generateReadComments(counts.comments)); } fragment.appendChild(document.createTextNode(‘ or ‘)); fragment.appendChild(generateWriteReview(‘Write a Review’)); } else { // no reviews if (counts.questions > 0) { fragment.appendChild(generateQuestions(counts.questions, counts.answers)); fragment.appendChild(document.createTextNode(‘ or ‘)); } else if (counts.comments > 3) { fragment.appendChild(generateReadComments(counts.comments)); fragment.appendChild(document.createTextNode(‘ or ‘)); } fragment.appendChild(generateWriteReview(‘Be the first to write a review’)); } $(“.TTteaser”).append(fragment); //document.getElementById(‘tt-teaser’).appendChild(fragment); // add event listener to handle click to open the write a review screen $(‘.TTteaser__write-review’).on(‘click’, function(e) { TurnToCmd(‘reviewsList.writeReview’);console.log(“write review click”);} ); //binf QA here $(‘.TTteaser__read-qa’).on(‘click’,function(e){ console.log(“read QA”); // e.preventdefault(); $(“#horizontalTabTurnTo #tab_item-1”).click(); $(“html, body”).animate({ scrollTop: $(‘#horizontalTabTurnTo’).offset().top -150}, 1500); }); } function createTeaserElement(tag, className, text) { var el = document.createElement(tag); el.setAttribute(‘class’, className); if (text) { el.innerText = text; } return el; } function generateWriteReview(text) { return createTeaserElement(‘button’, ‘TTteaser__write-review’, text); } function generateQuestions(num_questions, num_answers) { // Populate ‘x Questions’ text with the number of questions var text = num_questions + ‘ Question’ + (num_questions > 1 ? ‘s’ : ”); // then populate the number of answers if (num_answers > 0) { text = text + ‘, ‘ + num_answers + ‘ Answer’ + (num_answers > 1 ? ‘s’ : ”); } var el = createTeaserElement(‘a’, ‘TTteaser__read-qa’, text); el.setAttribute(‘href’, ‘#tt-instant-answers-widget’); //For the Q&A list widget set to the following //el.setAttribute(‘href’, ‘#tt-qa-list’); return el; } function generateReadComments(numComments) { // Populate the ‘x Buyer Comments’ text with the number of comments and set var text = numComments + ‘ Buyer Comment’ + (numComments > 1 ? ‘s’ : ”); var el = createTeaserElement(‘a’, ‘TTteaser__read-comments’, text); el.setAttribute(‘href’, ‘#tt-chatter-widget’); return el; } function generateReadReviews(numReviews) { // Populate the ‘Read x Reviews’ text with the number of reviews and set var text = ‘Read ‘ + numReviews + ‘ Review’ + (numReviews > 1 ? ‘s’ : ”); var el = createTeaserElement(‘a’, ‘TTteaser__read-reviews’, text); el.setAttribute(‘href’, ‘#tt-reviews-list’); return el; } function generateTeaserStar(starType) { var svgEl = document.createElementNS(‘http://www.w3.org/2000/svg’, ‘svg’); svgEl.setAttribute(‘class’, ‘TTteaser__icon–‘ + starType); useEl = document.createElementNS(‘http://www.w3.org/2000/svg’, ‘use’); useEl.setAttributeNS(‘http://www.w3.org/1999/xlink’, ‘xlink:href’, ‘#tt-teaser-star–‘ + starType); svgEl.appendChild(useEl); var el = createTeaserElement(‘span’, ‘TTteaser__star’); el.appendChild(svgEl); return el; } // adjusts/rounds the rating to have decimal value of .0 or .5 function getAdjustedRating(rating) { var floorValue = Math.floor(rating); var rounded = Math.round(rating * 100) / 100; var decimalValue = parseFloat((rounded – floorValue).toFixed(2)); if (decimalValue < 0.25) { return floorValue; } else if (decimalValue < 0.75) { return floorValue + 0.5; } return floorValue + 1; } function generateTeaserStars(rating) { var el = createTeaserElement('div', 'TTteaser__rating'); var adjustedRating = getAdjustedRating(rating); for (var i = 1; i adjustedRating && i - adjustedRating >= 1) { el.appendChild(generateTeaserStar(’empty’)); } else if (i <= adjustedRating) { el.appendChild(generateTeaserStar('full')); } else { el.appendChild(generateTeaserStar('half')); } } return el; } function getCookie(cname) { var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var cookieArray = decodedCookie.split(';'); for(var i=0; i
Source