├── .babelrc
├── .editorconfig
├── .eslintrc.json
├── .gitattributes
├── .github
└── workflows
│ └── npmpublish.yml
├── .gitignore
├── .npmignore
├── .nvmrc
├── .travis.yml
├── LICENSE
├── Readme.md
├── js
├── adslot.js
├── dfpslotsprovider.js
├── index.js
├── manager.js
└── utils.js
├── karma.conf.js
├── lib
├── adslot.js
├── dfpslotsprovider.js
├── index.js
├── manager.js
└── utils.js
├── package-lock.json
├── package.json
└── spec
├── test-adslot.js
├── test-dfpslotsprovider.js
└── test-manager.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/env", "@babel/react"],
3 | "plugins": ["@babel/plugin-proposal-class-properties"]
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "plugins": [
4 | "babel",
5 | "react"
6 | ],
7 | "globals": {
8 | "googletag": true,
9 | "describe": true,
10 | "expect": true,
11 | "it": true,
12 | "beforeAll": true,
13 | "afterAll": true,
14 | "beforeEach": true,
15 | "afterEach": true,
16 | "window": true,
17 | "document": true
18 | },
19 | "parser": "babel-eslint",
20 | "parserOptions": {
21 | "ecmaVersion": 6
22 | },
23 | "rules": {
24 | "prefer-arrow-callback": 0,
25 | "react/jsx-filename-extension": 0,
26 | "react/no-unused-prop-types": 0,
27 | "react/require-default-props": 0,
28 | "react/forbid-prop-types": 0,
29 | "import/prefer-default-export": 0,
30 | "class-methods-use-this": 0,
31 | "no-plusplus": 0
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Package
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v1
13 | - uses: actions/setup-node@v1
14 | with:
15 | node-version: 8.12
16 | - run: npm install
17 | - run: npm run dist
18 |
19 | publish-npm:
20 | needs: build
21 | runs-on: ubuntu-latest
22 | steps:
23 | - uses: actions/checkout@v1
24 | - uses: actions/setup-node@v1
25 | with:
26 | node-version: 8.12
27 | registry-url: https://registry.npmjs.org/
28 | - run: npm publish
29 | env:
30 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
31 |
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Git ignore for NGS projects
2 |
3 | # Ignore hidden folders #
4 | # This takes care of .tmp, .sass-cache, and many others #
5 | .*/
6 |
7 | # Ignore OS generated files #
8 | .DS_Store*
9 | ehthumbs.db
10 | Icon?
11 | Thumbs.db
12 |
13 | # Ignore packages #
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Ignore all log files
24 | *.log
25 |
26 | dist
27 | node_modules
28 | bower_components
29 | .tmp
30 |
31 | # ignore test coverage results
32 | coverage
33 |
34 | # Ignore Compass Cache
35 | .sass-cache
36 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | examples/
2 | spec/
3 | karma.conf.js
4 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.12
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | script:
3 | - nvm use
4 | - npm install
5 | - npm run eslint
6 | - npm run dist
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 Jonatan Alexis Anauati
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # React DFP · [](https://travis-ci.org/jaanauati/react-dfp) [](https://github.com/jaanauati/react-dfp) [](https://github.com/jaanauati/react-dfp) [](https://github.com/jaanauati/react-dfp/blob/master/LICENSE) [](https://github.com/jaanauati/react-dfp/blob/master/LICENSE)
2 |
3 | A React implementation of the google [DFP](https://developers.google.com/doubleclick-gpt/reference "GPT Reference") API. This package is inspired in the awesome library [jquery.dfp](https://github.com/coop182/jquery.dfp.js), and aims to provide its same ease of usage but, of course, taking into consideration the react concepts & lifecycle features.
4 |
5 | ## Installation:
6 |
7 | To install just run the following command (no other dependencies are required):
8 |
9 | ```bash
10 | npm install --save react-dfp
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn add react-dfp
17 | ```
18 |
19 | You can find more details in the [React-dfp site](https://react-dfp.surge.sh/).
20 |
21 | ## Getting started
22 |
23 | React-dfp has been designed in a very React-ish way, its main goal is to be able to serve DFP ads in your React application just using React components, which means that you won't have to programmatically perform any initialization call when your page loads.
24 |
25 | Here a quick example, notice that ads will render in your page as soon as your component is mounted, through its components (DFPSlotsProvider and AdSlot), react-dfp is making a full abstraction of the google dfp/gpt API.
26 |
27 | ```javascript
28 | import React, { Component } from 'react';
29 | import { DFPSlotsProvider, AdSlot } from 'react-dfp';
30 | ...
31 | class Page extends Component {
32 | render() {
33 | ...
34 |
35 | return (
36 |
37 | ...
38 |
39 | ...
40 | /* you can override the props */
41 |
42 | ...
43 |
44 | ...
45 |
46 | ...
47 |
48 | ...
49 |
50 | );
51 | }
52 | }
53 | ```
54 |
55 | ## Examples
56 |
57 | See the [React-dfp site](https://react-dfp.surge.sh) for more examples (basic example, how to have refreshable ads, etc).
58 |
59 | ## Documentation
60 |
61 | You can find the React-dfp documentation [on the website](https://react-dfp.surge.sh). The site is divided into many sections that describe each one the Components, properties and also the DFPManager API (in case you need to use this one manually).
62 |
63 | The website is also full of live/working examples.
64 |
65 | You can find the source code of the website here: https://github.com/jaanauati/react-dfp-website.
66 |
67 | ## Wanna help?
68 |
69 | I certainly know that test cases need to be improved, but, as long as your syntax is clean, submit test cases and, of course, all the interfaces are kept working, all kind of contribution is welcome.
70 |
71 | ## Complaints.
72 |
73 | Pull requests are welcome 🍻.
74 |
--------------------------------------------------------------------------------
/js/adslot.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import DFPManager from './manager';
4 | import { Context } from './dfpslotsprovider';
5 |
6 | let dynamicAdCount = 0;
7 |
8 | export class AdSlot extends React.Component {
9 | static propTypes = {
10 | dfpNetworkId: PropTypes.string,
11 | adUnit: PropTypes.string,
12 | sizes: PropTypes.arrayOf(
13 | PropTypes.oneOfType([
14 | PropTypes.arrayOf(PropTypes.number),
15 | PropTypes.string,
16 | ]),
17 | ),
18 | renderOutOfThePage: PropTypes.bool,
19 | sizeMapping: PropTypes.arrayOf(PropTypes.object),
20 | fetchNow: PropTypes.bool,
21 | adSenseAttributes: PropTypes.object,
22 | targetingArguments: PropTypes.object,
23 | onSlotRender: PropTypes.func,
24 | onSlotRegister: PropTypes.func,
25 | onSlotIsViewable: PropTypes.func,
26 | onSlotVisibilityChanged: PropTypes.func,
27 | shouldRefresh: PropTypes.func,
28 | slotId: PropTypes.string,
29 | className: PropTypes.string,
30 | };
31 |
32 | static defaultProps = {
33 | fetchNow: false,
34 | };
35 |
36 | constructor(props) {
37 | super(props);
38 | this.doRegisterSlot = this.doRegisterSlot.bind(this);
39 | this.generateSlotId = this.generateSlotId.bind(this);
40 | this.getSlotId = this.getSlotId.bind(this);
41 | this.mapContextToAdSlotProps = this.mapContextToAdSlotProps.bind(this);
42 | this.slotShouldRefresh = this.slotShouldRefresh.bind(this);
43 | this.slotRenderEnded = this.slotRenderEnded.bind(this);
44 | this.slotRegisterCallback = this.slotRegisterCallback.bind(this);
45 | this.slotIsViewable = this.slotIsViewable.bind(this);
46 | this.slotVisibilityChanged = this.slotVisibilityChanged.bind(this);
47 | this.getClasses = this.getClasses.bind(this);
48 | this.state = {
49 | slotId: this.props.slotId || null,
50 | className: this.props.className || '',
51 | };
52 | this.adElementRef = React.createRef ? React.createRef() : (element) => {
53 | this.adElementRef = element;
54 | };
55 | }
56 |
57 | componentDidMount() {
58 | // register this ad-unit in the , when available.
59 | if (this.context !== undefined && this.context.newSlotCallback) {
60 | this.context.newSlotCallback();
61 | }
62 | this.registerSlot();
63 | }
64 |
65 | componentWillUnmount() {
66 | this.unregisterSlot();
67 | }
68 |
69 | getSlotId() {
70 | return this.props.slotId || this.state.slotId;
71 | }
72 |
73 | getClasses() {
74 | const baseClass = 'adunitContainer';
75 | const extraClasses = this.state.className.split(' ');
76 | extraClasses.push(baseClass);
77 | return extraClasses;
78 | }
79 |
80 | generateSlotId() {
81 | return `adSlot-${dynamicAdCount++}`;
82 | }
83 |
84 | mapContextToAdSlotProps() {
85 | const context = this.context;
86 | const mappedProps = {};
87 | if (context.dfpNetworkId !== undefined) {
88 | mappedProps.dfpNetworkId = context.dfpNetworkId;
89 | }
90 | if (context.dfpAdUnit !== undefined) {
91 | mappedProps.adUnit = context.dfpAdUnit;
92 | }
93 | if (context.dfpSizeMapping !== undefined) {
94 | mappedProps.sizeMapping = context.dfpSizeMapping;
95 | }
96 | if (context.dfpTargetingArguments !== undefined) {
97 | mappedProps.targetingArguments = context.dfpTargetingArguments;
98 | }
99 | return mappedProps;
100 | }
101 |
102 | doRegisterSlot() {
103 | DFPManager.registerSlot({
104 | ...this.mapContextToAdSlotProps(),
105 | ...this.props,
106 | ...this.state,
107 | slotShouldRefresh: this.slotShouldRefresh,
108 | });
109 | if (this.props.fetchNow === true) {
110 | DFPManager.load(this.getSlotId());
111 | }
112 | DFPManager.attachSlotRenderEnded(this.slotRenderEnded);
113 | DFPManager.attachSlotIsViewable(this.slotIsViewable);
114 | DFPManager.attachSlotVisibilityChanged(this.slotVisibilityChanged);
115 |
116 | this.slotRegisterCallback();
117 | }
118 |
119 | registerSlot() {
120 | if (this.state.slotId === null) {
121 | this.setState({
122 | slotId: this.generateSlotId(),
123 | }, this.doRegisterSlot);
124 | } else {
125 | this.doRegisterSlot();
126 | }
127 | }
128 |
129 | unregisterSlot() {
130 | DFPManager.unregisterSlot({
131 | ...this.mapContextToAdSlotProps(),
132 | ...this.props,
133 | ...this.state,
134 | });
135 | DFPManager.detachSlotRenderEnded(this.slotRenderEnded);
136 | DFPManager.detachSlotIsViewable(this.slotIsViewable);
137 | DFPManager.detachSlotVisibilityChanged(this.slotVisibilityChanged);
138 | }
139 |
140 | slotRenderEnded(eventData) {
141 | if (eventData.slotId === this.getSlotId()) {
142 | if (this.props.onSlotRender !== undefined) {
143 | // now that slot has rendered we have access to the ref
144 | const params = {
145 | ...eventData,
146 | adElementRef: this.adElementRef,
147 | };
148 | this.props.onSlotRender(params);
149 | }
150 | }
151 | }
152 |
153 | slotRegisterCallback() {
154 | if (typeof this.props.onSlotRegister === 'function') {
155 | this.props.onSlotRegister({
156 | slotId: this.getSlotId(),
157 | sizes: this.props.sizes,
158 | slotCount: dynamicAdCount,
159 | adElementRef: this.adElementRef,
160 | });
161 | }
162 | }
163 |
164 | slotIsViewable(eventData) {
165 | if (eventData.slotId === this.getSlotId()) {
166 | if (this.props.onSlotIsViewable !== undefined) {
167 | this.props.onSlotIsViewable(eventData);
168 | }
169 | }
170 | }
171 |
172 | slotVisibilityChanged(eventData) {
173 | if (eventData.slotId === this.getSlotId()) {
174 | if (this.props.onSlotVisibilityChanged !== undefined) {
175 | this.props.onSlotVisibilityChanged(eventData);
176 | }
177 | }
178 | }
179 |
180 | slotShouldRefresh() {
181 | let r = true;
182 | if (this.props.shouldRefresh !== undefined) {
183 | r = this.props.shouldRefresh({
184 | ...this.mapContextToAdSlotProps(),
185 | ...this.props,
186 | slotId: this.getSlotId(),
187 | });
188 | }
189 | return r;
190 | }
191 |
192 | render() {
193 | const { slotId } = this.state;
194 | const props = { className: 'adBox' };
195 | if (slotId !== null) {
196 | props.id = slotId;
197 | }
198 |
199 | return (
200 |
203 | );
204 | }
205 | }
206 |
207 | if (Context === null) {
208 | // React < 16.3
209 | AdSlot.contextTypes = {
210 | dfpNetworkId: PropTypes.string,
211 | dfpAdUnit: PropTypes.string,
212 | dfpSizeMapping: PropTypes.arrayOf(PropTypes.object),
213 | dfpTargetingArguments: PropTypes.object,
214 | newSlotCallback: PropTypes.func,
215 | };
216 | } else {
217 | AdSlot.contextType = Context;
218 | }
219 |
220 |
221 | export default AdSlot;
222 |
--------------------------------------------------------------------------------
/js/dfpslotsprovider.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import DFPManager from './manager';
4 |
5 | // React.createContext is undefined for React < 16.3
6 | export const Context = React.createContext ? React.createContext({
7 | dfpNetworkId: null,
8 | dfpAdUnit: null,
9 | dfpSizeMapping: null,
10 | dfpTargetingArguments: null,
11 | newSlotCallback: null,
12 | }) : null;
13 |
14 | export default class DFPSlotsProvider extends React.Component {
15 | static propTypes = {
16 | children: PropTypes.oneOfType([
17 | PropTypes.element,
18 | PropTypes.array,
19 | ]).isRequired,
20 | autoLoad: PropTypes.bool,
21 | autoReload: PropTypes.shape({
22 | dfpNetworkId: PropTypes.bool,
23 | personalizedAds: PropTypes.bool,
24 | cookieOption: PropTypes.bool,
25 | singleRequest: PropTypes.bool,
26 | disableInitialLoad: PropTypes.bool,
27 | adUnit: PropTypes.bool,
28 | sizeMapping: PropTypes.bool,
29 | adSenseAttributes: PropTypes.bool,
30 | targetingArguments: PropTypes.bool,
31 | collapseEmptyDivs: PropTypes.bool,
32 | lazyLoad: PropTypes.bool,
33 | }),
34 | dfpNetworkId: PropTypes.string.isRequired,
35 | personalizedAds: PropTypes.bool,
36 | cookieOption: PropTypes.bool,
37 | singleRequest: PropTypes.bool,
38 | disableInitialLoad: PropTypes.bool,
39 | adUnit: PropTypes.string,
40 | sizeMapping: PropTypes.arrayOf(PropTypes.object),
41 | adSenseAttributes: PropTypes.object,
42 | targetingArguments: PropTypes.object,
43 | collapseEmptyDivs: PropTypes.oneOfType([
44 | PropTypes.bool,
45 | PropTypes.object,
46 | ]),
47 | adSenseAttrs: PropTypes.object,
48 | lazyLoad: PropTypes.oneOfType([
49 | PropTypes.bool,
50 | PropTypes.shape({
51 | fetchMarginPercent: PropTypes.number,
52 | renderMarginPercent: PropTypes.number,
53 | mobileScaling: PropTypes.number,
54 | }),
55 | ]),
56 | limitedAds: PropTypes.bool,
57 | };
58 |
59 | static defaultProps = {
60 | autoLoad: true,
61 | autoReload: {
62 | dfpNetworkId: false,
63 | personalizedAds: false,
64 | cookieOption: false,
65 | singleRequest: false,
66 | disableInitialLoad: false,
67 | adUnit: false,
68 | sizeMapping: false,
69 | adSenseAttributes: false,
70 | targetingArguments: false,
71 | collapseEmptyDivs: false,
72 | lazyLoad: false,
73 | },
74 | personalizedAds: true,
75 | cookieOption: true,
76 | singleRequest: true,
77 | disableInitialLoad: false,
78 | collapseEmptyDivs: null,
79 | lazyLoad: false,
80 | limitedAds: false,
81 | };
82 |
83 | constructor(props) {
84 | super(props);
85 | this.loadAdsIfPossible = this.loadAdsIfPossible.bind(this);
86 | this.newSlotCallback = this.newSlotCallback.bind(this);
87 | this.applyConfigs = this.applyConfigs.bind(this);
88 | this.shouldReloadConfig = this.shouldReloadConfig.bind(this);
89 | this.attachLoadCallback = this.attachLoadCallback.bind(this);
90 | this.getContextValue = this.getContextValue.bind(this);
91 | this.loadAlreadyCalled = false;
92 | this.loadCallbackAttached = false;
93 | this.shouldReloadAds = false;
94 | this.totalSlots = 0;
95 | this.contextValue = {};
96 | if (Context === null) {
97 | this.getChildContext = () => this.getContextValue();
98 | }
99 | }
100 |
101 | componentDidMount() {
102 | this.applyConfigs();
103 | if (this.props.autoLoad && !this.loadAdsIfPossible()) {
104 | this.attachLoadCallback();
105 | }
106 | }
107 |
108 | shouldComponentUpdate(nextProps) {
109 | this.shouldReloadAds = this.shouldReloadConfig(nextProps);
110 | if (nextProps.children !== this.props.children) {
111 | return true;
112 | }
113 | if (nextProps.autoLoad && !this.props.autoLoad) {
114 | return true;
115 | }
116 | return this.shouldReloadAds;
117 | }
118 |
119 | componentDidUpdate() {
120 | this.applyConfigs();
121 | if (this.props.autoLoad) {
122 | if (this.loadAlreadyCalled) {
123 | if (this.shouldReloadAds) {
124 | DFPManager.reload();
125 | }
126 | } else if (!this.loadAdsIfPossible()) {
127 | this.attachLoadCallback();
128 | }
129 | }
130 | this.shouldReloadAds = false;
131 | }
132 |
133 | getContextValue() {
134 | const {
135 | props: {
136 | dfpNetworkId,
137 | adUnit: dfpAdUnit,
138 | sizeMapping: dfpSizeMapping,
139 | targetingArguments: dfpTargetingArguments,
140 | },
141 | contextValue: {
142 | dfpNetworkId: ctxDfpNetworkId,
143 | adUnit: ctxDfpAdUnit,
144 | sizeMapping: ctxDfpSizeMapping,
145 | targetingArguments: ctxDfpTargetingArguments,
146 | },
147 | } = this;
148 | // performance: update context value object only when any of its
149 | // props is updated.
150 | if (
151 | dfpNetworkId !== ctxDfpNetworkId ||
152 | dfpAdUnit !== ctxDfpAdUnit ||
153 | dfpSizeMapping !== ctxDfpSizeMapping ||
154 | dfpTargetingArguments !== ctxDfpTargetingArguments
155 | ) {
156 | this.contextValue = {
157 | dfpNetworkId,
158 | dfpAdUnit,
159 | dfpSizeMapping,
160 | dfpTargetingArguments,
161 | newSlotCallback: this.newSlotCallback,
162 | };
163 | }
164 | return this.contextValue;
165 | }
166 |
167 | applyConfigs() {
168 | DFPManager.configurePersonalizedAds(this.props.personalizedAds);
169 | DFPManager.configureCookieOption(this.props.cookieOption);
170 | DFPManager.configureSingleRequest(this.props.singleRequest);
171 | DFPManager.configureDisableInitialLoad(this.props.disableInitialLoad);
172 | DFPManager.configureLazyLoad(
173 | !!this.props.lazyLoad,
174 | typeof this.props.lazyLoad === 'boolean' ? null : this.props.lazyLoad,
175 | );
176 | DFPManager.setAdSenseAttributes(this.props.adSenseAttributes);
177 | DFPManager.setCollapseEmptyDivs(this.props.collapseEmptyDivs);
178 | DFPManager.configureLimitedAds(this.props.limitedAds);
179 | }
180 |
181 | attachLoadCallback() {
182 | if (this.loadCallbackAttached === false) {
183 | DFPManager.on('slotRegistered', this.loadAdsIfPossible);
184 | this.loadCallbackAttached = true;
185 | return true;
186 | }
187 | return false;
188 | }
189 |
190 | // pretty strait-forward interface that children ad slots use to register
191 | // with their DFPSlotProvider parent node.
192 | newSlotCallback() {
193 | this.totalSlots++;
194 | }
195 |
196 | // Checks all the mounted children ads have been already registered
197 | // in the DFPManager before trying to call the gpt load scripts.
198 | // This is helpful when trying to fetch ads with a single request.
199 | loadAdsIfPossible() {
200 | let r = false;
201 | if (Object.keys(DFPManager.getRegisteredSlots()).length >= this.totalSlots) {
202 | DFPManager.removeListener('slotRegistered', this.loadAdsIfPossible);
203 | DFPManager.load();
204 | this.loadAlreadyCalled = true;
205 | this.loadCallbackAttached = false;
206 | r = true;
207 | }
208 | return r;
209 | }
210 |
211 | shouldReloadConfig(nextProps) {
212 | const reloadConfig = nextProps.autoReload || this.props.autoReload;
213 | if (this.props.autoLoad || nextProps.autoLoad) {
214 | if (typeof reloadConfig === 'object') {
215 | const attrs = Object.keys(reloadConfig);
216 | // eslint-disable-next-line guard-for-in, no-restricted-syntax
217 | for (const i in attrs) {
218 | const propName = attrs[i];
219 | // eslint-disable-next-line
220 | if (reloadConfig[propName] === true && this.props[propName] !== nextProps[propName]) {
221 | return true;
222 | }
223 | }
224 | }
225 | }
226 | return false;
227 | }
228 |
229 | render() {
230 | const { children } = this.props;
231 | if (Context === null) {
232 | return children;
233 | }
234 | return (
235 |
236 | {children}
237 |
238 | );
239 | }
240 | }
241 |
242 | if (Context === null) {
243 | // React < 16.3
244 | DFPSlotsProvider.childContextTypes = {
245 | dfpNetworkId: PropTypes.string,
246 | dfpAdUnit: PropTypes.string,
247 | dfpSizeMapping: PropTypes.arrayOf(PropTypes.object),
248 | dfpTargetingArguments: PropTypes.object,
249 | newSlotCallback: PropTypes.func,
250 | };
251 | }
252 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | import manager from './manager';
2 | import slot from './adslot';
3 | import provider from './dfpslotsprovider';
4 |
5 | export const DFPManager = manager;
6 | export const AdSlot = slot;
7 | export const DFPSlotsProvider = provider;
8 |
--------------------------------------------------------------------------------
/js/manager.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';
2 | import * as Utils from './utils';
3 |
4 | let loadPromise = null;
5 | let googleGPTScriptLoadPromise = null;
6 | let singleRequestEnabled = true;
7 | let disableInitialLoadEnabled = false;
8 | let lazyLoadEnabled = false;
9 | let lazyLoadConfig = null;
10 | let servePersonalizedAds = true;
11 | let serveCookies = true;
12 | const registeredSlots = {};
13 | let managerAlreadyInitialized = false;
14 | const globalTargetingArguments = {};
15 | const globalAdSenseAttributes = {};
16 | let limitedAds = false;
17 |
18 | const DFPManager = Object.assign(new EventEmitter().setMaxListeners(0), {
19 |
20 | singleRequestIsEnabled() {
21 | return singleRequestEnabled;
22 | },
23 |
24 | configureSingleRequest(value) {
25 | singleRequestEnabled = !!value;
26 | },
27 |
28 | disableInitialLoadIsEnabled() {
29 | return disableInitialLoadEnabled;
30 | },
31 |
32 | configureDisableInitialLoad(value) {
33 | disableInitialLoadEnabled = !!value;
34 | },
35 |
36 | configureLazyLoad(enable = true, config = null) {
37 | let conf = null;
38 | if (config !== null && typeof config === 'object') {
39 | conf = { ...config };
40 | }
41 | lazyLoadEnabled = !!enable;
42 | lazyLoadConfig = conf;
43 | },
44 |
45 | lazyLoadIsEnabled() {
46 | return lazyLoadEnabled;
47 | },
48 |
49 | limitedAdsIsEnabled() {
50 | return limitedAds;
51 | },
52 |
53 | configureLimitedAds(value) {
54 | limitedAds = !!value;
55 | },
56 |
57 | getLazyLoadConfig() {
58 | return lazyLoadConfig;
59 | },
60 |
61 | getAdSenseAttribute(key) {
62 | return globalAdSenseAttributes[key];
63 | },
64 |
65 | configurePersonalizedAds(value) {
66 | servePersonalizedAds = value;
67 | },
68 |
69 | configureCookieOption(value) {
70 | serveCookies = value;
71 | },
72 |
73 | personalizedAdsEnabled() {
74 | return servePersonalizedAds;
75 | },
76 |
77 | cookiesEnabled() {
78 | return serveCookies;
79 | },
80 |
81 | setAdSenseAttribute(key, value) {
82 | this.setAdSenseAttributes({ [key]: value });
83 | },
84 |
85 | getAdSenseAttributes() {
86 | return { ...globalAdSenseAttributes };
87 | },
88 |
89 | setAdSenseAttributes(attrs) {
90 | Object.assign(globalAdSenseAttributes, attrs);
91 | if (managerAlreadyInitialized === true) {
92 | this.getGoogletag().then((googletag) => {
93 | googletag.cmd.push(() => {
94 | const pubadsService = googletag.pubads();
95 | Object.keys(globalAdSenseAttributes).forEach(
96 | (key) => {
97 | pubadsService.set(key, globalTargetingArguments[key]);
98 | },
99 | );
100 | });
101 | });
102 | }
103 | },
104 |
105 | setTargetingArguments(data) {
106 | Object.assign(globalTargetingArguments, data);
107 | const availableKeys = Object.keys(registeredSlots);
108 | availableKeys.forEach((slotId) => {
109 | registeredSlots[slotId].targetingArguments = data;
110 | });
111 | if (managerAlreadyInitialized === true) {
112 | this.getGoogletag().then((googletag) => {
113 | googletag.cmd.push(() => {
114 | const pubadsService = googletag.pubads();
115 | Object.keys(globalTargetingArguments).forEach((varName) => {
116 | if (pubadsService) {
117 | pubadsService.setTargeting(varName, globalTargetingArguments[varName]);
118 | }
119 | });
120 | });
121 | });
122 | }
123 | },
124 |
125 | getTargetingArguments() {
126 | return { ...globalTargetingArguments };
127 | },
128 |
129 | getSlotProperty(slotId, propName) {
130 | const slot = this.getRegisteredSlots()[slotId];
131 | let ret = null;
132 | if (slot !== undefined) {
133 | ret = slot[propName] || ret;
134 | }
135 | return ret;
136 | },
137 |
138 | getSlotTargetingArguments(slotId) {
139 | const propValue = this.getSlotProperty(slotId, 'targetingArguments');
140 | return propValue ? { ...propValue } : null;
141 | },
142 |
143 | getSlotAdSenseAttributes(slotId) {
144 | const propValue = this.getSlotProperty(slotId, 'adSenseAttributes');
145 | return propValue ? { ...propValue } : null;
146 | },
147 |
148 | init() {
149 | if (managerAlreadyInitialized === false) {
150 | managerAlreadyInitialized = true;
151 | this.getGoogletag().then((googletag) => {
152 | googletag.cmd.push(() => {
153 | const pubadsService = googletag.pubads();
154 | pubadsService.addEventListener('slotRenderEnded', (event) => {
155 | const slotId = event.slot.getSlotElementId();
156 | this.emit('slotRenderEnded', { slotId, event });
157 | });
158 | pubadsService.addEventListener('impressionViewable', (event) => {
159 | const slotId = event.slot.getSlotElementId();
160 | this.emit('impressionViewable', { slotId, event });
161 | });
162 | pubadsService.addEventListener('slotVisibilityChanged', (event) => {
163 | const slotId = event.slot.getSlotElementId();
164 | this.emit('slotVisibilityChanged', { slotId, event });
165 | });
166 | pubadsService.setRequestNonPersonalizedAds(
167 | this.personalizedAdsEnabled() ? 0 : 1,
168 | );
169 | pubadsService.setCookieOptions(
170 | this.cookiesEnabled() ? 0 : 1,
171 | );
172 | });
173 | });
174 | }
175 | },
176 |
177 | getGoogletag() {
178 | if (googleGPTScriptLoadPromise === null) {
179 | googleGPTScriptLoadPromise = Utils.loadGPTScript(limitedAds);
180 | }
181 | return googleGPTScriptLoadPromise;
182 | },
183 |
184 | setCollapseEmptyDivs(collapse) {
185 | this.collapseEmptyDivs = collapse;
186 | },
187 |
188 | load(...slots) {
189 | if (loadPromise === null) {
190 | loadPromise = this.doLoad(...slots);
191 | } else {
192 | loadPromise = loadPromise.then(
193 | () => this.doLoad(...slots),
194 | );
195 | }
196 | },
197 |
198 | doLoad(...slots) {
199 | this.init();
200 | let availableSlots = {};
201 |
202 | if (slots.length > 0) {
203 | availableSlots = slots.filter(
204 | slotId => Object.prototype.hasOwnProperty.call(registeredSlots, slotId),
205 | );
206 | } else {
207 | availableSlots = Object.keys(registeredSlots);
208 | }
209 | availableSlots = availableSlots.filter(
210 | id => !registeredSlots[id].loading && !registeredSlots[id].gptSlot,
211 | );
212 | availableSlots.forEach((slotId) => {
213 | registeredSlots[slotId].loading = true;
214 | });
215 | return this.gptLoadAds(availableSlots);
216 | },
217 |
218 | gptLoadAds(slotsToInitialize) {
219 | return new Promise((resolve) => {
220 | this.getGoogletag().then((googletag) => {
221 | this.configureInitialOptions(googletag);
222 | slotsToInitialize.forEach((currentSlotId) => {
223 | registeredSlots[currentSlotId].loading = false;
224 |
225 | googletag.cmd.push(() => {
226 | const slot = registeredSlots[currentSlotId];
227 | let gptSlot;
228 | const adUnit = `${slot.dfpNetworkId}/${slot.adUnit}`;
229 | if (slot.renderOutOfThePage === true) {
230 | gptSlot = googletag.defineOutOfPageSlot(adUnit, currentSlotId);
231 | } else {
232 | gptSlot = googletag.defineSlot(adUnit, slot.sizes, currentSlotId);
233 | }
234 | if (gptSlot !== null) {
235 | slot.gptSlot = gptSlot;
236 | const slotTargetingArguments = this.getSlotTargetingArguments(currentSlotId);
237 | if (slotTargetingArguments !== null) {
238 | Object.keys(slotTargetingArguments).forEach((varName) => {
239 | if (slot && slot.gptSlot) {
240 | slot.gptSlot.setTargeting(varName, slotTargetingArguments[varName]);
241 | }
242 | });
243 | }
244 | const slotAdSenseAttributes = this.getSlotAdSenseAttributes(currentSlotId);
245 | if (slotAdSenseAttributes !== null) {
246 | Object.keys(slotAdSenseAttributes).forEach((varName) => {
247 | slot.gptSlot.set(varName, slotAdSenseAttributes[varName]);
248 | });
249 | }
250 | slot.gptSlot.addService(googletag.pubads());
251 | if (slot.sizeMapping) {
252 | let smbuilder = googletag.sizeMapping();
253 | slot.sizeMapping.forEach((mapping) => {
254 | smbuilder = smbuilder.addSize(mapping.viewport, mapping.sizes);
255 | });
256 | slot.gptSlot.defineSizeMapping(smbuilder.build());
257 | }
258 | }
259 | });
260 | });
261 | this.configureOptions(googletag);
262 | googletag.cmd.push(() => {
263 | googletag.enableServices();
264 | slotsToInitialize.forEach((theSlotId) => {
265 | googletag.display(theSlotId);
266 | });
267 | resolve();
268 | });
269 | });
270 | });
271 | },
272 |
273 | // configure those gpt parameters that need to be set before pubsads service
274 | // initialization.
275 | configureInitialOptions(googletag) {
276 | googletag.cmd.push(() => {
277 | if (this.disableInitialLoadIsEnabled()) {
278 | googletag.pubads().disableInitialLoad();
279 | }
280 | });
281 | },
282 |
283 | configureOptions(googletag) {
284 | googletag.cmd.push(() => {
285 | const pubadsService = googletag.pubads();
286 | pubadsService.setRequestNonPersonalizedAds(
287 | this.personalizedAdsEnabled() ? 0 : 1,
288 | );
289 | pubadsService.setCookieOptions(
290 | this.cookiesEnabled() ? 0 : 1,
291 | );
292 | const targetingArguments = this.getTargetingArguments();
293 | // set global targetting arguments
294 | Object.keys(targetingArguments).forEach((varName) => {
295 | if (pubadsService) {
296 | pubadsService.setTargeting(varName, targetingArguments[varName]);
297 | }
298 | });
299 | // set global adSense attributes
300 | const adSenseAttributes = this.getAdSenseAttributes();
301 | Object.keys(adSenseAttributes).forEach((key) => {
302 | pubadsService.set(key, adSenseAttributes[key]);
303 | });
304 | if (this.lazyLoadIsEnabled()) {
305 | const config = this.getLazyLoadConfig();
306 | if (config) {
307 | pubadsService.enableLazyLoad(config);
308 | } else {
309 | pubadsService.enableLazyLoad();
310 | }
311 | }
312 | if (this.singleRequestIsEnabled()) {
313 | pubadsService.enableSingleRequest();
314 | }
315 | if (this.collapseEmptyDivs === true || this.collapseEmptyDivs === false) {
316 | pubadsService.collapseEmptyDivs(this.collapseEmptyDivs);
317 | }
318 | });
319 | },
320 |
321 | getRefreshableSlots(...slotsArray) {
322 | const slots = {};
323 | if (slotsArray.length === 0) {
324 | const slotsToRefresh = Object.keys(registeredSlots)
325 | .map(k => registeredSlots[k]);
326 | return slotsToRefresh.reduce((last, slot) => {
327 | if (slot.slotShouldRefresh() === true) {
328 | slots[slot.slotId] = slot;
329 | }
330 | return slots;
331 | }, slots);
332 | }
333 | return slotsArray.reduce((last, slotId) => {
334 | const slot = registeredSlots[slotId];
335 | if (typeof slot !== 'undefined') {
336 | slots[slotId] = slot;
337 | }
338 | return slots;
339 | }, slots);
340 | },
341 |
342 | refresh(...slots) {
343 | if (loadPromise === null) {
344 | this.load();
345 | } else {
346 | loadPromise.then(() => {
347 | this.gptRefreshAds(
348 | Object.keys(
349 | this.getRefreshableSlots(...slots),
350 | ),
351 | );
352 | });
353 | }
354 | },
355 |
356 | gptRefreshAds(slots) {
357 | return this.getGoogletag().then((googletag) => {
358 | this.configureOptions(googletag);
359 | googletag.cmd.push(() => {
360 | const pubadsService = googletag.pubads();
361 | const slotsToRefreshArray = slots.map(slotId => registeredSlots[slotId].slotId);
362 | pubadsService.refresh(slotsToRefreshArray);
363 | });
364 | });
365 | },
366 |
367 | reload(...slots) {
368 | return this.destroyGPTSlots(...slots).then(() => this.load());
369 | },
370 |
371 | destroyGPTSlots(...slotsToDestroy) {
372 | if (slotsToDestroy.length === 0) {
373 | // eslint-disable-next-line no-param-reassign
374 | slotsToDestroy = Object.keys(registeredSlots);
375 | }
376 | return new Promise((resolve) => {
377 | const slots = [];
378 | // eslint-disable-next-line guard-for-in,no-restricted-syntax
379 | for (const idx in slotsToDestroy) {
380 | const slotId = slotsToDestroy[idx];
381 | const slot = registeredSlots[slotId];
382 | slots.push(slot);
383 | }
384 | this.getGoogletag()
385 | .then((googletag) => {
386 | googletag.cmd.push(() => {
387 | if (managerAlreadyInitialized === true) {
388 | if (slotsToDestroy.length > 0) {
389 | // eslint-disable-next-line guard-for-in,no-restricted-syntax
390 | for (const idx in slots) {
391 | const slot = slots[idx];
392 | slots.push(slot.gptSlot);
393 | delete slot.gptSlot;
394 | }
395 | googletag.destroySlots(slots);
396 | } else {
397 | googletag.destroySlots();
398 | }
399 | }
400 | resolve(slotsToDestroy);
401 | });
402 | });
403 | });
404 | },
405 |
406 | registerSlot({
407 | slotId,
408 | dfpNetworkId,
409 | adUnit,
410 | sizes,
411 | renderOutOfThePage,
412 | sizeMapping,
413 | adSenseAttributes,
414 | targetingArguments,
415 | slotShouldRefresh,
416 | }, autoLoad = true) {
417 | if (!Object.prototype.hasOwnProperty.call(registeredSlots, slotId)) {
418 | registeredSlots[slotId] = {
419 | slotId,
420 | sizes,
421 | renderOutOfThePage,
422 | dfpNetworkId,
423 | adUnit,
424 | adSenseAttributes,
425 | targetingArguments,
426 | sizeMapping,
427 | slotShouldRefresh,
428 | loading: false,
429 | };
430 | this.emit('slotRegistered', { slotId });
431 | if (autoLoad === true && loadPromise !== null) {
432 | loadPromise = loadPromise.catch().then(() => {
433 | const slot = registeredSlots[slotId];
434 | if (typeof slot !== 'undefined') {
435 | const { loading, gptSlot } = slot;
436 | if (loading === false && !gptSlot) {
437 | this.load(slotId);
438 | }
439 | }
440 | });
441 | }
442 | }
443 | },
444 |
445 | unregisterSlot({ slotId }) {
446 | this.destroyGPTSlots(slotId);
447 | delete registeredSlots[slotId];
448 | },
449 |
450 | getRegisteredSlots() {
451 | return registeredSlots;
452 | },
453 |
454 | attachSlotRenderEnded(cb) {
455 | this.on('slotRenderEnded', cb);
456 | },
457 |
458 | detachSlotRenderEnded(cb) {
459 | this.removeListener('slotRenderEnded', cb);
460 | },
461 |
462 | attachSlotVisibilityChanged(cb) {
463 | this.on('slotVisibilityChanged', cb);
464 | },
465 |
466 | detachSlotVisibilityChanged(cb) {
467 | this.removeListener('slotVisibilityChanged', cb);
468 | },
469 |
470 | attachSlotIsViewable(cb) {
471 | this.on('impressionViewable', cb);
472 | },
473 |
474 | detachSlotIsViewable(cb) {
475 | this.removeListener('impressionViewable', cb);
476 | },
477 |
478 | });
479 |
480 | export default DFPManager;
481 |
--------------------------------------------------------------------------------
/js/utils.js:
--------------------------------------------------------------------------------
1 | const GPT_SRC = {
2 | standard: 'securepubads.g.doubleclick.net',
3 | limitedAds: 'pagead2.googlesyndication.com',
4 | };
5 |
6 | function doloadGPTScript(resolve, reject, limitedAds) {
7 | window.googletag = window.googletag || {};
8 | window.googletag.cmd = window.googletag.cmd || [];
9 |
10 | const scriptTag = document.createElement('script');
11 | scriptTag.src = `${document.location.protocol}//${limitedAds ? GPT_SRC.limitedAds : GPT_SRC.standard}/tag/js/gpt.js`;
12 | scriptTag.async = true;
13 | scriptTag.type = 'text/javascript';
14 | scriptTag.onerror = function scriptTagOnError(errs) {
15 | reject(errs);
16 | };
17 | scriptTag.onload = function scriptTagOnLoad() {
18 | resolve(window.googletag);
19 | };
20 | document.getElementsByTagName('head')[0].appendChild(scriptTag);
21 | }
22 |
23 | export function loadGPTScript(limitedAds = false) {
24 | return new Promise((resolve, reject) => {
25 | doloadGPTScript(resolve, reject, limitedAds);
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function karmaConfig(config) {
2 | config.set({
3 | basePath: '',
4 | frameworks: ['jasmine'],
5 | files: [
6 | 'lib/*.js',
7 | 'spec/*.js',
8 | ],
9 | preprocessors: {
10 | 'lib/*.js': ['webpack'],
11 | 'spec/*.js': ['webpack'],
12 | },
13 | client: {
14 | captureConsole: true,
15 | jasmine: {
16 | random: false,
17 | },
18 | },
19 | webpack: {
20 | mode: 'development',
21 | module: {
22 | rules: [{
23 | test: /\.js$/,
24 | exclude: /(node_modules)/,
25 | use: {
26 | loader: 'babel-loader',
27 | options: {
28 | presets: ['@babel/preset-env', '@babel/preset-react'],
29 | },
30 | },
31 | }],
32 | },
33 | },
34 | webpackMiddleware: {
35 | stats: 'errors-only',
36 | },
37 | reporters: ['mocha'],
38 | port: 9877,
39 | colors: true,
40 | autoWatch: false,
41 | browsers: ['jsdom'],
42 | singleRun: true,
43 | browserNoActivityTimeout: 2000,
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/lib/adslot.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = exports.AdSlot = void 0;
7 |
8 | var _react = _interopRequireDefault(require("react"));
9 |
10 | var _propTypes = _interopRequireDefault(require("prop-types"));
11 |
12 | var _manager = _interopRequireDefault(require("./manager"));
13 |
14 | var _dfpslotsprovider = require("./dfpslotsprovider");
15 |
16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17 |
18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
19 |
20 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
21 |
22 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
23 |
24 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
25 |
26 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
27 |
28 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
29 |
30 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
31 |
32 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
33 |
34 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
35 |
36 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
37 |
38 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
39 |
40 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
41 |
42 | var dynamicAdCount = 0;
43 |
44 | var AdSlot =
45 | /*#__PURE__*/
46 | function (_React$Component) {
47 | _inherits(AdSlot, _React$Component);
48 |
49 | function AdSlot(props) {
50 | var _this;
51 |
52 | _classCallCheck(this, AdSlot);
53 |
54 | _this = _possibleConstructorReturn(this, _getPrototypeOf(AdSlot).call(this, props));
55 | _this.doRegisterSlot = _this.doRegisterSlot.bind(_assertThisInitialized(_assertThisInitialized(_this)));
56 | _this.generateSlotId = _this.generateSlotId.bind(_assertThisInitialized(_assertThisInitialized(_this)));
57 | _this.getSlotId = _this.getSlotId.bind(_assertThisInitialized(_assertThisInitialized(_this)));
58 | _this.mapContextToAdSlotProps = _this.mapContextToAdSlotProps.bind(_assertThisInitialized(_assertThisInitialized(_this)));
59 | _this.slotShouldRefresh = _this.slotShouldRefresh.bind(_assertThisInitialized(_assertThisInitialized(_this)));
60 | _this.slotRenderEnded = _this.slotRenderEnded.bind(_assertThisInitialized(_assertThisInitialized(_this)));
61 | _this.slotRegisterCallback = _this.slotRegisterCallback.bind(_assertThisInitialized(_assertThisInitialized(_this)));
62 | _this.slotIsViewable = _this.slotIsViewable.bind(_assertThisInitialized(_assertThisInitialized(_this)));
63 | _this.slotVisibilityChanged = _this.slotVisibilityChanged.bind(_assertThisInitialized(_assertThisInitialized(_this)));
64 | _this.getClasses = _this.getClasses.bind(_assertThisInitialized(_assertThisInitialized(_this)));
65 | _this.state = {
66 | slotId: _this.props.slotId || null,
67 | className: _this.props.className || ''
68 | };
69 | _this.adElementRef = _react.default.createRef ? _react.default.createRef() : function (element) {
70 | _this.adElementRef = element;
71 | };
72 | return _this;
73 | }
74 |
75 | _createClass(AdSlot, [{
76 | key: "componentDidMount",
77 | value: function componentDidMount() {
78 | // register this ad-unit in the , when available.
79 | if (this.context !== undefined && this.context.newSlotCallback) {
80 | this.context.newSlotCallback();
81 | }
82 |
83 | this.registerSlot();
84 | }
85 | }, {
86 | key: "componentWillUnmount",
87 | value: function componentWillUnmount() {
88 | this.unregisterSlot();
89 | }
90 | }, {
91 | key: "getSlotId",
92 | value: function getSlotId() {
93 | return this.props.slotId || this.state.slotId;
94 | }
95 | }, {
96 | key: "getClasses",
97 | value: function getClasses() {
98 | var baseClass = 'adunitContainer';
99 | var extraClasses = this.state.className.split(' ');
100 | extraClasses.push(baseClass);
101 | return extraClasses;
102 | }
103 | }, {
104 | key: "generateSlotId",
105 | value: function generateSlotId() {
106 | return "adSlot-".concat(dynamicAdCount++);
107 | }
108 | }, {
109 | key: "mapContextToAdSlotProps",
110 | value: function mapContextToAdSlotProps() {
111 | var context = this.context;
112 | var mappedProps = {};
113 |
114 | if (context.dfpNetworkId !== undefined) {
115 | mappedProps.dfpNetworkId = context.dfpNetworkId;
116 | }
117 |
118 | if (context.dfpAdUnit !== undefined) {
119 | mappedProps.adUnit = context.dfpAdUnit;
120 | }
121 |
122 | if (context.dfpSizeMapping !== undefined) {
123 | mappedProps.sizeMapping = context.dfpSizeMapping;
124 | }
125 |
126 | if (context.dfpTargetingArguments !== undefined) {
127 | mappedProps.targetingArguments = context.dfpTargetingArguments;
128 | }
129 |
130 | return mappedProps;
131 | }
132 | }, {
133 | key: "doRegisterSlot",
134 | value: function doRegisterSlot() {
135 | _manager.default.registerSlot(_objectSpread({}, this.mapContextToAdSlotProps(), this.props, this.state, {
136 | slotShouldRefresh: this.slotShouldRefresh
137 | }));
138 |
139 | if (this.props.fetchNow === true) {
140 | _manager.default.load(this.getSlotId());
141 | }
142 |
143 | _manager.default.attachSlotRenderEnded(this.slotRenderEnded);
144 |
145 | _manager.default.attachSlotIsViewable(this.slotIsViewable);
146 |
147 | _manager.default.attachSlotVisibilityChanged(this.slotVisibilityChanged);
148 |
149 | this.slotRegisterCallback();
150 | }
151 | }, {
152 | key: "registerSlot",
153 | value: function registerSlot() {
154 | if (this.state.slotId === null) {
155 | this.setState({
156 | slotId: this.generateSlotId()
157 | }, this.doRegisterSlot);
158 | } else {
159 | this.doRegisterSlot();
160 | }
161 | }
162 | }, {
163 | key: "unregisterSlot",
164 | value: function unregisterSlot() {
165 | _manager.default.unregisterSlot(_objectSpread({}, this.mapContextToAdSlotProps(), this.props, this.state));
166 |
167 | _manager.default.detachSlotRenderEnded(this.slotRenderEnded);
168 |
169 | _manager.default.detachSlotIsViewable(this.slotIsViewable);
170 |
171 | _manager.default.detachSlotVisibilityChanged(this.slotVisibilityChanged);
172 | }
173 | }, {
174 | key: "slotRenderEnded",
175 | value: function slotRenderEnded(eventData) {
176 | if (eventData.slotId === this.getSlotId()) {
177 | if (this.props.onSlotRender !== undefined) {
178 | // now that slot has rendered we have access to the ref
179 | var params = _objectSpread({}, eventData, {
180 | adElementRef: this.adElementRef
181 | });
182 |
183 | this.props.onSlotRender(params);
184 | }
185 | }
186 | }
187 | }, {
188 | key: "slotRegisterCallback",
189 | value: function slotRegisterCallback() {
190 | if (typeof this.props.onSlotRegister === 'function') {
191 | this.props.onSlotRegister({
192 | slotId: this.getSlotId(),
193 | sizes: this.props.sizes,
194 | slotCount: dynamicAdCount,
195 | adElementRef: this.adElementRef
196 | });
197 | }
198 | }
199 | }, {
200 | key: "slotIsViewable",
201 | value: function slotIsViewable(eventData) {
202 | if (eventData.slotId === this.getSlotId()) {
203 | if (this.props.onSlotIsViewable !== undefined) {
204 | this.props.onSlotIsViewable(eventData);
205 | }
206 | }
207 | }
208 | }, {
209 | key: "slotVisibilityChanged",
210 | value: function slotVisibilityChanged(eventData) {
211 | if (eventData.slotId === this.getSlotId()) {
212 | if (this.props.onSlotVisibilityChanged !== undefined) {
213 | this.props.onSlotVisibilityChanged(eventData);
214 | }
215 | }
216 | }
217 | }, {
218 | key: "slotShouldRefresh",
219 | value: function slotShouldRefresh() {
220 | var r = true;
221 |
222 | if (this.props.shouldRefresh !== undefined) {
223 | r = this.props.shouldRefresh(_objectSpread({}, this.mapContextToAdSlotProps(), this.props, {
224 | slotId: this.getSlotId()
225 | }));
226 | }
227 |
228 | return r;
229 | }
230 | }, {
231 | key: "render",
232 | value: function render() {
233 | var slotId = this.state.slotId;
234 | var props = {
235 | className: 'adBox'
236 | };
237 |
238 | if (slotId !== null) {
239 | props.id = slotId;
240 | }
241 |
242 | return _react.default.createElement("div", {
243 | className: this.getClasses().join(' ').trim()
244 | }, _react.default.createElement("div", _extends({
245 | ref: this.adElementRef
246 | }, props)));
247 | }
248 | }]);
249 |
250 | return AdSlot;
251 | }(_react.default.Component);
252 |
253 | exports.AdSlot = AdSlot;
254 |
255 | _defineProperty(AdSlot, "propTypes", {
256 | dfpNetworkId: _propTypes.default.string,
257 | adUnit: _propTypes.default.string,
258 | sizes: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.number), _propTypes.default.string])),
259 | renderOutOfThePage: _propTypes.default.bool,
260 | sizeMapping: _propTypes.default.arrayOf(_propTypes.default.object),
261 | fetchNow: _propTypes.default.bool,
262 | adSenseAttributes: _propTypes.default.object,
263 | targetingArguments: _propTypes.default.object,
264 | onSlotRender: _propTypes.default.func,
265 | onSlotRegister: _propTypes.default.func,
266 | onSlotIsViewable: _propTypes.default.func,
267 | onSlotVisibilityChanged: _propTypes.default.func,
268 | shouldRefresh: _propTypes.default.func,
269 | slotId: _propTypes.default.string,
270 | className: _propTypes.default.string
271 | });
272 |
273 | _defineProperty(AdSlot, "defaultProps", {
274 | fetchNow: false
275 | });
276 |
277 | if (_dfpslotsprovider.Context === null) {
278 | // React < 16.3
279 | AdSlot.contextTypes = {
280 | dfpNetworkId: _propTypes.default.string,
281 | dfpAdUnit: _propTypes.default.string,
282 | dfpSizeMapping: _propTypes.default.arrayOf(_propTypes.default.object),
283 | dfpTargetingArguments: _propTypes.default.object,
284 | newSlotCallback: _propTypes.default.func
285 | };
286 | } else {
287 | AdSlot.contextType = _dfpslotsprovider.Context;
288 | }
289 |
290 | var _default = AdSlot;
291 | exports.default = _default;
--------------------------------------------------------------------------------
/lib/dfpslotsprovider.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = exports.Context = void 0;
7 |
8 | var _react = _interopRequireDefault(require("react"));
9 |
10 | var _propTypes = _interopRequireDefault(require("prop-types"));
11 |
12 | var _manager = _interopRequireDefault(require("./manager"));
13 |
14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15 |
16 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
17 |
18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
19 |
20 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
21 |
22 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
23 |
24 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
25 |
26 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
27 |
28 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
29 |
30 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
31 |
32 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
33 |
34 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
35 |
36 | // React.createContext is undefined for React < 16.3
37 | var Context = _react.default.createContext ? _react.default.createContext({
38 | dfpNetworkId: null,
39 | dfpAdUnit: null,
40 | dfpSizeMapping: null,
41 | dfpTargetingArguments: null,
42 | newSlotCallback: null
43 | }) : null;
44 | exports.Context = Context;
45 |
46 | var DFPSlotsProvider =
47 | /*#__PURE__*/
48 | function (_React$Component) {
49 | _inherits(DFPSlotsProvider, _React$Component);
50 |
51 | function DFPSlotsProvider(props) {
52 | var _this;
53 |
54 | _classCallCheck(this, DFPSlotsProvider);
55 |
56 | _this = _possibleConstructorReturn(this, _getPrototypeOf(DFPSlotsProvider).call(this, props));
57 | _this.loadAdsIfPossible = _this.loadAdsIfPossible.bind(_assertThisInitialized(_assertThisInitialized(_this)));
58 | _this.newSlotCallback = _this.newSlotCallback.bind(_assertThisInitialized(_assertThisInitialized(_this)));
59 | _this.applyConfigs = _this.applyConfigs.bind(_assertThisInitialized(_assertThisInitialized(_this)));
60 | _this.shouldReloadConfig = _this.shouldReloadConfig.bind(_assertThisInitialized(_assertThisInitialized(_this)));
61 | _this.attachLoadCallback = _this.attachLoadCallback.bind(_assertThisInitialized(_assertThisInitialized(_this)));
62 | _this.getContextValue = _this.getContextValue.bind(_assertThisInitialized(_assertThisInitialized(_this)));
63 | _this.loadAlreadyCalled = false;
64 | _this.loadCallbackAttached = false;
65 | _this.shouldReloadAds = false;
66 | _this.totalSlots = 0;
67 | _this.contextValue = {};
68 |
69 | if (Context === null) {
70 | _this.getChildContext = function () {
71 | return _this.getContextValue();
72 | };
73 | }
74 |
75 | return _this;
76 | }
77 |
78 | _createClass(DFPSlotsProvider, [{
79 | key: "componentDidMount",
80 | value: function componentDidMount() {
81 | this.applyConfigs();
82 |
83 | if (this.props.autoLoad && !this.loadAdsIfPossible()) {
84 | this.attachLoadCallback();
85 | }
86 | }
87 | }, {
88 | key: "shouldComponentUpdate",
89 | value: function shouldComponentUpdate(nextProps) {
90 | this.shouldReloadAds = this.shouldReloadConfig(nextProps);
91 |
92 | if (nextProps.children !== this.props.children) {
93 | return true;
94 | }
95 |
96 | if (nextProps.autoLoad && !this.props.autoLoad) {
97 | return true;
98 | }
99 |
100 | return this.shouldReloadAds;
101 | }
102 | }, {
103 | key: "componentDidUpdate",
104 | value: function componentDidUpdate() {
105 | this.applyConfigs();
106 |
107 | if (this.props.autoLoad) {
108 | if (this.loadAlreadyCalled) {
109 | if (this.shouldReloadAds) {
110 | _manager.default.reload();
111 | }
112 | } else if (!this.loadAdsIfPossible()) {
113 | this.attachLoadCallback();
114 | }
115 | }
116 |
117 | this.shouldReloadAds = false;
118 | }
119 | }, {
120 | key: "getContextValue",
121 | value: function getContextValue() {
122 | var _this$props = this.props,
123 | dfpNetworkId = _this$props.dfpNetworkId,
124 | dfpAdUnit = _this$props.adUnit,
125 | dfpSizeMapping = _this$props.sizeMapping,
126 | dfpTargetingArguments = _this$props.targetingArguments,
127 | _this$contextValue = this.contextValue,
128 | ctxDfpNetworkId = _this$contextValue.dfpNetworkId,
129 | ctxDfpAdUnit = _this$contextValue.adUnit,
130 | ctxDfpSizeMapping = _this$contextValue.sizeMapping,
131 | ctxDfpTargetingArguments = _this$contextValue.targetingArguments; // performance: update context value object only when any of its
132 | // props is updated.
133 |
134 | if (dfpNetworkId !== ctxDfpNetworkId || dfpAdUnit !== ctxDfpAdUnit || dfpSizeMapping !== ctxDfpSizeMapping || dfpTargetingArguments !== ctxDfpTargetingArguments) {
135 | this.contextValue = {
136 | dfpNetworkId: dfpNetworkId,
137 | dfpAdUnit: dfpAdUnit,
138 | dfpSizeMapping: dfpSizeMapping,
139 | dfpTargetingArguments: dfpTargetingArguments,
140 | newSlotCallback: this.newSlotCallback
141 | };
142 | }
143 |
144 | return this.contextValue;
145 | }
146 | }, {
147 | key: "applyConfigs",
148 | value: function applyConfigs() {
149 | _manager.default.configurePersonalizedAds(this.props.personalizedAds);
150 |
151 | _manager.default.configureCookieOption(this.props.cookieOption);
152 |
153 | _manager.default.configureSingleRequest(this.props.singleRequest);
154 |
155 | _manager.default.configureDisableInitialLoad(this.props.disableInitialLoad);
156 |
157 | _manager.default.configureLazyLoad(!!this.props.lazyLoad, typeof this.props.lazyLoad === 'boolean' ? null : this.props.lazyLoad);
158 |
159 | _manager.default.setAdSenseAttributes(this.props.adSenseAttributes);
160 |
161 | _manager.default.setCollapseEmptyDivs(this.props.collapseEmptyDivs);
162 |
163 | _manager.default.configureLimitedAds(this.props.limitedAds);
164 | }
165 | }, {
166 | key: "attachLoadCallback",
167 | value: function attachLoadCallback() {
168 | if (this.loadCallbackAttached === false) {
169 | _manager.default.on('slotRegistered', this.loadAdsIfPossible);
170 |
171 | this.loadCallbackAttached = true;
172 | return true;
173 | }
174 |
175 | return false;
176 | } // pretty strait-forward interface that children ad slots use to register
177 | // with their DFPSlotProvider parent node.
178 |
179 | }, {
180 | key: "newSlotCallback",
181 | value: function newSlotCallback() {
182 | this.totalSlots++;
183 | } // Checks all the mounted children ads have been already registered
184 | // in the DFPManager before trying to call the gpt load scripts.
185 | // This is helpful when trying to fetch ads with a single request.
186 |
187 | }, {
188 | key: "loadAdsIfPossible",
189 | value: function loadAdsIfPossible() {
190 | var r = false;
191 |
192 | if (Object.keys(_manager.default.getRegisteredSlots()).length >= this.totalSlots) {
193 | _manager.default.removeListener('slotRegistered', this.loadAdsIfPossible);
194 |
195 | _manager.default.load();
196 |
197 | this.loadAlreadyCalled = true;
198 | this.loadCallbackAttached = false;
199 | r = true;
200 | }
201 |
202 | return r;
203 | }
204 | }, {
205 | key: "shouldReloadConfig",
206 | value: function shouldReloadConfig(nextProps) {
207 | var reloadConfig = nextProps.autoReload || this.props.autoReload;
208 |
209 | if (this.props.autoLoad || nextProps.autoLoad) {
210 | if (_typeof(reloadConfig) === 'object') {
211 | var attrs = Object.keys(reloadConfig); // eslint-disable-next-line guard-for-in, no-restricted-syntax
212 |
213 | for (var i in attrs) {
214 | var propName = attrs[i]; // eslint-disable-next-line
215 |
216 | if (reloadConfig[propName] === true && this.props[propName] !== nextProps[propName]) {
217 | return true;
218 | }
219 | }
220 | }
221 | }
222 |
223 | return false;
224 | }
225 | }, {
226 | key: "render",
227 | value: function render() {
228 | var children = this.props.children;
229 |
230 | if (Context === null) {
231 | return children;
232 | }
233 |
234 | return _react.default.createElement(Context.Provider, {
235 | value: this.getContextValue()
236 | }, children);
237 | }
238 | }]);
239 |
240 | return DFPSlotsProvider;
241 | }(_react.default.Component);
242 |
243 | exports.default = DFPSlotsProvider;
244 |
245 | _defineProperty(DFPSlotsProvider, "propTypes", {
246 | children: _propTypes.default.oneOfType([_propTypes.default.element, _propTypes.default.array]).isRequired,
247 | autoLoad: _propTypes.default.bool,
248 | autoReload: _propTypes.default.shape({
249 | dfpNetworkId: _propTypes.default.bool,
250 | personalizedAds: _propTypes.default.bool,
251 | cookieOption: _propTypes.default.bool,
252 | singleRequest: _propTypes.default.bool,
253 | disableInitialLoad: _propTypes.default.bool,
254 | adUnit: _propTypes.default.bool,
255 | sizeMapping: _propTypes.default.bool,
256 | adSenseAttributes: _propTypes.default.bool,
257 | targetingArguments: _propTypes.default.bool,
258 | collapseEmptyDivs: _propTypes.default.bool,
259 | lazyLoad: _propTypes.default.bool
260 | }),
261 | dfpNetworkId: _propTypes.default.string.isRequired,
262 | personalizedAds: _propTypes.default.bool,
263 | cookieOption: _propTypes.default.bool,
264 | singleRequest: _propTypes.default.bool,
265 | disableInitialLoad: _propTypes.default.bool,
266 | adUnit: _propTypes.default.string,
267 | sizeMapping: _propTypes.default.arrayOf(_propTypes.default.object),
268 | adSenseAttributes: _propTypes.default.object,
269 | targetingArguments: _propTypes.default.object,
270 | collapseEmptyDivs: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.object]),
271 | adSenseAttrs: _propTypes.default.object,
272 | lazyLoad: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.shape({
273 | fetchMarginPercent: _propTypes.default.number,
274 | renderMarginPercent: _propTypes.default.number,
275 | mobileScaling: _propTypes.default.number
276 | })]),
277 | limitedAds: _propTypes.default.bool
278 | });
279 |
280 | _defineProperty(DFPSlotsProvider, "defaultProps", {
281 | autoLoad: true,
282 | autoReload: {
283 | dfpNetworkId: false,
284 | personalizedAds: false,
285 | cookieOption: false,
286 | singleRequest: false,
287 | disableInitialLoad: false,
288 | adUnit: false,
289 | sizeMapping: false,
290 | adSenseAttributes: false,
291 | targetingArguments: false,
292 | collapseEmptyDivs: false,
293 | lazyLoad: false
294 | },
295 | personalizedAds: true,
296 | cookieOption: true,
297 | singleRequest: true,
298 | disableInitialLoad: false,
299 | collapseEmptyDivs: null,
300 | lazyLoad: false,
301 | limitedAds: false
302 | });
303 |
304 | if (Context === null) {
305 | // React < 16.3
306 | DFPSlotsProvider.childContextTypes = {
307 | dfpNetworkId: _propTypes.default.string,
308 | dfpAdUnit: _propTypes.default.string,
309 | dfpSizeMapping: _propTypes.default.arrayOf(_propTypes.default.object),
310 | dfpTargetingArguments: _propTypes.default.object,
311 | newSlotCallback: _propTypes.default.func
312 | };
313 | }
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.DFPSlotsProvider = exports.AdSlot = exports.DFPManager = void 0;
7 |
8 | var _manager = _interopRequireDefault(require("./manager"));
9 |
10 | var _adslot = _interopRequireDefault(require("./adslot"));
11 |
12 | var _dfpslotsprovider = _interopRequireDefault(require("./dfpslotsprovider"));
13 |
14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15 |
16 | var DFPManager = _manager.default;
17 | exports.DFPManager = DFPManager;
18 | var AdSlot = _adslot.default;
19 | exports.AdSlot = AdSlot;
20 | var DFPSlotsProvider = _dfpslotsprovider.default;
21 | exports.DFPSlotsProvider = DFPSlotsProvider;
--------------------------------------------------------------------------------
/lib/manager.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _events = require("events");
9 |
10 | var Utils = _interopRequireWildcard(require("./utils"));
11 |
12 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
13 |
14 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
15 |
16 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
17 |
18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
19 |
20 | var loadPromise = null;
21 | var googleGPTScriptLoadPromise = null;
22 | var singleRequestEnabled = true;
23 | var disableInitialLoadEnabled = false;
24 | var lazyLoadEnabled = false;
25 | var lazyLoadConfig = null;
26 | var servePersonalizedAds = true;
27 | var serveCookies = true;
28 | var registeredSlots = {};
29 | var managerAlreadyInitialized = false;
30 | var globalTargetingArguments = {};
31 | var globalAdSenseAttributes = {};
32 | var limitedAds = false;
33 | var DFPManager = Object.assign(new _events.EventEmitter().setMaxListeners(0), {
34 | singleRequestIsEnabled: function singleRequestIsEnabled() {
35 | return singleRequestEnabled;
36 | },
37 | configureSingleRequest: function configureSingleRequest(value) {
38 | singleRequestEnabled = !!value;
39 | },
40 | disableInitialLoadIsEnabled: function disableInitialLoadIsEnabled() {
41 | return disableInitialLoadEnabled;
42 | },
43 | configureDisableInitialLoad: function configureDisableInitialLoad(value) {
44 | disableInitialLoadEnabled = !!value;
45 | },
46 | configureLazyLoad: function configureLazyLoad() {
47 | var enable = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
48 | var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
49 | var conf = null;
50 |
51 | if (config !== null && _typeof(config) === 'object') {
52 | conf = _objectSpread({}, config);
53 | }
54 |
55 | lazyLoadEnabled = !!enable;
56 | lazyLoadConfig = conf;
57 | },
58 | lazyLoadIsEnabled: function lazyLoadIsEnabled() {
59 | return lazyLoadEnabled;
60 | },
61 | limitedAdsIsEnabled: function limitedAdsIsEnabled() {
62 | return limitedAds;
63 | },
64 | configureLimitedAds: function configureLimitedAds(value) {
65 | limitedAds = !!value;
66 | },
67 | getLazyLoadConfig: function getLazyLoadConfig() {
68 | return lazyLoadConfig;
69 | },
70 | getAdSenseAttribute: function getAdSenseAttribute(key) {
71 | return globalAdSenseAttributes[key];
72 | },
73 | configurePersonalizedAds: function configurePersonalizedAds(value) {
74 | servePersonalizedAds = value;
75 | },
76 | configureCookieOption: function configureCookieOption(value) {
77 | serveCookies = value;
78 | },
79 | personalizedAdsEnabled: function personalizedAdsEnabled() {
80 | return servePersonalizedAds;
81 | },
82 | cookiesEnabled: function cookiesEnabled() {
83 | return serveCookies;
84 | },
85 | setAdSenseAttribute: function setAdSenseAttribute(key, value) {
86 | this.setAdSenseAttributes(_defineProperty({}, key, value));
87 | },
88 | getAdSenseAttributes: function getAdSenseAttributes() {
89 | return _objectSpread({}, globalAdSenseAttributes);
90 | },
91 | setAdSenseAttributes: function setAdSenseAttributes(attrs) {
92 | Object.assign(globalAdSenseAttributes, attrs);
93 |
94 | if (managerAlreadyInitialized === true) {
95 | this.getGoogletag().then(function (googletag) {
96 | googletag.cmd.push(function () {
97 | var pubadsService = googletag.pubads();
98 | Object.keys(globalAdSenseAttributes).forEach(function (key) {
99 | pubadsService.set(key, globalTargetingArguments[key]);
100 | });
101 | });
102 | });
103 | }
104 | },
105 | setTargetingArguments: function setTargetingArguments(data) {
106 | Object.assign(globalTargetingArguments, data);
107 | var availableKeys = Object.keys(registeredSlots);
108 | availableKeys.forEach(function (slotId) {
109 | registeredSlots[slotId].targetingArguments = data;
110 | });
111 |
112 | if (managerAlreadyInitialized === true) {
113 | this.getGoogletag().then(function (googletag) {
114 | googletag.cmd.push(function () {
115 | var pubadsService = googletag.pubads();
116 | Object.keys(globalTargetingArguments).forEach(function (varName) {
117 | if (pubadsService) {
118 | pubadsService.setTargeting(varName, globalTargetingArguments[varName]);
119 | }
120 | });
121 | });
122 | });
123 | }
124 | },
125 | getTargetingArguments: function getTargetingArguments() {
126 | return _objectSpread({}, globalTargetingArguments);
127 | },
128 | getSlotProperty: function getSlotProperty(slotId, propName) {
129 | var slot = this.getRegisteredSlots()[slotId];
130 | var ret = null;
131 |
132 | if (slot !== undefined) {
133 | ret = slot[propName] || ret;
134 | }
135 |
136 | return ret;
137 | },
138 | getSlotTargetingArguments: function getSlotTargetingArguments(slotId) {
139 | var propValue = this.getSlotProperty(slotId, 'targetingArguments');
140 | return propValue ? _objectSpread({}, propValue) : null;
141 | },
142 | getSlotAdSenseAttributes: function getSlotAdSenseAttributes(slotId) {
143 | var propValue = this.getSlotProperty(slotId, 'adSenseAttributes');
144 | return propValue ? _objectSpread({}, propValue) : null;
145 | },
146 | init: function init() {
147 | var _this = this;
148 |
149 | if (managerAlreadyInitialized === false) {
150 | managerAlreadyInitialized = true;
151 | this.getGoogletag().then(function (googletag) {
152 | googletag.cmd.push(function () {
153 | var pubadsService = googletag.pubads();
154 | pubadsService.addEventListener('slotRenderEnded', function (event) {
155 | var slotId = event.slot.getSlotElementId();
156 |
157 | _this.emit('slotRenderEnded', {
158 | slotId: slotId,
159 | event: event
160 | });
161 | });
162 | pubadsService.addEventListener('impressionViewable', function (event) {
163 | var slotId = event.slot.getSlotElementId();
164 |
165 | _this.emit('impressionViewable', {
166 | slotId: slotId,
167 | event: event
168 | });
169 | });
170 | pubadsService.addEventListener('slotVisibilityChanged', function (event) {
171 | var slotId = event.slot.getSlotElementId();
172 |
173 | _this.emit('slotVisibilityChanged', {
174 | slotId: slotId,
175 | event: event
176 | });
177 | });
178 | pubadsService.setRequestNonPersonalizedAds(_this.personalizedAdsEnabled() ? 0 : 1);
179 | pubadsService.setCookieOptions(_this.cookiesEnabled() ? 0 : 1);
180 | });
181 | });
182 | }
183 | },
184 | getGoogletag: function getGoogletag() {
185 | if (googleGPTScriptLoadPromise === null) {
186 | googleGPTScriptLoadPromise = Utils.loadGPTScript(limitedAds);
187 | }
188 |
189 | return googleGPTScriptLoadPromise;
190 | },
191 | setCollapseEmptyDivs: function setCollapseEmptyDivs(collapse) {
192 | this.collapseEmptyDivs = collapse;
193 | },
194 | load: function load() {
195 | var _this2 = this;
196 |
197 | for (var _len = arguments.length, slots = new Array(_len), _key = 0; _key < _len; _key++) {
198 | slots[_key] = arguments[_key];
199 | }
200 |
201 | if (loadPromise === null) {
202 | loadPromise = this.doLoad.apply(this, slots);
203 | } else {
204 | loadPromise = loadPromise.then(function () {
205 | return _this2.doLoad.apply(_this2, slots);
206 | });
207 | }
208 | },
209 | doLoad: function doLoad() {
210 | this.init();
211 | var availableSlots = {};
212 |
213 | for (var _len2 = arguments.length, slots = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
214 | slots[_key2] = arguments[_key2];
215 | }
216 |
217 | if (slots.length > 0) {
218 | availableSlots = slots.filter(function (slotId) {
219 | return Object.prototype.hasOwnProperty.call(registeredSlots, slotId);
220 | });
221 | } else {
222 | availableSlots = Object.keys(registeredSlots);
223 | }
224 |
225 | availableSlots = availableSlots.filter(function (id) {
226 | return !registeredSlots[id].loading && !registeredSlots[id].gptSlot;
227 | });
228 | availableSlots.forEach(function (slotId) {
229 | registeredSlots[slotId].loading = true;
230 | });
231 | return this.gptLoadAds(availableSlots);
232 | },
233 | gptLoadAds: function gptLoadAds(slotsToInitialize) {
234 | var _this3 = this;
235 |
236 | return new Promise(function (resolve) {
237 | _this3.getGoogletag().then(function (googletag) {
238 | _this3.configureInitialOptions(googletag);
239 |
240 | slotsToInitialize.forEach(function (currentSlotId) {
241 | registeredSlots[currentSlotId].loading = false;
242 | googletag.cmd.push(function () {
243 | var slot = registeredSlots[currentSlotId];
244 | var gptSlot;
245 | var adUnit = "".concat(slot.dfpNetworkId, "/").concat(slot.adUnit);
246 |
247 | if (slot.renderOutOfThePage === true) {
248 | gptSlot = googletag.defineOutOfPageSlot(adUnit, currentSlotId);
249 | } else {
250 | gptSlot = googletag.defineSlot(adUnit, slot.sizes, currentSlotId);
251 | }
252 |
253 | if (gptSlot !== null) {
254 | slot.gptSlot = gptSlot;
255 |
256 | var slotTargetingArguments = _this3.getSlotTargetingArguments(currentSlotId);
257 |
258 | if (slotTargetingArguments !== null) {
259 | Object.keys(slotTargetingArguments).forEach(function (varName) {
260 | if (slot && slot.gptSlot) {
261 | slot.gptSlot.setTargeting(varName, slotTargetingArguments[varName]);
262 | }
263 | });
264 | }
265 |
266 | var slotAdSenseAttributes = _this3.getSlotAdSenseAttributes(currentSlotId);
267 |
268 | if (slotAdSenseAttributes !== null) {
269 | Object.keys(slotAdSenseAttributes).forEach(function (varName) {
270 | slot.gptSlot.set(varName, slotAdSenseAttributes[varName]);
271 | });
272 | }
273 |
274 | slot.gptSlot.addService(googletag.pubads());
275 |
276 | if (slot.sizeMapping) {
277 | var smbuilder = googletag.sizeMapping();
278 | slot.sizeMapping.forEach(function (mapping) {
279 | smbuilder = smbuilder.addSize(mapping.viewport, mapping.sizes);
280 | });
281 | slot.gptSlot.defineSizeMapping(smbuilder.build());
282 | }
283 | }
284 | });
285 | });
286 |
287 | _this3.configureOptions(googletag);
288 |
289 | googletag.cmd.push(function () {
290 | googletag.enableServices();
291 | slotsToInitialize.forEach(function (theSlotId) {
292 | googletag.display(theSlotId);
293 | });
294 | resolve();
295 | });
296 | });
297 | });
298 | },
299 | // configure those gpt parameters that need to be set before pubsads service
300 | // initialization.
301 | configureInitialOptions: function configureInitialOptions(googletag) {
302 | var _this4 = this;
303 |
304 | googletag.cmd.push(function () {
305 | if (_this4.disableInitialLoadIsEnabled()) {
306 | googletag.pubads().disableInitialLoad();
307 | }
308 | });
309 | },
310 | configureOptions: function configureOptions(googletag) {
311 | var _this5 = this;
312 |
313 | googletag.cmd.push(function () {
314 | var pubadsService = googletag.pubads();
315 | pubadsService.setRequestNonPersonalizedAds(_this5.personalizedAdsEnabled() ? 0 : 1);
316 | pubadsService.setCookieOptions(_this5.cookiesEnabled() ? 0 : 1);
317 |
318 | var targetingArguments = _this5.getTargetingArguments(); // set global targetting arguments
319 |
320 |
321 | Object.keys(targetingArguments).forEach(function (varName) {
322 | if (pubadsService) {
323 | pubadsService.setTargeting(varName, targetingArguments[varName]);
324 | }
325 | }); // set global adSense attributes
326 |
327 | var adSenseAttributes = _this5.getAdSenseAttributes();
328 |
329 | Object.keys(adSenseAttributes).forEach(function (key) {
330 | pubadsService.set(key, adSenseAttributes[key]);
331 | });
332 |
333 | if (_this5.lazyLoadIsEnabled()) {
334 | var config = _this5.getLazyLoadConfig();
335 |
336 | if (config) {
337 | pubadsService.enableLazyLoad(config);
338 | } else {
339 | pubadsService.enableLazyLoad();
340 | }
341 | }
342 |
343 | if (_this5.singleRequestIsEnabled()) {
344 | pubadsService.enableSingleRequest();
345 | }
346 |
347 | if (_this5.collapseEmptyDivs === true || _this5.collapseEmptyDivs === false) {
348 | pubadsService.collapseEmptyDivs(_this5.collapseEmptyDivs);
349 | }
350 | });
351 | },
352 | getRefreshableSlots: function getRefreshableSlots() {
353 | var slots = {};
354 |
355 | for (var _len3 = arguments.length, slotsArray = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
356 | slotsArray[_key3] = arguments[_key3];
357 | }
358 |
359 | if (slotsArray.length === 0) {
360 | var slotsToRefresh = Object.keys(registeredSlots).map(function (k) {
361 | return registeredSlots[k];
362 | });
363 | return slotsToRefresh.reduce(function (last, slot) {
364 | if (slot.slotShouldRefresh() === true) {
365 | slots[slot.slotId] = slot;
366 | }
367 |
368 | return slots;
369 | }, slots);
370 | }
371 |
372 | return slotsArray.reduce(function (last, slotId) {
373 | var slot = registeredSlots[slotId];
374 |
375 | if (typeof slot !== 'undefined') {
376 | slots[slotId] = slot;
377 | }
378 |
379 | return slots;
380 | }, slots);
381 | },
382 | refresh: function refresh() {
383 | var _this6 = this;
384 |
385 | for (var _len4 = arguments.length, slots = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
386 | slots[_key4] = arguments[_key4];
387 | }
388 |
389 | if (loadPromise === null) {
390 | this.load();
391 | } else {
392 | loadPromise.then(function () {
393 | _this6.gptRefreshAds(Object.keys(_this6.getRefreshableSlots.apply(_this6, slots)));
394 | });
395 | }
396 | },
397 | gptRefreshAds: function gptRefreshAds(slots) {
398 | var _this7 = this;
399 |
400 | return this.getGoogletag().then(function (googletag) {
401 | _this7.configureOptions(googletag);
402 |
403 | googletag.cmd.push(function () {
404 | var pubadsService = googletag.pubads();
405 | var slotsToRefreshArray = slots.map(function (slotId) {
406 | return registeredSlots[slotId].slotId;
407 | });
408 | pubadsService.refresh(slotsToRefreshArray);
409 | });
410 | });
411 | },
412 | reload: function reload() {
413 | var _this8 = this;
414 |
415 | return this.destroyGPTSlots.apply(this, arguments).then(function () {
416 | return _this8.load();
417 | });
418 | },
419 | destroyGPTSlots: function destroyGPTSlots() {
420 | var _this9 = this;
421 |
422 | for (var _len5 = arguments.length, slotsToDestroy = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
423 | slotsToDestroy[_key5] = arguments[_key5];
424 | }
425 |
426 | if (slotsToDestroy.length === 0) {
427 | // eslint-disable-next-line no-param-reassign
428 | slotsToDestroy = Object.keys(registeredSlots);
429 | }
430 |
431 | return new Promise(function (resolve) {
432 | var slots = []; // eslint-disable-next-line guard-for-in,no-restricted-syntax
433 |
434 | for (var idx in slotsToDestroy) {
435 | var slotId = slotsToDestroy[idx];
436 | var slot = registeredSlots[slotId];
437 | slots.push(slot);
438 | }
439 |
440 | _this9.getGoogletag().then(function (googletag) {
441 | googletag.cmd.push(function () {
442 | if (managerAlreadyInitialized === true) {
443 | if (slotsToDestroy.length > 0) {
444 | // eslint-disable-next-line guard-for-in,no-restricted-syntax
445 | for (var _idx in slots) {
446 | var _slot = slots[_idx];
447 | slots.push(_slot.gptSlot);
448 | delete _slot.gptSlot;
449 | }
450 |
451 | googletag.destroySlots(slots);
452 | } else {
453 | googletag.destroySlots();
454 | }
455 | }
456 |
457 | resolve(slotsToDestroy);
458 | });
459 | });
460 | });
461 | },
462 | registerSlot: function registerSlot(_ref) {
463 | var _this10 = this;
464 |
465 | var slotId = _ref.slotId,
466 | dfpNetworkId = _ref.dfpNetworkId,
467 | adUnit = _ref.adUnit,
468 | sizes = _ref.sizes,
469 | renderOutOfThePage = _ref.renderOutOfThePage,
470 | sizeMapping = _ref.sizeMapping,
471 | adSenseAttributes = _ref.adSenseAttributes,
472 | targetingArguments = _ref.targetingArguments,
473 | slotShouldRefresh = _ref.slotShouldRefresh;
474 | var autoLoad = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
475 |
476 | if (!Object.prototype.hasOwnProperty.call(registeredSlots, slotId)) {
477 | registeredSlots[slotId] = {
478 | slotId: slotId,
479 | sizes: sizes,
480 | renderOutOfThePage: renderOutOfThePage,
481 | dfpNetworkId: dfpNetworkId,
482 | adUnit: adUnit,
483 | adSenseAttributes: adSenseAttributes,
484 | targetingArguments: targetingArguments,
485 | sizeMapping: sizeMapping,
486 | slotShouldRefresh: slotShouldRefresh,
487 | loading: false
488 | };
489 | this.emit('slotRegistered', {
490 | slotId: slotId
491 | });
492 |
493 | if (autoLoad === true && loadPromise !== null) {
494 | loadPromise = loadPromise.catch().then(function () {
495 | var slot = registeredSlots[slotId];
496 |
497 | if (typeof slot !== 'undefined') {
498 | var loading = slot.loading,
499 | gptSlot = slot.gptSlot;
500 |
501 | if (loading === false && !gptSlot) {
502 | _this10.load(slotId);
503 | }
504 | }
505 | });
506 | }
507 | }
508 | },
509 | unregisterSlot: function unregisterSlot(_ref2) {
510 | var slotId = _ref2.slotId;
511 | this.destroyGPTSlots(slotId);
512 | delete registeredSlots[slotId];
513 | },
514 | getRegisteredSlots: function getRegisteredSlots() {
515 | return registeredSlots;
516 | },
517 | attachSlotRenderEnded: function attachSlotRenderEnded(cb) {
518 | this.on('slotRenderEnded', cb);
519 | },
520 | detachSlotRenderEnded: function detachSlotRenderEnded(cb) {
521 | this.removeListener('slotRenderEnded', cb);
522 | },
523 | attachSlotVisibilityChanged: function attachSlotVisibilityChanged(cb) {
524 | this.on('slotVisibilityChanged', cb);
525 | },
526 | detachSlotVisibilityChanged: function detachSlotVisibilityChanged(cb) {
527 | this.removeListener('slotVisibilityChanged', cb);
528 | },
529 | attachSlotIsViewable: function attachSlotIsViewable(cb) {
530 | this.on('impressionViewable', cb);
531 | },
532 | detachSlotIsViewable: function detachSlotIsViewable(cb) {
533 | this.removeListener('impressionViewable', cb);
534 | }
535 | });
536 | var _default = DFPManager;
537 | exports.default = _default;
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.loadGPTScript = loadGPTScript;
7 | var GPT_SRC = {
8 | standard: 'securepubads.g.doubleclick.net',
9 | limitedAds: 'pagead2.googlesyndication.com'
10 | };
11 |
12 | function doloadGPTScript(resolve, reject, limitedAds) {
13 | window.googletag = window.googletag || {};
14 | window.googletag.cmd = window.googletag.cmd || [];
15 | var scriptTag = document.createElement('script');
16 | scriptTag.src = "".concat(document.location.protocol, "//").concat(limitedAds ? GPT_SRC.limitedAds : GPT_SRC.standard, "/tag/js/gpt.js");
17 | scriptTag.async = true;
18 | scriptTag.type = 'text/javascript';
19 |
20 | scriptTag.onerror = function scriptTagOnError(errs) {
21 | reject(errs);
22 | };
23 |
24 | scriptTag.onload = function scriptTagOnLoad() {
25 | resolve(window.googletag);
26 | };
27 |
28 | document.getElementsByTagName('head')[0].appendChild(scriptTag);
29 | }
30 |
31 | function loadGPTScript() {
32 | var limitedAds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
33 | return new Promise(function (resolve, reject) {
34 | doloadGPTScript(resolve, reject, limitedAds);
35 | });
36 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-dfp",
3 | "version": "0.21.0",
4 | "homepage": "https://github.com/jaanauati/react-dfp/",
5 | "author": {
6 | "name": "Jonatan Alexis Anauati",
7 | "email": "barakawins@gmail.com"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/jaanauati/react-dfp.git"
12 | },
13 | "scripts": {
14 | "watch": "NODE_ENV=production babel --watch js --out-dir lib",
15 | "build": "NODE_ENV=production babel js --out-dir lib",
16 | "test": "karma start karma.conf.js 2> /dev/null",
17 | "clean": "rm ./lib/*.js",
18 | "eslint": "eslint js 2> /dev/null",
19 | "dist": "npm run eslint && npm run clean && npm run build && npm run test"
20 | },
21 | "bugs": {
22 | "mail": "barakawins@gmail.com",
23 | "url": "https://github.com/jaanauati/react-dfp/issues/"
24 | },
25 | "keywords": [
26 | "react",
27 | "gpt",
28 | "dfp",
29 | "google dfp",
30 | "google doubleclick for publishers",
31 | "advertising",
32 | "react-component"
33 | ],
34 | "main": "lib/index.js",
35 | "peerDependencies": {
36 | "react": ">=0.14.0",
37 | "react-dom": ">=0.14.0"
38 | },
39 | "devDependencies": {
40 | "@babel/cli": "^7.2.3",
41 | "@babel/core": "^7.2.2",
42 | "@babel/plugin-proposal-class-properties": "^7.3.0",
43 | "@babel/plugin-proposal-object-rest-spread": "^7.3.2",
44 | "@babel/polyfill": "^7.2.5",
45 | "@babel/preset-env": "^7.3.1",
46 | "@babel/preset-react": "^7.0.0",
47 | "array.from": "^1.0.1",
48 | "babel-eslint": "^8.2.2",
49 | "babel-loader": "^8.0.0-beta.3",
50 | "babel-preset-airbnb": "^2.2.3",
51 | "brfs": "^2.0.2",
52 | "chai": "^3.5.0",
53 | "escope": "^3.3.0",
54 | "eslint": "^4.19.1",
55 | "eslint-config-airbnb": "^14.1.0",
56 | "eslint-plugin-babel": "^5.1.0",
57 | "eslint-plugin-import": "^2.12.0",
58 | "eslint-plugin-jsx-a11y": "^4.0.0",
59 | "eslint-plugin-react": "^6.10.3",
60 | "estraverse": "^4.2.0",
61 | "jasmine-core": "^2.99.1",
62 | "jsdom": "^11.12.0",
63 | "karma": "^4.1.0",
64 | "karma-chai": "^0.1.0",
65 | "karma-jasmine": "^2.0.1",
66 | "karma-jsdom-launcher": "^6.1.3",
67 | "karma-mocha": "^1.3.0",
68 | "karma-mocha-reporter": "^2.2.5",
69 | "karma-script-launcher": "^1.0.0",
70 | "karma-sinon": "^1.0.4",
71 | "karma-webpack": "^3.0.5",
72 | "mocha": "^5.2.0",
73 | "react": "^16.4.2",
74 | "react-addons-test-utils": ">= 15.0.2",
75 | "react-dom": "^16.4.2",
76 | "sinon": "^2.3.1",
77 | "sinon-chai": "^2.14.0",
78 | "webpack": "^4.41.6"
79 | },
80 | "engines": {
81 | "node": ">=6.0.0"
82 | },
83 | "licenses": [
84 | {
85 | "type": "MIT",
86 | "url": "http://github.com/jaanauati/react-dfp/raw/master/LICENSE"
87 | }
88 | ],
89 | "dependencies": {
90 | "babel-plugin-transform-object-assign": "^6.22.0",
91 | "prop-types": "^15.6.1"
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/spec/test-adslot.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import ReactTestUtils from 'react-dom/test-utils';
5 | import { expect } from 'chai';
6 | import sinon from 'sinon';
7 |
8 | import { AdSlot, DFPManager } from '../lib';
9 |
10 | describe('AdSlot', () => {
11 | describe('Component markup', () => {
12 | const compProps = {
13 | dfpNetworkId: '1000',
14 | adUnit: 'foo/bar/baz',
15 | sizes: [[728, 90], 'fluid'],
16 | };
17 | const compTwoProps = {
18 | ...compProps,
19 | slotId: 'testElement',
20 | };
21 |
22 | it('renders an AdSlot with the given elementId', () => {
23 | const component = ReactTestUtils.renderIntoDocument();
24 | const box = ReactTestUtils.findRenderedDOMComponentWithClass(component, 'adBox');
25 | expect(box.id).to.equal('testElement');
26 | });
27 |
28 | it('renders two AdSlots and verify that those get different ids', () => {
29 | let componentOne = ReactTestUtils.renderIntoDocument();
30 | let componentTwo = ReactTestUtils.renderIntoDocument();
31 | componentOne = ReactTestUtils.findRenderedDOMComponentWithClass(componentOne, 'adBox');
32 | componentTwo = ReactTestUtils.findRenderedDOMComponentWithClass(componentTwo, 'adBox');
33 | expect(componentOne.id).to.not.equal(componentTwo.id);
34 | });
35 | });
36 |
37 | describe('DFPManager Interaction', () => {
38 | beforeEach(() => {
39 | DFPManager.registerSlot = sinon.spy(DFPManager, 'registerSlot');
40 | DFPManager.unregisterSlot = sinon.spy(DFPManager, 'unregisterSlot');
41 | });
42 |
43 | it('Registers an AdSlot', () => {
44 | const compProps = {
45 | dfpNetworkId: '1000',
46 | adUnit: 'foo/bar/baz',
47 | slotId: 'testElement1',
48 | sizes: [[728, 90]],
49 | };
50 |
51 | ReactTestUtils.renderIntoDocument();
52 |
53 | sinon.assert.calledOnce(DFPManager.registerSlot);
54 | sinon.assert.calledWithMatch(DFPManager.registerSlot, compProps);
55 | });
56 |
57 | it('Registers a refreshable AdSlot', () => {
58 | const compProps = {
59 | dfpNetworkId: '1000',
60 | adUnit: 'foo/bar/baz',
61 | slotId: 'testElement2',
62 | sizes: [[728, 90]],
63 | };
64 |
65 | ReactTestUtils.renderIntoDocument();
66 |
67 | expect(DFPManager.getRefreshableSlots()).to.contain.all.keys([compProps.slotId]);
68 | expect(DFPManager.getRefreshableSlots()[compProps.slotId]).to.contain.all.keys(compProps);
69 | });
70 |
71 | it('Registers a non refreshable AdSlot', () => {
72 | const compProps = {
73 | dfpNetworkId: '1000',
74 | adUnit: 'foo/bar/baz',
75 | slotId: 'testElement3',
76 | sizes: [[728, 90]],
77 | shouldRefresh: () => false,
78 | };
79 |
80 | ReactTestUtils.renderIntoDocument(
81 | ,
82 | );
83 | expect(Object.keys(DFPManager.getRefreshableSlots()).length).to.equal(0);
84 | });
85 |
86 | it('Refreshes arbitrary ads', () => {
87 | const compProps = {
88 | dfpNetworkId: '1000',
89 | adUnit: 'foo/bar/baz',
90 | sizes: [[728, 90]],
91 | };
92 |
93 | ReactTestUtils.renderIntoDocument(
94 |
95 |
96 |
false} />
97 |
98 | ,
99 | );
100 |
101 | expect(DFPManager.getRefreshableSlots()).to.contain.all.keys(
102 | ['refreshable-1', 'non-refreshable'],
103 | );
104 | expect(
105 | DFPManager.getRefreshableSlots(
106 | 'refreshable-1', 'refreshable-2', 'foo', 'bar',
107 | ),
108 | ).to.contain.all.keys(['refreshable-1', 'refreshable-2']);
109 | });
110 | it('Registers an AdSlot with adSense attributes', () => {
111 | const compProps = {
112 | dfpNetworkId: '1000',
113 | adUnit: 'foo/bar/baz',
114 | slotId: 'testElement4',
115 | sizes: [[728, 90]],
116 | adSenseAttributes: {
117 | site_url: 'www.mysite.com',
118 | adsense_border_color: '#000000',
119 | },
120 | };
121 | const comp2Props = {
122 | dfpNetworkId: '1000',
123 | adUnit: 'foo/bar/baz',
124 | slotId: 'testElement4-2',
125 | sizes: [[728, 90]],
126 | };
127 |
128 | ReactTestUtils.renderIntoDocument(
129 | ,
130 | );
131 | ReactTestUtils.renderIntoDocument(
132 | ,
133 | );
134 | expect(DFPManager.getSlotAdSenseAttributes(compProps.slotId))
135 | .to.deep.equal(compProps.adSenseAttributes);
136 | // make sure there are not side effects
137 | expect(DFPManager.getSlotAdSenseAttributes(comp2Props.slotId))
138 | .to.equal(null);
139 | });
140 |
141 | it('Registers an AdSlot without any adSense attribute', () => {
142 | const compProps = {
143 | dfpNetworkId: '1000',
144 | adUnit: 'foo/bar/baz',
145 | slotId: 'testElement4',
146 | sizes: [[728, 90]],
147 | };
148 |
149 | ReactTestUtils.renderIntoDocument(
150 | ,
151 | );
152 | expect(DFPManager.getSlotAdSenseAttributes(compProps.slotId))
153 | .to.equal(null);
154 | });
155 |
156 | it('Registers an AdSlot with custom targeting arguments', () => {
157 | const compProps = {
158 | dfpNetworkId: '1000',
159 | adUnit: 'foo/bar/baz',
160 | slotId: 'testElement4',
161 | sizes: [[728, 90]],
162 | targetingArguments: { team: 'river plate', player: 'pisculichi' },
163 | };
164 |
165 | ReactTestUtils.renderIntoDocument(
166 | ,
167 | );
168 | expect(DFPManager.getSlotTargetingArguments(compProps.slotId))
169 | .to.contain.all.keys(compProps.targetingArguments);
170 | });
171 |
172 | it('Registers an AdSlot without custom targeting arguments', () => {
173 | const compProps = {
174 | dfpNetworkId: '1000',
175 | adUnit: 'foo/bar/baz',
176 | slotId: 'testElement5',
177 | sizes: [[728, 90]],
178 | };
179 |
180 | ReactTestUtils.renderIntoDocument(
181 | ,
182 | );
183 | expect(DFPManager.getSlotTargetingArguments(compProps.slotId)).to.equal(null);
184 | });
185 |
186 |
187 | it('Unregisters an AdSlot', () => {
188 | const compProps = {
189 | dfpNetworkId: '1000',
190 | adUnit: 'foo/bar/baz',
191 | slotId: 'testElement6',
192 | sizes: [[728, 90]],
193 | };
194 |
195 | const component = ReactTestUtils.renderIntoDocument(
196 | ,
197 | );
198 |
199 | ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(component).parentNode);
200 |
201 | sinon.assert.calledOnce(DFPManager.unregisterSlot);
202 | sinon.assert.calledWithMatch(DFPManager.unregisterSlot,
203 | { slotId: compProps.slotId });
204 | });
205 |
206 | afterEach(() => {
207 | DFPManager.registerSlot.restore();
208 | DFPManager.unregisterSlot.restore();
209 | Object.keys(DFPManager.getRegisteredSlots()).forEach((slotId) => {
210 | DFPManager.unregisterSlot({ slotId });
211 | });
212 | });
213 | });
214 | });
215 |
--------------------------------------------------------------------------------
/spec/test-dfpslotsprovider.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import TestUtils from 'react-dom/test-utils';
4 | import { expect } from 'chai';
5 | import sinon from 'sinon';
6 |
7 | import { DFPSlotsProvider, AdSlot, DFPManager } from '../lib';
8 |
9 | describe('DFPSlotsProvider', () => {
10 | describe('GDPR', () => {
11 | beforeAll(() => {
12 | DFPManager.gptLoadAds = sinon.stub(
13 | DFPManager,
14 | 'gptLoadAds',
15 | ).resolves(true);
16 | DFPManager.load = sinon.spy(DFPManager, 'load');
17 | });
18 |
19 | it('Fetches personalized ads by default', () => {
20 | const otherProps = {
21 | dfpNetworkId: '1000',
22 | adUnit: 'foo/bar/baz',
23 | };
24 | TestUtils.renderIntoDocument(
25 |
26 |
27 | ,
28 | );
29 | expect(DFPManager.personalizedAdsEnabled()).to.equal(true);
30 | });
31 |
32 | it('Can disable personalized ads', () => {
33 | const otherProps = {
34 | dfpNetworkId: '1000',
35 | adUnit: 'foo/bar/baz',
36 | };
37 | TestUtils.renderIntoDocument(
38 |
39 |
40 | ,
41 | );
42 | expect(DFPManager.personalizedAdsEnabled()).to.equal(false);
43 | });
44 |
45 | it('Can enable personalized ads', () => {
46 | const otherProps = {
47 | dfpNetworkId: '1000',
48 | adUnit: 'foo/bar/baz',
49 | };
50 | TestUtils.renderIntoDocument(
51 |
52 |
53 | ,
54 | );
55 | expect(DFPManager.personalizedAdsEnabled()).to.equal(true);
56 | });
57 |
58 | it('Set cookies by default', () => {
59 | const otherProps = {
60 | dfpNetworkId: '1000',
61 | adUnit: 'foo/bar/baz',
62 | };
63 | TestUtils.renderIntoDocument(
64 |
65 |
66 | ,
67 | );
68 | expect(DFPManager.cookiesEnabled()).to.equal(true);
69 | });
70 |
71 | it('Can disable cookies', () => {
72 | const otherProps = {
73 | dfpNetworkId: '1000',
74 | adUnit: 'foo/bar/baz',
75 | };
76 | TestUtils.renderIntoDocument(
77 |
78 |
79 | ,
80 | );
81 | expect(DFPManager.cookiesEnabled()).to.equal(false);
82 | });
83 |
84 | it('Can enable cookies', () => {
85 | const otherProps = {
86 | dfpNetworkId: '1000',
87 | adUnit: 'foo/bar/baz',
88 | };
89 | TestUtils.renderIntoDocument(
90 |
91 |
92 | ,
93 | );
94 | expect(DFPManager.cookiesEnabled()).to.equal(true);
95 | });
96 |
97 |
98 | afterAll(() => {
99 | DFPManager.gptLoadAds.restore();
100 | DFPManager.load.restore();
101 | });
102 | });
103 |
104 | describe('Lazy Load', () => {
105 | beforeAll(() => {
106 | DFPManager.gptLoadAds = sinon.stub(
107 | DFPManager,
108 | 'gptLoadAds',
109 | ).resolves(true);
110 | DFPManager.load = sinon.spy(DFPManager, 'load');
111 | });
112 |
113 | it('Lazy load disabled by default', () => {
114 | const otherProps = {
115 | dfpNetworkId: '1000',
116 | adUnit: 'foo/bar/baz',
117 | };
118 | TestUtils.renderIntoDocument(
119 |
120 |
121 | ,
122 | );
123 | expect(DFPManager.lazyLoadIsEnabled()).to.equal(false);
124 | expect(DFPManager.getLazyLoadConfig()).to.equal(null);
125 | });
126 |
127 | it('Can enable lazy load', () => {
128 | const otherProps = {
129 | dfpNetworkId: '1000',
130 | adUnit: 'foo/bar/baz',
131 | };
132 | TestUtils.renderIntoDocument(
133 |
134 |
135 | ,
136 | );
137 | expect(DFPManager.lazyLoadIsEnabled()).to.equal(true);
138 | expect(DFPManager.getLazyLoadConfig()).to.equal(null);
139 | });
140 |
141 | it('Can pass arbitrary configs', () => {
142 | const otherProps = {
143 | dfpNetworkId: '1000',
144 | adUnit: 'foo/bar/baz',
145 | };
146 | const lazyLoadConfig = {
147 | fetchMarginPercent: 1,
148 | renderMarginPercent: 1,
149 | mobileScaling: 1,
150 | };
151 | TestUtils.renderIntoDocument(
152 |
153 |
154 | ,
155 | );
156 | expect(DFPManager.lazyLoadIsEnabled()).to.equal(true);
157 | expect(DFPManager.getLazyLoadConfig()).to.deep.equal(lazyLoadConfig);
158 | });
159 |
160 | it('Can disable lazyLoad', () => {
161 | const otherProps = {
162 | dfpNetworkId: '1000',
163 | adUnit: 'foo/bar/baz',
164 | lazyLoad: false,
165 | };
166 | TestUtils.renderIntoDocument(
167 |
168 |
169 | ,
170 | );
171 | expect(DFPManager.lazyLoadIsEnabled()).to.equal(false);
172 | expect(DFPManager.getLazyLoadConfig()).to.equal(null);
173 | });
174 |
175 | afterAll(() => {
176 | DFPManager.gptLoadAds.restore();
177 | DFPManager.load.restore();
178 | });
179 | });
180 |
181 | describe('Component markup', () => {
182 | let component;
183 |
184 | beforeAll(() => {
185 | DFPManager.gptLoadAds = sinon.stub(
186 | DFPManager,
187 | 'gptLoadAds',
188 | ).resolves(true);
189 | DFPManager.load = sinon.spy(DFPManager, 'load');
190 | });
191 |
192 | beforeEach(() => {
193 | const providerProps = {
194 | dfpNetworkId: '1000',
195 | adUnit: 'foo/bar/baz',
196 | };
197 |
198 | component = TestUtils.renderIntoDocument(
199 |
200 |
201 | ,
202 | );
203 | });
204 |
205 | it('renders an adBox with the given elementId', () => {
206 | const box = TestUtils.findRenderedDOMComponentWithClass(component, 'adBox');
207 | expect(box.id).to.equal('testElement3');
208 | });
209 |
210 | afterAll(() => {
211 | DFPManager.gptLoadAds.restore();
212 | DFPManager.load.restore();
213 | });
214 | });
215 |
216 | describe('DFPManager Api calls', () => {
217 | beforeAll(() => {
218 | DFPManager.gptLoadAds = sinon.stub(
219 | DFPManager,
220 | 'gptLoadAds',
221 | ).resolves(true);
222 | });
223 |
224 | beforeEach(() => {
225 | DFPManager.registerSlot = sinon.spy(DFPManager, 'registerSlot');
226 | DFPManager.unregisterSlot = sinon.spy(DFPManager, 'unregisterSlot');
227 | DFPManager.setCollapseEmptyDivs = sinon.spy(DFPManager, 'setCollapseEmptyDivs');
228 | DFPManager.load = sinon.spy(DFPManager, 'load');
229 | DFPManager.reload = sinon.spy(DFPManager, 'reload');
230 | DFPManager.configureLimitedAds = sinon.spy(DFPManager, 'configureLimitedAds');
231 | });
232 |
233 | it('Registers an AdSlot', () => {
234 | const providerProps = {
235 | dfpNetworkId: '1000',
236 | adUnit: 'foo/bar/baz',
237 | };
238 |
239 | const compProps = {
240 | slotId: 'testElement5',
241 | sizes: [[728, 90]],
242 | };
243 |
244 | TestUtils.renderIntoDocument(
245 |
246 |
247 | ,
248 | );
249 |
250 | sinon.assert.calledOnce(DFPManager.registerSlot);
251 | sinon.assert.calledWithMatch(DFPManager.registerSlot, { ...providerProps, ...compProps });
252 | sinon.assert.calledOnce(DFPManager.load);
253 | sinon.assert.notCalled(DFPManager.reload);
254 | });
255 |
256 | it('Does not reload ads when the prop dfpNetworkId is updated', () => {
257 | const providerProps = {
258 | dfpNetworkId: '1000',
259 | adUnit: 'foo/bar/baz',
260 | };
261 |
262 | const compProps = {
263 | slotId: 'testElement5',
264 | sizes: [[728, 90]],
265 | };
266 |
267 |
268 | const container = document.createElement('div');
269 | ReactDOM.render(
270 |
271 |
272 | ,
273 | container,
274 | );
275 |
276 | ReactDOM.render(
277 |
278 |
279 | ,
280 | container,
281 | );
282 |
283 | sinon.assert.calledOnce(DFPManager.registerSlot);
284 | sinon.assert.calledWithMatch(DFPManager.registerSlot, { ...providerProps, ...compProps });
285 | sinon.assert.calledOnce(DFPManager.load);
286 | sinon.assert.notCalled(DFPManager.reload);
287 | });
288 |
289 | it('Does not reload ads when the prop personalizedAds is updated', () => {
290 | const providerProps = {
291 | dfpNetworkId: '1000',
292 | adUnit: 'foo/bar/baz',
293 | };
294 |
295 | const compProps = {
296 | slotId: 'testElement5',
297 | sizes: [[728, 90]],
298 | };
299 |
300 | const container = document.createElement('div');
301 | ReactDOM.render(
302 |
303 |
304 | ,
305 | container,
306 | );
307 |
308 | ReactDOM.render(
309 |
310 |
311 | ,
312 | container,
313 | );
314 |
315 | sinon.assert.calledOnce(DFPManager.registerSlot);
316 | sinon.assert.calledWithMatch(DFPManager.registerSlot, { ...providerProps, ...compProps });
317 | sinon.assert.calledOnce(DFPManager.load);
318 | sinon.assert.notCalled(DFPManager.reload);
319 | });
320 |
321 | it('Reloads ads when any of the configured props is updated', () => {
322 | const providerProps = {
323 | dfpNetworkId: '1000',
324 | adUnit: 'foo/bar/baz',
325 | autoReload: { personalizedAds: true },
326 | };
327 |
328 | const compProps = {
329 | slotId: 'testElement5',
330 | sizes: [[728, 90]],
331 | };
332 |
333 |
334 | const container = document.createElement('div');
335 | ReactDOM.render(
336 |
337 |
338 | ,
339 | container,
340 | );
341 |
342 | ReactDOM.render(
343 |
344 |
345 | ,
346 | container,
347 | );
348 |
349 | sinon.assert.calledOnce(DFPManager.registerSlot);
350 | sinon.assert.calledOnce(DFPManager.load);
351 | sinon.assert.calledOnce(DFPManager.reload);
352 | });
353 |
354 | it('Ads are not reloaded when any of these props is updated: '
355 | + 'singleRequest, adUnit, sizeMapping, adSenseAttributes, '
356 | + 'targetingArguments, collapseEmptyDivs, adSenseAttrs, lazyLoad.'
357 | , () => {
358 | const providerProps = {
359 | dfpNetworkId: '1000',
360 | adUnit: 'foo/bar/baz',
361 | singleRequest: false,
362 | lazyLoad: false,
363 | };
364 |
365 | const compProps = {
366 | slotId: 'testElement5',
367 | sizes: [[728, 90]],
368 | };
369 |
370 |
371 | const container = document.createElement('div');
372 | ReactDOM.render(
373 |
374 |
375 | ,
376 | container,
377 | );
378 | const newProps = {
379 | singleRequest: true,
380 | adUnit: 'a/b',
381 | sizeMapping: [
382 | { viewport: [1024, 768], sizes: [[728, 90], [300, 250]] },
383 | { viewport: [900, 768], sizes: [[300, 250]] },
384 | ],
385 | adSenseAttributes: { site_url: 'example.com' },
386 | targetingArguments: { customKw: 'basic example' },
387 | collapseEmptyDivs: true,
388 | lazyLoad: true,
389 | };
390 |
391 | ReactDOM.render(
392 |
393 |
394 | ,
395 | container,
396 | );
397 |
398 | sinon.assert.calledOnce(DFPManager.registerSlot);
399 | sinon.assert.calledOnce(DFPManager.load);
400 | sinon.assert.notCalled(DFPManager.reload);
401 | });
402 |
403 | it('Can dissable auto-refresh', () => {
404 | const providerProps = {
405 | dfpNetworkId: '1000',
406 | adUnit: 'foo/bar/baz',
407 | };
408 |
409 | const compProps = {
410 | slotId: 'testElement5',
411 | sizes: [[728, 90]],
412 | };
413 |
414 |
415 | const container = document.createElement('div');
416 | ReactDOM.render(
417 |
418 |
419 | ,
420 | container,
421 | );
422 |
423 | ReactDOM.render(
424 |
428 |
429 | ,
430 | container,
431 | );
432 |
433 | sinon.assert.calledOnce(DFPManager.registerSlot);
434 | sinon.assert.calledWithMatch(DFPManager.registerSlot, { ...providerProps, ...compProps });
435 | sinon.assert.calledOnce(DFPManager.load);
436 | sinon.assert.notCalled(DFPManager.reload);
437 | });
438 | it('Gets singleRequest enabled by default', () => {
439 | const providerProps = {
440 | dfpNetworkId: '1000',
441 | adUnit: 'foo/bar/baz',
442 | };
443 |
444 | const compProps = {
445 | slotId: 'testElement6',
446 | sizes: [[728, 90]],
447 | };
448 |
449 | TestUtils.renderIntoDocument(
450 |
451 |
452 | ,
453 | );
454 |
455 | expect(DFPManager.singleRequestIsEnabled()).equal(true);
456 | });
457 |
458 | it('Can disable singleRequest', () => {
459 | const providerProps = {
460 | dfpNetworkId: '1000',
461 | adUnit: 'foo/bar/baz',
462 | singleRequest: false,
463 | };
464 |
465 | const compProps = {
466 | slotId: 'testElement7',
467 | sizes: [[728, 90]],
468 | };
469 |
470 | TestUtils.renderIntoDocument(
471 |
472 |
473 | ,
474 | );
475 |
476 | expect(DFPManager.singleRequestIsEnabled()).equal(false);
477 | });
478 |
479 | it('Can enable singleRequest', () => {
480 | const providerProps = {
481 | dfpNetworkId: '1000',
482 | adUnit: 'foo/bar/baz',
483 | singleRequest: true,
484 | };
485 |
486 | const compProps = {
487 | slotId: 'testElement8',
488 | sizes: [[728, 90]],
489 | };
490 |
491 | TestUtils.renderIntoDocument(
492 |
493 |
494 | ,
495 | );
496 |
497 | expect(DFPManager.singleRequestIsEnabled()).equal(true);
498 | });
499 |
500 | it('Disables singleRequest', () => {
501 | const providerProps = {
502 | dfpNetworkId: '1000',
503 | adUnit: 'foo/bar/baz',
504 | singleRequest: false,
505 | };
506 |
507 | const compProps = {
508 | slotId: 'testElement9',
509 | sizes: [[728, 90]],
510 | };
511 |
512 | TestUtils.renderIntoDocument(
513 |
514 |
515 | ,
516 | );
517 |
518 | expect(DFPManager.singleRequestIsEnabled()).equal(false);
519 | });
520 |
521 | it('disableInitialLoad is not enabled by default', () => {
522 | const providerProps = {
523 | dfpNetworkId: '1000',
524 | adUnit: 'foo/bar/baz',
525 | };
526 |
527 | const compProps = {
528 | slotId: 'testElement8',
529 | sizes: [[728, 90]],
530 | };
531 |
532 | TestUtils.renderIntoDocument(
533 |
534 |
535 | ,
536 | );
537 |
538 | expect(DFPManager.disableInitialLoadIsEnabled()).equal(false);
539 | });
540 |
541 | it('Can turn on disableInitialLoad', () => {
542 | const providerProps = {
543 | dfpNetworkId: '1000',
544 | adUnit: 'foo/bar/baz',
545 | disableInitialLoad: true,
546 | };
547 |
548 | const compProps = {
549 | slotId: 'testElement8',
550 | sizes: [[728, 90]],
551 | };
552 |
553 | TestUtils.renderIntoDocument(
554 |
555 |
556 | ,
557 | );
558 |
559 | expect(DFPManager.disableInitialLoadIsEnabled()).equal(true);
560 | });
561 |
562 | it('Can turn off disableInitialLoad', () => {
563 | const providerProps = {
564 | dfpNetworkId: '1000',
565 | adUnit: 'foo/bar/baz',
566 | disableInitialLoad: false,
567 | };
568 |
569 | const compProps = {
570 | slotId: 'testElement8',
571 | sizes: [[728, 90]],
572 | };
573 |
574 | TestUtils.renderIntoDocument(
575 |
576 |
577 | ,
578 | );
579 |
580 | expect(DFPManager.disableInitialLoadIsEnabled()).equal(false);
581 | });
582 |
583 | it('Registers a refreshable AdSlot', () => {
584 | const providerProps = {
585 | dfpNetworkId: '1000',
586 | adUnit: 'foo/bar/baz',
587 | };
588 |
589 | const compProps = {
590 | slotId: 'testElement10',
591 | sizes: [[728, 90]],
592 | };
593 |
594 | TestUtils.renderIntoDocument(
595 |
596 |
597 | ,
598 | );
599 |
600 | expect(DFPManager.getRefreshableSlots()).to.contain.all.keys([compProps.slotId]);
601 | expect(DFPManager.getRefreshableSlots()[compProps.slotId]).to.contain.all.keys(
602 | { ...providerProps, ...compProps },
603 | );
604 | });
605 |
606 | it('Registers a non refreshable AdSlot', () => {
607 | const providerProps = {
608 | dfpNetworkId: '1000',
609 | adUnit: 'foo/bar/baz',
610 | };
611 |
612 | const compProps = {
613 | slotId: 'testElement11',
614 | sizes: [[728, 90]],
615 | shouldRefresh: () => false,
616 | };
617 |
618 | TestUtils.renderIntoDocument(
619 |
620 |
621 | ,
622 | );
623 | expect(Object.keys(DFPManager.getRefreshableSlots()).length).to.equal(0);
624 | });
625 |
626 | it('Registers global adSense attributes', () => {
627 | const providerProps = {
628 | dfpNetworkId: '1000',
629 | adUnit: 'foo/bar/baz',
630 | adSenseAttributes: {
631 | site_url: 'www.mysite.com',
632 | adsense_border_color: '#0000FF',
633 | },
634 | };
635 | const compProps = {
636 | slotId: 'testElement12',
637 | sizes: [[728, 90]],
638 | };
639 | TestUtils.renderIntoDocument(
640 |
641 |
642 | ,
643 | );
644 | expect(DFPManager.getAdSenseAttributes())
645 | .to.deep.equal(providerProps.adSenseAttributes);
646 | expect(DFPManager.getSlotAdSenseAttributes(compProps.slotId))
647 | .to.deep.equal(null);
648 | });
649 |
650 | it('Registers an AdSlot with adSense attributes', () => {
651 | const providerProps = {
652 | dfpNetworkId: '1000',
653 | adUnit: 'foo/bar/baz',
654 | adSenseAttributes: {
655 | site_url: 'www.mysite.com',
656 | adsense_border_color: '#0000FF',
657 | },
658 | };
659 | const compProps = {
660 | slotId: 'testElement13',
661 | sizes: [[728, 90]],
662 | adSenseAttributes: {
663 | site_url: 'www.mysite.com',
664 | adsense_border_color: '#000000',
665 | adsense_channel_ids: '271828183+314159265',
666 | },
667 | };
668 | const comp2Props = {
669 | slotId: 'testElement14',
670 | sizes: [[728, 90]],
671 | };
672 | TestUtils.renderIntoDocument(
673 |
674 |
675 |
676 | ,
677 | );
678 | expect(DFPManager.getAdSenseAttributes())
679 | .to.deep.equal(providerProps.adSenseAttributes);
680 | expect(DFPManager.getSlotAdSenseAttributes(compProps.slotId))
681 | .to.deep.equal(compProps.adSenseAttributes);
682 | expect(DFPManager.getSlotAdSenseAttributes(comp2Props.slotId))
683 | .to.deep.equal(null);
684 | });
685 |
686 | it('Registers an AdSlot with custom targeting arguments', () => {
687 | const providerProps = {
688 | dfpNetworkId: '1000',
689 | adUnit: 'foo/bar/baz',
690 | targetingArguments: { team: 'river plate', player: 'pisculichi' },
691 | };
692 | const compProps = {
693 | slotId: 'testElement15',
694 | sizes: [[728, 90]],
695 | };
696 | TestUtils.renderIntoDocument(
697 |
698 |
699 | ,
700 | );
701 | expect(DFPManager.getSlotTargetingArguments(compProps.slotId))
702 | .to.contain.all.keys(providerProps.targetingArguments);
703 | });
704 |
705 | it('Registers an AdSlot without custom targeting arguments', () => {
706 | const providerProps = {
707 | dfpNetworkId: '1000',
708 | adUnit: 'foo/bar/baz',
709 | };
710 | const compProps = {
711 | slotId: 'testElement16',
712 | sizes: [[728, 90]],
713 | };
714 |
715 | TestUtils.renderIntoDocument(
716 |
717 |
718 | ,
719 | );
720 | expect(DFPManager.getSlotTargetingArguments(compProps.slotId)).to.equal(null);
721 | });
722 |
723 |
724 | it('Unregisters an AdSlot', () => {
725 | const providerProps = {
726 | dfpNetworkId: '1000',
727 | adUnit: 'foo/bar/baz',
728 | };
729 | const compProps = {
730 | slotId: 'testElement17',
731 | sizes: [[728, 90]],
732 | };
733 |
734 |
735 | const component = TestUtils.renderIntoDocument(
736 |
737 |
738 | ,
739 | );
740 |
741 | // eslint-disable-next-line react/no-find-dom-node
742 | ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(component).parentNode);
743 |
744 | sinon.assert.calledOnce(DFPManager.unregisterSlot);
745 | sinon.assert.calledWithMatch(
746 | DFPManager.unregisterSlot,
747 | { slotId: compProps.slotId },
748 | );
749 | });
750 |
751 | it('collapseEmptyDivs is disabled by default', () => {
752 | const providerProps = {
753 | dfpNetworkId: '1000',
754 | adUnit: 'foo/bar/baz',
755 | };
756 |
757 | const compProps = {
758 | slotId: 'testElement18',
759 | sizes: [[728, 90]],
760 | };
761 |
762 | TestUtils.renderIntoDocument(
763 |
764 |
765 | ,
766 | );
767 |
768 | sinon.assert.calledOnce(DFPManager.setCollapseEmptyDivs);
769 | sinon.assert.calledWith(DFPManager.setCollapseEmptyDivs, null);
770 | });
771 |
772 | it('enable collapseEmptyDivs and set parameter to false', () => {
773 | const providerProps = {
774 | dfpNetworkId: '1000',
775 | adUnit: 'foo/bar/baz',
776 | collapseEmptyDivs: false,
777 | };
778 |
779 | const compProps = {
780 | slotId: 'testElement19',
781 | sizes: [[728, 90]],
782 | };
783 |
784 | TestUtils.renderIntoDocument(
785 |
786 |
787 | ,
788 | );
789 |
790 | sinon.assert.calledOnce(DFPManager.setCollapseEmptyDivs);
791 | sinon.assert.calledWith(DFPManager.setCollapseEmptyDivs, false);
792 | });
793 |
794 | it('Does configureLimitedAds if prop is provided', () => {
795 | const providerProps = {
796 | dfpNetworkId: '1000',
797 | adUnit: 'foo/bar/baz',
798 | limitedAds: true,
799 | };
800 |
801 | const container = document.createElement('div');
802 | ReactDOM.render(
803 | ,
804 | container,
805 | );
806 |
807 | sinon.assert.calledOnce(DFPManager.configureLimitedAds);
808 | sinon.assert.calledWith(DFPManager.configureLimitedAds, true);
809 | });
810 |
811 | afterEach(() => {
812 | DFPManager.registerSlot.restore();
813 | DFPManager.unregisterSlot.restore();
814 | DFPManager.setCollapseEmptyDivs.restore();
815 | Object.keys(DFPManager.getRegisteredSlots()).forEach((slotId) => {
816 | DFPManager.unregisterSlot({ slotId });
817 | });
818 | DFPManager.load.restore();
819 | DFPManager.reload.restore();
820 | DFPManager.configureLimitedAds.restore();
821 | });
822 |
823 | afterAll(() => {
824 | DFPManager.gptLoadAds.restore();
825 | });
826 | });
827 | });
828 |
--------------------------------------------------------------------------------
/spec/test-manager.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import sinon from 'sinon';
3 | import { DFPManager } from '../lib';
4 |
5 | describe('DFPManager', () => {
6 | describe('GDPR - personalized ads', () => {
7 | it('Fetches personalized ads by default', function registersAdSlot() {
8 | expect(DFPManager.personalizedAdsEnabled()).equal(true);
9 | });
10 | it('Can disable personalized ads', function registersAdSlot() {
11 | DFPManager.configurePersonalizedAds(false);
12 | expect(DFPManager.personalizedAdsEnabled()).equal(false);
13 | });
14 | it('Can enable personalized ads', function registersAdSlot() {
15 | DFPManager.configurePersonalizedAds(true);
16 | expect(DFPManager.personalizedAdsEnabled()).equal(true);
17 | });
18 | });
19 |
20 | describe('GDPR - cookies', () => {
21 | it('Sets cookies by default', function registersAdSlot() {
22 | expect(DFPManager.cookiesEnabled()).equal(true);
23 | });
24 | it('Can disable cookies', function registersAdSlot() {
25 | DFPManager.configureCookieOption(false);
26 | expect(DFPManager.cookiesEnabled()).equal(false);
27 | });
28 | it('Can enable cookies', function registersAdSlot() {
29 | DFPManager.configureCookieOption(true);
30 | expect(DFPManager.cookiesEnabled()).equal(true);
31 | });
32 | });
33 |
34 | describe('Lazy Loading', () => {
35 | it('Lazy load is disabled by default', function registersAdSlot() {
36 | expect(DFPManager.lazyLoadIsEnabled()).equal(false);
37 | expect(DFPManager.getLazyLoadConfig()).equal(null);
38 | });
39 | it('Can enable lazy load', function canEnableLazyLoad() {
40 | DFPManager.configureLazyLoad(true);
41 | expect(DFPManager.lazyLoadIsEnabled()).equal(true);
42 | });
43 | it('There isnt custom config by default', function noConfigByDefault() {
44 | DFPManager.configureLazyLoad(true);
45 | expect(DFPManager.getLazyLoadConfig()).equal(null);
46 | });
47 | it('Can pass any arbitrary config', function noConfigByDefault() {
48 | DFPManager.configureLazyLoad(true, { renderMarginPercent: 1 });
49 | expect(DFPManager.getLazyLoadConfig()).to.deep.equal({
50 | renderMarginPercent: 1,
51 | });
52 | });
53 | it('Can disable lazy load', function canDisableLazyLoad() {
54 | DFPManager.configureLazyLoad(false);
55 | expect(DFPManager.lazyLoadIsEnabled()).equal(false);
56 | });
57 | });
58 |
59 | describe('Limited Ads', () => {
60 | it('should have Limited Ads disabled by default', function registersAdSlot() {
61 | expect(DFPManager.limitedAdsIsEnabled()).equal(false);
62 | });
63 | it('should configure Limited Ads', function registersAdSlot() {
64 | DFPManager.configureLimitedAds(true);
65 | expect(DFPManager.limitedAdsIsEnabled()).equal(true);
66 | });
67 | });
68 |
69 | describe('Single Request', () => {
70 | it('Gets singleRequest enabled by default', function registersAdSlot() {
71 | expect(DFPManager.singleRequestIsEnabled()).equal(true);
72 | });
73 | it('Can disable singleRequest', function registersAdSlot() {
74 | DFPManager.configureSingleRequest(false);
75 | expect(DFPManager.singleRequestIsEnabled()).equal(false);
76 | });
77 | it('Can enable singleRequest', function registersAdSlot() {
78 | DFPManager.configureSingleRequest(true);
79 | expect(DFPManager.singleRequestIsEnabled()).equal(true);
80 | });
81 | });
82 |
83 | describe('Disable Initial Load', () => {
84 | it('disableInitiaLoad disabled by default', function testDisableInitialLoad1() {
85 | expect(DFPManager.disableInitialLoadIsEnabled()).equal(false);
86 | });
87 | it('Can enable disableInitialLoad', function testDisableInitialLoad2() {
88 | DFPManager.configureDisableInitialLoad(true);
89 | expect(DFPManager.disableInitialLoadIsEnabled()).equal(true);
90 | });
91 | it('Can disable disableInitialLoad', function testDisableInitialLoad3() {
92 | DFPManager.configureDisableInitialLoad(false);
93 | expect(DFPManager.disableInitialLoadIsEnabled()).equal(false);
94 | });
95 | });
96 |
97 | describe('AdSense attributes', () => {
98 | beforeEach(function beforeEach() {
99 | this.argsList1 = {
100 | page_url: 'www.mysite.com',
101 | adsense_url_color: '#000000',
102 | };
103 | this.argsList2 = { adsense_ad_format: '250x250_as' };
104 | DFPManager.setAdSenseAttributes(this.argsList1);
105 | DFPManager.setAdSenseAttribute('adsense_ad_format', '250x250_as');
106 | });
107 |
108 | it('Properly tracks global AdSense attributes', function registersAdSlot() {
109 | expect(DFPManager.getAdSenseAttributes()).to.contain.keys(
110 | { ...this.argsList1, ...this.argsList2 },
111 | );
112 | });
113 | });
114 |
115 | describe('Targeting arguments', () => {
116 | beforeEach(function beforeEach() {
117 | this.argsList1 = { k: 'yeah' };
118 | this.argsList2 = { k: 'yeah' };
119 | DFPManager.setTargetingArguments(this.argsList1);
120 | DFPManager.setTargetingArguments(this.argsList2);
121 | });
122 |
123 | it('Registers global targeting arguments', function registersAdSlot() {
124 | expect(DFPManager.getTargetingArguments()).to.contain.keys(
125 | { ...this.argsList1, ...this.argsList2 },
126 | );
127 | });
128 | });
129 |
130 | describe('Creation of ad slots ', () => {
131 | beforeAll(function before() {
132 | DFPManager.gptLoadAds = sinon.stub(
133 | DFPManager,
134 | 'gptLoadAds',
135 | ).resolves(true);
136 | DFPManager.gptRefreshAds = sinon.stub(
137 | DFPManager,
138 | 'gptRefreshAds',
139 | ).resolves(true);
140 | DFPManager.destroyGPTSlots = sinon.stub(
141 | DFPManager,
142 | 'destroyGPTSlots',
143 | ).resolves(true);
144 | this.slotProps = {
145 | dfpNetworkId: '1000',
146 | adUnit: 'foo/bar/baz',
147 | sizes: [[728, 90]],
148 | adSenseAttributes: {
149 | site_url: 'www.mysite.com',
150 | adsense_border_color: '#000000',
151 | },
152 | slotShouldRefresh: () => true,
153 | };
154 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement1' });
155 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement2' });
156 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement3' });
157 | DFPManager.load();
158 | DFPManager.refresh();
159 | });
160 |
161 | it('Registers ad slots', function registersAdSlot() {
162 | expect(Object.keys(DFPManager.getRegisteredSlots()).length).to.equal(3);
163 | expect(DFPManager.getRegisteredSlots()).to.contain.all
164 | .keys(['testElement1', 'testElement2', 'testElement3']);
165 | expect(DFPManager.getRegisteredSlots().testElement1)
166 | .to.contain.all.keys({ ...this.slotProps, slotId: 'testElement1' });
167 | expect(DFPManager.getRegisteredSlots().testElement2)
168 | .to.contain.all.keys({ ...this.slotProps, slotId: 'testElement2' });
169 | expect(DFPManager.getRegisteredSlots().testElement3)
170 | .to.contain.all.keys({ ...this.slotProps, slotId: 'testElement3' });
171 | expect(DFPManager.getRegisteredSlots().testElement1)
172 | .to.deep.include({ ...this.slotProps, slotId: 'testElement1' });
173 | expect(DFPManager.getRegisteredSlots().testElement2)
174 | .to.deep.include({ ...this.slotProps, slotId: 'testElement2' });
175 | expect(DFPManager.getRegisteredSlots().testElement3)
176 | .to.deep.include({ ...this.slotProps, slotId: 'testElement3' });
177 | });
178 |
179 | it('Loads all the ads by default', function adsLoaded() {
180 | sinon.assert.calledOnce(DFPManager.gptLoadAds);
181 | sinon.assert.calledWith(
182 | DFPManager.gptLoadAds,
183 | ['testElement1', 'testElement2', 'testElement3'],
184 | );
185 | });
186 |
187 | it('Refreshes all the ads by default', function adsLoaded() {
188 | sinon.assert.calledOnce(DFPManager.gptRefreshAds);
189 | sinon.assert.calledWith(
190 | DFPManager.gptRefreshAds,
191 | ['testElement1', 'testElement2', 'testElement3'],
192 | );
193 | });
194 |
195 | afterAll(function afterEach() {
196 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement1' });
197 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement2' });
198 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement3' });
199 | DFPManager.gptLoadAds.restore();
200 | DFPManager.gptRefreshAds.restore();
201 | DFPManager.destroyGPTSlots.restore();
202 | });
203 | });
204 |
205 | describe('Initalization of arbitrary slots ', () => {
206 | beforeAll(function before() {
207 | DFPManager.gptLoadAds = sinon.stub(
208 | DFPManager,
209 | 'gptLoadAds',
210 | ).resolves(true);
211 | DFPManager.gptRefreshAds = sinon.stub(
212 | DFPManager,
213 | 'gptRefreshAds',
214 | ).resolves(true);
215 | DFPManager.destroyGPTSlots = sinon.stub(
216 | DFPManager,
217 | 'destroyGPTSlots',
218 | ).resolves(true);
219 | this.slotProps = {
220 | dfpNetworkId: '1000',
221 | adUnit: 'foo/bar/baz',
222 | sizes: [[728, 90]],
223 | adSenseAttributes: {
224 | site_url: 'www.mysite.com',
225 | adsense_border_color: '#000000',
226 | },
227 | slotShouldRefresh: () => true,
228 | };
229 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement4' }, false);
230 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement5' }, false);
231 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement6' }, false);
232 | DFPManager.registerSlot({ ...this.slotProps, slotId: 'testElement7' }, false);
233 | DFPManager.load('testElement4', 'testElement6', 'testElement7');
234 | DFPManager.refresh('testElement4', 'testElement7');
235 | });
236 |
237 | it('Loads arbitrary ads', function adsLoaded() {
238 | sinon.assert.calledOnce(DFPManager.gptLoadAds);
239 | sinon.assert.calledWith(
240 | DFPManager.gptLoadAds,
241 | ['testElement4', 'testElement6', 'testElement7'],
242 | );
243 | });
244 |
245 | it('Refreshes arbitrary ads', function adsLoaded() {
246 | sinon.assert.calledOnce(DFPManager.gptRefreshAds);
247 | sinon.assert.calledWith(
248 | DFPManager.gptRefreshAds, ['testElement4', 'testElement7'],
249 | );
250 | });
251 |
252 | afterAll(function afterEach() {
253 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement4' });
254 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement5' });
255 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement6' });
256 | DFPManager.unregisterSlot({ ...this.slotProps, slotId: 'testElement7' });
257 | DFPManager.gptLoadAds.restore();
258 | DFPManager.gptRefreshAds.restore();
259 | DFPManager.destroyGPTSlots.restore();
260 | });
261 | });
262 | });
263 |
--------------------------------------------------------------------------------