UNPKG

@mediarithmics/plugins-nodejs-sdk

Version:

This is the mediarithmics nodejs to help plugin developers bootstrapping their plugin without having to deal with most of the plugin boilerplate

152 lines 7.44 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RecommendationsHandlebarsEngine = exports.HandlebarsEngine = exports.buildURLHandlebarsRootContext = void 0; const handlebars_1 = __importDefault(require("handlebars")); const lodash_1 = __importDefault(require("lodash")); const numeral_1 = __importDefault(require("numeral")); const index_1 = require("../../mediarithmics/plugins/ad-renderer/utils/index"); const formatPrice = (price, pattern) => { const number = (0, numeral_1.default)(price); return number.format(pattern); }; const encodeClickUrl = () => (redirectUrls, clickUrl) => { const urls = redirectUrls.slice(0); urls.push({ url: clickUrl, redirect_count: 0, }); return (0, index_1.generateEncodedClickUrl)(urls); }; const placeHolder = '{{MICS_AD_CONTENT_ID}}'; const uriEncodePlaceHolder = encodeURI(placeHolder); const doubleEncodedUriPlaceHolder = encodeURI(encodeURI(placeHolder)); // Encode recommendation click url => contains the contentId of the recommendation that will be // insrted into the campaign log const encodeRecoClickUrlHelper = () => (idx, rootContext, recommendation) => { rootContext.private.clickableContents.push({ item_id: recommendation.$id, catalog_token: recommendation.$catalog_token, $content_id: idx, }); // recommendation.url replace placeHolder by idx const filledRedirectUrls = rootContext.private.redirectUrls.map((clickUrlInfo) => { const cloned = lodash_1.default.clone(clickUrlInfo); const url1 = lodash_1.default.replace(cloned.url, placeHolder, idx.toString()); const url2 = lodash_1.default.replace(url1, uriEncodePlaceHolder, idx.toString()); cloned.url = lodash_1.default.replace(url2, doubleEncodedUriPlaceHolder, idx.toString()); return cloned; }); const recommendationUrl = recommendation.$url ? recommendation.$url : ''; return encodeClickUrl()(filledRedirectUrls, recommendationUrl); }; const buildURLHandlebarsRootContext = (adRenderRequest, instanceContext) => { return { REQUEST: adRenderRequest, CREATIVE: { CLICK_URL: instanceContext.creative_click_url, WIDTH: instanceContext.width, HEIGHT: instanceContext.height, }, ORGANISATION_ID: instanceContext.displayAd.organisation_id, // Hack, it should come from the AdRendererRequest AD_GROUP_ID: adRenderRequest.ad_group_id, MEDIA_ID: adRenderRequest.media_id, ENCODED_MEDIA_ID: adRenderRequest.media_id ? encodeURIComponent(adRenderRequest.media_id) : undefined, CAMPAIGN_ID: adRenderRequest.campaign_id, CREATIVE_ID: adRenderRequest.creative_id, CACHE_BUSTER: Date.now().toString(), IAS_CLIENT_ID: instanceContext.ias_client_id, CB: Date.now().toString(), }; }; exports.buildURLHandlebarsRootContext = buildURLHandlebarsRootContext; class HandlebarsEngine { // Initialisation of the engine. Done once at every InstanceContext rebuild. init() { this.engine = handlebars_1.default.create(); /* Generic Helpers */ this.engine.registerHelper('formatPrice', formatPrice); this.engine.registerHelper('toJson', (object) => JSON.stringify(object)); } enableProfileDataLayer() { this.engine.registerHelper('profileData', (opts) => { // See https://blog.osteele.com/2007/12/cheap-monads/ const $N = {}; // Check if we have the value in the DataLayer if ((((((opts || $N).data || $N).root || $N).private || $N).profileData || $N)[opts.hash.fieldName]) { return opts.data.root.private.profileData[opts.hash.fieldName]; } else { return opts.hash.defaultValue; } }); } parse(template) { return handlebars_1.default.parse(template); } // TODO: Test this thing getMacros(internals) { class MacroScanner extends handlebars_1.default.Visitor { constructor() { super(...arguments); this.macros = []; } } // The Handlebars Compiler library is documented there: https://github.com/wycats/handlebars.js/blob/master/docs/compiler-api.md MacroScanner.prototype.MustacheStatement = function (macro) { if (macro.type === 'MustacheStatement') { const pathExpression = macro.path; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call this.macros.push({ parts: pathExpression.parts }); } // We're just here to visit, we don't want to break anything, so let's call the "default function" to process MustacheStatement handlebars_1.default.Visitor.prototype.MustacheStatement.call(this, macro); }; const scanner = new MacroScanner(); scanner.accept(internals); return scanner.macros; } compile(template) { // Handlebars.compile() can take a string or an AST return this.engine.compile(template); } } exports.HandlebarsEngine = HandlebarsEngine; class RecommendationsHandlebarsEngine extends HandlebarsEngine { constructor() { super(); } // Initialisation of the engine. Done once at every InstanceContext rebuild. init() { super.init(); /* URL Encoding witchcraft */ // We need to have 2 elements when doing the URL encoding: // 1. The "click tracking" array passed in the rootContext (for click tracking) // 2. The final URL (landing page, etc.) passed as a parameter of the helper // // In order to have both, we need to play smart and use an Handlebar partial // This handlebar partial is just a way to add "@root" as a parameter of the helper before calling it // // This is how the encodeClickUrl partial should be used in templates: // {{> encodeClickUrl url="http://www.mediarithmics.com/en/"}} const encodeClickUrlPartial = '{{encodeClickUrlInternal @root.private.redirectUrls url}}'; this.engine.registerPartial('encodeClickUrl', encodeClickUrlPartial); this.engine.registerHelper('encodeClickUrlInternal', encodeClickUrl()); // Same story than previously but this time the partial will inject: // @index -> the index of the recommendation, which is used to include it in the URL // @root -> the root context // this -> the recommendation item // Warning, this partial should only be used in a {{#each recommendations}}{{/each}} block // The $url field of the recommendation will be used as the final URL // // This is how the partial should be used in templates: // {{> encodeRecoClickUrl}} const encodeRecoClickUrlPartial = '{{encodeRecoClickUrlInternal @index @root this}}'; this.engine.registerPartial('encodeRecoClickUrl', encodeRecoClickUrlPartial); this.engine.registerHelper('encodeRecoClickUrlInternal', encodeRecoClickUrlHelper()); } } exports.RecommendationsHandlebarsEngine = RecommendationsHandlebarsEngine; //# sourceMappingURL=HandlebarsEngine.js.map