asrouter-admin.bundle.js (75428B)
1 /*! 2 * 3 * NOTE: This file is generated by webpack from ASRouterAdmin.jsx 4 * using the npm bundle task. 5 * 6 */ 7 var ASRouterAdminRenderUtils; 8 /******/ (() => { // webpackBootstrap 9 /******/ "use strict"; 10 /******/ var __webpack_modules__ = ([ 11 /* 0 */, 12 /* 1 */ 13 /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 14 15 __webpack_require__.r(__webpack_exports__); 16 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 17 /* harmony export */ ASRouterUtils: () => (/* binding */ ASRouterUtils) 18 /* harmony export */ }); 19 /* harmony import */ var _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); 20 /* This Source Code Form is subject to the terms of the Mozilla Public 21 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 22 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 23 24 25 26 const ASRouterUtils = { 27 addListener(listener) { 28 if (globalThis.ASRouterAddParentListener) { 29 globalThis.ASRouterAddParentListener(listener); 30 } 31 }, 32 removeListener(listener) { 33 if (globalThis.ASRouterRemoveParentListener) { 34 globalThis.ASRouterRemoveParentListener(listener); 35 } 36 }, 37 sendMessage(action) { 38 if (globalThis.ASRouterMessage) { 39 return globalThis.ASRouterMessage(action); 40 } 41 throw new Error(`Unexpected call:\n${JSON.stringify(action, null, 3)}`); 42 }, 43 blockById(id, options) { 44 return ASRouterUtils.sendMessage({ 45 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.BLOCK_MESSAGE_BY_ID, 46 data: { id, ...options }, 47 }); 48 }, 49 modifyMessageJson(content) { 50 return ASRouterUtils.sendMessage({ 51 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.MODIFY_MESSAGE_JSON, 52 data: { content }, 53 }); 54 }, 55 executeAction(button_action) { 56 return ASRouterUtils.sendMessage({ 57 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.USER_ACTION, 58 data: button_action, 59 }); 60 }, 61 unblockById(id) { 62 return ASRouterUtils.sendMessage({ 63 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_MESSAGE_BY_ID, 64 data: { id }, 65 }); 66 }, 67 unblockAll() { 68 return ASRouterUtils.sendMessage({ 69 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_ALL, 70 }); 71 }, 72 resetGroupImpressions() { 73 return ASRouterUtils.sendMessage({ 74 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.RESET_GROUPS_STATE, 75 }); 76 }, 77 resetMessageImpressions() { 78 return ASRouterUtils.sendMessage({ 79 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.RESET_MESSAGE_STATE, 80 }); 81 }, 82 resetScreenImpressions() { 83 return ASRouterUtils.sendMessage({ 84 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.RESET_SCREEN_IMPRESSIONS, 85 }); 86 }, 87 blockBundle(bundle) { 88 return ASRouterUtils.sendMessage({ 89 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.BLOCK_BUNDLE, 90 data: { bundle }, 91 }); 92 }, 93 unblockBundle(bundle) { 94 return ASRouterUtils.sendMessage({ 95 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.UNBLOCK_BUNDLE, 96 data: { bundle }, 97 }); 98 }, 99 overrideMessage(id) { 100 return ASRouterUtils.sendMessage({ 101 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.OVERRIDE_MESSAGE, 102 data: { id }, 103 }); 104 }, 105 editState(key, value) { 106 return ASRouterUtils.sendMessage({ 107 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.EDIT_STATE, 108 data: { [key]: value }, 109 }); 110 }, 111 openPBWindow(content) { 112 ASRouterUtils.sendMessage({ 113 type: "FORCE_PRIVATE_BROWSING_WINDOW", 114 data: { message: { content } }, 115 }); 116 }, 117 sendTelemetry(ping) { 118 return ASRouterUtils.sendMessage({ 119 type: _modules_ActorConstants_mjs__WEBPACK_IMPORTED_MODULE_0__.MESSAGE_TYPE_HASH.AS_ROUTER_TELEMETRY_USER_EVENT, 120 data: ping, 121 }); 122 }, 123 getPreviewEndpoint() { 124 return null; 125 }, 126 }; 127 128 129 /***/ }), 130 /* 2 */ 131 /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { 132 133 __webpack_require__.r(__webpack_exports__); 134 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 135 /* harmony export */ MESSAGE_TYPE_HASH: () => (/* binding */ MESSAGE_TYPE_HASH), 136 /* harmony export */ MESSAGE_TYPE_LIST: () => (/* binding */ MESSAGE_TYPE_LIST) 137 /* harmony export */ }); 138 /* vim: set ts=2 sw=2 sts=2 et tw=80: */ 139 /* This Source Code Form is subject to the terms of the Mozilla Public 140 * License, v. 2.0. If a copy of the MPL was not distributed with this 141 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 142 143 const MESSAGE_TYPE_LIST = [ 144 "BLOCK_MESSAGE_BY_ID", 145 "USER_ACTION", 146 "IMPRESSION", 147 "TRIGGER", 148 // PB is Private Browsing 149 "PBNEWTAB_MESSAGE_REQUEST", 150 "DOORHANGER_TELEMETRY", 151 "TOOLBAR_BADGE_TELEMETRY", 152 "MOMENTS_PAGE_TELEMETRY", 153 "INFOBAR_TELEMETRY", 154 "SPOTLIGHT_TELEMETRY", 155 "TOAST_NOTIFICATION_TELEMETRY", 156 "MENU_MESSAGE_TELEMETRY", 157 "NEWTAB_MESSAGE_TELEMETRY", 158 "AS_ROUTER_TELEMETRY_USER_EVENT", 159 160 // Admin types 161 "ADMIN_CONNECT_STATE", 162 "UNBLOCK_MESSAGE_BY_ID", 163 "UNBLOCK_ALL", 164 "BLOCK_BUNDLE", 165 "UNBLOCK_BUNDLE", 166 "DISABLE_PROVIDER", 167 "ENABLE_PROVIDER", 168 "EVALUATE_JEXL_EXPRESSION", 169 "EXPIRE_QUERY_CACHE", 170 "FORCE_ATTRIBUTION", 171 "FORCE_PRIVATE_BROWSING_WINDOW", 172 "OVERRIDE_MESSAGE", 173 "MODIFY_MESSAGE_JSON", 174 "RESET_PROVIDER_PREF", 175 "SET_PROVIDER_USER_PREF", 176 "RESET_GROUPS_STATE", 177 "RESET_MESSAGE_STATE", 178 "RESET_SCREEN_IMPRESSIONS", 179 "EDIT_STATE", 180 ]; 181 182 const MESSAGE_TYPE_HASH = MESSAGE_TYPE_LIST.reduce((hash, value) => { 183 hash[value] = value; 184 return hash; 185 }, {}); 186 187 188 /***/ }), 189 /* 3 */ 190 /***/ ((module) => { 191 192 module.exports = React; 193 194 /***/ }), 195 /* 4 */ 196 /***/ ((module) => { 197 198 module.exports = ReactDOM; 199 200 /***/ }), 201 /* 5 */ 202 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 203 204 __webpack_require__.r(__webpack_exports__); 205 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 206 /* harmony export */ SimpleHashRouter: () => (/* binding */ SimpleHashRouter) 207 /* harmony export */ }); 208 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); 209 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 210 /* This Source Code Form is subject to the terms of the Mozilla Public 211 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 212 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 213 214 215 class SimpleHashRouter extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { 216 constructor(props) { 217 super(props); 218 this.onHashChange = this.onHashChange.bind(this); 219 this.state = { 220 hash: __webpack_require__.g.location.hash 221 }; 222 } 223 onHashChange() { 224 this.setState({ 225 hash: __webpack_require__.g.location.hash 226 }); 227 } 228 componentWillMount() { 229 __webpack_require__.g.addEventListener("hashchange", this.onHashChange); 230 } 231 componentWillUnmount() { 232 __webpack_require__.g.removeEventListener("hashchange", this.onHashChange); 233 } 234 render() { 235 const [, ...routes] = this.state.hash.split("-"); 236 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement(this.props.children, { 237 location: { 238 hash: this.state.hash, 239 routes 240 } 241 }); 242 } 243 } 244 245 /***/ }), 246 /* 6 */ 247 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 248 249 __webpack_require__.r(__webpack_exports__); 250 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 251 /* harmony export */ CopyButton: () => (/* binding */ CopyButton) 252 /* harmony export */ }); 253 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); 254 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 255 function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } 256 /* This Source Code Form is subject to the terms of the Mozilla Public 257 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 258 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 259 260 261 const CopyButton = ({ 262 className, 263 label, 264 copiedLabel, 265 inputSelector, 266 transformer, 267 ...props 268 }) => { 269 const [copied, setCopied] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); 270 const timeout = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); 271 const onClick = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => { 272 let text = document.querySelector(inputSelector).value; 273 if (transformer) { 274 text = transformer(text); 275 } 276 navigator.clipboard.writeText(text); 277 clearTimeout(timeout.current); 278 setCopied(true); 279 timeout.current = setTimeout(() => setCopied(false), 1500); 280 }, [inputSelector, transformer]); 281 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", _extends({ 282 className: className, 283 onClick: () => onClick() 284 }, props), copied && copiedLabel || label); 285 }; 286 287 /***/ }), 288 /* 7 */ 289 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 290 291 __webpack_require__.r(__webpack_exports__); 292 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 293 /* harmony export */ ImpressionsSection: () => (/* binding */ ImpressionsSection) 294 /* harmony export */ }); 295 /* harmony import */ var _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 296 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 297 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__); 298 /* This Source Code Form is subject to the terms of the Mozilla Public 299 * License, v. 2.0. If a copy of the MPL was not distributed with this 300 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 301 302 303 304 const stringify = json => JSON.stringify(json, null, 2); 305 const ImpressionsSection = ({ 306 messageImpressions, 307 groupImpressions, 308 screenImpressions 309 }) => { 310 const handleSaveMessageImpressions = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(newImpressions => { 311 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.editState("messageImpressions", newImpressions); 312 }, []); 313 const handleSaveGroupImpressions = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(newImpressions => { 314 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.editState("groupImpressions", newImpressions); 315 }, []); 316 const handleSaveScreenImpressions = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(newImpressions => { 317 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.editState("screenImpressions", newImpressions); 318 }, []); 319 const handleResetMessageImpressions = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { 320 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 321 type: "RESET_MESSAGE_STATE" 322 }); 323 }, []); 324 const handleResetGroupImpressions = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { 325 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 326 type: "RESET_GROUPS_STATE" 327 }); 328 }, []); 329 const handleResetScreenImpressions = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { 330 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 331 type: "RESET_SCREEN_IMPRESSIONS" 332 }); 333 }, []); 334 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 335 className: "impressions-section" 336 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(ImpressionsItem, { 337 impressions: messageImpressions, 338 label: "Message Impressions", 339 description: "Message impressions are stored in an object, where each key is a message ID and each value is an array of timestamps. They are cleaned up when a message with that ID stops existing in ASRouter state (such as at the end of an experiment).", 340 onSave: handleSaveMessageImpressions, 341 onReset: handleResetMessageImpressions 342 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(ImpressionsItem, { 343 impressions: groupImpressions, 344 label: "Group Impressions", 345 description: "Group impressions are stored in an object, where each key is a group ID and each value is an array of timestamps. They are never cleaned up.", 346 onSave: handleSaveGroupImpressions, 347 onReset: handleResetGroupImpressions 348 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(ImpressionsItem, { 349 impressions: screenImpressions, 350 label: "Screen Impressions", 351 description: "Screen impressions are stored in an object, where each key is a screen ID and each value is the most recent timestamp that screen was shown. They are never cleaned up.", 352 onSave: handleSaveScreenImpressions, 353 onReset: handleResetScreenImpressions 354 })); 355 }; 356 const ImpressionsItem = ({ 357 impressions, 358 label, 359 description, 360 validator, 361 onSave, 362 onReset 363 }) => { 364 const [json, setJson] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(stringify(impressions)); 365 const modified = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(false); 366 const isValidJson = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(text => { 367 try { 368 JSON.parse(text); 369 return validator ? validator(text) : true; 370 } catch (e) { 371 return false; 372 } 373 }, [validator]); 374 const jsonIsInvalid = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(() => !isValidJson(json), [json, isValidJson]); 375 const handleChange = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(e => { 376 setJson(e.target.value); 377 modified.current = true; 378 }, []); 379 const handleSave = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { 380 if (jsonIsInvalid) { 381 return; 382 } 383 const newImpressions = JSON.parse(json); 384 modified.current = false; 385 onSave(newImpressions); 386 }, [json, jsonIsInvalid, onSave]); 387 const handleReset = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { 388 modified.current = false; 389 onReset(); 390 }, [onReset]); 391 (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { 392 if (!modified.current) { 393 setJson(stringify(impressions)); 394 } 395 }, [impressions]); 396 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 397 className: "impressions-item" 398 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 399 className: "impressions-category" 400 }, label), description ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("p", { 401 className: "impressions-description" 402 }, description) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 403 className: "impressions-inner-box" 404 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 405 className: "impressions-buttons" 406 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 407 className: "button primary", 408 disabled: jsonIsInvalid, 409 onClick: handleSave 410 }, "Save"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 411 className: "button reset", 412 onClick: handleReset 413 }, "Reset")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 414 className: "impressions-editor" 415 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("textarea", { 416 rows: "15", 417 value: json, 418 onChange: handleChange 419 })))); 420 }; 421 422 /***/ }) 423 /******/ ]); 424 /************************************************************************/ 425 /******/ // The module cache 426 /******/ var __webpack_module_cache__ = {}; 427 /******/ 428 /******/ // The require function 429 /******/ function __webpack_require__(moduleId) { 430 /******/ // Check if module is in cache 431 /******/ var cachedModule = __webpack_module_cache__[moduleId]; 432 /******/ if (cachedModule !== undefined) { 433 /******/ return cachedModule.exports; 434 /******/ } 435 /******/ // Create a new module (and put it into the cache) 436 /******/ var module = __webpack_module_cache__[moduleId] = { 437 /******/ // no module.id needed 438 /******/ // no module.loaded needed 439 /******/ exports: {} 440 /******/ }; 441 /******/ 442 /******/ // Execute the module function 443 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 444 /******/ 445 /******/ // Return the exports of the module 446 /******/ return module.exports; 447 /******/ } 448 /******/ 449 /************************************************************************/ 450 /******/ /* webpack/runtime/compat get default export */ 451 /******/ (() => { 452 /******/ // getDefaultExport function for compatibility with non-harmony modules 453 /******/ __webpack_require__.n = (module) => { 454 /******/ var getter = module && module.__esModule ? 455 /******/ () => (module['default']) : 456 /******/ () => (module); 457 /******/ __webpack_require__.d(getter, { a: getter }); 458 /******/ return getter; 459 /******/ }; 460 /******/ })(); 461 /******/ 462 /******/ /* webpack/runtime/define property getters */ 463 /******/ (() => { 464 /******/ // define getter functions for harmony exports 465 /******/ __webpack_require__.d = (exports, definition) => { 466 /******/ for(var key in definition) { 467 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 468 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 469 /******/ } 470 /******/ } 471 /******/ }; 472 /******/ })(); 473 /******/ 474 /******/ /* webpack/runtime/global */ 475 /******/ (() => { 476 /******/ __webpack_require__.g = (function() { 477 /******/ if (typeof globalThis === 'object') return globalThis; 478 /******/ try { 479 /******/ return this || new Function('return this')(); 480 /******/ } catch (e) { 481 /******/ if (typeof window === 'object') return window; 482 /******/ } 483 /******/ })(); 484 /******/ })(); 485 /******/ 486 /******/ /* webpack/runtime/hasOwnProperty shorthand */ 487 /******/ (() => { 488 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 489 /******/ })(); 490 /******/ 491 /******/ /* webpack/runtime/make namespace object */ 492 /******/ (() => { 493 /******/ // define __esModule on exports 494 /******/ __webpack_require__.r = (exports) => { 495 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 496 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 497 /******/ } 498 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 499 /******/ }; 500 /******/ })(); 501 /******/ 502 /************************************************************************/ 503 var __webpack_exports__ = {}; 504 // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. 505 (() => { 506 __webpack_require__.r(__webpack_exports__); 507 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 508 /* harmony export */ ASRouterAdmin: () => (/* binding */ ASRouterAdmin), 509 /* harmony export */ ASRouterAdminInner: () => (/* binding */ ASRouterAdminInner), 510 /* harmony export */ ToggleMessageJSON: () => (/* binding */ ToggleMessageJSON), 511 /* harmony export */ renderASRouterAdmin: () => (/* binding */ renderASRouterAdmin), 512 /* harmony export */ toBinary: () => (/* binding */ toBinary) 513 /* harmony export */ }); 514 /* harmony import */ var _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); 515 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); 516 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__); 517 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); 518 /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_2__); 519 /* harmony import */ var _SimpleHashRouter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); 520 /* harmony import */ var _CopyButton__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6); 521 /* harmony import */ var _ImpressionsSection__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7); 522 /* This Source Code Form is subject to the terms of the Mozilla Public 523 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 524 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 525 526 527 528 529 530 531 532 533 // Convert a UTF-8 string to a string in which only one byte of each 534 // 16-bit unit is occupied. This is necessary to comply with `btoa` API constraints. 535 function toBinary(string) { 536 const codeUnits = new Uint16Array(string.length); 537 for (let i = 0; i < codeUnits.length; i++) { 538 codeUnits[i] = string.charCodeAt(i); 539 } 540 return btoa(String.fromCharCode(...Array.from(new Uint8Array(codeUnits.buffer)))); 541 } 542 function relativeTime(timestamp) { 543 if (!timestamp) { 544 return ""; 545 } 546 const seconds = Math.floor((Date.now() - timestamp) / 1000); 547 const minutes = Math.floor((Date.now() - timestamp) / 60000); 548 if (seconds < 2) { 549 return "just now"; 550 } else if (seconds < 60) { 551 return `${seconds} seconds ago`; 552 } else if (minutes === 1) { 553 return "1 minute ago"; 554 } else if (minutes < 600) { 555 return `${minutes} minutes ago`; 556 } 557 return new Date(timestamp).toLocaleString(); 558 } 559 class ToggleMessageJSON extends (react__WEBPACK_IMPORTED_MODULE_1___default().PureComponent) { 560 constructor(props) { 561 super(props); 562 this.handleClick = this.handleClick.bind(this); 563 } 564 handleClick() { 565 this.props.toggleJSON(this.props.msgId); 566 } 567 render() { 568 let direction = this.props.isCollapsed ? "forward" : "down"; 569 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 570 className: "clearButton", 571 onClick: this.handleClick 572 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 573 className: `icon small icon-arrowhead-${direction}` 574 })); 575 } 576 } 577 class ASRouterAdminInner extends (react__WEBPACK_IMPORTED_MODULE_1___default().PureComponent) { 578 constructor(props) { 579 super(props); 580 this.handleEnabledToggle = this.handleEnabledToggle.bind(this); 581 this.handleUserPrefToggle = this.handleUserPrefToggle.bind(this); 582 this.onChangeFilters = this.onChangeFilters.bind(this); 583 this.onClearFilters = this.onClearFilters.bind(this); 584 this.unblockAll = this.unblockAll.bind(this); 585 this.resetAllJSON = this.resetAllJSON.bind(this); 586 this.handleExpressionEval = this.handleExpressionEval.bind(this); 587 this.onChangeTargetingParameters = this.onChangeTargetingParameters.bind(this); 588 this.onChangeAttributionParameters = this.onChangeAttributionParameters.bind(this); 589 this.setAttribution = this.setAttribution.bind(this); 590 this.onCopyTargetingParams = this.onCopyTargetingParams.bind(this); 591 this.onNewTargetingParams = this.onNewTargetingParams.bind(this); 592 this.resetMessageState = this.resetMessageState.bind(this); 593 this.toggleJSON = this.toggleJSON.bind(this); 594 this.toggleAllMessages = this.toggleAllMessages.bind(this); 595 this.resetGroupImpressions = this.resetGroupImpressions.bind(this); 596 this.onMessageFromParent = this.onMessageFromParent.bind(this); 597 this.setStateFromParent = this.setStateFromParent.bind(this); 598 this.setState = this.setState.bind(this); 599 this.state = { 600 filterGroups: [], 601 filterProviders: [], 602 filterTemplates: [], 603 filtersCollapsed: true, 604 collapsedMessages: [], 605 modifiedMessages: [], 606 messageBlockList: [], 607 multiProfileMessageBlocklist: [], 608 evaluationStatus: {}, 609 stringTargetingParameters: null, 610 newStringTargetingParameters: null, 611 copiedToClipboard: false, 612 attributionParameters: { 613 source: "addons.mozilla.org", 614 medium: "referral", 615 campaign: "non-fx-button", 616 content: `rta:${btoa("uBlock0@raymondhill.net")}`, 617 experiment: "ua-onboarding", 618 variation: "chrome", 619 ua: "Google Chrome 123", 620 dltoken: "00000000-0000-0000-0000-000000000000" 621 } 622 }; 623 } 624 onMessageFromParent({ 625 type, 626 data 627 }) { 628 // These only exists due to onPrefChange events in ASRouter 629 switch (type) { 630 case "UpdateAdminState": 631 { 632 this.setStateFromParent(data); 633 break; 634 } 635 } 636 } 637 async setStateFromParent(data) { 638 await this.setState(data); 639 if (!this.state.stringTargetingParameters) { 640 const stringTargetingParameters = {}; 641 for (const param of Object.keys(data.targetingParameters)) { 642 stringTargetingParameters[param] = JSON.stringify(data.targetingParameters[param], null, 2); 643 } 644 await this.setState({ 645 stringTargetingParameters 646 }); 647 } 648 } 649 componentWillMount() { 650 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.addListener(this.onMessageFromParent); 651 const endpoint = _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.getPreviewEndpoint(); 652 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 653 type: "ADMIN_CONNECT_STATE", 654 data: { 655 endpoint 656 } 657 }).then(this.setStateFromParent); 658 } 659 componentWillUnmount() { 660 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.removeListener(this.onMessageFromParent); 661 } 662 handleBlock(msg) { 663 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.blockById(msg.id); 664 } 665 handleUnblock(msg) { 666 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.unblockById(msg.id); 667 } 668 resetJSON(msg) { 669 // reset the displayed JSON for the given message 670 let textarea = document.getElementById(`${msg.id}-textarea`); 671 textarea.value = JSON.stringify(msg, null, 2); 672 textarea.classList.remove("errorState"); 673 // remove the message from the list of modified IDs 674 let index = this.state.modifiedMessages.indexOf(msg.id); 675 this.setState(prevState => ({ 676 modifiedMessages: [...prevState.modifiedMessages.slice(0, index), ...prevState.modifiedMessages.slice(index + 1)] 677 })); 678 } 679 resetAllJSON() { 680 // reset the displayed JSON for each modified message 681 for (const msgId of this.state.modifiedMessages) { 682 const msg = this.state.messages.find(m => m.id === msgId); 683 const textarea = document.getElementById(`${msgId}-textarea`); 684 if (textarea) { 685 textarea.value = JSON.stringify(msg, null, 2); 686 textarea.classList.remove("errorState"); 687 } 688 } 689 this.setState({ 690 modifiedMessages: [] 691 }); 692 } 693 showMessage(msg) { 694 if (msg.template === "pb_newtab") { 695 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.openPBWindow(msg.content); 696 } else { 697 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.overrideMessage(msg.id).then(state => this.setStateFromParent(state)); 698 } 699 } 700 async resetMessageState() { 701 await Promise.all([_asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.resetMessageImpressions(), _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.resetGroupImpressions(), _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.resetScreenImpressions(), _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.unblockAll()]); 702 let data = await _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 703 type: "ADMIN_CONNECT_STATE", 704 data: { 705 endpoint: _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.getPreviewEndpoint() 706 } 707 }); 708 await this.setStateFromParent(data); 709 } 710 expireCache() { 711 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 712 type: "EXPIRE_QUERY_CACHE" 713 }); 714 } 715 resetPref() { 716 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 717 type: "RESET_PROVIDER_PREF" 718 }); 719 } 720 resetGroupImpressions() { 721 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.resetGroupImpressions().then(this.setStateFromParent); 722 } 723 resetMessageImpressions() { 724 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.resetMessageImpressions().then(this.setStateFromParent); 725 } 726 handleExpressionEval() { 727 const context = {}; 728 for (const param of Object.keys(this.state.stringTargetingParameters)) { 729 const value = this.state.stringTargetingParameters[param]; 730 context[param] = value ? JSON.parse(value) : null; 731 } 732 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 733 type: "EVALUATE_JEXL_EXPRESSION", 734 data: { 735 expression: this.refs.expressionInput.value || "undefined", 736 context 737 } 738 }).then(this.setStateFromParent); 739 } 740 onChangeTargetingParameters(event) { 741 const { 742 name: eventName 743 } = event.target; 744 const { 745 value 746 } = event.target; 747 let targetingParametersError = null; 748 try { 749 JSON.parse(value); 750 event.target.classList.remove("errorState"); 751 } catch (e) { 752 console.error(`Error parsing value of parameter ${eventName}`); 753 event.target.classList.add("errorState"); 754 targetingParametersError = { 755 id: eventName 756 }; 757 } 758 this.setState(({ 759 stringTargetingParameters 760 }) => { 761 const updatedParameters = { 762 ...stringTargetingParameters 763 }; 764 updatedParameters[eventName] = value; 765 return { 766 copiedToClipboard: false, 767 evaluationStatus: {}, 768 stringTargetingParameters: updatedParameters, 769 targetingParametersError 770 }; 771 }); 772 } 773 unblockAll() { 774 return _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.unblockAll().then(this.setStateFromParent); 775 } 776 async handleEnabledToggle(event) { 777 const provider = this.state.providerPrefs.find(p => p.id === event.target.dataset.provider); 778 const userPrefInfo = this.state.userPrefs; 779 const isUserEnabled = provider.id in userPrefInfo ? userPrefInfo[provider.id] : true; 780 const isSystemEnabled = provider.enabled; 781 const isEnabling = event.target.checked; 782 if (isEnabling) { 783 if (!isUserEnabled) { 784 await _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 785 type: "SET_PROVIDER_USER_PREF", 786 data: { 787 id: provider.id, 788 value: true 789 } 790 }); 791 } 792 if (!isSystemEnabled) { 793 await _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 794 type: "ENABLE_PROVIDER", 795 data: provider.id 796 }); 797 } 798 } else { 799 await _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 800 type: "DISABLE_PROVIDER", 801 data: provider.id 802 }); 803 } 804 this.setState({ 805 filterProviders: [] 806 }); 807 } 808 handleUserPrefToggle(event) { 809 const action = { 810 type: "SET_PROVIDER_USER_PREF", 811 data: { 812 id: event.target.dataset.provider, 813 value: event.target.checked 814 } 815 }; 816 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage(action); 817 this.setState({ 818 filterProviders: [] 819 }); 820 } 821 onChangeFilters(event) { 822 // this function handles both provider filter and group filter. the checkbox 823 // will have dataset.provider if it's a provider checkbox, and dataset.group 824 // if it's a group checkbox. 825 let stateKey; 826 let itemValue; 827 let { 828 checked 829 } = event.target; 830 if (event.target.dataset.provider) { 831 stateKey = "filterProviders"; 832 itemValue = event.target.dataset.provider; 833 } else if (event.target.dataset.group) { 834 stateKey = "filterGroups"; 835 itemValue = event.target.dataset.group; 836 } else if (event.target.dataset.template) { 837 stateKey = "filterTemplates"; 838 itemValue = event.target.dataset.template; 839 } else { 840 return; 841 } 842 this.setState(prevState => { 843 let newValue; 844 if (checked) { 845 newValue = prevState[stateKey].includes(itemValue) ? prevState[stateKey] : prevState[stateKey].concat(itemValue); 846 } else { 847 newValue = prevState[stateKey].filter(item => item !== itemValue); 848 } 849 return { 850 [stateKey]: newValue 851 }; 852 }); 853 } 854 onClearFilters() { 855 this.setState({ 856 filterProviders: [], 857 filterGroups: [], 858 filterTemplates: [] 859 }); 860 } 861 862 // Simulate a copy event that sets to clipboard all targeting paramters and values 863 onCopyTargetingParams() { 864 const stringTargetingParameters = { 865 ...this.state.stringTargetingParameters 866 }; 867 for (const key of Object.keys(stringTargetingParameters)) { 868 // If the value is not set the parameter will be lost when we stringify 869 if (stringTargetingParameters[key] === undefined) { 870 stringTargetingParameters[key] = null; 871 } 872 } 873 const setClipboardData = e => { 874 e.preventDefault(); 875 e.clipboardData.setData("text", JSON.stringify(stringTargetingParameters, null, 2)); 876 document.removeEventListener("copy", setClipboardData); 877 this.setState({ 878 copiedToClipboard: true 879 }); 880 }; 881 document.addEventListener("copy", setClipboardData); 882 document.execCommand("copy"); 883 } 884 onNewTargetingParams(event) { 885 this.setState({ 886 newStringTargetingParameters: event.target.value 887 }); 888 event.target.classList.remove("errorState"); 889 this.refs.targetingParamsEval.innerText = ""; 890 try { 891 const stringTargetingParameters = JSON.parse(event.target.value); 892 this.setState({ 893 stringTargetingParameters 894 }); 895 } catch (e) { 896 event.target.classList.add("errorState"); 897 this.refs.targetingParamsEval.innerText = e.message; 898 } 899 } 900 toggleJSON(msgId) { 901 if (this.state.collapsedMessages.includes(msgId)) { 902 let index = this.state.collapsedMessages.indexOf(msgId); 903 this.setState(prevState => ({ 904 collapsedMessages: [...prevState.collapsedMessages.slice(0, index), ...prevState.collapsedMessages.slice(index + 1)] 905 })); 906 } else { 907 this.setState(prevState => ({ 908 collapsedMessages: prevState.collapsedMessages.concat(msgId) 909 })); 910 } 911 } 912 onMessageChanged(msgId) { 913 if (!this.state.modifiedMessages.includes(msgId)) { 914 this.setState(prevState => ({ 915 modifiedMessages: prevState.modifiedMessages.concat(msgId) 916 })); 917 } 918 } 919 renderMessageItem(msg) { 920 const isBlockedByGroup = this.state.groups.filter(group => msg.groups.includes(group.id)).some(group => !group.enabled); 921 const msgProvider = this.state.providers.find(provider => provider.id === msg.provider) || {}; 922 const isProviderExcluded = msgProvider.exclude && msgProvider.exclude.includes(msg.id); 923 const isMessageBlocked = this.state.messageBlockList.includes(msg.id) || this.state.messageBlockList.includes(msg.campaign) || this.state.multiProfileMessageBlocklist.includes(msg.id); 924 const isBlocked = isMessageBlocked || isBlockedByGroup || isProviderExcluded; 925 const impressions = this.state.messageImpressions[msg.id] ? this.state.messageImpressions[msg.id].length : 0; 926 const isCollapsed = this.state.collapsedMessages.includes(msg.id); 927 const isModified = this.state.modifiedMessages.includes(msg.id); 928 const aboutMessagePreviewSupported = ["infobar", "spotlight", "cfr_doorhanger", "feature_callout", "pb_newtab"].includes(msg.template); 929 let itemClassName = "message-item"; 930 if (isBlocked) { 931 itemClassName += " blocked"; 932 } 933 let messageStats = []; 934 let messageStatsString; 935 if (impressions) { 936 messageStats.push(`${impressions} impressions`); 937 } 938 if (isMessageBlocked) { 939 messageStats.push("message blocked"); 940 } else if (isBlockedByGroup) { 941 messageStats.push("message group blocked"); 942 } else if (isProviderExcluded) { 943 messageStats.push("excluded by provider"); 944 } 945 if (messageStats.length) { 946 messageStatsString = `(${messageStats.join(", ")})`; 947 } 948 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 949 className: itemClassName, 950 key: `${msg.id}-${msg.provider}` 951 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 952 className: "button-box baseline" 953 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 954 className: "message-id monospace" 955 }, msg.id), " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 956 className: "message-stats small-text" 957 }, messageStatsString)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 958 className: "button-box" 959 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(ToggleMessageJSON, { 960 msgId: `${msg.id}`, 961 toggleJSON: this.toggleJSON, 962 isCollapsed: isCollapsed 963 }), 964 // eslint-disable-next-line no-nested-ternary 965 isBlocked ? null : isModified ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 966 className: "restore", 967 onClick: () => this.resetJSON(msg) 968 }, "Reset") : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 969 className: "primary show", 970 onClick: () => this.showMessage(msg) 971 }, "Show"), isBlocked || !isModified ? null : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 972 className: "primary modify", 973 onClick: () => this.modifyJson(msg) 974 }, "Modify"), aboutMessagePreviewSupported ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_CopyButton__WEBPACK_IMPORTED_MODULE_4__.CopyButton, { 975 transformer: text => `about:messagepreview?json=${encodeURIComponent(toBinary(text))}`, 976 label: "Share", 977 copiedLabel: "Copied!", 978 inputSelector: `#${msg.id}-textarea`, 979 className: "share" 980 }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 981 className: `button${isBlocked ? " primary" : ""}`, 982 onClick: () => isBlocked ? this.handleUnblock(msg) : this.handleBlock(msg) 983 }, isBlocked ? "Unblock" : "Block")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("pre", { 984 className: isCollapsed ? "collapsed" : "expanded" 985 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("textarea", { 986 id: `${msg.id}-textarea`, 987 name: msg.id, 988 className: "message-textarea", 989 disabled: isBlocked, 990 rows: "30", 991 onChange: event => { 992 try { 993 JSON.parse(event.target.value); 994 event.target.classList.remove("errorState"); 995 } catch (e) { 996 event.target.classList.add("errorState"); 997 } 998 this.onMessageChanged(msg.id); 999 }, 1000 spellCheck: "false" 1001 }, JSON.stringify(msg, null, 2)))); 1002 } 1003 modifyJson(content) { 1004 const message = JSON.parse(document.getElementById(`${content.id}-textarea`).value); 1005 if (message.template === "pb_newtab") { 1006 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.openPBWindow(message.content); 1007 } else { 1008 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.modifyMessageJson(message).then(state => { 1009 this.setStateFromParent(state); 1010 }); 1011 } 1012 } 1013 toggleAllMessages(messagesToShow) { 1014 if (this.state.collapsedMessages.length) { 1015 this.setState({ 1016 collapsedMessages: [] 1017 }); 1018 } else { 1019 Array.prototype.forEach.call(messagesToShow, msg => { 1020 this.setState(prevState => ({ 1021 collapsedMessages: prevState.collapsedMessages.concat(msg.id) 1022 })); 1023 }); 1024 } 1025 } 1026 filterMessages() { 1027 let messages = [...this.state.messages]; 1028 if (this.state.filterProviders.length) { 1029 messages = messages.filter(msg => this.state.filterProviders.includes(msg.provider)); 1030 } 1031 if (this.state.filterGroups.length) { 1032 messages = messages.filter(msg => msg.groups?.some(group => this.state.filterGroups.includes(group)) || !msg.groups?.length && this.state.filterGroups.includes("none")); 1033 } 1034 if (this.state.filterTemplates.length) { 1035 messages = messages.filter(msg => this.state.filterTemplates.includes(msg.template)); 1036 } 1037 return messages; 1038 } 1039 renderMessages() { 1040 if (!this.state.messages) { 1041 return null; 1042 } 1043 const messagesToShow = this.filterMessages(); 1044 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("p", { 1045 className: "helpLink" 1046 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1047 className: "icon icon-small-spacer icon-info" 1048 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("ul", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, "To modify a message, change the JSON and click 'Modify' to see your changes."), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, "Click \"Reset\" to restore the JSON to the original."), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, "Click \"Share\" to copy a link to the clipboard that can be used to preview the message by opening the link in Nightly/local builds."))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1049 className: "button-box" 1050 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1051 className: "small no-margins", 1052 onClick: () => this.toggleAllMessages(messagesToShow) 1053 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1054 className: `icon small icon-small-spacer icon-arrowhead-${this.state.collapsedMessages.length ? "forward" : "down"}` 1055 }), this.state.collapsedMessages.length ? "Expand all" : "Collapse all"), this.state.modifiedMessages.length ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1056 className: "small no-margins messages-reset", 1057 onClick: this.resetAllJSON 1058 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1059 className: "icon small icon-small-spacer icon-undo" 1060 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "Reset all JSON")) : null, this.state.messageBlockList.length ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1061 className: "small no-margins unblock-all", 1062 onClick: this.unblockAll 1063 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "Unblock all")) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1064 className: "small no-margins", 1065 onClick: this.resetMessageState 1066 }, "Reset FxMS state")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1067 className: "messages-list" 1068 }, messagesToShow.map(msg => this.renderMessageItem(msg)))); 1069 } 1070 renderFilters() { 1071 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1072 className: "filters" 1073 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1074 className: "button-box" 1075 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1076 className: "small no-margins", 1077 onClick: () => this.setState(prevState => ({ 1078 filtersCollapsed: !prevState.filtersCollapsed 1079 })) 1080 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1081 className: `icon small icon-small-spacer icon-arrowhead-${this.state.filtersCollapsed ? "forward" : "down"}` 1082 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "Filters")), this.state.filterProviders.length || this.state.filterGroups.length || this.state.filterTemplates.length ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1083 className: "small no-margins", 1084 onClick: this.onClearFilters 1085 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1086 className: "icon small icon-small-spacer icon-dismiss" 1087 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "Clear")) : null), this.state.filtersCollapsed ? null : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1088 className: "row" 1089 }, this.state.messages ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h3", null, "Templates"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1090 className: "col" 1091 }, this.state.messages.map(message => message.template).filter( 1092 // eslint-disable-next-line no-shadow 1093 (value, index, self) => self.indexOf(value) === index).map(template => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("label", { 1094 key: template 1095 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1096 type: "checkbox", 1097 "data-template": template, 1098 checked: this.state.filterTemplates.includes(template), 1099 onChange: this.onChangeFilters 1100 }), template)))) : null, this.state.groups ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h3", null, "Groups"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1101 className: "col" 1102 }, this.state.groups.map(group => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("label", { 1103 key: group.id 1104 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1105 type: "checkbox", 1106 "data-group": group.id, 1107 checked: this.state.filterGroups.includes(group.id), 1108 onChange: this.onChangeFilters 1109 }), group.id)))) : null, this.state.providers ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h3", null, "Providers"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1110 className: "col" 1111 }, this.state.providers.map(provider => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("label", { 1112 key: provider.id 1113 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1114 type: "checkbox", 1115 "data-provider": provider.id, 1116 checked: this.state.filterProviders.includes(provider.id), 1117 onChange: this.onChangeFilters 1118 }), provider.id)))) : null)); 1119 } 1120 renderProviders() { 1121 const providersConfig = this.state.providerPrefs; 1122 const providerInfo = this.state.providers; 1123 const userPrefInfo = this.state.userPrefs; 1124 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("table", { 1125 className: "bordered-table", 1126 id: "providers-table" 1127 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("thead", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1128 className: "fixed-width" 1129 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1130 className: "no-wrap" 1131 }, "Provider"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Source"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1132 className: "no-wrap" 1133 }, "Last Updated"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tbody", null, providersConfig.map((provider, i) => { 1134 const isTestProvider = provider.id.includes("_local_testing"); 1135 const info = providerInfo.find(p => p.id === provider.id) || {}; 1136 const isUserEnabled = provider.id in userPrefInfo ? userPrefInfo[provider.id] : true; 1137 const isSystemEnabled = isTestProvider || provider.enabled; 1138 let label = "local"; 1139 if (provider.type === "remote") { 1140 label = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "endpoint (", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1141 className: "small-text", 1142 target: "_blank", 1143 href: info.url, 1144 rel: "noopener noreferrer" 1145 }, info.url), ")"); 1146 } else if (provider.type === "remote-settings") { 1147 label = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "remote settings (", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1148 className: "small-text", 1149 target: "_blank", 1150 href: `https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/${provider.collection}/records`, 1151 rel: "noopener noreferrer" 1152 }, provider.collection), ")"); 1153 } else if (provider.type === "remote-experiments") { 1154 label = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "remote settings (", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1155 className: "small-text", 1156 target: "_blank", 1157 href: "https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/nimbus-desktop-experiments/records", 1158 rel: "noopener noreferrer" 1159 }, "nimbus-desktop-experiments"), ")"); 1160 } 1161 let reasonsDisabled = []; 1162 if (!isSystemEnabled) { 1163 reasonsDisabled.push("system pref"); 1164 } 1165 if (!isUserEnabled) { 1166 reasonsDisabled.push("user pref"); 1167 } 1168 if (reasonsDisabled.length) { 1169 label = `disabled via ${reasonsDisabled.join(", ")}`; 1170 } 1171 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", { 1172 key: i 1173 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, isTestProvider ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1174 type: "checkbox", 1175 disabled: true, 1176 readOnly: true, 1177 checked: true 1178 }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1179 type: "checkbox", 1180 "data-provider": provider.id, 1181 checked: isUserEnabled && isSystemEnabled, 1182 onChange: this.handleEnabledToggle 1183 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, provider.id), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1184 className: `sourceLabel${isUserEnabled && isSystemEnabled ? "" : " isDisabled"}` 1185 }, label)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1186 className: "no-wrap" 1187 }, info.lastUpdated ? relativeTime(info.lastUpdated) : "")); 1188 }))); 1189 } 1190 renderMessageGroups() { 1191 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("table", { 1192 className: "bordered-table", 1193 id: "groups-table" 1194 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("thead", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1195 className: "fixed-width" 1196 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1197 className: "no-wrap" 1198 }, "Group"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1199 className: "no-wrap" 1200 }, "Impressions"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Frequency caps"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "User preferences"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tbody", null, this.state.groups && this.state.groups.map(({ 1201 id, 1202 enabled, 1203 frequency, 1204 userPreferences = [] 1205 }) => { 1206 let frequencyCaps = []; 1207 if (!frequency) { 1208 frequencyCaps.push("n/a"); 1209 } else { 1210 if (frequency.custom) { 1211 for (let f of frequency.custom) { 1212 let { 1213 period 1214 } = f; 1215 let periodString = ""; 1216 if (period >= 2419200000 && period % 2419200000 < 604800000) { 1217 let months = Math.round(period / 2419200000); 1218 periodString = months === 1 ? "/month" : ` in ${months}mos`; 1219 } else if (period >= 604800000 && period % 604800000 < 86400000) { 1220 let weeks = Math.round(period / 604800000); 1221 periodString = weeks === 1 ? "/week" : ` in ${weeks}wks`; 1222 } else if (period >= 86400000 && period % 86400000 < 3600000) { 1223 let days = Math.round(period / 86400000); 1224 periodString = days === 1 ? "/day" : ` in ${days}d`; 1225 } else { 1226 periodString = ` in ${period}ms`; 1227 } 1228 frequencyCaps.push(`${f.cap}${periodString}`); 1229 } 1230 } 1231 if ("lifetime" in frequency) { 1232 frequencyCaps.push(`${frequency.lifetime}/lifetime`); 1233 } 1234 } 1235 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", { 1236 key: id 1237 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1238 className: "fixed-width" 1239 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1240 type: "checkbox", 1241 checked: enabled, 1242 disabled: true 1243 })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1244 className: "no-wrap" 1245 }, id), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1246 className: "no-wrap" 1247 }, this._getGroupImpressionsCount(id, frequency)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, frequencyCaps.join(", "))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1248 className: "monospace small-text" 1249 }, userPreferences.join(", ")))); 1250 }))); 1251 } 1252 renderTargetingParameters() { 1253 // There was no error and the result is truthy 1254 const success = this.state.evaluationStatus.success && !!this.state.evaluationStatus.result; 1255 const result = JSON.stringify(this.state.evaluationStatus.result, null, 2) || ""; 1256 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("table", { 1257 className: "targeting-table" 1258 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tbody", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1259 colSpan: "2" 1260 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Evaluate JEXL expression"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", { 1261 className: "jexl-evaluator-row" 1262 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1263 colSpan: "2" 1264 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1265 className: "jexl-evaluator" 1266 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1267 className: "jexl-evaluator-textareas" 1268 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1269 className: "jexl-evaluator-input" 1270 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("textarea", { 1271 className: "monospace no-margins", 1272 ref: "expressionInput", 1273 rows: "10", 1274 cols: "60", 1275 placeholder: "Evaluate JEXL expressions and mock parameters by changing their values below", 1276 spellCheck: "false" 1277 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1278 className: "primary no-margins", 1279 onClick: this.handleExpressionEval 1280 }, "Evaluate")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1281 className: "jexl-evaluator-output" 1282 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("textarea", { 1283 className: "monospace no-margins", 1284 readOnly: true, 1285 rows: "10", 1286 cols: "40", 1287 placeholder: "<evaluation result>", 1288 value: result, 1289 spellCheck: "false" 1290 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1291 className: "jexl-status" 1292 }, "Status: ", success ? "✅" : "❌")))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1293 colSpan: "2" 1294 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Modify targeting parameters"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1295 className: "no-margins", 1296 onClick: this.onCopyTargetingParams, 1297 disabled: this.state.copiedToClipboard 1298 }, this.state.copiedToClipboard ? "Parameters copied!" : "Copy parameters"))), this.state.stringTargetingParameters && Object.keys(this.state.stringTargetingParameters).map((param, i) => { 1299 const value = this.state.stringTargetingParameters[param]; 1300 const errorState = this.state.targetingParametersError && this.state.targetingParametersError.id === param; 1301 const largeEditor = value?.length > 30 || value?.match(/[\nR]/); 1302 const className = `monospace no-margins targeting-editor${errorState ? " errorState" : ""}${largeEditor ? " large" : " small"}`; 1303 const inputComp = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("textarea", { 1304 name: param, 1305 className: className, 1306 value: value, 1307 rows: largeEditor ? "10" : "1", 1308 cols: largeEditor ? "60" : "28", 1309 onChange: this.onChangeTargetingParameters, 1310 spellCheck: "false" 1311 }); 1312 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", { 1313 key: i 1314 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, param), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, inputComp)); 1315 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1316 colSpan: "2" 1317 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Attribution parameters"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1318 colSpan: "2" 1319 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("p", null, "This forces the browser to set some attribution parameters, useful for testing the Return To AMO feature. Clicking on 'Force Attribution', with the default values in each field, will demo the Return To AMO flow with the addon called 'uBlock Origin'. If you wish to try different attribution parameters, enter them in the text boxes. If you wish to try a different addon with the Return To AMO flow, make sure the 'content' text box has a string that is 'rta:base64(addonID)', the base64 string of the addonID prefixed with 'rta:'. The addon must currently be a recommended addon on AMO. Then click 'Force Attribution'. Clicking on 'Force Attribution' with blank text boxes reset attribution data."))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Source"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1320 className: "monospace no-margins", 1321 type: "text", 1322 size: "36", 1323 name: "source", 1324 placeholder: "addons.mozilla.org", 1325 value: this.state.attributionParameters.source, 1326 onChange: this.onChangeAttributionParameters 1327 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Medium"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1328 className: "monospace no-margins", 1329 type: "text", 1330 size: "36", 1331 name: "medium", 1332 placeholder: "referral", 1333 value: this.state.attributionParameters.medium, 1334 onChange: this.onChangeAttributionParameters 1335 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Campaign"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1336 className: "monospace no-margins", 1337 type: "text", 1338 size: "36", 1339 name: "campaign", 1340 placeholder: "non-fx-button", 1341 value: this.state.attributionParameters.campaign, 1342 onChange: this.onChangeAttributionParameters 1343 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Content"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1344 className: "monospace no-margins", 1345 type: "text", 1346 size: "36", 1347 name: "content", 1348 placeholder: `rta:${btoa("uBlock0@raymondhill.net")}`, 1349 value: this.state.attributionParameters.content, 1350 onChange: this.onChangeAttributionParameters 1351 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Experiment"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1352 className: "monospace no-margins", 1353 type: "text", 1354 size: "36", 1355 name: "experiment", 1356 placeholder: "ua-onboarding", 1357 value: this.state.attributionParameters.experiment, 1358 onChange: this.onChangeAttributionParameters 1359 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Variation"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1360 className: "monospace no-margins", 1361 type: "text", 1362 size: "36", 1363 name: "variation", 1364 placeholder: "chrome", 1365 value: this.state.attributionParameters.variation, 1366 onChange: this.onChangeAttributionParameters 1367 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "User Agent"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1368 className: "monospace no-margins", 1369 type: "text", 1370 size: "36", 1371 name: "ua", 1372 placeholder: "Google Chrome 123", 1373 value: this.state.attributionParameters.ua, 1374 onChange: this.onChangeAttributionParameters 1375 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, "Download Token"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("input", { 1376 className: "monospace no-margins", 1377 type: "text", 1378 size: "36", 1379 name: "dltoken", 1380 placeholder: "00000000-0000-0000-0000-000000000000", 1381 value: this.state.attributionParameters.dltoken, 1382 onChange: this.onChangeAttributionParameters 1383 }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1384 colSpan: "2" 1385 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1386 className: "primary no-margins", 1387 onClick: this.setAttribution 1388 }, "Force attribution"))))); 1389 } 1390 onChangeAttributionParameters(event) { 1391 const { 1392 name: eventName, 1393 value 1394 } = event.target; 1395 this.setState(({ 1396 attributionParameters 1397 }) => { 1398 const updatedParameters = { 1399 ...attributionParameters 1400 }; 1401 updatedParameters[eventName] = value; 1402 return { 1403 attributionParameters: updatedParameters 1404 }; 1405 }); 1406 } 1407 setAttribution() { 1408 _asrouter_utils_mjs__WEBPACK_IMPORTED_MODULE_0__.ASRouterUtils.sendMessage({ 1409 type: "FORCE_ATTRIBUTION", 1410 data: this.state.attributionParameters 1411 }).then(this.setStateFromParent); 1412 } 1413 _getGroupImpressionsCount(id, frequency) { 1414 if (frequency) { 1415 return this.state.groupImpressions[id] ? this.state.groupImpressions[id].length : 0; 1416 } 1417 return "n/a"; 1418 } 1419 renderErrorMessage({ 1420 id, 1421 errors 1422 }) { 1423 const providerId = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", { 1424 rowSpan: errors.length 1425 }, id); 1426 // .reverse() so that the last error (most recent) is first 1427 return errors.map(({ 1428 error, 1429 timestamp 1430 }, cellKey) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", { 1431 key: cellKey 1432 }, cellKey === errors.length - 1 ? providerId : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, error.message), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("td", null, relativeTime(timestamp)))).reverse(); 1433 } 1434 renderErrors() { 1435 const providersWithErrors = this.state.providers && this.state.providers.filter(p => p.errors && p.errors.length); 1436 if (providersWithErrors && providersWithErrors.length) { 1437 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("table", { 1438 className: "errorReporting" 1439 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("thead", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tr", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("th", null, "Provider"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("th", null, "Message"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("th", null, "Timestamp"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("tbody", null, providersWithErrors.map(this.renderErrorMessage))); 1440 } 1441 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("p", null, "No errors"); 1442 } 1443 renderSection() { 1444 const [section] = this.props.location.routes; 1445 switch (section) { 1446 case "targeting": 1447 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement((react__WEBPACK_IMPORTED_MODULE_1___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Targeting utilities"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1448 className: "button-box" 1449 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1450 className: "no-margins", 1451 onClick: this.expireCache, 1452 title: "Values are cached for some targeting attributes (see ASRouterTargeting). This expires the query cache." 1453 }, "Expire cache")), this.renderTargetingParameters()); 1454 case "impressions": 1455 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement((react__WEBPACK_IMPORTED_MODULE_1___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Impressions"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_ImpressionsSection__WEBPACK_IMPORTED_MODULE_5__.ImpressionsSection, { 1456 messageImpressions: this.state.messageImpressions, 1457 groupImpressions: this.state.groupImpressions, 1458 screenImpressions: this.state.screenImpressions 1459 })); 1460 case "errors": 1461 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement((react__WEBPACK_IMPORTED_MODULE_1___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "ASRouter errors"), this.renderErrors()); 1462 default: 1463 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement((react__WEBPACK_IMPORTED_MODULE_1___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Message providers", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1464 className: "small", 1465 title: "Restore all provider settings that ship with Firefox", 1466 onClick: this.resetPref 1467 }, "Restore default prefs")), this.state.providers ? this.renderProviders() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Message groups", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1468 className: "small", 1469 onClick: this.resetGroupImpressions 1470 }, "Reset group impressions")), this.state.groups ? this.renderMessageGroups() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h2", null, "Messages", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("button", { 1471 className: "small", 1472 onClick: this.resetMessageImpressions 1473 }, "Reset message impressions")), this.renderFilters(), this.renderMessages()); 1474 } 1475 } 1476 render() { 1477 if (!this.state.devtoolsEnabled) { 1478 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1479 className: "asrouter-admin" 1480 }, "You must enable the ASRouter Admin page by setting", " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("code", null, "browser.newtabpage.activity-stream.asrouter.devtoolsEnabled"), " ", "to ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("code", null, "true"), " and then reloading this page."); 1481 } 1482 const [section] = this.props.location.routes; 1483 return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("div", { 1484 className: "asrouter-admin" 1485 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("aside", { 1486 className: "sidebar" 1487 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("ul", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1488 href: "#devtools", 1489 className: "category", 1490 "data-selected": section ? null : "" 1491 }, "General")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1492 href: "#devtools-targeting", 1493 className: "category", 1494 "data-selected": section === "targeting" ? "" : null 1495 }, "Targeting")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1496 href: "#devtools-impressions", 1497 className: "category", 1498 "data-selected": section === "impressions" ? "" : null 1499 }, "Impressions")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("li", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1500 href: "#devtools-errors", 1501 className: "category", 1502 "data-selected": section === "errors" ? "" : null 1503 }, "Errors")))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("main", { 1504 className: "main-panel" 1505 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("h1", null, "ASRouter Admin"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("p", { 1506 className: "helpLink" 1507 }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", { 1508 className: "icon icon-small-spacer icon-info" 1509 }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "Need help using these tools? Check out our", " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", { 1510 target: "blank", 1511 href: "https://firefox-source-docs.mozilla.org/browser/components/asrouter/docs/debugging-docs.html" 1512 }, "documentation"))), this.renderSection())); 1513 } 1514 } 1515 const ASRouterAdmin = props => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_SimpleHashRouter__WEBPACK_IMPORTED_MODULE_3__.SimpleHashRouter, null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(ASRouterAdminInner, props)); 1516 function renderASRouterAdmin() { 1517 react_dom__WEBPACK_IMPORTED_MODULE_2___default().render(/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(ASRouterAdmin, null), document.getElementById("root")); 1518 } 1519 })(); 1520 1521 ASRouterAdminRenderUtils = __webpack_exports__; 1522 /******/ })() 1523 ;