Powering the advertising
221 | that powers the internet™
222 |
├── .gitignore
├── .jshintrc
├── CHANGELOG.md
├── CONTRIBUTION.md
├── Gruntfile.js
├── LICENSE.md
├── README.md
├── dist
├── appnexus-html5-lib.js
└── appnexus-html5-lib.min.js
├── docs
├── Google Web Designer
│ └── Standard
│ │ ├── an_logo.png
│ │ └── index.html
├── Walkthrough-For-Adobe-Edge-Created-Ads.md
├── Walkthrough-For-Google-Web-Designer-Created-Ads.md
└── Walkthrough-For-Manually-Created-Ads.md
├── examples
├── ads.json
├── expanding-push
│ ├── css
│ │ └── main.css
│ ├── images
│ │ └── an_logo.png
│ └── index.html
├── expanding
│ ├── css
│ │ └── main.css
│ ├── images
│ │ └── an_logo.png
│ └── index.html
├── interstitial
│ ├── images
│ │ └── an_logo.png
│ ├── index.html
│ ├── js
│ │ └── script.js
│ ├── media
│ │ └── video.mp4
│ └── styles
│ │ └── style.css
└── standard
│ ├── css
│ └── main.css
│ ├── images
│ └── an_logo.png
│ └── index.html
├── package.json
├── src
├── client.js
└── lib
│ ├── event-listener.js
│ ├── guid.js
│ ├── porthole.js
│ └── utils.js
└── test
├── client-ready-test.js
├── client-test.js
├── helpers
├── fixtures.js
└── jsdom.js
├── lib-event-listener-test.js
├── lib-guid-test.js
├── lib-porthole-test.js
└── lib-utils-test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .idea
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "browser": true,
3 | "node": true,
4 | "es5": true,
5 | "esnext": true,
6 | "bitwise": false,
7 | "curly": false,
8 | "eqeqeq": true,
9 | "eqnull": true,
10 | "immed": true,
11 | "latedef": false,
12 | "laxcomma": true,
13 | "newcap": true,
14 | "noarg": true,
15 | "undef": true,
16 | "strict": true,
17 | "trailing": true,
18 | "smarttabs": true,
19 | "white": true
20 | }
21 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | == HEAD
2 |
--------------------------------------------------------------------------------
/CONTRIBUTION.md:
--------------------------------------------------------------------------------
1 | # Contribution Guidelines
2 |
3 | ## Commit Messages (for Internal AppNexus Contributors)
4 |
5 | Prepend your commit message with the approrpiate JIRA ticket number: `CR-####: ...`.
6 |
7 | ## Merging Branches
8 |
9 | Squashing commits is not mandatory, but can optionally be done to clean up Git history if your change is particularly large.
10 |
11 | ## Raising Github Issues
12 |
13 | If you are an external (non-AppNexus employee) contributor, feel free to create Github issues, and the internal team will respond as soon as possible.
14 |
15 | # Development Guidelines
16 |
17 | ## Installation
18 |
19 | Run the following from the directory where you want the repo to live in
20 |
21 | ```
22 | git clone Powering the advertising
28 | ## Development
29 |
30 | You can develop and test on the fly by running:
31 |
32 | ```
33 | npm run watch
34 | ```
35 | This will automatically rebuild `appnexus-html5-lib.js` when any of the files under `src` change.
36 |
37 |
38 | To view example creatives, simply double click on them to open them up in your browser.
39 |
40 |
41 | ## Building
42 |
43 | To build the minified version run:
44 |
45 | ```
46 | npm run build
47 | ```
48 |
49 | This will build two files, `appnexus-html5-lib.js` and `appnexus-html5-lib.min.js`.
50 |
51 |
52 | ## Deploying
53 |
54 | For deploying run:
55 |
56 | ```
57 | npm version [major|minor|patch]
58 | ```
59 |
60 | This will build the library, test it, and create a tag with the new version if tests don't fail.
61 |
62 |
63 | The appnexus-html-lib.min.js needs to be placed on the CDN in a folder corresponding to its version number. for example `https://acdn.adnxs.com/html5-lib/1.0.0/appnexus-html5-lib.min.js`. Always use the version tag file under dist.
64 |
65 | Dont forget to update the examples to point to the latest tag been released.
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = function (grunt) {
4 |
5 | // Project dependency tasks
6 | grunt.loadNpmTasks('grunt-browserify');
7 | grunt.loadNpmTasks('grunt-contrib-clean');
8 | //grunt.loadNpmTasks('grunt-contrib-concat');
9 | grunt.loadNpmTasks('grunt-contrib-uglify');
10 | //grunt.loadNpmTasks('grunt-contrib-qunit');
11 | //grunt.loadNpmTasks('grunt-contrib-jshint');
12 | grunt.loadNpmTasks('grunt-contrib-watch');
13 |
14 | // Project configuration
15 | grunt.initConfig({
16 | pkg: grunt.file.readJSON('package.json'),
17 | clean: {
18 | dist: ['dist/*'],
19 | },
20 | browserify: {
21 | client: {
22 | browserifyOptions: {
23 | debug: true,
24 | noParse: true
25 | },
26 | src: ['src/client.js'],
27 | dest: 'dist/<%= pkg.name %>.js'
28 | }
29 | },
30 | uglify: {
31 | client: {
32 | options: {
33 | banner: '/*\n * <%= pkg.description %> for Client\n * Author: <%= pkg.author.name %> (<%= pkg.author.email %>) \n * Website: <%= pkg.author.url %>\n * <%= pkg.license %> Licensed.\n *\n * <%= pkg.name %>.min.js <%= pkg.version %>\n */\n '
34 | },
35 | src: 'dist/<%= pkg.name %>.js',
36 | dest: 'dist/<%= pkg.name %>.min.js'
37 | }
38 | },
39 | watch: {
40 | test: {
41 | files: 'src/**/*.js',
42 | tasks: ['browserify']
43 | }
44 | }
45 | });
46 |
47 | //grunt.registerTask('default', ['jshint', 'qunit', 'clean', 'concat', 'uglify']);
48 | grunt.registerTask('default', ['clean', 'browserify', 'watch']);
49 | grunt.registerTask('build', ['clean', 'browserify', 'uglify']);
50 | };
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2016 AppNexus Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AppNexus HTML5 Client Library
2 |
3 | The AppNexus HTML5 client library helps integrate HTML5 ads into websites in a safe and secure manner.
4 |
5 | ## API Documentation
6 |
7 | ### `APPNEXUS` Object
8 |
9 | The `APPNEXUS` object is the base object of the API which provides actions to pass down to the publisher website.
10 |
11 |
12 |
13 | ### Method `APPNEXUS.ready(callback) : void`
14 |
15 | The `APPNEXUS.ready()` will trigger `callback` function once the APPNEXUS object has been initialized and the page has been loaded.
16 |
17 | ``` js
18 | APPNEXUS.ready(function () {
19 | var readMoreButton = document.getElementById('read-more-button');
20 |
21 | readMoreButton.addEventListener("click", function () {
22 | APPNEXUS.click();
23 | });
24 | });
25 | ```
26 |
27 | #### Multiple `APPNEXUS.ready()` calls
28 |
29 | The library also supports multiple `APPNEXUS.ready()` calls per page. You might want to do this if you have multiple functions that want to check if the APPNEXUS object is initialized and the page is loaded.
30 |
31 | `interaction.js`
32 |
33 | ``` js
34 | APPNEXUS.ready(function () {
35 | var readMoreButton = document.getElementById('read-more-button');
36 |
37 | readMoreButton.addEventListener("click", function () {
38 | APPNEXUS.click();
39 | });
40 | });
41 | ```
42 |
43 | `layout.js`
44 |
45 | ``` js
46 | APPNEXUS.ready(function () {
47 | var fullscreenButton = document.getElementById('fullscreen-button');
48 |
49 | APPNEXUS.setExpandProperties({
50 | width: 600,
51 | height: 500,
52 | floating: true,
53 | expand: {
54 | easing: 'ease-in-out',
55 | duration: 1000
56 | }
57 | });
58 |
59 | // Expands on click
60 | fullscreenButton.addEventListener("hover", function () {
61 | APPNEXUS.expand();
62 | });
63 | });
64 | ```
65 |
66 |
67 |
68 | ### Method `APPNEXUS.click([url]) : void`
69 |
70 | Opens a new window linking to the clickthrough URL or to the specified URL if the `url` parameter is specified.
71 |
72 |
73 | *NOTE: Click-tracking is currently not available when a URL is specified with the `url` parameter.*
74 |
75 | ``` js
76 | APPNEXUS.ready(function () {
77 | var readMoreButton = document.getElementById('read-more-button');
78 | var facebookButton = document.getElementById('facebook-button');
79 |
80 | readMoreButton.addEventListener("click", function () {
81 | APPNEXUS.click();
82 | });
83 |
84 | facebookButton.addEventListener("click", function () {
85 | // Does not use click-tracking
86 | APPNEXUS.click('http://www.facebook/myawesomeprofile');
87 | });
88 | });
89 | ```
90 |
91 |
92 | ### Method `APPNEXUS.setExpandProperties(properties) : void`
93 |
94 | Sets the expanding properties of an ad, whether that's an interstitial, a push over, or a floating ad.
95 |
96 | Options for `properties` settings:
97 |
98 | | Property | Type | Value | Default |
99 | |----------------|---------|-------|---------|
100 | | `width` | Number | The expanded `width` in pixels | Current ad "width" |
101 | | `height` | Number | The expanded `height` in pixles | Current ad "height" |
102 | | `floating` | Boolean | Makes the ad float or push content | `false` |
103 | | `anchor` | String | Can be one of the following: `"top-right"`, `"bottom-right"`, `"bottom-left"`, or `"top-left"`
*NOTE: Only works when the `floating` flag is set to true.* | `"top-left"`
104 | | `expand` | Object | Expanding easing animation of the frame. See `easing-properties` option settings. | |
105 | | `collapse` | Object | Collapsing easing animation of the frame. See `easing-properties` option settings. | Inherits `expand` property |
106 | | `interstital` | Boolean | Sets the ad as a full screen interstitial with a light box overlay | `false` |
107 | | `overlayColor` | String | The CSS color of the light box overlay | `"rgba(0,0,0,0.5)"` |
108 |
109 | *NOTE: Setting `interstitial` to `true` will ignore the `floating` value if set together*
110 |
111 |
112 | Options for `easing-properties` settings:
113 |
114 | | Property | Type | Value | Default |
115 | |----------------|---------|-------|---------|
116 | | `easing` | String | CSS [transition-timing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function). | No easing by default |
117 | | `duration` | Number | CSS transtion duration for `easing` | `400` |
118 |
119 |
120 | Some examples for `APPNEXUS.setExpandingProperties()`:
121 |
122 | **Interstitial Ad Example**
123 |
124 | ``` js
125 | // Ad must call `APPNEXUS.expand()` inside the `APPNEXUS.ready`
126 | APPNEXUS.ready(function () {
127 | ...
128 | APPNEXUS.setExpandProperties({
129 | interstitial : true
130 | });
131 | APPNEXUS.expand();
132 | });
133 | ```
134 |
135 | **Expanding (Push Over) Ad Example**
136 |
137 | ``` js
138 | // Original ad size 720x90 that expands to 720x275
139 | APPNEXUS.ready(function () {
140 | var button = document.getElementById('button');
141 |
142 | APPNEXUS.setExpandProperties({
143 | height: 275,
144 | expand: {
145 | easing: 'ease-in-out',
146 | duration: 1000
147 | },
148 | collapse: {
149 | easing: 'ease-in-out',
150 | duration: 500
151 | }
152 | });
153 |
154 | // Expands on click
155 | button.addEventListener("click", function () {
156 | APPNEXUS.expand();
157 | });
158 | });
159 | ```
160 |
161 | **Expanding (Floating) Ad Example**
162 |
163 | ``` js
164 | // Original ad size 300x250 that expands to 600x500
165 | APPNEXUS.ready(function () {
166 | var button = document.getElementById('button');
167 |
168 | APPNEXUS.setExpandProperties({
169 | width: 600,
170 | height: 500,
171 | floating: true,
172 | expand: {
173 | easing: 'ease-in-out',
174 | duration: 1000
175 | }
176 | });
177 |
178 | // Expands on click
179 | button.addEventListener("click", function () {
180 | APPNEXUS.expand();
181 | });
182 | });
183 | ```
184 |
185 |
186 | ### Method `APPNEXUS.getExpandProperties() : Object`
187 |
188 | Returns the current set expanding properties.
189 |
190 |
191 | ### Method `APPNEXUS.expand() : void`
192 |
193 | Triggers the ad to expand to the size specified by the expanding properties in `APPNEXUS.setExpandProperties()`.
194 |
195 |
196 | ### Method `APPNEXUS.collapse() : void`
197 |
198 | Triggers the ad to collapse to the original size.
199 |
200 |
201 |
202 | ### Method `APPNEXUS.getClickTag() : string`
203 |
204 | returns the current clickTag url passed to the creative. This is useful for integration with other ad builders such as adobe edge.
205 |
206 | This function can be called before `APPNEXUS.ready` has fired.
207 |
208 | **Example usage**
209 |
210 | ```
211 | var clickTag = APPNEXUS.getClickTag();
212 | ```
213 |
214 |
215 | ### Method `APPNEXUS.getMacroByName(string) : string`
216 |
217 | returns the value of a given macro passed to the creative. This is useful for GDPR purposes.
218 |
219 | *NOTE: Only works the two `GDPR` macros.*
220 |
221 | **Example usage**
222 |
223 | ```javascript
224 | APPNEXUS.ready(function () {
225 | clickthrough.addEventListener("click", function () {
226 | APPNEXUS.getMacroByName("GDPR_APPLIES");
227 | APPNEXUS.getMacroByName("GDPR_CONSENT_STRING");
228 | });
229 | });
230 | ```
231 |
232 |
233 | ## Usage Documentation
234 |
235 | Visit the links below for walkthroughs on how to use the AppNexus HTML5 library in a few specific cases:
236 |
237 | - [Integrating the AppNexus HTML5 Library with Manually Created Creatives](https://github.com/appnexus/appnexus-html5-lib/blob/master/docs/Walkthrough-For-Manually-Created-Ads.md)
238 | - [Integrating the AppNexus HTML5 Library with Ads Created in Google Web Designer](https://wiki.appnexus.com/display/industry/Integrating+the+AppNexus+HTML5+Library+with+Ads+Created+in+Google+Web+Designer)
239 | - [Integrating the AppNexus HTML5 Library with Ads Created in Adobe Edge](https://wiki.appnexus.com/display/industry/Integrating+the+AppNexus+HTML5+Library+with+Ads+Created+in+Adobe+Edge)
240 |
241 | ## Development
242 |
243 | For instructions on how to develop this library, see the [Contribution Guidelines](https://github.com/appnexus/appnexus-html5-lib/blob/master/CONTRIBUTION.md).
--------------------------------------------------------------------------------
/dist/appnexus-html5-lib.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;otargetOrigin
can be a url or *
297 | * @public
298 | * @param {Object} data - Payload
299 | * @param {String} targetOrigin
300 | */
301 | post: function(data, targetOrigin) {},
302 | /**
303 | * Add an event listener to receive messages.
304 | * @public
305 | * @param {Function} eventListenerCallback
306 | * @returns {Function} eventListenerCallback
307 | */
308 | addEventListener: function(f) {},
309 | /**
310 | * Remove an event listener.
311 | * @public
312 | * @param {Function} eventListenerCallback
313 | */
314 | removeEventListener: function(f) {}
315 | };
316 |
317 | Porthole.WindowProxyBase = PortholeClass.extend({
318 | init: function(targetWindowName) {
319 | if (targetWindowName === undefined) {
320 | targetWindowName = '';
321 | }
322 | this.targetWindowName = targetWindowName;
323 | this.origin = window.location.protocol + '//' + window.location.host;
324 | this.eventListeners = [];
325 | },
326 |
327 | getTargetWindowName: function() {
328 | return this.targetWindowName;
329 | },
330 |
331 | getOrigin: function() {
332 | return this.origin;
333 | },
334 |
335 | /**
336 | * Lookup window object based on target window name
337 | * @private
338 | * @return {string} targetWindow
339 | */
340 | getTargetWindow: function() {
341 | return Porthole.WindowProxy.getTargetWindow(this.targetWindowName);
342 | },
343 |
344 | post: function(data, targetOrigin) {
345 | if (targetOrigin === undefined) {
346 | targetOrigin = '*';
347 | }
348 | this.dispatchMessage({
349 | 'data' : data,
350 | 'sourceOrigin' : this.getOrigin(),
351 | 'targetOrigin' : targetOrigin,
352 | 'sourceWindowName' : window.name,
353 | 'targetWindowName' : this.getTargetWindowName()
354 | });
355 | },
356 |
357 | addEventListener: function(f) {
358 | this.eventListeners.push(f);
359 | return f;
360 | },
361 |
362 | removeEventListener: function(f) {
363 | var index;
364 | try {
365 | index = this.eventListeners.indexOf(f);
366 | this.eventListeners.splice(index, 1);
367 | } catch(e) {
368 | this.eventListeners = [];
369 | }
370 | },
371 |
372 | dispatchEvent: function(event) {
373 | var i;
374 | for (i = 0; i < this.eventListeners.length; i++) {
375 | try {
376 | this.eventListeners[i](event);
377 | } catch(e) {
378 | Porthole.error(e);
379 | }
380 | }
381 | }
382 | });
383 |
384 | /**
385 | * Legacy browser implementation of proxy window object to post message to target window
386 | *
387 | * @private
388 | * @constructor
389 | * @param {string} proxyIFrameUrl - Fully qualified url to proxy iframe
390 | * @param {string} targetWindowName - Name of the proxy iframe window
391 | */
392 | Porthole.WindowProxyLegacy = Porthole.WindowProxyBase.extend({
393 | init: function(proxyIFrameUrl, targetWindowName) {
394 | this._super(targetWindowName);
395 |
396 | if (proxyIFrameUrl !== null) {
397 | this.proxyIFrameName = this.targetWindowName + 'ProxyIFrame';
398 | this.proxyIFrameLocation = proxyIFrameUrl;
399 |
400 | // Create the proxy iFrame and add to dom
401 | this.proxyIFrameElement = this.createIFrameProxy();
402 | } else {
403 | // Won't be able to send messages
404 | this.proxyIFrameElement = null;
405 | Porthole.trace("proxyIFrameUrl is null, window will be a receiver only");
406 | this.post = function(){ throw new Error("Receiver only window");};
407 | }
408 | },
409 |
410 | /**
411 | * Create an iframe and load the proxy
412 | *
413 | * @private
414 | * @returns iframe
415 | */
416 | createIFrameProxy: function() {
417 | var iframe = document.createElement('iframe');
418 |
419 | iframe.setAttribute('id', this.proxyIFrameName);
420 | iframe.setAttribute('name', this.proxyIFrameName);
421 | iframe.setAttribute('src', this.proxyIFrameLocation);
422 | // IE needs this otherwise resize event is not fired
423 | iframe.setAttribute('frameBorder', '1');
424 | iframe.setAttribute('scrolling', 'auto');
425 | // Need a certain size otherwise IE7 does not fire resize event
426 | iframe.setAttribute('width', 30);
427 | iframe.setAttribute('height', 30);
428 | iframe.setAttribute('style', 'position: absolute; left: -100px; top:0px;');
429 | // IE needs this because setting style attribute is broken. No really.
430 | if (iframe.style.setAttribute) {
431 | iframe.style.setAttribute('cssText', 'position: absolute; left: -100px; top:0px;');
432 | }
433 | document.body.appendChild(iframe);
434 | return iframe;
435 | },
436 |
437 | dispatchMessage: function(message) {
438 | var encode = window.encodeURIComponent;
439 |
440 | if (this.proxyIFrameElement) {
441 | var src = this.proxyIFrameLocation + '#' + encode(Porthole.WindowProxy.serialize(message));
442 | this.proxyIFrameElement.setAttribute('src', src);
443 | this.proxyIFrameElement.height = this.proxyIFrameElement.height > 50 ? 50 : 100;
444 | }
445 | }
446 | });
447 |
448 | /**
449 | * Implementation for modern browsers that supports it
450 | */
451 | Porthole.WindowProxyHTML5 = Porthole.WindowProxyBase.extend({
452 | init: function(proxyIFrameUrl, targetWindowName) {
453 | this._super(targetWindowName);
454 | this.eventListenerCallback = null;
455 | },
456 |
457 | dispatchMessage: function(message) {
458 | this.getTargetWindow().postMessage(Porthole.WindowProxy.serialize(message), message.targetOrigin);
459 | },
460 |
461 | addEventListener: function(f) {
462 | if (this.eventListeners.length === 0) {
463 | var self = this;
464 | if (window.addEventListener) {
465 | this.eventListenerCallback = function(event) { self.eventListener(self, event); };
466 | window.addEventListener('message', this.eventListenerCallback, false);
467 | } else if (window.attachEvent) {
468 | // Make IE8 happy, just not that 1. postMessage only works for IFRAMES/FRAMES http://blogs.msdn.com/b/ieinternals/archive/2009/09/16/bugs-in-ie8-support-for-html5-postmessage-sessionstorage-and-localstorage.aspx
469 | this.eventListenerCallback = function(event) { self.eventListener(self, window.event); };
470 | window.attachEvent("onmessage", this.eventListenerCallback);
471 | }
472 | }
473 | return this._super(f);
474 | },
475 |
476 | removeEventListener: function(f) {
477 | this._super(f);
478 |
479 | if (this.eventListeners.length === 0) {
480 | if (window.removeEventListener) {
481 | window.removeEventListener('message', this.eventListenerCallback);
482 | } else if (window.detachEvent) { // Make IE8, happy, see above
483 | // see jquery, detachEvent needed property on element, by name of that event, to properly expose it to GC
484 | if (typeof window.onmessage === 'undefined') window.onmessage = null;
485 | window.detachEvent('onmessage', this.eventListenerCallback);
486 | }
487 | this.eventListenerCallback = null;
488 | }
489 | },
490 |
491 | eventListener: function(self, nativeEvent) {
492 | var data = Porthole.WindowProxy.unserialize(nativeEvent.data);
493 | if (data && (self.targetWindowName === '' || data.sourceWindowName == self.targetWindowName)) {
494 | self.dispatchEvent(new Porthole.MessageEvent(data.data, nativeEvent.origin, self));
495 | }
496 | }
497 | });
498 |
499 | if (!window.postMessage) {
500 | Porthole.trace('Using legacy browser support');
501 | Porthole.WindowProxy = Porthole.WindowProxyLegacy.extend({});
502 | } else {
503 | Porthole.trace('Using built-in browser support');
504 | Porthole.WindowProxy = Porthole.WindowProxyHTML5.extend({});
505 | }
506 |
507 | /**
508 | * Serialize an object using JSON.stringify
509 | *
510 | * @param {Object} obj The object to be serialized
511 | * @return {String}
512 | */
513 | Porthole.WindowProxy.serialize = function(obj) {
514 | if (typeof JSON === 'undefined') {
515 | throw new Error('Porthole serialization depends on JSON!');
516 | }
517 |
518 | return JSON.stringify(obj);
519 | };
520 |
521 | /**
522 | * Unserialize using JSON.parse
523 | *
524 | * @param {String} text Serialization
525 | * @return {Object}
526 | */
527 | Porthole.WindowProxy.unserialize = function(text) {
528 | if (typeof JSON === 'undefined') {
529 | throw new Error('Porthole unserialization dependens on JSON!');
530 | }
531 | try {
532 | var json = JSON.parse(text);
533 | } catch (e) {
534 | return false;
535 | }
536 | return json;
537 | };
538 |
539 | Porthole.WindowProxy.getTargetWindow = function(targetWindowName) {
540 | if (targetWindowName === '') {
541 | return window.parent;
542 | } else if (targetWindowName === 'top' || targetWindowName === 'parent') {
543 | return window[targetWindowName];
544 | }
545 | return window.frames[targetWindowName];
546 | };
547 |
548 | /**
549 | * @classdesc Event object to be passed to registered event handlers
550 | * @class
551 | * @param {String} data
552 | * @param {String} origin - url of window sending the message
553 | * @param {Object} source - window object sending the message
554 | */
555 | Porthole.MessageEvent = function MessageEvent(data, origin, source) {
556 | this.data = data;
557 | this.origin = origin;
558 | this.source = source;
559 | };
560 |
561 | /**
562 | * @classdesc Dispatcher object to relay messages.
563 | * @public
564 | * @constructor
565 | */
566 | Porthole.WindowProxyDispatcher = {
567 | /**
568 | * Forward a message event to the target window
569 | * @private
570 | */
571 | forwardMessageEvent: function(e) {
572 | var message,
573 | decode = window.decodeURIComponent,
574 | targetWindow,
575 | windowProxy;
576 |
577 | if (document.location.hash.length > 0) {
578 | // Eat the hash character
579 | message = Porthole.WindowProxy.unserialize(decode(document.location.hash.substr(1)));
580 |
581 | targetWindow = Porthole.WindowProxy.getTargetWindow(message.targetWindowName);
582 |
583 | windowProxy =
584 | Porthole.WindowProxyDispatcher.findWindowProxyObjectInWindow(
585 | targetWindow,
586 | message.sourceWindowName
587 | );
588 |
589 | if (windowProxy) {
590 | if (windowProxy.origin === message.targetOrigin || message.targetOrigin === '*') {
591 | windowProxy.dispatchEvent(
592 | new Porthole.MessageEvent(message.data, message.sourceOrigin, windowProxy));
593 | } else {
594 | Porthole.error('Target origin ' +
595 | windowProxy.origin +
596 | ' does not match desired target of ' +
597 | message.targetOrigin);
598 | }
599 | } else {
600 | Porthole.error('Could not find window proxy object on the target window');
601 | }
602 | }
603 | },
604 |
605 | /**
606 | * Look for a window proxy object in the target window
607 | * @private
608 | */
609 | findWindowProxyObjectInWindow: function(w, sourceWindowName) {
610 | var i;
611 |
612 | if (w) {
613 | for (i in w) {
614 | if (Object.prototype.hasOwnProperty.call(w, i)) {
615 | try {
616 | // Ensure that we're finding the proxy object
617 | // that is declared to be targetting the window that is calling us
618 | if (w[i] !== null &&
619 | typeof w[i] === 'object' &&
620 | w[i] instanceof w.Porthole.WindowProxy &&
621 | w[i].getTargetWindowName() === sourceWindowName) {
622 | return w[i];
623 | }
624 | } catch(e) {
625 | // Swallow exception in case we access an object we shouldn't
626 | }
627 | }
628 | }
629 | }
630 | return null;
631 | },
632 |
633 | /**
634 | * Start a proxy to relay messages.
635 | * @public
636 | */
637 | start: function() {
638 | if (window.addEventListener) {
639 | window.addEventListener('resize',
640 | Porthole.WindowProxyDispatcher.forwardMessageEvent,
641 | false);
642 | } else if (window.attachEvent && window.postMessage !== 'undefined') {
643 | window.attachEvent('onresize',
644 | Porthole.WindowProxyDispatcher.forwardMessageEvent);
645 | } else if (document.body.attachEvent) {
646 | window.attachEvent('onresize',
647 | Porthole.WindowProxyDispatcher.forwardMessageEvent);
648 | } else {
649 | // Should never happen
650 | Porthole.error('Cannot attach resize event');
651 | }
652 | }
653 | };
654 |
655 | module.exports = Porthole;
656 |
657 | },{}]},{},[1]);
658 |
--------------------------------------------------------------------------------
/dist/appnexus-html5-lib.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * AppNexus HTML5 Client Library for Client
3 | * Author: AppNexus ()
4 | * Website: http://www.appnexus.com
5 | * Apache-2.0 Licensed.
6 | *
7 | * appnexus-html5-lib.min.js 1.4.1
8 | */
9 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g
219 |
220 |
221 | that powers the internet™
222 |
55 | ``` 56 | 57 | __Brief Technical Explanation__ 58 | 59 | When the page is loaded and the HTML5 Library has loaded, `APPNEXUS.ready()` is called. `APPNEXUS.click()` is called inside the `addEventListener` callback function — this means that it happens when the user clicks on the `clickthrough` element. 60 | 61 | See `README.md` for additional technical documentation on `APPNEXUS.ready()` and `APPNEXUS.click()`. 62 | 63 | 64 | ## Expanding Ads 65 | All expanding ads require a file called `index.html`. Each of the following steps makes changes to that file. 66 | 67 | ####Step 1: Add the AppNexus HTML5 Library 68 | You can find the AppNexus' HTML5 JavaScript library at this URL: [https://acdn.adnxs.com/html5-lib/1.4.1/appnexus-html5-lib.min.js](https://acdn.adnxs.com/html5-lib/1.4.1/appnexus-html5-lib.min.js). 69 | 70 | You must add it to the ad's `index.html` file, inside the `
` tag, in a ` 76 | 77 | ``` 78 | 79 | ####Step 2: Add a clickthrough element 80 | Add a unique id `"clickthrough"` to `