/* eslint-disable no-undef */ export default { name: '<%= options.componentName %>', data: () => ({ adSlot: null, mapping: [], currentSizeMappingIndex: null, windowResizeListenerDebounce: null, isEmpty: true, }), props: { adUnit: { type: String, required: true, }, size: { type: [Array, String], required: true, }, sizeMapping: { type: Array, required: false, default: () => [], }, id: { type: [Number, String], required: false, default: () => Math.random().toString(36).substring(5), }, isResponsive: { type: Boolean, required: false, default: '<%= options.responsive %>' === 'true', }, windowResizeDebounce: { type: Number, required: false, default: 300, }, collapseEmptyDiv: { type: Boolean, required: false, default: null, }, }, computed: { ghostMode() { return this.$config.ads.ghostMode ?? '<%= options.ghostMode %>' === true }, networkCode() { const { $gptAds } = this return $gptAds ? $gptAds.networkCode : null }, adUnitPath() { const { networkCode, adUnit } = this return `/${networkCode}/${adUnit}` }, divId() { const { id } = this return `div-gpt-ad-${id}-0` }, formattedSize() { return this.formatSizeList(this.size) }, style() { if (this.ghostMode) { const { formattedSize, currentSizeMappingIndex, mapping } = this let baseSize = formattedSize if (currentSizeMappingIndex !== null) { baseSize = mapping[currentSizeMappingIndex][1] } const size = Array.isArray(baseSize[0]) ? baseSize[0] : [baseSize[0], baseSize[1]] const [width, height] = size return { margin: '0 auto', width: `${width}px`, height: `${height}px`, border: '1px solid black', } } return null }, }, methods: { /** * Formats a given size to make it compatible with GPT * If size is an Array, it is returned as is * If size is a string, it is formatted so that 123x456 becomes [123, 456] * * @param {Array,string} size The size * @return {Array} Formatted size */ formatSize(size) { if (Array.isArray(size)) { return size } if (typeof size === 'string') { return size.split('x').map((value) => parseInt(value, 10)) } return [] }, /** * Formats a given list of sizes to make it compatible with GPT API * If sizesList is an Array, it is returned as is * If sizesList is a string, it is formatted so that * 123x456,654x321 becomes [[123, 456], [654, 321]] * * @param {Array,string} sizesList The sizes * @return {Array} Formatted sizes list */ formatSizeList(sizesList) { if (Array.isArray(sizesList)) { return sizesList } if (typeof sizesList === 'string') { return sizesList.split(',').map((size) => this.formatSize(size)) } return [] }, /** * Refresh ad slot */ refreshSlot() { console.log('Refreshing slot.') googletag.pubads().refresh([this.adSlot]) }, handleSlotRenderEnded(event) { if (event.slot.getSlotId().getDomId() !== this.divId) { return } this.isEmpty = !!event.isEmpty }, /** * Window resize event listener * Attached only when responsive mode is enabled, it checks wether a different size * mapping can be activated after resize and forces the slot to be refreshed if it's * the case */ handleWindowResize() { const { windowResizeDebounce } = this clearTimeout(this.windowResizeListenerDebounce) this.windowResizeListenerDebounce = setTimeout(() => { const currentSizeMappingIndex = this.getCurrentSizeMappingIndex() if (currentSizeMappingIndex !== this.currentSizeMappingIndex) { if (!this.ghostMode) { this.refreshSlot() } this.currentSizeMappingIndex = currentSizeMappingIndex } }, windowResizeDebounce) }, /** * Gets the current size mapping index * * @return {Number} The current size mapping index */ getCurrentSizeMappingIndex() { const mapping = this.mapping || [] let index = null mapping.some((size, i) => { const [browserSize] = size const [width, height] = browserSize const mediaQuery = `(min-width: ${width}px) and (min-height: ${height}px)` if (window.matchMedia(mediaQuery).matches) { index = i return true } return false }) return index }, }, mounted() { if (!window.googletag) { return } const { adUnitPath, divId, sizeMapping, isResponsive, collapseEmptyDiv } = this // Init Ad slot googletag.cmd.push(() => { const pubadsService = googletag.pubads() pubadsService.addEventListener( 'slotRenderEnded', this.handleSlotRenderEnded ) pubadsService.setTargeting('path', this.$route.path) const adSlot = googletag .defineSlot(adUnitPath, this.formattedSize, divId) .addService(pubadsService) // Collapse empty div slot-level override if (collapseEmptyDiv !== null) { adSlot.setCollapseEmptyDiv(collapseEmptyDiv) } // Build size mapping if any if (sizeMapping.length > 0) { const mapping = googletag.sizeMapping() sizeMapping.forEach((size) => { const browserSize = this.formatSize(size[0]) const adSizes = this.formatSizeList(size[1]) mapping.addSize(browserSize, adSizes) this.mapping.push([browserSize, adSizes]) }) adSlot.defineSizeMapping(mapping.build()) } // Init responsive behavior if (this.sizeMapping.length > 0 && isResponsive) { const currentSizeMappingIndex = this.getCurrentSizeMappingIndex() this.currentSizeMappingIndex = currentSizeMappingIndex window.addEventListener('resize', this.handleWindowResize) } this.adSlot = adSlot this.$gptAds.slots.push(adSlot) if (!this.ghostMode) { googletag.display(divId) if (this.$gptAds.individualRefresh) { this.refreshSlot() } } }) }, beforeDestroy() { console.log('Destroying ad.') if (!googletag) { return } // Destroy ad slot googletag.cmd.push(() => { googletag.destroySlots([this.adSlot]) }) // Remove window resize listener window.removeEventListener('resize', this.handleWindowResize) }, render(h) { const { divId, style, isEmpty } = this const classAttr = isEmpty ? '<%= options.emptyClass %>' : '' return h('div', { style, attrs: { id: divId, class: classAttr, }, domProps: { innerHTML: '' }, }) }, }