24 | Powering
25 | the advertising
26 | that powers
27 | the Internet™
28 |
29 |
30 |
31 |
32 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/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 html5-lib
23 | cd html5-lib
24 | npm install
25 | ```
26 |
27 |
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 |
17 | Powering
18 | the advertising
19 | that powers
20 | the Internet™
21 |
22 |
23 |
24 |
25 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/docs/Walkthrough-For-Google-Web-Designer-Created-Ads.md:
--------------------------------------------------------------------------------
1 | #How To: Integrate the AppNexus HTML5 Library with Ads Created in Google Web Designer
2 |
3 | ##Converting an Existing Google Web Designer Ad
4 |
5 | If you are working with an ad already created in Google Web Designer, you can follow these instructions to modify that ad so it works with the AppNexus HTML5 Library.
6 |
7 |
8 | ##Standard Ads
9 | The corresponding example for this section can be found in this folder under `/Google Web Designer/Standard/`.
10 |
11 | ###Step 1: Find the `index.html` file
12 | Before we begin, find the folder containing the Google Web Designer-created ad. You may only have a file with a `.zip` extension—in this case, you must unzip the file to reveal a folder containing its various assets.
13 |
14 |
15 | Then, look for and open the file named `index.html`. This file is where we will make all of the necessary changes in the steps below.
16 |
17 |
18 | ###Step 2: Add the AppNexus HTML5 Library
19 | First, we will have to make sure the actual HTML5 Library is linked to inside `index.html`. This should be done right before the `` tag in the `index.html` file, by adding the following `
25 |
26 | ```
27 |
28 |
29 |
30 | ###Step 3: Replace hard-coded URL with `APPNEXUS.getClickTag()`
31 | Generally, you may have an ad created in Google Web Designer that has a hard-coded URL embedded.
32 |
33 | In order to remove them, search for `gwd-events="handlers"` in the same `index.html` file. You should find a block of code that looks like this:
34 |
35 | ```html
36 |
42 | ```
43 | Note that the hardcoded URL (in this case, `https://appnexus.com`) will vary based on the specific ad you are working with.
44 |
45 | Then, replace `"https://appnexus.com"` (including the quotation marks) with `APPNEXUS.getClickTag()`, so that this function looks like:
46 |
47 | ```html
48 |
54 | ```
55 |
56 | ###Step 4: Finish and Upload
57 |
58 | Then, ZIP the folder containing `index.html` and the rest of your ad's assets and upload it using the [AppNexus Hosted HTML5 Creative Upload](https://wiki.appnexus.com/display/console/Upload+Hosted+HTML5+Creatives).
--------------------------------------------------------------------------------
/examples/expanding/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: Helvetica,Arial,sans-serif;
5 | text-align: center;
6 | overflow:hidden;
7 | }
8 | .container {
9 | overflow: hidden;
10 | background-color: #000000;
11 | height: 248px;
12 | width: 298px;
13 | border: 1px solid #666666;
14 | cursor: pointer;
15 | }
16 | .tagline {
17 | margin: 20px;
18 | padding: 0;
19 | position: relative;
20 | bottom: -26px;
21 | }
22 | .tagline .text {
23 | display: block;
24 | color: #ffffff;
25 | font-size: 28px;
26 | font-weight: 100;
27 | text-align: left;
28 | animation: fadein 2s;
29 | -moz-animation: fadein 2s; /* Firefox */
30 | -webkit-animation: fadein 2s; /* Safari and Chrome */
31 | -o-animation: fadein 2s; /* Opera */
32 | }
33 | strong {
34 | color: #FF8700;
35 | }
36 | .logo {
37 | left: 0;
38 | margin: 20px;
39 | position: relative;
40 | }
41 | .logo img {
42 | width: 115px;
43 | }
44 | .slinky {
45 | left: 149px;
46 | margin: 0;
47 | position: absolute;
48 | top: 1px;
49 | }
50 | .slinky img {
51 | width: 150px;
52 | }
53 | #trademark {
54 | color: #FF8700;
55 | font-size: 20px;
56 | vertical-align: top;
57 | }
58 | .expandedContainer {
59 | animation: containerExpandDown 2s;
60 | -moz-animation: containerExpandDown 2s;
61 | -webkit-animation: containerExpandDown 2s;
62 | -o-animation: containerExpandDown 2s;
63 |
64 | height: 600px;
65 | }
66 | .expandedTagline {
67 | animation: taglineExpandDown 2s;
68 | -moz-animation: taglineExpandDown 2s;
69 | -webkit-animation: taglineExpandDown 2s;
70 | -o-animation: taglineExpandDown 2s;
71 |
72 | bottom: -380px;
73 | }
74 |
75 | .hidden {
76 | display: none;
77 | }
78 |
79 | .overlay {
80 | position: absolute;
81 | top: 0;
82 | left: 0;
83 | width: 100%;
84 | height: 100%;
85 | z-index: 100;
86 | background-color: rgba(0,0,0,0.5); /*dim the background*/
87 | }
88 |
89 | .interstitial {
90 | margin: 0 auto;
91 | height: 480px;
92 | width: 320px;
93 | }
94 |
95 | .interstitial .tagline {
96 | bottom: -255px;
97 | }
98 |
99 | @keyframes fadein {
100 | from {
101 | opacity:0;
102 | }
103 | to {
104 | opacity:1;
105 | }
106 | }
107 | @-moz-keyframes fadein { /* Firefox */
108 | from {
109 | opacity:0;
110 | }
111 | to {
112 | opacity:1;
113 | }
114 | }
115 | @-webkit-keyframes fadein { /* Safari and Chrome */
116 | from {
117 | opacity:0;
118 | }
119 | to {
120 | opacity:1;
121 | }
122 | }
123 | @-o-keyframes fadein { /* Opera */
124 | from {
125 | opacity:0;
126 | }
127 | to {
128 | opacity: 1;
129 | }
130 | }
131 |
132 | @keyframes containerExpandDown {
133 | from {
134 | height: 300px;
135 | }
136 | to {
137 | height: 600px;
138 | }
139 | }
140 |
141 | @keyframes taglineExpandDown {
142 | from {
143 | bottom: -76px;
144 | }
145 | to {
146 | bottom: -380px;
147 | }
148 | }
--------------------------------------------------------------------------------
/examples/expanding-push/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: Helvetica,Arial,sans-serif;
5 | text-align: center;
6 | overflow:hidden;
7 | }
8 | .container {
9 | overflow: hidden;
10 | background-color: #000000;
11 | height: 248px;
12 | width: 298px;
13 | border: 1px solid #666666;
14 | cursor: pointer;
15 |
16 | }
17 | .tagline {
18 | margin: 20px;
19 | padding: 0;
20 | position: relative;
21 | bottom: -26px;
22 | }
23 | .tagline .text {
24 | display: block;
25 | color: #ffffff;
26 | font-size: 28px;
27 | font-weight: 100;
28 | text-align: left;
29 | animation: fadein 2s;
30 | -moz-animation: fadein 2s; /* Firefox */
31 | -webkit-animation: fadein 2s; /* Safari and Chrome */
32 | -o-animation: fadein 2s; /* Opera */
33 | }
34 | strong {
35 | color: #FF8700;
36 | }
37 | .logo {
38 | left: 0;
39 | margin: 20px;
40 | position: relative;
41 | }
42 | .logo img {
43 | width: 115px;
44 | }
45 | .slinky {
46 | left: 149px;
47 | margin: 0;
48 | position: absolute;
49 | top: 1px;
50 | }
51 | .slinky img {
52 | width: 150px;
53 | }
54 | #trademark {
55 | color: #FF8700;
56 | font-size: 20px;
57 | vertical-align: top;
58 | }
59 | .expandedContainer {
60 | animation: containerExpandDown 2s;
61 | -moz-animation: containerExpandDown 2s;
62 | -webkit-animation: containerExpandDown 2s;
63 | -o-animation: containerExpandDown 2s;
64 |
65 | height: 600px;
66 | }
67 | .expandedTagline {
68 | animation: taglineExpandDown 2s;
69 | -moz-animation: taglineExpandDown 2s;
70 | -webkit-animation: taglineExpandDown 2s;
71 | -o-animation: taglineExpandDown 2s;
72 |
73 | bottom: -380px;
74 | }
75 |
76 | .hidden {
77 | display: none;
78 | }
79 |
80 | .overlay {
81 | position: absolute;
82 | top: 0;
83 | left: 0;
84 | width: 100%;
85 | height: 100%;
86 | z-index: 100;
87 | background-color: rgba(0,0,0,0.5); /*dim the background*/
88 | }
89 |
90 | .interstitial {
91 | margin: 0 auto;
92 | height: 480px;
93 | width: 320px;
94 | }
95 |
96 | .interstitial .tagline {
97 | bottom: -255px;
98 | }
99 |
100 | @keyframes fadein {
101 | from {
102 | opacity:0;
103 | }
104 | to {
105 | opacity:1;
106 | }
107 | }
108 | @-moz-keyframes fadein { /* Firefox */
109 | from {
110 | opacity:0;
111 | }
112 | to {
113 | opacity:1;
114 | }
115 | }
116 | @-webkit-keyframes fadein { /* Safari and Chrome */
117 | from {
118 | opacity:0;
119 | }
120 | to {
121 | opacity:1;
122 | }
123 | }
124 | @-o-keyframes fadein { /* Opera */
125 | from {
126 | opacity:0;
127 | }
128 | to {
129 | opacity: 1;
130 | }
131 | }
132 |
133 | @keyframes containerExpandDown {
134 | from {
135 | height: 300px;
136 | }
137 | to {
138 | height: 600px;
139 | }
140 | }
141 |
142 | @keyframes taglineExpandDown {
143 | from {
144 | bottom: -76px;
145 | }
146 | to {
147 | bottom: -380px;
148 | }
149 | }
--------------------------------------------------------------------------------
/docs/Walkthrough-For-Adobe-Edge-Created-Ads.md:
--------------------------------------------------------------------------------
1 | #How To: Integrate the AppNexus HTML5 Library with Ads Created in Adobe Edge
2 |
3 | ##Converting an Existing Adobe Edge Ad
4 |
5 | If you are working with an ad already created in Adobe Edge, you can follow these instructions to modify that ad so it works with the AppNexus HTML5 Library.
6 |
7 |
8 | ##Standard Ads
9 |
10 | ###Step 1: Find the right files to edit
11 | Before we begin, find the folder containing the Adobe Edge-created ad. You may only have a file with a `.zip` extension—in this case, you must unzip the file to reveal a folder containing its various assets.
12 |
13 | Then, look for and open the file named `index.html`. There will also be a file with the extension `.js` at the root level of the ad folder, that looks like `300x250edge.js` (it has also been named `edgeActions.js` in some cases). These files are where we will make all of the necessary changes in the steps below.
14 |
15 |
16 | ###Step 2: Add the AppNexus HTML5 Library
17 | We will have to make sure the actual AppNexus HTML5 Library is linked to inside `index.html`. The library can be found here: [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)
18 |
19 | Linking the library in should be done inside the `` tag in the `index.html` file, by adding the following `
25 |
26 | ```
27 |
28 |
29 |
30 | ###Step 3: Add Click Event
31 | For this step, you will have to make all your changes to the `300x250edge.js`/`...edge.js`/`edgeActions.js` file.
32 |
33 | ####Without Existing Click Event
34 | You could see an Adobe Edge JavaScript function that handles events, but has no existing click events. This looks something like this:
35 |
36 | ```javascript
37 | //Edge symbol: 'stage'
38 | (function(symbolName){
39 | Symbol.bindTriggerAction(compId,symbolName,"Default Timeline",16750,function(sym,e){
40 | sym.play(0);
41 | });
42 | //Edge binding end
43 | })("stage");
44 | ```
45 |
46 | In order to support the click event using the `APPNEXUS.click()` function, you will have to add in the following function:
47 |
48 | ```javascript
49 | Symbol.bindElementAction(compId,symbolName,"${Stage}","click",function(sym,e){
50 | APPNEXUS.click();
51 | });
52 | ```
53 | Putting it all together, you will have a function that looks like this:
54 |
55 | ```javascript
56 | //Edge symbol: 'stage'
57 | (function(symbolName){
58 | Symbol.bindTriggerAction(compId,symbolName,"Default Timeline",16750,function(sym,e){
59 | sym.play(0);
60 | });
61 | Symbol.bindElementAction(compId,symbolName,"${Stage}","click",function(sym,e){
62 | APPNEXUS.click();
63 | });
64 | //Edge binding end
65 | })("stage");
66 | ```
67 |
68 | ####With Existing Click Event
69 | You could see an Adobe Edge JavaScript function that handles events, and already has an existing click event. This looks something like this:
70 |
71 | ```javascript
72 | //Edge symbol: 'stage'
73 | (function(symbolName){
74 | Symbol.bindElementAction(compId, symbolName, "${Stage}", "click", function(sym, e) {
75 | window.open("https://appnexus.com");
76 | });
77 | //Edge binding end
78 | })("stage");
79 | ```
80 |
81 | In order to support the click event using the `APPNEXUS.click()` function, you will have to replace the hard-coded URL, `https://appnexus.com` to add in the function `APPNEXUS.getClickTag()`.
82 |
83 |
84 | Putting it all together, you will have a function that looks like this:
85 |
86 | ```javascript
87 | //Edge symbol: 'stage'
88 | (function(symbolName){
89 | Symbol.bindElementAction(compId, symbolName, "${Stage}", "click", function(sym, e) {
90 | window.open(APPNEXUS.getClickTag());
91 | });
92 | //Edge binding end
93 | })("stage");
94 | ```
95 |
--------------------------------------------------------------------------------
/test/client-test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var jsdom = require('./helpers/jsdom');
3 | var sinon = require('sinon');
4 | var fixtures = require('./helpers/fixtures');
5 | var Porthole;
6 |
7 | var windowObject;
8 |
9 | describe('appnexus-html5-lib client', function () {
10 | beforeEach(function (done) {
11 | jsdom.createPage(fixtures.HTML5_ADVERTISEMENT_URL, fixtures.HTML5_ADVERTISEMENT, [fixtures.LIB_SOURCE_CLIENT], function (window) {
12 | windowObject = window;
13 | global.window = window
14 | window = window || {};
15 |
16 | global.document = window.document;
17 | Porthole = require('../src/lib/porthole');;
18 | var windowProxy = new Porthole.WindowProxy('http://localhost');
19 |
20 | var adData = {
21 | macros: {
22 | 'macro_1': 'lorem',
23 | 'macro_2': 'ipsum',
24 | }
25 | }
26 | windowProxy.post({ action: 'setAdData', parameters: adData });
27 |
28 | done();
29 | });
30 | });
31 |
32 | it('triggered APPNEXUS.ready', function (done) {
33 | windowObject.APPNEXUS.ready(function () {
34 | done();
35 | });
36 | });
37 |
38 | it('gets correct clickTag from URL', function (done) {
39 | windowObject.APPNEXUS.ready(function () {
40 | expect(windowObject.APPNEXUS.getClickTag()).to.equal(fixtures.HTML5_CLICK_URL);
41 | done();
42 | });
43 | });
44 |
45 | it('check APPNEXUS.click() opens new window', function (done) {
46 | var spy = sinon.spy(windowObject, 'open');
47 |
48 | windowObject.APPNEXUS.ready(function () {
49 | windowObject.APPNEXUS.click();
50 | expect(spy.calledOnce).to.equal(true);
51 | done();
52 | });
53 | });
54 |
55 | it('check APPNEXUS.setExpandProperties() sends postMessage to host', function (done) {
56 | var spy = sinon.spy(windowObject, 'postMessage');
57 |
58 | windowObject.APPNEXUS.ready(function () {
59 | windowObject.APPNEXUS.setExpandProperties({
60 | width: 600,
61 | height: 800
62 | });
63 | expect(spy.withArgs(fixtures.SET_EXPAND_PROPS_MESSAGE).calledOnce).to.equal(true);
64 | done();
65 | });
66 | });
67 |
68 | it('check APPNEXUS.getExpandProperties() returns the correct properties', function (done) {
69 | windowObject.APPNEXUS.ready(function () {
70 | windowObject.APPNEXUS.setExpandProperties({
71 | width: 600,
72 | height: 800
73 | });
74 |
75 | expect(windowObject.APPNEXUS.getExpandProperties()).to.eql({ width: 600, height: 800 });
76 | done();
77 | });
78 | });
79 |
80 | it('check APPNEXUS.expand() sends postMessage to host', function (done) {
81 | var spy = sinon.spy(windowObject, 'postMessage');
82 |
83 | windowObject.APPNEXUS.ready(function () {
84 | windowObject.APPNEXUS.expand();
85 | expect(spy.withArgs(fixtures.EXPAND_MESSAGE).calledOnce).to.equal(true);
86 | done();
87 | });
88 | });
89 |
90 | it('check APPNEXUS.collapse() sends postMessage to host', function (done) {
91 | var spy = sinon.spy(windowObject, 'postMessage');
92 |
93 | windowObject.APPNEXUS.ready(function () {
94 | windowObject.APPNEXUS.collapse();
95 | expect(spy.withArgs(fixtures.COLLAPSE_MESSAGE).calledOnce).to.equal(true);
96 | done();
97 | });
98 | });
99 |
100 | it('should return undefined for a non existing macro', function (done) {
101 | windowObject.APPNEXUS.ready(function () {
102 | expect(windowObject.APPNEXUS.getMacroByName("non_existing_macro")).to.equal(undefined)
103 | done();
104 | })
105 | })
106 |
107 | it('should call getMacroByName once when attempting to require a macro value', function (done) {
108 | var macro = 'blah';
109 | var spy = sinon.spy();
110 | sinon.replace(windowObject.APPNEXUS, 'getMacroByName', spy);
111 |
112 | windowObject.APPNEXUS.ready(function () {
113 | windowObject.APPNEXUS.getMacroByName(macro);
114 | expect(spy.withArgs(macro).calledOnce).to.equal(true);
115 | expect(spy.calledOnce).to.be.true;
116 | sinon.restore();
117 | done();
118 | });
119 | });
120 | });
--------------------------------------------------------------------------------
/src/client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Porthole = require('./lib/porthole');
4 | var EventListener = require('./lib/event-listener');
5 |
6 | function AppNexusHTML5Lib () {
7 | var self = this;
8 | this.debug = false;
9 | this.inFrame = false;
10 | this.EventListener = EventListener;
11 |
12 | var isClient = false;
13 | var readyCalled = false;
14 | var isPageLoaded = false;
15 | var expandProperties = {}
16 | var dispatcher = new EventListener();
17 | var clientPorthole;
18 | var adData = {};
19 | var clickTag = '';
20 |
21 | try {
22 | this.inFrame = (window.self !== window.top);
23 | } catch (e) {
24 | this.inFrame = true;
25 | }
26 |
27 | dispatcher.addEventListener('ready', function () {
28 | if (readyCalled) {
29 | if (self.debug) console.info('Client initialized!');
30 | }
31 | });
32 |
33 | /**
34 | * Setup porthole so we can talk to our parent and listen to messages from it
35 | */
36 | var initPorthole = function(){
37 | clientPorthole = new Porthole.WindowProxy();
38 | clientPorthole.addEventListener(handleMessages);
39 | clientPorthole.post({ action: 'ready'}); //notify parent we are ready
40 | };
41 |
42 | var checkReady = function (f){ /in/.test(document.readyState) ? setTimeout(function () { checkReady(f); } , 9) : f(); }
43 | checkReady(function (){
44 | isPageLoaded = true;
45 | if (!!Object.keys(adData).length) {
46 | dispatcher.dispatchEvent('ready');
47 | }
48 | });
49 |
50 | var openUrl = function(url){
51 | window.open(url, "_blank");
52 | };
53 |
54 | /**
55 | * Listen to messages that come from the parent window
56 | * @param messageEvent
57 | */
58 | var handleMessages = function(messageEvent){
59 | switch(messageEvent.data.action) {
60 | case 'setAdData': //receive data about the ad
61 | adData = messageEvent.data.parameters;
62 | if (isPageLoaded) {
63 | dispatcher.dispatchEvent('ready');
64 | }
65 | break;
66 | }
67 | };
68 |
69 | function getParameterByName(name) {
70 | var match = RegExp('[?&]' + name +
71 | '=([^&]*)').exec(window.location.search);
72 | return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
73 | }
74 |
75 | this.ready = function (callback) {
76 | if (typeof callback === 'function') {
77 | dispatcher.addEventListener('ready', callback);
78 | }
79 |
80 | if (!readyCalled) {
81 | initPorthole();
82 | readyCalled = true;
83 | clickTag = this.getClickTag();
84 | self.debug = !self.inFrame;
85 | }
86 | }
87 |
88 | this.getClickTag = function(){
89 | return getParameterByName('clickTag');
90 | };
91 |
92 | this.click = function () {
93 | clickTag = this.getClickTag();
94 | if (!clickTag) console.log('No clickTag defined: click event will open a blank page');
95 | openUrl(clickTag);
96 | if (self.debug) console.info('Client send action: click');
97 | }
98 |
99 | this.setExpandProperties = function (props) {
100 | if (!readyCalled || !clientPorthole) throw new Error('APPNEXUS library has not been initialized. APPNEXUS.ready() must be called first');
101 | expandProperties = props;
102 | clientPorthole.post({ action: 'set-expand-properties', properties: props });
103 | if (self.debug) console.info('Client send action: set-expand-properties');
104 | }
105 |
106 | this.getExpandProperties = function () {
107 | return expandProperties;
108 | }
109 |
110 | this.expand = function () {
111 | if (!readyCalled || !clientPorthole) throw new Error('APPNEXUS library has not been initialized. APPNEXUS.ready() must be called first');
112 | clientPorthole.post({ action: 'expand' });
113 | if (self.debug) console.info('Client send action: expand');
114 | }
115 |
116 | this.collapse = function () {
117 | if (!readyCalled || !clientPorthole) throw new Error('APPNEXUS library has not been initialized. APPNEXUS.ready() must be called first');
118 | clientPorthole.post({ action: 'collapse' });
119 | if (self.debug) console.info('Client send action: collapse');
120 | }
121 |
122 | this.getMacroByName = function (macro) {
123 | if (!readyCalled || !clientPorthole) throw new Error('APPNEXUS library has not been initialized. APPNEXUS.ready() must be called first');
124 |
125 | if(adData && adData.macros && adData.macros[macro] !== undefined){
126 | return adData.macros[macro];
127 | }
128 | else {
129 | return undefined;
130 | }
131 | }
132 | }
133 |
134 | var APPNEXUS = new AppNexusHTML5Lib();
135 | if (typeof window !== 'undefined') {
136 | window.APPNEXUS = APPNEXUS;
137 | }
138 |
139 | module.exports = APPNEXUS;
--------------------------------------------------------------------------------
/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 |
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.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=0;c--)this.__listeners__[c].name===a&&this.__listeners__[c].callback===b&&this.__listeners__.splice(c,1)},d.prototype.dispatchEvent=function(a){for(var b=this.__listeners__.length-1;b>=0;b--)this.__listeners__[this.__listeners__.length-b-1].name===a&&this.__listeners__[this.__listeners__.length-b-1].callback()},b.exports=d},{}],3:[function(a,b,c){var d=!1,e=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/,f=function(){};f.extend=function(a){function b(){!d&&this.init&&this.init.apply(this,arguments)}var c=this.prototype;d=!0;var f=new this;d=!1;for(var g in a)f[g]="function"==typeof a[g]&&"function"==typeof c[g]&&e.test(a[g])?function(a,b){return function(){var d=this._super;this._super=c[a];var e=b.apply(this,arguments);return this._super=d,e}}(g,a[g]):a[g];return b.prototype=f,b.prototype.constructor=b,b.extend=arguments.callee,b};var g={debug:!1,trace:function(a){this.debug&&void 0!==window.console&&window.console.log("Porthole: "+a)},error:function(a){void 0!==typeof window.console&&"function"==typeof window.console.error&&window.console.error("Porthole: "+a)}};g.WindowProxy=function(){},g.WindowProxy.prototype={post:function(a,b){},addEventListener:function(a){},removeEventListener:function(a){}},g.WindowProxyBase=f.extend({init:function(a){void 0===a&&(a=""),this.targetWindowName=a,this.origin=window.location.protocol+"//"+window.location.host,this.eventListeners=[]},getTargetWindowName:function(){return this.targetWindowName},getOrigin:function(){return this.origin},getTargetWindow:function(){return g.WindowProxy.getTargetWindow(this.targetWindowName)},post:function(a,b){void 0===b&&(b="*"),this.dispatchMessage({data:a,sourceOrigin:this.getOrigin(),targetOrigin:b,sourceWindowName:window.name,targetWindowName:this.getTargetWindowName()})},addEventListener:function(a){return this.eventListeners.push(a),a},removeEventListener:function(a){var b;try{b=this.eventListeners.indexOf(a),this.eventListeners.splice(b,1)}catch(c){this.eventListeners=[]}},dispatchEvent:function(a){var b;for(b=0;b50?50:100}}}),g.WindowProxyHTML5=g.WindowProxyBase.extend({init:function(a,b){this._super(b),this.eventListenerCallback=null},dispatchMessage:function(a){this.getTargetWindow().postMessage(g.WindowProxy.serialize(a),a.targetOrigin)},addEventListener:function(a){if(0===this.eventListeners.length){var b=this;window.addEventListener?(this.eventListenerCallback=function(a){b.eventListener(b,a)},window.addEventListener("message",this.eventListenerCallback,!1)):window.attachEvent&&(this.eventListenerCallback=function(a){b.eventListener(b,window.event)},window.attachEvent("onmessage",this.eventListenerCallback))}return this._super(a)},removeEventListener:function(a){this._super(a),0===this.eventListeners.length&&(window.removeEventListener?window.removeEventListener("message",this.eventListenerCallback):window.detachEvent&&("undefined"==typeof window.onmessage&&(window.onmessage=null),window.detachEvent("onmessage",this.eventListenerCallback)),this.eventListenerCallback=null)},eventListener:function(a,b){var c=g.WindowProxy.unserialize(b.data);!c||""!==a.targetWindowName&&c.sourceWindowName!=a.targetWindowName||a.dispatchEvent(new g.MessageEvent(c.data,b.origin,a))}}),window.postMessage?(g.trace("Using built-in browser support"),g.WindowProxy=g.WindowProxyHTML5.extend({})):(g.trace("Using legacy browser support"),g.WindowProxy=g.WindowProxyLegacy.extend({})),g.WindowProxy.serialize=function(a){if("undefined"==typeof JSON)throw new Error("Porthole serialization depends on JSON!");return JSON.stringify(a)},g.WindowProxy.unserialize=function(a){if("undefined"==typeof JSON)throw new Error("Porthole unserialization dependens on JSON!");try{var b=JSON.parse(a)}catch(c){return!1}return b},g.WindowProxy.getTargetWindow=function(a){return""===a?window.parent:"top"===a||"parent"===a?window[a]:window.frames[a]},g.MessageEvent=function(a,b,c){this.data=a,this.origin=b,this.source=c},g.WindowProxyDispatcher={forwardMessageEvent:function(a){var b,c,d,e=window.decodeURIComponent;document.location.hash.length>0&&(b=g.WindowProxy.unserialize(e(document.location.hash.substr(1))),c=g.WindowProxy.getTargetWindow(b.targetWindowName),d=g.WindowProxyDispatcher.findWindowProxyObjectInWindow(c,b.sourceWindowName),d?d.origin===b.targetOrigin||"*"===b.targetOrigin?d.dispatchEvent(new g.MessageEvent(b.data,b.sourceOrigin,d)):g.error("Target origin "+d.origin+" does not match desired target of "+b.targetOrigin):g.error("Could not find window proxy object on the target window"))},findWindowProxyObjectInWindow:function(a,b){var c;if(a)for(c in a)if(Object.prototype.hasOwnProperty.call(a,c))try{if(null!==a[c]&&"object"==typeof a[c]&&a[c]instanceof a.Porthole.WindowProxy&&a[c].getTargetWindowName()===b)return a[c]}catch(d){}return null},start:function(){window.addEventListener?window.addEventListener("resize",g.WindowProxyDispatcher.forwardMessageEvent,!1):window.attachEvent&&"undefined"!==window.postMessage?window.attachEvent("onresize",g.WindowProxyDispatcher.forwardMessageEvent):document.body.attachEvent?window.attachEvent("onresize",g.WindowProxyDispatcher.forwardMessageEvent):g.error("Cannot attach resize event")}},b.exports=g},{}]},{},[1]);
--------------------------------------------------------------------------------
/docs/Walkthrough-For-Manually-Created-Ads.md:
--------------------------------------------------------------------------------
1 | #Intergrating the AppNexus HTML5 Library with Manually Created HTML5 Ads
2 |
3 | ## Table of Contents
4 | - [Standard Ads](#standard-ads)
5 | - [Expanding Ads](#expanding-ads)
6 | - [Interstitial Ads](#interstitial-ads)
7 |
8 |
9 | ## Standard Ads
10 | All standard ads require a file called `index.html`. Each of the following steps makes changes to that file.
11 |
12 | ####Step 1: Add the AppNexus HTML5 Library
13 |
14 | 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).
15 |
16 | You must add it to the ad's `index.html` file, inside the `` tag, in a `
22 |
23 | ```
24 |
25 | ####Step 2: Add a clickthrough element
26 | Add a unique id `"clickthrough"` to `
` (if that does not exist, you can add it to the `` tag),
27 |
28 | so that this:
29 |
30 | ```html
31 |
37 | ```
38 |
39 | ####Step 3: Handle click event
40 | Standard ads will make use of the `APPNEXUS.ready()` and `APPNEXUS.click()` functions (provided by the AppNexus HTML5 JavaScript library you added in Step 1).
41 |
42 | Before the closing body tag (``), you can copy and paste the following `
54 |
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 `
` (if that does not exist, you can add it to the `
` tag),
81 |
82 | so this:
83 |
84 | ```html
85 |
86 | ```
87 | becomes
88 |
89 | ```html
90 |
91 | ```
92 |
93 | ####Step 3: Configure expanding properties
94 | To make your expanding ad work properly, you will need to add the following `
143 |