├── .editorconfig ├── .gitattributes ├── .gitignore ├── .pr-preview.json ├── LICENSE ├── README.md ├── examples ├── ReadMe.txt ├── black480-Ad.png ├── black480-Content.png ├── black480.png ├── create_video_with_ffmpeg.sh ├── creatives │ ├── banner_nonlinear.html │ ├── banner_nonlinear.js │ ├── base_simid_creative.js │ ├── extender.html │ ├── extender.js │ ├── google_maps_client.js │ ├── hover_nonlinear.html │ ├── hover_nonlinear.js │ ├── selector.html │ ├── selector.js │ ├── simid_map.css │ ├── simid_map.html │ ├── simid_map_creative.js │ ├── simid_overlay.html │ ├── simid_overlay.js │ ├── survey.html │ ├── survey.js │ ├── testers_nonlinear.html │ └── testers_nonlinear.js ├── media │ ├── Big_Buck_Bunny.mp4 │ ├── IAB.mp4 │ ├── IAB480-small.mp4 │ ├── IAB480-smaller.mp4 │ ├── IAB480.mp4 │ ├── LearnMoreEndCard.jpg │ ├── black_countdown.mp4 │ ├── fakeAd.mp4 │ ├── fakeContent.mp4 │ ├── shortAd.mp4 │ ├── sintel_trailer-480p.mp4 │ └── visitSiteEndCard.jpg ├── player │ ├── simid_player.html │ └── simid_player.js └── simid_protocol.js ├── images ├── IABTechLabLogo.jpg ├── collapse-non-linear-workflow.png ├── dgrm-Creative-clickThru.png ├── dgrm-Creative-collapseNonlinear.png ├── dgrm-Creative-expandNonlinear.png ├── dgrm-Creative-requestNavigation.png ├── dgrm-creative-requestNavigation.png ├── dgrm-init-normal.png ├── dgrm-init-reject-timeout.png ├── dgrm-init-reject.png ├── dgrm-init-resolve-delay.png ├── dgrm-init-resolve-timeout.png ├── dgrm-init-special.png ├── dgrm-requestChangeAdDuration-known.png ├── dgrm-requestChangeAdDuration-unknown.png ├── dgrm-requestSkip.png ├── dgrm-requestStop.png ├── dgrm-session-init-sequence.png ├── dgrm-simid-initialization.png ├── dgrm-startCreative-normal.png ├── dgrm-startCreative-reject.png ├── expand-non-linear-workflow.png ├── infinity-duration-value.png ├── init-normal-flow-gray.png ├── init-normal-flow.png ├── init-reject.png ├── init-resolve-timeout.png ├── init-special-cases.png ├── non-linear-intro.png ├── nonlinear-user-experience.png ├── positive-duration-value.png ├── simid_diagram_1_overlay.png ├── simid_diagram_2_api_models.png ├── simid_diagram_3_assets.png ├── simid_diagram_4_loading.png ├── source │ ├── simid_diagram_1_overlay.ai │ ├── simid_diagram_2_api_models.ai │ ├── simid_diagram_3_assets.ai │ └── simid_diagram_4_loading.ai ├── startCreative-normal.png └── startCreative-reject.png ├── index.bs ├── index.html ├── scripts ├── build.sh └── watch.sh ├── simid-1.0.1.html ├── simid-1.0.1.images ├── IABTechLabLogo.jpg ├── dgrm-init-normal.png ├── dgrm-init-reject-timeout.png ├── dgrm-init-reject.png ├── dgrm-init-resolve-delay.png ├── dgrm-init-special.png ├── dgrm-requestChangeAdDuration-known.png ├── dgrm-requestChangeAdDuration-unknown.png ├── dgrm-requestSkip.png ├── dgrm-requestStop.png ├── dgrm-session-init-sequence.png ├── dgrm-simid-initialization.png ├── dgrm-startCreative-normal.png ├── dgrm-startCreative-reject.png ├── infinity-duration-value.png ├── init-normal-flow-gray.png ├── init-normal-flow.png ├── init-reject.png ├── init-resolve-timeout.png ├── init-special-cases.png ├── positive-duration-value.png ├── simid_diagram_1_overlay.png ├── simid_diagram_2_api_models.png ├── simid_diagram_3_assets.png ├── simid_diagram_4_loading.png ├── startCreative-normal.png └── startCreative-reject.png ├── simid-1.0.html ├── simid-1.0.images ├── IABTechLabLogo.jpg ├── simid_diagram_1_overlay.png ├── simid_diagram_2_api_models.png ├── simid_diagram_3_assets.png ├── simid_diagram_4_loading.png └── source │ ├── simid_diagram_1_overlay.ai │ ├── simid_diagram_2_api_models.ai │ ├── simid_diagram_3_assets.ai │ └── simid_diagram_4_loading.ai ├── simid-1.1.0.html └── simid-1.1.0.images ├── IABTechLabLogo.jpg ├── collapse-non-linear-workflow.png ├── dgrm-Creative-clickThru.png ├── dgrm-Creative-collapseNonlinear.png ├── dgrm-Creative-expandNonlinear.png ├── dgrm-Creative-requestNavigation.png ├── dgrm-init-normal.png ├── dgrm-init-reject-timeout.png ├── dgrm-init-reject.png ├── dgrm-init-resolve-delay.png ├── dgrm-init-resolve-timeout.png ├── dgrm-init-special.png ├── dgrm-requestChangeAdDuration-known.png ├── dgrm-requestChangeAdDuration-unknown.png ├── dgrm-requestSkip.png ├── dgrm-requestStop.png ├── dgrm-session-init-sequence.png ├── dgrm-simid-initialization.png ├── dgrm-startCreative-normal.png ├── dgrm-startCreative-reject.png ├── expand-non-linear-workflow.png ├── infinity-duration-value.png ├── init-normal-flow-gray.png ├── init-normal-flow.png ├── init-reject.png ├── init-resolve-timeout.png ├── init-special-cases.png ├── non-linear-intro.png ├── nonlinear-user-experience.png ├── positive-duration-value.png ├── simid_diagram_1_overlay.png ├── simid_diagram_2_api_models.png ├── simid_diagram_3_assets.png ├── simid_diagram_4_loading.png ├── source ├── simid_diagram_1_overlay.ai ├── simid_diagram_2_api_models.ai ├── simid_diagram_3_assets.ai └── simid_diagram_4_loading.ai ├── startCreative-normal.png └── startCreative-reject.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | max_line_length = 80 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.bs diff=html linguist-language=HTML 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | // index.html 2 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed", 4 | "title": "SIVIC" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2021 IAB Technology Laboratory, Inc 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Secure Interactive Media Interface Definition (SIMID) 2 | 3 | The latest human-readable version is available at https://interactiveadvertisingbureau.github.io/SIMID/ 4 | 5 | * [SIMID version 1.0](https://interactiveadvertisingbureau.github.io/SIMID/simid-1.0.html) 6 | * [SIMID version 1.0.1]( https://interactiveadvertisingbureau.github.io/SIMID/simid-1.0.1.html) 7 | * [SIMID version 1.1]( https://interactiveadvertisingbureau.github.io/SIMID/simid-1.1.0.html) 8 | 9 | The sample implementation is available at https://interactiveadvertisingbureau.github.io/SIMID/examples/player/simid_player.html 10 | 11 | Home page on iabtechlab.com - https://iabtechlab.com/SIMID 12 | 13 | ## Contributing 14 | 15 | 1. Check out this repository to your local machine. 16 | 17 | 2. Run `scripts/watch.sh` inside your local copy. This will automatically build `index.html` every time you save a change to `index.bs`. 18 | 19 | 3. Create a branch to store specific edits you want to contribute. 20 | 21 | 4. Make changes to `index.bs` and check the updated version of `index.html` for your changes. 22 | 23 | 5. Commit your changes and file a pull request to get them reviewed and merged. 24 | -------------------------------------------------------------------------------- /examples/ReadMe.txt: -------------------------------------------------------------------------------- 1 | In order to try out the example ad. 2 | 1. Start up a web browser, for example: 3 | python3 -m http.server 4 | 2. navigate to http://localhost:8000/examples/player/simid_player.html 5 | 3. Press the "Start ad playback" button. 6 | -------------------------------------------------------------------------------- /examples/black480-Ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/black480-Ad.png -------------------------------------------------------------------------------- /examples/black480-Content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/black480-Content.png -------------------------------------------------------------------------------- /examples/black480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/black480.png -------------------------------------------------------------------------------- /examples/create_video_with_ffmpeg.sh: -------------------------------------------------------------------------------- 1 | # Generates a black video with a countdown timer. 2 | # Requires ffmpeg 3 | 4 | fps=10; 5 | seconds=30; 6 | mantissaDigits=1; 7 | fontSize=200; 8 | fontFile="/usr/share/fonts/truetype/msttcorefonts/timesbd.ttf" 9 | 10 | 11 | ffmpeg -loop 1 -i black480-Ad.png -c:v libx264 \ 12 | -r $fps -t $seconds -pix_fmt yuv420p \ 13 | -vf "fps=$fps,drawtext=fontfile=$fontFile:fontcolor=yellow:fontsize=$fontSize:x=(w-text_w)/2:y=(h-text_h)/2:text='%{eif\:($seconds-t)\:d}.%{eif\:(mod($seconds-t, 1)*pow(10,$mantissaDigits))\:d\:$mantissaDigits}'"\ 14 | fakeAd.mp4; 15 | 16 | seconds=120; 17 | ffmpeg -loop 1 -i black480-Content.png -c:v libx264 \ 18 | -r $fps -t $seconds -pix_fmt yuv420p \ 19 | -vf "fps=$fps,drawtext=fontfile=$fontFile:fontcolor=green:fontsize=$fontSize:x=(w-text_w)/2:y=(h-text_h)/2:text='%{eif\:($seconds-t)\:d}.%{eif\:(mod($seconds-t, 1)*pow(10,$mantissaDigits))\:d\:$mantissaDigits}'"\ 20 | fakeContent.mp4; 21 | -------------------------------------------------------------------------------- /examples/creatives/banner_nonlinear.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 42 | 43 | 44 | 45 |
46 |

47 | 50 |
51 | 57 | 58 | -------------------------------------------------------------------------------- /examples/creatives/banner_nonlinear.js: -------------------------------------------------------------------------------- 1 | const Y_OFFSET_PERCENTAGE = .7; 2 | const X_OFFSET_PERCENTAGE = .15; 3 | const WIDTH_PERCENTAGE = .7; 4 | const HEIGHT_PERCENTAGE = .15; 5 | 6 | /** 7 | * A sample SIMID non-linear ad for a banner ad that shows a website when clicked on. 8 | * P.S: Not all websites can be shown and they would need to allow the x-frame-options 9 | * header to sameorigin for the ad to display correctly. More information here: 10 | * https://web.dev/samesite-cookies-explained/ 11 | */ 12 | class BannerNonLinear extends BaseSimidCreative { 13 | constructor() { 14 | super(); 15 | 16 | /** 17 | * The desired text on the banner. 18 | * @private {?string} 19 | */ 20 | this.bannerText_ = null; 21 | 22 | /** 23 | * The web URL to be displayed. 24 | * @private {?string} 25 | */ 26 | this.webUrl_ = null; 27 | 28 | this.addButtonClickActions_(); 29 | } 30 | 31 | /** @override */ 32 | onInit(eventData) { 33 | this.updateInternalOnInit(eventData); 34 | this.validateAndParseAdParams_(eventData); 35 | this.updateCreativeWithParams_(); 36 | this.dynamicResize_(); 37 | } 38 | 39 | /** 40 | * Checks validity of ad parameters and rejects with proper message if invalid. 41 | * @param {!Object} eventData an object that contains information details for a particular event 42 | * such as event type, unique Ids, creativeData and environmentData. 43 | * @private 44 | */ 45 | validateAndParseAdParams_(eventData) { 46 | if (!this.creativeData.adParameters) { 47 | this.simidProtocol.reject(eventData, { 48 | errorCode: CreativeErrorCode.UNSPECIFIED, 49 | message: 'Ad parameters not found' 50 | }); 51 | return; 52 | } 53 | 54 | let adParams = ""; 55 | try { 56 | adParams = JSON.parse(this.creativeData.adParameters); 57 | } catch (exception) { 58 | this.simidProtocol.reject(eventData, { 59 | errorCode: CreativeErrorCode.CREATIVE_INTERNAL_ERROR, 60 | message: 'Invalid JSON input for ad parameters' 61 | }); 62 | return; 63 | } 64 | 65 | this.bannerText_ = adParams['bannerText']; 66 | this.webUrl_ = adParams['webUrl']; 67 | 68 | if (!this.webUrl_) { 69 | this.simidProtocol.reject(eventData, { 70 | errorCode: CreativeErrorCode.UNSPECIFIED, 71 | message: 'Required field webUrl not found' 72 | }); 73 | return; 74 | } 75 | 76 | this.simidProtocol.resolve(eventData, {}); 77 | } 78 | 79 | updateCreativeWithParams_() { 80 | document.getElementById('ad_text').textContent = this.bannerText_; 81 | document.getElementById('webpage_container').src = this.webUrl_; 82 | } 83 | 84 | /** 85 | * Adds actions to different buttons available on the overlay. 86 | * @private 87 | */ 88 | addButtonClickActions_() { 89 | this.sendMessageOnButtonClick_('close_ad', CreativeMessage.REQUEST_STOP); 90 | 91 | this.sendMessageOnButtonClick_('ad_text', CreativeMessage.EXPAND_NONLINEAR, () => { 92 | document.getElementById('ad_text').classList.add('hidden'); 93 | document.getElementById('content_box').classList.remove('hidden'); 94 | }); 95 | 96 | this.sendMessageOnButtonClick_('minimize_ad', CreativeMessage.COLLAPSE_NONLINEAR, () => { 97 | document.getElementById('ad_text').classList.remove('hidden'); 98 | document.getElementById('content_box').classList.add('hidden'); 99 | }); 100 | } 101 | 102 | /** 103 | * Sends a SIMID message whenever an element is clicked. 104 | * @param {String} elementName The name of the element. 105 | * @param {String} message The message to send to the player. 106 | * @param {?Function} callback This gets executed after the message to the player is sent. 107 | * @private 108 | */ 109 | sendMessageOnButtonClick_(elementName, message, callback) { 110 | const sendMessageFunction = () => { 111 | this.simidProtocol.sendMessage(message); 112 | if (callback) {callback()}; 113 | } 114 | document.getElementById(elementName).addEventListener( 115 | 'click', sendMessageFunction); 116 | } 117 | 118 | /** 119 | * Repositions the banner ad according to the dimensions of the video player 120 | * by calculating desired dimensions and sending a resize request to creative. 121 | * @private 122 | */ 123 | dynamicResize_() { 124 | // This ad requests that the player resize it and move it, so that it is centered within the player. 125 | let creativeDimensions = {}; 126 | creativeDimensions.x = this.environmentData.videoDimensions.width * X_OFFSET_PERCENTAGE; 127 | creativeDimensions.y = this.environmentData.videoDimensions.height * Y_OFFSET_PERCENTAGE; 128 | creativeDimensions.width = this.environmentData.videoDimensions.width * WIDTH_PERCENTAGE; 129 | creativeDimensions.height = this.environmentData.videoDimensions.height * HEIGHT_PERCENTAGE; 130 | const params = { 131 | videoDimensions: this.environmentData.videoDimensions, 132 | creativeDimensions: creativeDimensions 133 | }; 134 | this.requestResize(params); 135 | } 136 | } -------------------------------------------------------------------------------- /examples/creatives/base_simid_creative.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A subclass of a SIMID ad that implements functionality that will 3 | * be the same for all simid ads. 4 | */ 5 | class BaseSimidCreative { 6 | constructor() { 7 | /** 8 | * Data about the creative, not known until after init. 9 | * @protected {?Object} 10 | */ 11 | this.creativeData = null; 12 | 13 | /** 14 | * Data about the environment the creative plays in, not known until after init. 15 | * @protected {?Object} 16 | */ 17 | this.environmentData = null; 18 | 19 | /** 20 | * The most recent video state from the player. 21 | * @protected {?Object} 22 | */ 23 | this.videoState = { 24 | currentSrc:'', 25 | currentTime: -1, // Time not yet known 26 | duration: -1, // duration unknown 27 | ended: false, 28 | muted: false, 29 | paused: false, 30 | volume: 0.5, 31 | fullscreen: false 32 | } 33 | 34 | 35 | /** 36 | * The simid version, once the player makes it known. 37 | * @protected {String} 38 | */ 39 | this.simidVersion = ''; 40 | 41 | /** 42 | * The protocol for sending and receiving messages. 43 | * @protected {!SimidProtocol} 44 | */ 45 | this.simidProtocol = new SimidProtocol(); 46 | 47 | this.addListeners_(); 48 | } 49 | 50 | /** 51 | * Sets up the creative to listen for messages from the player 52 | * @private 53 | */ 54 | addListeners_() { 55 | this.simidProtocol.addListener(PlayerMessage.INIT, this.onInit.bind(this)); 56 | this.simidProtocol.addListener(PlayerMessage.START_CREATIVE, this.onStart.bind(this)); 57 | this.simidProtocol.addListener(PlayerMessage.FATAL_ERROR, this.onFatalError.bind(this)); 58 | this.simidProtocol.addListener(PlayerMessage.AD_STOPPED, this.onAdStopped.bind(this)); 59 | this.simidProtocol.addListener(PlayerMessage.AD_SKIPPED, this.onAdSkipped.bind(this)); 60 | this.simidProtocol.addListener(PlayerMessage.LOG, this.onReceivePlayerLog.bind(this)); 61 | this.simidProtocol.addListener(PlayerMessage.RESIZE, this.onReceiveResize.bind(this)); 62 | // Handlers with different video events. 63 | this.simidProtocol.addListener(MediaMessage.DURATION_CHANGE, this.onDurationChange.bind(this)); 64 | this.simidProtocol.addListener(MediaMessage.ENDED, this.onVideoEnded.bind(this)); 65 | this.simidProtocol.addListener(MediaMessage.ERROR, this.onVideoError.bind(this)); 66 | this.simidProtocol.addListener(MediaMessage.PAUSE, this.onPause.bind(this)); 67 | this.simidProtocol.addListener(MediaMessage.PLAY, this.onPlay.bind(this)); 68 | this.simidProtocol.addListener(MediaMessage.PLAYING, this.onPlaying.bind(this)); 69 | this.simidProtocol.addListener(MediaMessage.SEEKED, this.onSeeked.bind(this)); 70 | this.simidProtocol.addListener(MediaMessage.SEEKING, this.onSeeking.bind(this)); 71 | this.simidProtocol.addListener(MediaMessage.TIME_UPDATE, this.onTimeUpdate.bind(this)); 72 | this.simidProtocol.addListener(MediaMessage.VOLUME_CHANGE, this.onVolumeChange.bind(this)); 73 | } 74 | 75 | ready() { 76 | this.simidProtocol.createSession(); 77 | } 78 | 79 | /** 80 | * Receives init message from the player. 81 | * @param {!Object} eventData Data from the event. 82 | * @protected 83 | */ 84 | onInit(eventData) { 85 | this.updateInternalOnInit(eventData); 86 | this.simidProtocol.resolve(eventData, {}); 87 | } 88 | 89 | /** 90 | * Updates internal data on initialization call. 91 | * 92 | * Note: When overriding the onInit function and not wishing 93 | * to always resolve, subclasses may instead use this function. 94 | * @param {!Object} eventData Data from the event. 95 | * @protected 96 | */ 97 | updateInternalOnInit(eventData) { 98 | this.creativeData = eventData.args.creativeData; 99 | this.environmentData = eventData.args.environmentData; 100 | this.videoState.muted = this.environmentData.muted; 101 | this.videoState.volume = this.environmentData.volume; 102 | } 103 | 104 | /** 105 | * Receives start message from the player. 106 | * @param {!Object} eventData Data from the event. 107 | * @protected 108 | */ 109 | onStart(eventData) { 110 | // Acknowledge that the ad is started. 111 | this.simidProtocol.resolve(eventData, {}); 112 | console.log('Simid creative started.') 113 | } 114 | 115 | /** 116 | * Called when the creative receives the fatal error message from the player. 117 | * @protected 118 | */ 119 | onFatalError(eventData) { 120 | // After resolving the iframe with this ad should be cleaned up. 121 | this.simidProtocol.resolve(eventData, {}); 122 | } 123 | 124 | /** 125 | * Called when the creative receives the stop message from the player. 126 | * @protected 127 | */ 128 | onAdStopped(eventData) { 129 | // After resolving the iframe with this ad should be cleaned up. 130 | this.simidProtocol.resolve(eventData, {}); 131 | } 132 | 133 | /** 134 | * Called when the creative receives the skip message from the player. 135 | * @protected 136 | */ 137 | onAdSkipped(eventData) { 138 | // After resolving the iframe with this ad should be cleaned up. 139 | this.simidProtocol.resolve(eventData, {}); 140 | } 141 | 142 | /** 143 | * Called when the creative receives a resize message from the player. 144 | * @param {!Object} eventData Data from the event. 145 | * @protected 146 | */ 147 | onReceiveResize(eventData) { 148 | this.environmentData.creativeDimensions = eventData.args.creativeDimensions; 149 | this.environmentData.videoDimensions = eventData.args.videoDimensions; 150 | } 151 | 152 | /** 153 | * Opens the click through url and lets the player know about it. 154 | * @protected 155 | */ 156 | clickThru() { 157 | 158 | } 159 | 160 | /** 161 | * Asks the player for the state of the video element. 162 | * @protected 163 | */ 164 | fetchMediaState() { 165 | this.simidProtocol.sendMessage(CreativeMessage.GET_MEDIA_STATE, {}) 166 | .then((data) => this.onGetMediaStateResolve(data)); 167 | } 168 | 169 | /** 170 | * @protected 171 | */ 172 | onGetMediaStateResolve(data) { 173 | this.videoState = data; 174 | } 175 | 176 | /** 177 | * @protected 178 | */ 179 | onDurationChange(data) { 180 | this.videoState.duration = data.args.duration; 181 | } 182 | 183 | /** 184 | * @protected 185 | */ 186 | onVideoEnded() { 187 | this.videoState.ended = true; 188 | } 189 | 190 | /** 191 | * @protected 192 | */ 193 | onVideoError() { 194 | // no op for this example 195 | } 196 | 197 | /** 198 | * @protected 199 | */ 200 | onPause() { 201 | this.videoState.paused = true; 202 | } 203 | 204 | /** 205 | * @protected 206 | */ 207 | onPlay() { 208 | this.videoState.paused = false; 209 | } 210 | 211 | /** 212 | * @protected 213 | */ 214 | onPlaying() { 215 | this.videoState.paused = false; 216 | } 217 | 218 | /** 219 | * @protected 220 | */ 221 | onSeeked() { 222 | // no op for this example 223 | } 224 | 225 | /** 226 | * @protected 227 | */ 228 | onSeeking() { 229 | // no op for this example 230 | } 231 | 232 | /** 233 | * @protected 234 | */ 235 | onTimeUpdate(data) { 236 | this.videoState.currentTime = data.args.currentTime; 237 | } 238 | 239 | /** 240 | * @protected 241 | */ 242 | onVolumeChange(data) { 243 | this.videoState.volume = data.args.volume; 244 | } 245 | 246 | onReceivePlayerLog(data) { 247 | const logMessage = data.args.message; 248 | console.log("Received message from player: " + logMessage); 249 | } 250 | 251 | /** 252 | * Sends message requesting to resize creative based off of given resizeParameters 253 | * @param {!Object} resizeParams An object with the video & creative dimensions. 254 | */ 255 | requestResize(resizeParams) { 256 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_RESIZE, resizeParams).then( () => { 257 | this.environmentData.creativeDimensions = resizeParams.creativeDimensions; 258 | this.environmentData.videoDimensions = resizeParams.videoDimensions; 259 | }); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /examples/creatives/extender.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 83 | 89 | 90 | -------------------------------------------------------------------------------- /examples/creatives/extender.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A sample SIMID ad demonstrating a selector. 3 | */ 4 | class Extender extends BaseSimidCreative { 5 | constructor() { 6 | super(); 7 | 8 | /** True if the player said it would extend duration. */ 9 | this.extendDuration_ = false; 10 | } 11 | 12 | /** @override */ 13 | onStart(eventData) { 14 | super.onStart(eventData); 15 | // Don't fetch the media state right away in case the video is not yet loaded. 16 | setTimeout(() => { 17 | this.fetchMediaState(); 18 | }, 1500); 19 | } 20 | 21 | /** @override */ 22 | onGetMediaStateResolve(data) { 23 | super.onGetMediaStateResolve(data); 24 | const mediaDuration = data['duration']; 25 | const params = { 26 | 'duration': mediaDuration + 5, 27 | }; 28 | 29 | // Ask the player if we can extend duration 5 seconds to show end cards. 30 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_CHANGE_AD_DURATION, params).then(() => { 31 | this.extendDuration_ = true; 32 | }).catch(() => { 33 | console.log('Player does not support requested duration change.'); 34 | }); 35 | } 36 | 37 | onVideoEnded() { 38 | if (this.extendDuration_) { 39 | this.showEndCards(); 40 | } 41 | } 42 | 43 | showEndCards() { 44 | const firstTag = document.getElementById('firstTag'); 45 | firstTag.classList.remove('hidden'); 46 | firstTag.classList.add('fade-in'); 47 | const secondTag = document.getElementById('secondTag'); 48 | secondTag.classList.remove('hidden'); 49 | secondTag.classList.add('fade-in'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/creatives/google_maps_client.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_MAP_LAT = 37.422004; 2 | const DEFAULT_MAP_LNG = -122.081402; 3 | const DEFAULT_ZOOM = 13; 4 | const DEFAULT_LOCATION_NUM_DISPLAYED = 4; 5 | const MARKER_SIZE = 25; 6 | 7 | /** 8 | * This class handles all map related functionality, including 9 | * displaying the map, calculating & displaying directions, tracking all 10 | * interactions with the map, logging error messages with SimidProtocol, 11 | * and handling any errors that result from the Maps API. 12 | */ 13 | class GoogleMapsClient { 14 | /** 15 | * A GoogleMapsClient object handles 16 | * @param {!string} query The search query string. 17 | * @param {!string} markerUrl The marker image url. 18 | * @param {?google.maps.LatLng} coordinates User coordinates. 19 | * @param {!SimidProtocol} simidProtocol The SimidProtocol object. 20 | * @param {!function} onMapsClientComplete A function that determine's the object's behavior in case 21 | * of an error. 22 | * @param {!Element} travelMethod The HTML element containing the travel method drop down menu. 23 | * @param {!Element} timeDisplay The HTML div where the travel time is to be displayed. 24 | */ 25 | constructor(query, markerUrl, simidProtocol, onMapsClientComplete, travelMethod, 26 | timeDisplay, coordinates = new google.maps.LatLng(DEFAULT_MAP_LAT, DEFAULT_MAP_LNG)) { 27 | /** 28 | * The LatLng coordinates representing the user's current location. 29 | * @private @const {!google.maps.LatLng} 30 | */ 31 | this.currentLocation_ = coordinates; 32 | /** 33 | * The string representing the search query. 34 | * @private @const {string} 35 | */ 36 | this.searchQuery_ = query; 37 | /** 38 | * The desired marker image's string URL. 39 | * @private @const {string} 40 | */ 41 | this.markerImage_ = markerUrl; 42 | /** 43 | * The DirectionsRenderer object that displays directions from 44 | * the given request. 45 | * @private @const {!google.maps.DirectionsRenderer} 46 | */ 47 | this.directionsRenderer_ = new google.maps.DirectionsRenderer(); 48 | /** 49 | * A SimidProtocol object from creative. 50 | * @private @const {!SimidProtocol} 51 | */ 52 | this.simidProtocol = simidProtocol; 53 | /** 54 | * The function that determines the class's behavior in case of an API error. 55 | * @private @const {!function} 56 | */ 57 | this.onMapsClientComplete_ = onMapsClientComplete; 58 | /** 59 | * The element from the document where the travel method selector lives. 60 | * @private @const {!Element} 61 | */ 62 | this.travelMethodElement_ = travelMethod; 63 | /** 64 | * The element from the document where the time display div lives. 65 | * @private @const {!Element} 66 | */ 67 | this.timeDisplayElement_ = timeDisplay; 68 | /** 69 | * A map object from the Google Maps API. 70 | * @private {?google.maps.Map} 71 | */ 72 | this.map_ = null; 73 | /** 74 | * The LatLng coordinates representing the location most recently 75 | * selected by the user, defaulting to the closest location. 76 | * @private {?google.maps.LatLng} 77 | */ 78 | this.activeLocation_ = null; 79 | } 80 | 81 | /** 82 | * Loads a map object that currently defaults to a hardcoded location. 83 | * @param {!Element} mapElement The div within the main document where the map is to be displayed. 84 | */ 85 | displayMap(mapElement) { 86 | this.map_ = new google.maps.Map(mapElement, { 87 | zoom: DEFAULT_ZOOM, 88 | center: this.currentLocation_ 89 | }); 90 | this.findNearby_(); 91 | } 92 | 93 | /** 94 | * Searches for the closest corresponding locations based off of the given search parameter, 95 | * and places pins on the map that represent the closest locations. 96 | * @private 97 | */ 98 | findNearby_() { 99 | const request = { 100 | location: this.currentLocation_, 101 | name: this.searchQuery_, 102 | openNow: true, 103 | rankBy: google.maps.places.RankBy.DISTANCE 104 | }; 105 | const placeService = new google.maps.places.PlacesService(this.map_); 106 | placeService.nearbySearch(request, this.displayResults_.bind(this)); 107 | } 108 | 109 | /** 110 | * Displays the closest advertisement's locations to a user's current location. 111 | * @param {!Array} results An array of Place Results from the search query. 112 | * @param {!google.maps.places.PlacesServiceStatus} status The status returned 113 | * by the PlacesService on the completion of its searches. 114 | * @private 115 | */ 116 | displayResults_(results, status) { 117 | if (status == google.maps.places.PlacesServiceStatus.OK) { 118 | //Active location is set to the closest location to start. 119 | this.activeLocation_ = results[0].geometry.location; 120 | for (let i = 0; i < DEFAULT_LOCATION_NUM_DISPLAYED; i++) { 121 | this.placeMapMarker_(results[i]); 122 | } 123 | this.displayDirections_(); 124 | } else { 125 | const statusErrorMessage = { 126 | message: "ERROR: Failed to complete search: " + status, 127 | }; 128 | this.simidProtocol.sendMessage(CreativeMessage.LOG, statusErrorMessage); 129 | this.onMapsClientComplete_(); 130 | } 131 | } 132 | 133 | /** 134 | * Creates and displays a marker on the map representing a given place. 135 | * @param {!Object} place A Place Result object. 136 | * @private 137 | */ 138 | placeMapMarker_(place) { 139 | const placeIcon = { 140 | url: this.markerImage_, 141 | scaledSize: new google.maps.Size(MARKER_SIZE, MARKER_SIZE) 142 | }; 143 | const placeMarker = new google.maps.Marker({ 144 | map: this.map_, 145 | position: place.geometry.location, 146 | icon: placeIcon 147 | }); 148 | ///Recalculate directions if a different active marker is selected. 149 | placeMarker.addListener('click', () => { 150 | this.activeLocation_ = place.geometry.location; 151 | this.displayDirections_(); 152 | }); 153 | } 154 | 155 | /** 156 | * Displays the route between the starting loaction and destination 157 | * based off of the selected travel mode. 158 | * @private 159 | */ 160 | displayDirections_() { 161 | this.directionsRenderer_.setMap(this.map_); 162 | this.calculateRoute_(); 163 | this.calculateTravelTime_() 164 | this.travelMethodElement_.addEventListener("change", () => { 165 | this.calculateRoute_(); 166 | this.calculateTravelTime_(); 167 | }); 168 | } 169 | 170 | /** 171 | * Calculates the route between the user's current location and current 172 | * active location based off of the selected travel mode. 173 | * @private 174 | */ 175 | calculateRoute_() { 176 | const dirService = new google.maps.DirectionsService(); 177 | const selectedMode = this.travelMethodElement_.value; 178 | dirService.route( 179 | { 180 | origin: this.currentLocation_, 181 | destination: this.activeLocation_, 182 | travelMode: [selectedMode] 183 | }, 184 | (response, status) => { 185 | if (status == "OK") { 186 | this.directionsRenderer_.setDirections(response); 187 | } else { 188 | const directionsErrorMessage = { 189 | message: "ERROR: Failed to load directions: " + status, 190 | }; 191 | this.simidProtocol.sendMessage(CreativeMessage.LOG, directionsErrorMessage); 192 | } 193 | } 194 | ); 195 | } 196 | 197 | /** 198 | * Calculates the time it takes to travel from the origin to the destination. 199 | * @private 200 | */ 201 | calculateTravelTime_() { 202 | const travelMode = this.travelMethodElement_.value; 203 | const matrixService = new google.maps.DistanceMatrixService(); 204 | matrixService.getDistanceMatrix( 205 | { 206 | origins: [this.currentLocation_], 207 | destinations: [this.activeLocation_], 208 | travelMode: [travelMode], 209 | unitSystem: google.maps.UnitSystem.IMPERIAL 210 | }, this.getTravelTime_.bind(this)); 211 | } 212 | 213 | /** 214 | * Gets the travel time from the distanceMatrix response. 215 | * @param {!google.maps.DistanceMatrixResponse} response An object containing 216 | * distance and duration information for the given origin & destination. 217 | * @param {!google.maps.DistanceMatrixStatus} travelStatus The status returned 218 | * by the Distance Matrix on the completion of its calculations. 219 | * @private 220 | */ 221 | getTravelTime_(response, travelStatus) { 222 | if (travelStatus == 'OK') { 223 | const results = response.rows[0].elements; 224 | this.displayTravelTimes_(results[results.length - 1].duration.text); 225 | } 226 | } 227 | 228 | /** 229 | * Adds the travel time to the creative display. 230 | * @param {string} timeString The string object representing travel time. 231 | * @private 232 | */ 233 | displayTravelTimes_(timeString) { 234 | const transportMethod = this.travelMethodElement_.value.toLowerCase(); 235 | this.timeDisplayElement_.innerText = "By "+transportMethod+": " + timeString; 236 | } 237 | } -------------------------------------------------------------------------------- /examples/creatives/hover_nonlinear.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 33 | 34 |
35 | 36 |

Hover over banner to expand

37 |
38 | 44 | 45 | -------------------------------------------------------------------------------- /examples/creatives/hover_nonlinear.js: -------------------------------------------------------------------------------- 1 | const DIMENSION_GROWTH = 1.4; 2 | const OFFSET_SHIFT = .2; 3 | const EDGE_DIMENSION_GROWTH = 1.2; 4 | 5 | /** This creative expands and collapses when the user hovers over the banner. */ 6 | class HoverNonLinear extends BaseSimidCreative { 7 | constructor() { 8 | super(); 9 | 10 | this.initialDimensions_ = null; 11 | this.videoDimensionWidth_ = null; 12 | this.videoDimensionHeight_ = null; 13 | } 14 | 15 | /** @override */ 16 | onInit(eventData) { 17 | super.onInit(eventData); 18 | this.initialDimensions_ = this.environmentData.creativeDimensions; 19 | this.videoDimensionHeight_ = this.environmentData.videoDimensions.height; 20 | this.videoDimensionWidth_ = this.environmentData.videoDimensions.width; 21 | } 22 | 23 | /** @override */ 24 | onStart(eventData) { 25 | super.onStart(eventData); 26 | this.addActions_(); 27 | } 28 | 29 | /** 30 | * Adds actions to different buttons available on the overlay. 31 | * @private 32 | */ 33 | addActions_() { 34 | this.sendMessageOnClick_('close_ad', CreativeMessage.REQUEST_STOP); 35 | this.onHover_('content_container', 'mouseover'); 36 | this.onMouseOut_('content_container', 'mouseout'); 37 | } 38 | 39 | /** 40 | * Sends a SIMID message whenever an element is clicked. 41 | * @param {String} elementName The name of the element. 42 | * @param {String} message The message to send to the player. 43 | * @private 44 | */ 45 | sendMessageOnClick_(elementName, message) { 46 | const sendMessageFunction = () => {this.simidProtocol.sendMessage(message);} 47 | document.getElementById(elementName).addEventListener('click', sendMessageFunction); 48 | } 49 | 50 | /** 51 | * Adds a hover event listener to the contents of the iframe that expands the iframe. 52 | * @param {String} elementName The name of the element. 53 | * @param {Event} event The event performed on the element. 54 | * @private 55 | */ 56 | onHover_(elementName, event) { 57 | const expandOnHoverFunction = () => { 58 | const newDimensions = {}; 59 | let resizeParams = {}; 60 | 61 | let desiredX; 62 | let desiredWidth; 63 | let desiredY; 64 | let desiredHeight; 65 | let fullCreativeWidth; 66 | let fullCreativeHeight; 67 | 68 | //Width grows 20% 69 | desiredWidth = this.initialDimensions_.width * DIMENSION_GROWTH; 70 | desiredX = parseInt(this.initialDimensions_.x) - parseInt(this.initialDimensions_.width * OFFSET_SHIFT); 71 | 72 | //Makes sure x offset fits in the player 73 | if (desiredX <= 0) { 74 | desiredX = 0; 75 | desiredWidth = this.initialDimensions_.width * EDGE_DIMENSION_GROWTH; 76 | } 77 | if (desiredX > this.videoDimensionWidth_) { 78 | return; 79 | } 80 | 81 | //Makes sure width fits in the player 82 | fullCreativeWidth = desiredX + desiredWidth; 83 | if (fullCreativeWidth > this.videoDimensionWidth_) { 84 | fullCreativeWidth = this.videoDimensionWidth_; 85 | desiredWidth = fullCreativeWidth - desiredX; 86 | } 87 | 88 | //Height grows 20% 89 | desiredHeight = this.initialDimensions_.height * DIMENSION_GROWTH; 90 | desiredY = parseInt(this.initialDimensions_.y) - parseInt(this.initialDimensions_.height * OFFSET_SHIFT); 91 | 92 | //Makes sure y offset fits in the player 93 | if (desiredY <= 0) { 94 | desiredY = 0; 95 | desiredHeight = this.initialDimensions_.height * EDGE_DIMENSION_GROWTH; 96 | } 97 | if (desiredY > this.videoDimensionHeight_) { 98 | return; 99 | } 100 | 101 | //Makes sure height fits in the player 102 | fullCreativeHeight = desiredY + desiredHeight; 103 | if (fullCreativeHeight > this.videoDimensionHeight_) { 104 | fullCreativeHeight = this.videoDimensionHeight_; 105 | desiredHeight = fullCreativeHeight - desiredY; 106 | } 107 | 108 | newDimensions.x = desiredX; 109 | newDimensions.y = desiredY; 110 | newDimensions.width = desiredWidth; 111 | newDimensions.height = desiredHeight; 112 | 113 | resizeParams = { 114 | creativeDimensions: newDimensions, 115 | videoDimensions: this.environmentData.videoDimensions, 116 | }; 117 | 118 | this.requestResize(resizeParams); 119 | } 120 | document.getElementById(elementName).addEventListener(event, expandOnHoverFunction); 121 | } 122 | 123 | /** 124 | * Adds a mouse out event listener to the contents of the iframe that shrinks the iframe 125 | * back to its original size. 126 | * @param {String} elementName The name of the element. 127 | * @param {Event} event The event performed on the element. 128 | * @private 129 | */ 130 | onMouseOut_(elementName, event) { 131 | const collpaseOnMouseOutFunction = () => { 132 | const restoreParams = { 133 | creativeDimensions: this.initialDimensions_, 134 | videoDimensions: this.environmentData.videoDimensions, 135 | }; 136 | 137 | this.requestResize(restoreParams); 138 | } 139 | document.getElementById(elementName).addEventListener(event, collpaseOnMouseOutFunction); 140 | } 141 | } -------------------------------------------------------------------------------- /examples/creatives/selector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 68 | 69 | 70 | 71 | 72 | 73 |
Select a Shorter Ad
74 | 75 | 76 | 82 | 83 | -------------------------------------------------------------------------------- /examples/creatives/selector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A sample SIMID ad demonstrating a selector. 3 | */ 4 | class SimidSelector extends BaseSimidCreative { 5 | constructor() { 6 | super(); 7 | /** 8 | * True if the user has clicked an ad. 9 | * @private {boolean} 10 | */ 11 | this.adSelected_ = false; 12 | } 13 | /** @override */ 14 | onStart(eventData) { 15 | super.onStart(eventData); 16 | setTimeout(() => { 17 | this.showElements(); 18 | this.openVideoWhenClicked(); 19 | }, 1000); 20 | 21 | // Give the user 10 seconds to select a shorter ad or continue with default ad. 22 | setTimeout(() => { 23 | if (!this.adSelected_) { 24 | this.hideElements(); 25 | } 26 | }, 10000); 27 | } 28 | 29 | /** 30 | * Causes two videos and a title (text) element to animate over the video. 31 | */ 32 | showElements() { 33 | document.getElementById('title').classList.add('showing'); 34 | document.getElementById('video1').classList.add('showing'); 35 | document.getElementById('video2').classList.add('showing'); 36 | } 37 | 38 | /** 39 | * Causes all overlayed elements to animate off the main video. 40 | */ 41 | hideElements() { 42 | document.getElementById('title').classList.remove('showing'); 43 | document.getElementById('video1').classList.remove('showing'); 44 | document.getElementById('video2').classList.remove('showing'); 45 | } 46 | 47 | /** 48 | * Asks the ad video to pause, plays the selected video and then ends the ad 49 | * once the selected video is played 50 | */ 51 | onVideoClicked (clickedVid, unclickedVid) { 52 | console.log('vid clicked'); 53 | this.adSelected_ = true; 54 | 55 | // Request that the ad playback pauses and then wait for the player to resolve. 56 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_PAUSE).then(() => { 57 | // Set the clicked video to take up the entire video area. 58 | clickedVid.classList.add('fullSize'); 59 | 60 | // Remove the unused elements. 61 | clickedVid.classList.remove('showing'); 62 | unclickedVid.classList.remove('showing'); 63 | document.getElementById('title').classList.remove('showing'); 64 | clickedVid.classList.remove('showing'); 65 | 66 | // When the clicked video ends terminate the ad. 67 | clickedVid.addEventListener('ended', () => this.onEnded()); 68 | 69 | // Start playback of the clicked video. This should work on all 70 | // browsers, since there was a user intent to play the video. 71 | clickedVid.play(); 72 | }); 73 | } 74 | 75 | /** 76 | * Sets up click listeners on the video elements. When a video element 77 | * is clicked on, onVideo clicked is called. 78 | */ 79 | openVideoWhenClicked() { 80 | const video1 = document.getElementById('video1'); 81 | const video2 = document.getElementById('video2'); 82 | 83 | video1.addEventListener('click', () => this.onVideoClicked(video1, video2)); 84 | video2.addEventListener('click', () => this.onVideoClicked(video2, video1)); 85 | } 86 | 87 | onEnded() { 88 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_STOP); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/creatives/simid_map.css: -------------------------------------------------------------------------------- 1 | /* Styling for maps creative */ 2 | 3 | /*Styling for initial state */ 4 | button { 5 | background: rgba(0,0,0,0.5); 6 | border: 1px solid rgba(255,255,255,0.5); 7 | border-right: 0; 8 | box-sizing: content-box; 9 | color: #fff; 10 | font-size: 18px; 11 | line-height: normal; 12 | min-width: 0; 13 | padding: 10px 6px 8px 10px; 14 | width: auto; 15 | text-align: center; 16 | transition: opacity .5s cubic-bezier(0.0,0.0,0.2,1); 17 | text-shadow: 0 0 4px rgba(0,0,0,0.75); 18 | cursor: pointer; 19 | } 20 | 21 | button:hover { 22 | background: rgba(0,0,0,0.7); 23 | } 24 | 25 | #findNearest{ 26 | position: absolute; 27 | bottom: 10px; 28 | left: 10px; 29 | padding: 15px; 30 | } 31 | 32 | .hidden { 33 | visibility: hidden; 34 | } 35 | 36 | #travelMethod { 37 | padding: 4px; 38 | background: rgba(0,0,0,0.5); 39 | border: 1px solid rgba(255,255,255,0.5); 40 | color: white; 41 | font-size: 18px; 42 | margin-left: 11px; 43 | top: 60px; 44 | position: absolute; 45 | } 46 | 47 | #timeDisplay { 48 | position: absolute; 49 | top: 65px; 50 | right: 200px; 51 | padding: 5px; 52 | border: 1px solid black; 53 | background-color: rgba(0,0,0,0.6); 54 | color: white; 55 | max-width: 110px; 56 | text-align: center; 57 | transition: opacity .5s cubic-bezier(0.0,0.0,0.2,1); 58 | text-shadow: 0 0 4px rgba(0,0,0,0.75); 59 | font-size: 16px; 60 | } 61 | 62 | #returnToAd { 63 | position: absolute; 64 | bottom: 10px; 65 | left: 10px; 66 | padding: 15px; 67 | } 68 | 69 | #skipAd { 70 | position: absolute; 71 | bottom: 10px; 72 | right: 10px; 73 | padding: 15px; 74 | padding-right: 30px; 75 | padding-left: 30px; 76 | } 77 | 78 | /*Allows map to display*/ 79 | html, body { 80 | height: 100%; 81 | margin: 0; 82 | padding: 0; 83 | } 84 | 85 | /* 86 | * Display the map in the center of the iframe, 87 | * scaled to allow button display 88 | */ 89 | #map { 90 | height: 90%; 91 | width: 100%; 92 | margin: auto; 93 | top: 180px; 94 | -ms-transform: translateY(-50%); 95 | transform: translateY(-50%); 96 | zoom: 1.15; 97 | overflow: visible !important; 98 | } 99 | 100 | .gm-style-mtc { 101 | top: 2px; 102 | } 103 | 104 | div.gm-style-mtc div { 105 | padding: 0px 12px !important;; 106 | } 107 | 108 | div.gm-svpc { 109 | top: 165px !important; 110 | } 111 | 112 | div.gmnoprint { 113 | top: 50px; 114 | } 115 | 116 | div.gmnoprint div { 117 | position: inherit; 118 | } -------------------------------------------------------------------------------- /examples/creatives/simid_map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 | 20 | 22 | 23 | -------------------------------------------------------------------------------- /examples/creatives/simid_map_creative.js: -------------------------------------------------------------------------------- 1 | const AdParamKeys = { 2 | BUTTON_LABEL: 'buttonLabel', 3 | SEARCH_QUERY: 'searchQuery', 4 | MARKER: 'marker', 5 | COORDINATES: 'userCoordinates' 6 | }; 7 | 8 | const TRAVEL_METHOD = "travelMethod"; 9 | const TIME_DISPLAY = "timeDisplay"; 10 | const FIND_NEAREST_TEMPLATE_TEXT = "Find Nearest "; 11 | const DEFAULT_BUTTON_LABEL = "Location"; 12 | const TRANSPORT_METHODS = ["Driving", "Walking", "Bicycling", "Transit"]; 13 | 14 | /** 15 | * A sample SIMID ad that shows a map of nearby locations. 16 | */ 17 | class SimidMapCreative extends BaseSimidCreative { 18 | constructor() { 19 | super(); 20 | 21 | /** 22 | * A Simid Map object where all of the Maps API calls are handled 23 | * @private {!SimidMap} 24 | */ 25 | this.googleMapsClient_ = null; 26 | /** 27 | * The desired marker image's string URL. 28 | * @private {?string} 29 | */ 30 | this.markerUrl_ = null; 31 | /** 32 | * The string representing the search query. 33 | * @private {?string} 34 | */ 35 | this.query_ = null; 36 | /** 37 | * The LatLng coordinates representing the user's current location. 38 | * @private {?google.maps.LatLng} 39 | */ 40 | this.coordinates_ = null; 41 | } 42 | 43 | /** @override */ 44 | onInit(eventData) { 45 | this.updateInternalOnInit(eventData); 46 | this.validateAndParseAdParams_(eventData); 47 | } 48 | 49 | /** 50 | * Checks validity of ad parameters and rejects with proper message if invalid. 51 | * @param eventData an object that contains information details for a particular event 52 | * such as event type, unique Ids, creativeData and environmentData. 53 | * @private 54 | */ 55 | validateAndParseAdParams_(eventData) { 56 | if (this.creativeData.adParameters == "") { 57 | this.simidProtocol.reject(eventData, {errorCode: CreativeErrorCode.UNSPECIFIED, 58 | message: "Ad parameters not found"}); 59 | return; 60 | } 61 | 62 | let adParams = ""; 63 | try { 64 | adParams = JSON.parse(this.creativeData.adParameters); 65 | } catch (exception) { 66 | this.simidProtocol.reject(eventData, {errorCode: CreativeErrorCode.CREATIVE_INTERNAL_ERROR, 67 | message: "Invalid JSON input for ad parameters"}); 68 | return; 69 | } 70 | this.buttonLabel_ = adParams[AdParamKeys.BUTTON_LABEL]; 71 | this.query_ = adParams[AdParamKeys.SEARCH_QUERY]; 72 | this.markerUrl_ = adParams[AdParamKeys.MARKER]; 73 | this.coordinates_ = adParams[AdParamKeys.COORDINATES]; 74 | 75 | if (!this.query_) { 76 | this.simidProtocol.reject(eventData, {errorCode: CreativeErrorCode.UNSPECIFIED, 77 | message: `Required field ${AdParamKeys.SEARCH_QUERY} not found`}); 78 | return; 79 | } 80 | this.simidProtocol.resolve(eventData, {}); 81 | } 82 | 83 | /** @override */ 84 | onStart(eventData) { 85 | super.onStart(eventData); 86 | this.specifyButtonFeatures_(this.buttonLabel_); 87 | } 88 | 89 | /** 90 | * Sets the text of the Find Nearest button and assigns it a click functionality. 91 | * @param {string=} buttonLabel Refers to the value given to the BUTTON_LABEL key in 92 | * the AdParamKeys object. The value should be representative of the advertised product's 93 | * category and can be specified by the advertisers. If the value is not specified, 94 | * then BUTTON_LABEL's value will default to Location. 95 | * @private 96 | */ 97 | specifyButtonFeatures_(buttonLabel = DEFAULT_BUTTON_LABEL) { 98 | const findNearestButton = document.getElementById('findNearest'); 99 | findNearestButton.innerText = FIND_NEAREST_TEMPLATE_TEXT + buttonLabel; 100 | findNearestButton.focus(); 101 | findNearestButton.onclick = () => this.prepareCreative_(); 102 | } 103 | 104 | prepareCreative_() { 105 | findNearest.classList.add("hidden"); 106 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_PAUSE).then(() => { 107 | const onMapsClientComplete = () => {this.playAd_()}; 108 | this.createMapState_(); 109 | this.googleMapsClient_ = new GoogleMapsClient(this.query_, 110 | this.markerUrl_, this.simidProtocol, onMapsClientComplete, document.getElementById(TRAVEL_METHOD), 111 | document.getElementById(TIME_DISPLAY), this.coordinates_); 112 | this.googleMapsClient_.displayMap(document.getElementById('map')); 113 | this.googleMapsClient_.addMapListener(); 114 | }).catch(() => { 115 | const pauseErrorMessage = { 116 | message: "WARNING: Request to pause ad failed", 117 | }; 118 | this.simidProtocol.sendMessage(CreativeMessage.LOG, pauseErrorMessage); 119 | }); 120 | } 121 | 122 | /** 123 | * Creates the Skip To Content and Return To Ad buttons once the user 124 | * grants permission to access their location and the map appears. 125 | * @private 126 | */ 127 | createMapState_() { 128 | const returnToAdButton = document.createElement("button"); 129 | returnToAdButton.textContent = "Return To Ad"; 130 | returnToAdButton.id = "returnToAd"; 131 | returnToAdButton.focus(); 132 | returnToAdButton.onclick = () => this.playAd_(returnToAdButton); 133 | 134 | const skipAdButton = document.createElement("button"); 135 | skipAdButton.id = "skipAd"; 136 | skipAdButton.textContent = "Skip Ad"; 137 | skipAdButton.onclick = () => this.playContent_(); 138 | 139 | const adContainer = document.getElementById('adContainer'); 140 | adContainer.appendChild(returnToAdButton); 141 | adContainer.appendChild(skipAdButton); 142 | this.createTravelDisplay_(); 143 | } 144 | 145 | /** 146 | * Creates an option element representing a mode of travel. 147 | * @param {!string} travelMode A string representing the 148 | * given mode of travel. 149 | * @private 150 | */ 151 | createTravelOption_(travelMode) { 152 | const travelOption = document.createElement("option"); 153 | travelOption.value = travelMode.toUpperCase(); 154 | travelOption.text = travelMode; 155 | return travelOption; 156 | } 157 | 158 | /** 159 | * Creates a drop down menu where users can choose between 160 | * different modes of travel to display directions for, and 161 | * creates area for travel time to be displayed. 162 | * @private 163 | */ 164 | createTravelDisplay_() { 165 | const travelChoicesContainer = document.getElementById('adContainer'); 166 | const travelMethod = document.createElement('select'); 167 | travelMethod.id = TRAVEL_METHOD; 168 | const timeDisplay = document.createElement("div"); 169 | timeDisplay.id = TIME_DISPLAY; 170 | TRANSPORT_METHODS.forEach((transportType) => { 171 | const newOption = this.createTravelOption_(transportType); 172 | travelMethod.add(newOption); 173 | }); 174 | travelChoicesContainer.append(travelMethod); 175 | travelChoicesContainer.append(timeDisplay); 176 | } 177 | 178 | /** 179 | * Continues to play the ad if user clicks on Return To Ad button. 180 | * @param {!Element} returnToAdButton Refers to the button that takes 181 | * a user back to the video ad. 182 | * @private 183 | */ 184 | playAd_(returnToAdButton) { 185 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_PLAY); 186 | returnToAdButton.classList.add("hidden"); 187 | const mapDiv = document.getElementById("map"); 188 | mapDiv.classList.add("hidden"); 189 | const travelDisplay = document.getElementById(TRAVEL_METHOD); 190 | const timeDisplay = document.getElementById(TIME_DISPLAY); 191 | travelDisplay.classList.add("hidden"); 192 | timeDisplay.classList.add("hidden"); 193 | } 194 | 195 | /** 196 | * Returns to video content if user clicks on Skip To Content button. 197 | * @private 198 | */ 199 | playContent_() { 200 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_SKIP); 201 | } 202 | } -------------------------------------------------------------------------------- /examples/creatives/simid_overlay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
Sample Simid Ad
23 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/creatives/simid_overlay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A sample SIMID ad that shows an overlay 3 | */ 4 | class SimidOverlay extends BaseSimidCreative { 5 | constructor() { 6 | super(); 7 | 8 | this.informationElem_ = document.getElementById('information'); 9 | 10 | this.addButtonClickActions_(); 11 | } 12 | 13 | /** @override */ 14 | onTimeUpdate(data) { 15 | super.onTimeUpdate(data); 16 | this.informationElem_.innerHTML = 'Current Ad Time ' + this.videoState.currentTime; 17 | } 18 | 19 | /** 20 | * Adds actions to different buttons available on the overlay. 21 | */ 22 | addButtonClickActions_() { 23 | this.sendMessageOnButtonClick_('request_play', CreativeMessage.REQUEST_PLAY); 24 | this.sendMessageOnButtonClick_('request_pause', CreativeMessage.REQUEST_PAUSE); 25 | this.sendMessageOnButtonClick_('request_full_screen', CreativeMessage.REQUEST_FULL_SCREEN); 26 | this.sendMessageOnButtonClick_('fatal_error', CreativeMessage.FATAL_ERROR); 27 | this.sendMessageOnButtonClick_('request_skip', CreativeMessage.REQUEST_SKIP); 28 | this.sendMessageOnButtonClick_('request_stop', CreativeMessage.REQUEST_STOP); 29 | this.sendMessageOnLog_(); 30 | this.sendMessageOnChangeDurationClick_(); 31 | } 32 | 33 | /** 34 | * Listens for a click event on a button 35 | * @param {String} elementName The name of the element. 36 | * @param {String} message The message to send to the player. 37 | * @private 38 | */ 39 | sendMessageOnButtonClick_(elementName, message) { 40 | const sendMessageFunction = () => {this.simidProtocol.sendMessage(message);} 41 | document.getElementById(elementName).addEventListener( 42 | 'click', sendMessageFunction.bind(this)); 43 | } 44 | 45 | /** 46 | * Listens for a click event on the log button and sends the 47 | * message contained in the input field to the player. 48 | * @private 49 | */ 50 | sendMessageOnLog_() { 51 | const sendLogFunction = () => { 52 | const logInputMessage = document.getElementById('log_input').value; 53 | const logMessage = { 54 | message: logInputMessage, 55 | }; 56 | this.simidProtocol.sendMessage(CreativeMessage.LOG, logMessage); 57 | } 58 | document.getElementById('log_button').addEventListener( 59 | 'click', sendLogFunction.bind(this)); 60 | } 61 | 62 | sendMessageOnChangeDurationClick_() { 63 | const sendAdDurationFunction = () => { 64 | const durationInput = document.getElementById('change_duration_input').value; 65 | const params = { 66 | 'duration': durationInput, 67 | }; 68 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_CHANGE_AD_DURATION, params).catch(() => { 69 | console.log('Player does not support requested duration change.'); 70 | }); 71 | } 72 | document.getElementById('change_duration_button').addEventListener( 73 | 'click', sendAdDurationFunction.bind(this)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/creatives/survey.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 56 | 57 | 58 | 59 | 60 | 61 |
Sample SIMID Survey Ad
62 |
63 |
64 |
65 |
66 |
67 |
68 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /examples/creatives/survey.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A sample SIMID ad that shows how to impliment a survey. 3 | */ 4 | class SimidSurvey extends BaseSimidCreative { 5 | constructor() { 6 | super(); 7 | 8 | /** 9 | * The current Question being asked. 10 | * @private 11 | */ 12 | this.currentQuestion_ = -1; 13 | 14 | /** 15 | * A list of questions to be asked. Default is no questions. 16 | * @private 17 | */ 18 | this.surveyQuestions_ = []; 19 | 20 | // listen to all buttons. 21 | for (let i = 0; i <= 2; i++) { 22 | const button = document.getElementById('option' + i); 23 | if (button) { 24 | // This examples just shows the next question. A true 25 | // implementation would record answers somehow. 26 | button.onclick = this.showNextQuestion.bind(this); 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * Shows the current question 33 | */ 34 | showQuestion() { 35 | const questionData = this.surveyQuestions_[this.currentQuestion_]; 36 | const questionElement = document.getElementById('current-question'); 37 | questionElement.innerHTML = questionData.question; 38 | for (let i = 0; i <= 2; i++) { 39 | const button = document.getElementById('option' + i); 40 | button.innerHTML = questionData.answers[i]; 41 | } 42 | document.getElementById('question').classList.add('showing'); 43 | } 44 | 45 | /** @override */ 46 | onStart(eventData) { 47 | super.onStart(eventData); 48 | this.surveyQuestions_ = JSON.parse(this.creativeData.adParameters); 49 | this.showNextQuestion(); 50 | } 51 | 52 | /** 53 | * Shows the next question. 54 | */ 55 | showNextQuestion() { 56 | document.getElementById('question').classList.remove('showing'); 57 | this.currentQuestion_ ++; 58 | if (this.currentQuestion_ >= this.surveyQuestions_.length) { 59 | // If the user answers all the questions skip the rest of the ad. 60 | this.simidProtocol.sendMessage(CreativeMessage.REQUEST_SKIP); 61 | return; 62 | } 63 | setTimeout(() => this.showQuestion(), 1000); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/creatives/testers_nonlinear.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 61 | 62 | 63 |
64 |
65 |
66 | 67 | 68 |
69 |
70 | 71 | 72 |
73 |
74 | 75 | 76 |
77 |
78 | 79 | 80 |
81 | 82 |
83 | 84 | 85 |
86 | 87 | 93 | 94 | -------------------------------------------------------------------------------- /examples/creatives/testers_nonlinear.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A sample SIMID nonlinear ad that shows an overlay of 3 | * buttons to showcase functionality 4 | */ 5 | class TestersNonLinear extends BaseSimidCreative { 6 | constructor() { 7 | super(); 8 | 9 | this.addButtonClickActions_(); 10 | } 11 | 12 | /** 13 | * Adds actions to different buttons available on the overlay. 14 | */ 15 | addButtonClickActions_() { 16 | this.sendMessageOnButtonClick_("close_ad", CreativeMessage.REQUEST_STOP); 17 | this.sendMessageOnButtonClick_("expand_button", CreativeMessage.EXPAND_NONLINEAR); 18 | this.sendMessageOnButtonClick_("collapse_button", CreativeMessage.COLLAPSE_NONLINEAR); 19 | } 20 | 21 | /** 22 | * Listens for a click event on a button 23 | * @param {String} elementName The name of the element. 24 | * @param {String} message The message to send to the player. 25 | * @private 26 | */ 27 | sendMessageOnButtonClick_(elementName, message) { 28 | const sendMessageFunction = () => {this.simidProtocol.sendMessage(message);} 29 | document.getElementById(elementName).addEventListener( 30 | 'click', sendMessageFunction.bind(this)); 31 | } 32 | 33 | /** When creative asks to resize itself, this sends a message to the player. */ 34 | onRequestResize() { 35 | const creativeDimensions = {}; 36 | 37 | creativeDimensions.x = document.getElementById('resize_x_val').value; 38 | creativeDimensions.y = document.getElementById('resize_y_val').value; 39 | creativeDimensions.width = document.getElementById('resize_width').value; 40 | creativeDimensions.height = document.getElementById('resize_height').value; 41 | 42 | const resizeParams = { 43 | videoDimensions: this.environmentData.videoDimensions, 44 | creativeDimensions: creativeDimensions 45 | }; 46 | 47 | this.requestResize(resizeParams); 48 | } 49 | } -------------------------------------------------------------------------------- /examples/media/Big_Buck_Bunny.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/Big_Buck_Bunny.mp4 -------------------------------------------------------------------------------- /examples/media/IAB.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/IAB.mp4 -------------------------------------------------------------------------------- /examples/media/IAB480-small.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/IAB480-small.mp4 -------------------------------------------------------------------------------- /examples/media/IAB480-smaller.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/IAB480-smaller.mp4 -------------------------------------------------------------------------------- /examples/media/IAB480.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/IAB480.mp4 -------------------------------------------------------------------------------- /examples/media/LearnMoreEndCard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/LearnMoreEndCard.jpg -------------------------------------------------------------------------------- /examples/media/black_countdown.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/black_countdown.mp4 -------------------------------------------------------------------------------- /examples/media/fakeAd.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/fakeAd.mp4 -------------------------------------------------------------------------------- /examples/media/fakeContent.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/fakeContent.mp4 -------------------------------------------------------------------------------- /examples/media/shortAd.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/shortAd.mp4 -------------------------------------------------------------------------------- /examples/media/sintel_trailer-480p.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/sintel_trailer-480p.mp4 -------------------------------------------------------------------------------- /examples/media/visitSiteEndCard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/examples/media/visitSiteEndCard.jpg -------------------------------------------------------------------------------- /examples/player/simid_player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | A very simple SIMID player 4 | 5 | 6 | 34 | 214 | 215 | 216 | 217 | 218 |
219 |

SIMID Sample Ad Player

220 | 221 | 222 | 223 | 234 | 235 | 236 | 237 | 243 | 244 | 245 | 246 | 259 | 260 | 261 | 262 | 264 | 265 | 266 | 267 | 270 | 271 | 272 | 273 | 276 | 277 | 278 | 279 | 280 | 281 |
Choose Sample Creative Template 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
Ad type 238 | 240 | 242 |
Media File 269 |
Interactive Creative File 275 |
AdParameters
282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 |
291 | 292 |
293 | 294 | 297 | 298 |
299 | 300 | 302 | 303 | 304 | 305 | -------------------------------------------------------------------------------- /examples/player/simid_player.js: -------------------------------------------------------------------------------- 1 | const NO_REQUESTED_DURATION = 0; 2 | const UNLIMITED_DURATION = -2; 3 | 4 | /** DISABLED and ENABLED constants are used to control the state of buttons. */ 5 | const DISABLED = true; 6 | const ENABLED = false; 7 | 8 | /** 9 | * All the logic for a simple SIMID player 10 | */ 11 | class SimidPlayer { 12 | 13 | /** 14 | * Sets up the creative iframe and starts listening for messages 15 | * from the creative. 16 | * @param {!Function} adComplete This function gets called when the ad stops. 17 | * @param {boolean} isLinearAd Represents if the ad is a linear one. 18 | */ 19 | constructor(adComplete, isLinearAd) { 20 | /** 21 | * The protocol for sending and receiving messages. 22 | * @protected {!SimidProtocol} 23 | */ 24 | this.simidProtocol = new SimidProtocol(); 25 | 26 | this.addListeners_(); 27 | 28 | /** 29 | * A reference to the video player on the players main page 30 | * @private {!Element} 31 | */ 32 | this.contentVideoElement_ = document.getElementById('video_player'); 33 | 34 | /** 35 | * A reference to a video player for playing ads. 36 | * @private {!Element} 37 | */ 38 | this.adVideoElement_ = document.getElementById('ad_video_player'); 39 | 40 | /** 41 | * A reference to the iframe holding the SIMID creative. 42 | * @private {?Element} 43 | */ 44 | this.simidIframe_ = null; 45 | 46 | /** 47 | * A reference to the promise returned when initialization was called. 48 | * @private {?Promise} 49 | */ 50 | this.initializationPromise_ = null; 51 | 52 | /** 53 | * A map of events tracked on the ad video element. 54 | * @private {!Map} 55 | */ 56 | this.adVideoTrackingEvents_ = new Map(); 57 | 58 | /** 59 | * A map of events tracked on the content video element. 60 | * @private {!Map} 61 | */ 62 | this.contentVideoTrackingEvents_ = new Map(); 63 | 64 | /** 65 | * A function to execute on ad completion 66 | * @private {!Function} 67 | */ 68 | this.adComplete_ = adComplete; 69 | 70 | /** 71 | * A boolean indicating what type of creative ad is. 72 | * @const @private {boolean} 73 | */ 74 | this.isLinearAd_ = isLinearAd; 75 | 76 | /** 77 | * A number indicating when the non linear ad started. 78 | * @private {?number} 79 | */ 80 | this.nonLinearStartTime_ = null; 81 | 82 | /** 83 | * The duration requested by the ad. 84 | * @private {number} 85 | */ 86 | this.requestedDuration_ = NO_REQUESTED_DURATION; 87 | 88 | /** 89 | * Resolution function for the session created message 90 | * @private {?Function} 91 | */ 92 | this.resolveSessionCreatedPromise_ = null; 93 | 94 | /** 95 | * A promise that resolves once the creative creates a session. 96 | * @private {!Promise} 97 | */ 98 | this.sessionCreatedPromise_ = new Promise((resolve, reject) => { 99 | this.resolveSessionCreatedPromise_ = resolve; 100 | }); 101 | 102 | /** 103 | * Resolution function for the ad being initialized. 104 | * @private {?Function} 105 | */ 106 | this.resolveInitializationPromise_ = null; 107 | 108 | /** 109 | * Reject function for the ad being initialized. 110 | * @private {?Function} 111 | */ 112 | this.rejectInitializationPromise_ = null; 113 | 114 | /** 115 | * An object containing the resized nonlinear creative's dimensions. 116 | * @private {?Object} 117 | */ 118 | this.nonLinearDimensions_ = null; 119 | 120 | /** The unique ID for the interval used to compares the requested change 121 | * duration and the current ad time. 122 | * @private {number} 123 | */ 124 | this.durationInterval_ = null; 125 | 126 | /** 127 | * A promise that resolves once the creative responds to initialization with resolve. 128 | * @private {!Promise} 129 | */ 130 | this.initializationPromise_ = new Promise((resolve, reject) => { 131 | this.resolveInitializationPromise_ = resolve; 132 | this.rejectInitializationPromise_ = reject; 133 | }); 134 | 135 | 136 | this.trackEventsOnAdVideoElement_(); 137 | this.trackEventsOnContentVideoElement_(); 138 | this.hideAdPlayer_(); 139 | } 140 | 141 | /** 142 | * Initializes an ad. This should be called before an ad plays. 143 | * Creates an iframe with the creative in it, then uses a promise 144 | * to call init on the creative as soon as the creative initializes 145 | * a session. 146 | */ 147 | initializeAd() { 148 | if (!this.isLinearAd_ && !this.isValidDimensions_(this.getNonlinearDimensions_())) { 149 | console.log('Unable to play a non-linear ad with dimensions bigger than the player. Please modify dimensions to a smaller size.'); 150 | return; 151 | } 152 | 153 | // After the iframe is created the player will wait until the ad 154 | // initializes the communication channel. Then it will call 155 | // sendInitMessage. 156 | this.simidIframe_ = this.createSimidIframe_(); 157 | 158 | if (!this.isLinearAd_) { 159 | this.displayNonlinearCreative_(); 160 | } 161 | 162 | this.requestDuration_ = NO_REQUESTED_DURATION; 163 | 164 | // Prepare for the case that init fails before sending 165 | // the init message. Initialization failing means abandoning 166 | // the ad. 167 | this.initializationPromise_.catch((e) => { 168 | this.onAdInitializedFailed_(e); 169 | }); 170 | 171 | // Using a promise means that the init message will 172 | // send as soon as the session is created. If the session 173 | // is already created this will send the init message immediately. 174 | this.sessionCreatedPromise_.then(() => { 175 | this.sendInitMessage_(); 176 | }); 177 | 178 | } 179 | 180 | /** 181 | * Plays a SIMID creative once it has responded to the initialize ad message. 182 | */ 183 | playAd() { 184 | 185 | // This example waits for the ad to be initialized, before playing video. 186 | // NOTE: Not all players will wait for session creation and initialization 187 | // before they start playback. 188 | this.initializationPromise_.then(() => { 189 | this.startCreativePlayback_() 190 | }); 191 | } 192 | 193 | /** Plays the video ad element. */ 194 | playAdVideo() { 195 | this.adVideoElement_.play(); 196 | } 197 | 198 | /** 199 | * Sets up an iframe for holding the simid element. 200 | * 201 | * @return {!Element} The iframe where the simid element lives. 202 | * @private 203 | */ 204 | createSimidIframe_() { 205 | const playerDiv = document.getElementById('player_div'); 206 | const simidIframe = document.createElement('iframe'); 207 | simidIframe.style.display = 'none'; 208 | // The target of the player to send messages to is the newly 209 | // created iframe. 210 | playerDiv.appendChild(simidIframe); 211 | 212 | if (this.isLinearAd_) { 213 | // Set up css to overlay the SIMID iframe over the entire video creative 214 | // only if linear. Non-linear ads will have dimension inputs for placement 215 | simidIframe.classList.add('linear_simid_creative'); 216 | } 217 | 218 | // Set the iframe creative, this should be an html creative. 219 | // TODO: This sample does not show what to do when loading fails. 220 | simidIframe.src = document.getElementById('creative_url').value; 221 | 222 | this.simidProtocol.setMessageTarget(simidIframe.contentWindow); 223 | simidIframe.setAttribute('allowFullScreen', ''); 224 | simidIframe.setAttribute('allow', 'geolocation'); 225 | return simidIframe; 226 | } 227 | 228 | /** 229 | * Listens to all relevant messages from the SIMID add. 230 | * @private 231 | */ 232 | addListeners_() { 233 | this.simidProtocol.addListener(ProtocolMessage.CREATE_SESSION, this.onSessionCreated_.bind(this)); 234 | this.simidProtocol.addListener(CreativeMessage.REQUEST_FULL_SCREEN, this.onRequestFullScreen.bind(this)); 235 | this.simidProtocol.addListener(CreativeMessage.REQUEST_PLAY, this.onRequestPlay.bind(this)); 236 | this.simidProtocol.addListener(CreativeMessage.REQUEST_PAUSE, this.onRequestPause.bind(this)); 237 | this.simidProtocol.addListener(CreativeMessage.FATAL_ERROR, this.onCreativeFatalError.bind(this)); 238 | this.simidProtocol.addListener(CreativeMessage.REQUEST_SKIP, this.onRequestSkip.bind(this)); 239 | this.simidProtocol.addListener(CreativeMessage.REQUEST_STOP, this.onRequestStop.bind(this)); 240 | this.simidProtocol.addListener(CreativeMessage.REQUEST_CHANGE_AD_DURATION, 241 | this.onRequestChangeAdDuration.bind(this)); 242 | this.simidProtocol.addListener(CreativeMessage.GET_MEDIA_STATE, this.onGetMediaState.bind(this)); 243 | this.simidProtocol.addListener(CreativeMessage.LOG, this.onReceiveCreativeLog.bind(this)); 244 | this.simidProtocol.addListener(CreativeMessage.EXPAND_NONLINEAR, this.onExpandResize.bind(this)); 245 | this.simidProtocol.addListener(CreativeMessage.COLLAPSE_NONLINEAR, this.onCollapse.bind(this)); 246 | this.simidProtocol.addListener(CreativeMessage.REQUEST_RESIZE, this.onRequestResize.bind(this)); 247 | } 248 | 249 | /** 250 | * Resolves the session created promise. 251 | * @private 252 | */ 253 | onSessionCreated_() { 254 | // Anything that must happen after the session is created can now happen 255 | // since this promise is resolved. 256 | this.resolveSessionCreatedPromise_(); 257 | } 258 | 259 | /** 260 | * Destroys the existing simid iframe. 261 | * @private 262 | */ 263 | destroySimidIframe_() { 264 | if (this.simidIframe_) { 265 | this.simidIframe_.remove(); 266 | this.simidIframe_ = null; 267 | this.simidProtocol.reset(); 268 | } 269 | for(let [key, func] of this.adVideoTrackingEvents_) { 270 | this.adVideoElement_.removeEventListener(key, func, true); 271 | } 272 | 273 | for(let [key, func] of this.contentVideoTrackingEvents_) { 274 | this.contentVideoElement_.removeEventListener(key, func, true); 275 | } 276 | 277 | this.adVideoTrackingEvents_.clear(); 278 | this.contentVideoTrackingEvents_.clear(); 279 | this.adComplete_(); 280 | } 281 | 282 | /** 283 | * Returns the full dimensions of an element within the player div. 284 | * @private 285 | * @return {!Object} 286 | */ 287 | getFullDimensions_(elem) { 288 | const videoRect = elem.getBoundingClientRect(); 289 | 290 | return { 291 | 'x' : 0, 292 | 'y' : 0, 293 | 'width' : videoRect.width, 294 | 'height' : videoRect.height, 295 | }; 296 | } 297 | 298 | /** 299 | * Checks whether the input dimensions are valid and fit in the player window. 300 | * @private 301 | * @param {!Object} dimensions A dimension that contains x, y, width & height fields. 302 | * @return {boolean} 303 | */ 304 | isValidDimensions_(dimensions) { 305 | const playerDiv = document.getElementById('player_div'); 306 | const playerRect = playerDiv.getBoundingClientRect(); 307 | 308 | const heightFits = parseInt(dimensions.y) + parseInt(dimensions.height) <= parseInt(playerRect.height); 309 | const widthFits = parseInt(dimensions.x) + parseInt(dimensions.width) <= parseInt(playerRect.width); 310 | 311 | return heightFits && widthFits; 312 | } 313 | 314 | /** 315 | * Returns the specified dimensions of the non-linear creative. 316 | * @private 317 | * @return {!Object} 318 | */ 319 | getNonlinearDimensions_() { 320 | if(this.nonLinearDimensions_) { 321 | return this.nonLinearDimensions_; 322 | } 323 | let newDimensions = {}; 324 | newDimensions.x = document.getElementById('x_val').value; 325 | newDimensions.y = document.getElementById('y_val').value; 326 | newDimensions.width = document.getElementById('width').value; 327 | newDimensions.height = document.getElementById('height').value; 328 | return newDimensions; 329 | } 330 | 331 | /** 332 | * Validates and displays the non-linear creative. 333 | * @private 334 | */ 335 | displayNonlinearCreative_() { 336 | const newDimensions = this.getNonlinearDimensions_(); 337 | 338 | if (!this.isValidDimensions_(newDimensions)) { 339 | console.log('Unable to play a non-linear ad with dimensions bigger than the player. Please modify dimensions to a smaller size.'); 340 | return; 341 | } else { 342 | this.setSimidIframeDimensions_(newDimensions); 343 | this.simidIframe_.style.position = "absolute"; 344 | 345 | this.contentVideoElement_.play(); 346 | 347 | const nonLinearDuration = document.getElementById('duration').value; 348 | this.requestedDuration_ = nonLinearDuration; 349 | } 350 | } 351 | 352 | /** 353 | * Changes the simid iframe dimensions to the given dimensions. 354 | * @private 355 | * @param {!Object} resizeDimensions A dimension that contains an x,y,width & height fields. 356 | */ 357 | setSimidIframeDimensions_(resizeDimensions) { 358 | this.simidIframe_.style.height = resizeDimensions.height; 359 | this.simidIframe_.style.width = resizeDimensions.width; 360 | this.simidIframe_.style.left = `${resizeDimensions.x}px`; 361 | this.simidIframe_.style.top = `${resizeDimensions.y}px`; 362 | } 363 | 364 | /** 365 | * The creative wants to expand the ad. 366 | * @param {!Object} incomingMessage Message sent from the creative to the player 367 | */ 368 | onExpandResize(incomingMessage) { 369 | if (this.isLinearAd_) { 370 | const errorMessage = { 371 | errorCode : CreativeErrorCode.EXPAND_NOT_POSSIBLE, 372 | message: 'Linear resize not yet supported.' 373 | } 374 | this.simidProtocol.reject(incomingMessage, errorMessage); 375 | console.log(errorMessage.message); 376 | 377 | } else { 378 | const fullDimensions = this.getFullDimensions_(this.contentVideoElement_); 379 | this.setSimidIframeDimensions_(fullDimensions); 380 | 381 | this.contentVideoElement_.pause(); 382 | this.simidProtocol.resolve(incomingMessage); 383 | } 384 | } 385 | 386 | /** 387 | * The creative wants to collapse the ad. 388 | * @param {!Object} incomingMessage Message sent from the creative to the player 389 | */ 390 | onCollapse(incomingMessage) { 391 | const newDimensions = this.getNonlinearDimensions_(); 392 | 393 | if (this.isLinearAd_) { 394 | const errorMessage = { 395 | message: 'Cannot collapse linear ads.' 396 | } 397 | this.simidProtocol.reject(incomingMessage, errorMessage); 398 | console.log(errorMessage.message); 399 | 400 | } else if (!this.isValidDimensions_(newDimensions)) { 401 | const errorMessage = { 402 | message: 'Unable to collapse to dimensions bigger than the player. Please modify dimensions to a smaller size.' 403 | } 404 | this.simidProtocol.reject(incomingMessage, errorMessage); 405 | console.log(errorMessage.message); 406 | 407 | } else { 408 | this.setSimidIframeDimensions_(newDimensions); 409 | this.simidIframe_.style.position = "absolute"; 410 | 411 | this.contentVideoElement_.play(); 412 | this.simidProtocol.resolve(incomingMessage); 413 | } 414 | } 415 | 416 | /** 417 | * The creative wants to resize the ad. 418 | * @param {!Object} incomingMessage Message sent from the creative to the player. 419 | */ 420 | onRequestResize(incomingMessage) { 421 | 422 | if (this.isLinearAd_) { 423 | const errorMessage = { 424 | errorCode : CreativeErrorCode.EXPAND_NOT_POSSIBLE, 425 | message: 'Linear resize not yet supported.' 426 | } 427 | this.simidProtocol.reject(incomingMessage, errorMessage); 428 | console.log(errorMessage.message); 429 | 430 | } else if (!this.isValidDimensions_(incomingMessage.args.creativeDimensions)) { 431 | const errorMessage = { 432 | errorCode : CreativeErrorCode.EXPAND_NOT_POSSIBLE, 433 | message: 'Unable to resize a non-linear ad with dimensions bigger than the player. Please modify dimensions to a smaller size.' 434 | } 435 | this.simidProtocol.reject(incomingMessage, errorMessage); 436 | console.log(errorMessage.message); 437 | 438 | } else { 439 | this.nonLinearDimensions_ = incomingMessage.args.creativeDimensions; 440 | this.setSimidIframeDimensions_(incomingMessage.args.creativeDimensions); 441 | this.simidProtocol.resolve(incomingMessage); 442 | } 443 | } 444 | 445 | /** 446 | * Initializes the SIMID creative with all data it needs. 447 | * @private 448 | */ 449 | sendInitMessage_() { 450 | const videoDimensions = this.getFullDimensions_(this.contentVideoElement_); 451 | // Since the creative starts as hidden it will take on the 452 | // video element dimensions, so tell the ad about those dimensions. 453 | const creativeDimensions = this.isLinearAd_ ? 454 | this.getFullDimensions_(this.contentVideoElement_) : 455 | this.getNonlinearDimensions_(); 456 | 457 | const environmentData = { 458 | 'videoDimensions': videoDimensions, 459 | 'creativeDimensions': creativeDimensions, 460 | 'fullscreen': false, 461 | 'fullscreenAllowed': true, 462 | 'variableDurationAllowed': true, 463 | 'skippableState': 'adHandles', // This player does not render a skip button. 464 | 'siteUrl': document.location.host, 465 | 'appId': '', // This is not relevant on desktop 466 | 'useragent': '', // This should be filled in for sdks and players 467 | 'deviceId': '', // This should be filled in on mobile 468 | 'muted': this.adVideoElement_.muted, 469 | 'volume': this.adVideoElement_.volume 470 | } 471 | 472 | const creativeData = { 473 | 'adParameters' : document.getElementById('ad_params').value, 474 | // These values should be populated from the VAST response. 475 | 'adId' : '', 476 | 'creativeId' : '', 477 | 'adServingId': '', 478 | 'clickThroughUrl': 'http://example.com' 479 | } 480 | 481 | if (!this.isLinearAd_) { 482 | creativeData['duration'] = document.getElementById('duration').value; 483 | } 484 | 485 | const initMessage = { 486 | 'environmentData' : environmentData, 487 | 'creativeData': creativeData 488 | } 489 | const initPromise = this.simidProtocol.sendMessage( 490 | PlayerMessage.INIT, initMessage); 491 | initPromise.then((args)=> { 492 | this.resolveInitializationPromise_(args); 493 | }).catch((args) => { 494 | this.rejectInitializationPromise_(args); 495 | }) 496 | } 497 | 498 | /** 499 | * Called once the creative responds positively to being initialized. 500 | * @private 501 | */ 502 | startCreativePlayback_() { 503 | // Once the ad is successfully initialized it can start. 504 | // If the ad is not visible it must be made visible here. 505 | this.showSimidIFrame_(); 506 | 507 | if (this.isLinearAd_) { 508 | this.playLinearVideoAd_(); 509 | } else { 510 | this.nonLinearStartTime_ = this.contentVideoElement_.currentTime; 511 | this.contentVideoElement_.play(); 512 | } 513 | 514 | this.simidProtocol.sendMessage(PlayerMessage.START_CREATIVE); 515 | // TODO: handle creative rejecting startCreative message. 516 | } 517 | 518 | /** 519 | * Pauses content video and plays linear ad. 520 | * @private 521 | */ 522 | playLinearVideoAd_() { 523 | this.contentVideoElement_.pause(); 524 | this.showAdPlayer_(); 525 | this.adVideoElement_.src = document.getElementById('video_url').value; 526 | this.adVideoElement_.play(); 527 | } 528 | 529 | /** 530 | * Called if the creative responds with reject after the player 531 | * initializes the ad. 532 | * @param {!Object} data 533 | * @private 534 | */ 535 | onAdInitializedFailed_(data) { 536 | console.log('Ad init failed. ' + JSON.stringify(data)); 537 | this.destroyIframeAndResumeContent_(); 538 | } 539 | 540 | /** @private */ 541 | hideSimidIFrame_() { 542 | this.simidIframe_.style.display = 'none'; 543 | } 544 | 545 | /** @private */ 546 | showSimidIFrame_() { 547 | this.simidIframe_.style.display = 'block'; 548 | } 549 | 550 | /** @private */ 551 | showAdPlayer_() { 552 | // show the ad video element 553 | this.adVideoElement_.style.display = 'block'; 554 | document.getElementById('ad_video_div').style.display = 'block'; 555 | } 556 | 557 | /** @private */ 558 | hideAdPlayer_() { 559 | // Unload the video 560 | this.adVideoElement_.style.display = 'none'; 561 | document.getElementById('ad_video_div').style.display = 'none'; 562 | } 563 | 564 | /** 565 | * Disables or enables stop, skip, and fatal error buttons 566 | * depending on if the ad is showing. 567 | * @private 568 | * @param {boolean} controlState If buttons should be disabled or enabled 569 | */ 570 | setCreativeControlsState_(controlState) { 571 | let adRequests = document.querySelectorAll(".ad_request"); 572 | adRequests.forEach((request) => { 573 | request.disabled = controlState; 574 | }); 575 | } 576 | 577 | /** 578 | * Tracks the events on the ad video element specified by the simid spec 579 | * @private 580 | */ 581 | trackEventsOnAdVideoElement_() { 582 | this.adVideoTrackingEvents_.set("durationchange", () => { 583 | this.simidProtocol.sendMessage(MediaMessage.DURATION_CHANGE, 584 | {'duration': this.adVideoElement_.duration}); 585 | }); 586 | this.adVideoTrackingEvents_.set("ended", this.videoComplete.bind(this)); 587 | this.adVideoTrackingEvents_.set("error", () => { 588 | this.simidProtocol.sendMessage(MediaMessage.ERROR, 589 | { 590 | 'error': '', // TODO fill in these values correctly 591 | 'message': '' 592 | }); 593 | }); 594 | this.adVideoTrackingEvents_.set("pause", () => { 595 | this.simidProtocol.sendMessage(MediaMessage.PAUSE); 596 | }); 597 | this.adVideoTrackingEvents_.set("play", () => { 598 | this.simidProtocol.sendMessage(MediaMessage.PLAY); 599 | }); 600 | this.adVideoTrackingEvents_.set("playing", () => { 601 | this.simidProtocol.sendMessage(MediaMessage.PLAYING); 602 | }); 603 | this.adVideoTrackingEvents_.set("seeked", () => { 604 | this.simidProtocol.sendMessage(MediaMessage.SEEKED); 605 | }); 606 | this.adVideoTrackingEvents_.set("seeking", () => { 607 | this.simidProtocol.sendMessage(MediaMessage.SEEKING); 608 | }); 609 | this.adVideoTrackingEvents_.set("timeupdate", () => { 610 | this.simidProtocol.sendMessage(MediaMessage.TIME_UPDATE, 611 | {'currentTime': this.adVideoElement_.currentTime}); 612 | this.compareAdAndRequestedDurations_(); 613 | }); 614 | this.adVideoTrackingEvents_.set("volumechange", () => { 615 | this.simidProtocol.sendMessage(MediaMessage.VOLUME_CHANGE, 616 | {'volume': this.adVideoElement_.volume}); 617 | }); 618 | 619 | for(let [key, func] of this.adVideoTrackingEvents_) { 620 | this.adVideoElement_.addEventListener(key, func, true); 621 | } 622 | } 623 | 624 | /** 625 | * Tracks the events on the content video element. 626 | * @private 627 | */ 628 | trackEventsOnContentVideoElement_() { 629 | this.contentVideoTrackingEvents_.set("timeupdate", () => { 630 | if (this.contentVideoElement_.currentTime - this.nonLinearStartTime_ > this.requestedDuration_) { 631 | this.stopAd(StopCode.NON_LINEAR_DURATION_COMPLETE); 632 | } 633 | }); 634 | 635 | for (let [key, func] of this.contentVideoTrackingEvents_) { 636 | this.contentVideoElement_.addEventListener(key, func, true); 637 | } 638 | } 639 | 640 | /** 641 | * Stops the ad and destroys the ad iframe. 642 | * @param {StopCode} reason The reason the ad will stop. 643 | */ 644 | stopAd(reason = StopCode.PLAYER_INITATED) { 645 | // The iframe is only hidden on ad stoppage. The ad might still request 646 | // tracking pixels before it is cleaned up. 647 | if (this.simidIframe_) { 648 | this.hideSimidIFrame_(); 649 | const closeMessage = { 650 | 'code': reason, 651 | } 652 | // Wait for the SIMID creative to acknowledge stop and then clean 653 | // up the iframe. 654 | this.simidProtocol.sendMessage(PlayerMessage.AD_STOPPED) 655 | .then(() => this.destroyIframeAndResumeContent_()); 656 | } 657 | } 658 | 659 | /** 660 | * Skips the ad and destroys the ad iframe. 661 | */ 662 | skipAd() { 663 | // The iframe is only hidden on ad skipped. The ad might still request 664 | // tracking pixels before it is cleaned up. 665 | this.hideSimidIFrame_(); 666 | // Wait for the SIMID creative to acknowledge skip and then clean 667 | // up the iframe. 668 | this.simidProtocol.sendMessage(PlayerMessage.AD_SKIPPED) 669 | .then(() => this.destroyIframeAndResumeContent_()); 670 | } 671 | 672 | /** 673 | * Removes the simid ad entirely and resumes video playback. 674 | * @private 675 | */ 676 | destroyIframeAndResumeContent_() { 677 | this.hideAdPlayer_(); 678 | this.adVideoElement_.src = ''; 679 | this.destroySimidIframe_(); 680 | this.setCreativeControlsState_(DISABLED); 681 | this.contentVideoElement_.play(); 682 | } 683 | 684 | /** The creative wants to go full screen. */ 685 | onRequestFullScreen(incomingMessage) { 686 | // The spec currently says to only request fullscreen for the iframe. 687 | let promise = null; 688 | if (this.simidIframe_.requestFullscreen) { 689 | promise = this.simidIframe_.requestFullscreen(); 690 | } else if (this.simidIframe_.mozRequestFullScreen) { 691 | // Our tests indicate firefox will probably not respect the request. 692 | promise = this.simidIframe_.mozRequestFullScreen(); 693 | } else if (this.simidIframe_.webkitRequestFullscreen) { 694 | promise = this.simidIframe_.webkitRequestFullscreen(); 695 | } else if (this.simidIframe_.msRequestFullscreen) { 696 | // Our tests indicate IE will probably not respect the request. 697 | promise = this.simidIframe_.msRequestFullscreen(); 698 | } 699 | if (promise) { 700 | promise.then(() => this.simidProtocol.resolve(incomingMessage)); 701 | } else { 702 | // TODO: Many browsers are not returning promises but are still 703 | // going full screen. Assuming resolve (bad). 704 | this.simidProtocol.resolve(incomingMessage) 705 | } 706 | } 707 | 708 | /** The creative wants to play video. */ 709 | onRequestPlay(incomingMessage) { 710 | 711 | if (this.isLinearAd_) { 712 | this.adVideoElement_.play() 713 | .then(() => this.simidProtocol.resolve(incomingMessage)) 714 | .catch(() => { 715 | const errorMessage = { 716 | errorCode : PlayerErrorCode.VIDEO_COULD_NOT_LOAD, 717 | message: 'The SIMID media could not be loaded.' 718 | } 719 | this.simidProtocol.reject(incomingMessage, errorMessage); 720 | }); 721 | } else { 722 | const errorMessage = { 723 | errorCode : CreativeErrorCode.PLAYBACK_AREA_UNUSABLE, 724 | message: 'Non linear ads do not play video.' 725 | } 726 | this.simidProtocol.reject(incomingMessage, errorMessage); 727 | } 728 | } 729 | 730 | /** The creative wants to pause video. */ 731 | onRequestPause(incomingMessage) { 732 | this.adVideoElement_.pause(); 733 | this.simidProtocol.resolve(incomingMessage); 734 | } 735 | 736 | /** Pauses the video ad element. */ 737 | pauseAd() { 738 | this.adVideoElement_.pause(); 739 | } 740 | 741 | /** The creative wants to stop with a fatal error. */ 742 | onCreativeFatalError(incomingMessage) { 743 | this.simidProtocol.resolve(incomingMessage); 744 | this.stopAd(StopCode.CREATIVE_INITIATED); 745 | } 746 | 747 | /** The creative wants to skip this ad. */ 748 | onRequestSkip(incomingMessage) { 749 | this.simidProtocol.resolve(incomingMessage); 750 | this.skipAd(); 751 | } 752 | 753 | /** The creative wants to stop the ad early. */ 754 | onRequestStop(incomingMessage) { 755 | this.simidProtocol.resolve(incomingMessage); 756 | this.stopAd(StopCode.CREATIVE_INITIATED); 757 | } 758 | 759 | /** 760 | * The player must implement sending tracking pixels from the creative. 761 | * This sample implementation does not show how to send tracking pixels or 762 | * replace macros. That should be done using the players standard workflow. 763 | */ 764 | onReportTracking(incomingMessage) { 765 | const requestedUrlArray = incomingMessage.args['trackingUrls'] 766 | console.log('The creative has asked for the player to ping ' + requestedUrlArray); 767 | } 768 | 769 | /** 770 | * Called when video playback is complete. 771 | * @private 772 | */ 773 | videoComplete() { 774 | this.simidProtocol.sendMessage(MediaMessage.ENDED); 775 | 776 | if (this.requestedDuration_ == NO_REQUESTED_DURATION) { 777 | this.stopAd(StopCode.MEDIA_PLAYBACK_COMPLETE); 778 | } 779 | 780 | //If the request duration is longer than the ad duration, the ad extends for the requested amount of time 781 | else if (this.requestedDuration_ != UNLIMITED_DURATION) { 782 | const durationChangeMs = (this.requestedDuration_ - this.adVideoElement_.duration) * 1000; 783 | setTimeout(() => { 784 | this.stopAd(StopCode.CREATIVE_INITIATED); 785 | }, durationChangeMs); 786 | } 787 | } 788 | 789 | /** 790 | * Called when creative requests a change in duration of ad. 791 | * @private 792 | */ 793 | onRequestChangeAdDuration(incomingMessage) { 794 | const newRequestedDuration = incomingMessage.args['duration']; 795 | if (newRequestedDuration != UNLIMITED_DURATION && newRequestedDuration < 0) { 796 | const durationErrorMessage = { 797 | errorCode: PlayerErrorCode.UNSUPPORTED_TIME, 798 | message: 'A negative duration is not valid.' 799 | } 800 | this.simidProtocol.reject(incomingMessage, durationErrorMessage); 801 | } 802 | else { 803 | this.requestedDuration_ = newRequestedDuration; 804 | //If requested duration is any other acceptable value 805 | this.compareAdAndRequestedDurations_(); 806 | this.simidProtocol.resolve(incomingMessage); 807 | } 808 | } 809 | 810 | /** 811 | * Compares the duration of the ad with the requested change duration. 812 | * If request duration is the same as the ad duration, ad ends as normal. 813 | * If request duration is unlimited, ad stays on screen until user closes ad. 814 | * If request duration is shorter, the ad stops early. 815 | * @private 816 | */ 817 | compareAdAndRequestedDurations_() { 818 | if (this.requestedDuration_ == NO_REQUESTED_DURATION || 819 | this.requestedDuration_ == UNLIMITED_DURATION) { 820 | //Note: Users can end the ad with unlimited duration with 821 | // the close ad button on the player 822 | return; 823 | } 824 | else if (this.adVideoElement_.currentTime >= this.requestedDuration_) { 825 | //Creative requested a duration shorter than the ad 826 | this.stopAd(StopCode.CREATIVE_INITATED); 827 | } 828 | } 829 | 830 | onGetMediaState(incomingMessage) { 831 | const mediaState = { 832 | 'currentSrc': this.adVideoElement_.currentSrc, 833 | 'currentTime': this.adVideoElement_.currentTime, 834 | 'duration': this.adVideoElement_.duration, 835 | 'ended': this.adVideoElement_.ended, 836 | 'muted': this.adVideoElement_.muted, 837 | 'paused': this.adVideoElement_.paused, 838 | 'volume': this.adVideoElement_.volume, 839 | 'fullscreen': this.adVideoElement_.fullscreen, 840 | } 841 | this.simidProtocol.resolve(incomingMessage, mediaState); 842 | } 843 | 844 | onReceiveCreativeLog(incomingMessage) { 845 | const logMessage = incomingMessage.args['message'] 846 | console.log("Received message from creative: " + logMessage); 847 | } 848 | 849 | sendLog(outgoingMessage) { 850 | const logMessage = { 851 | 'message': outgoingMessage, 852 | } 853 | this.simidProtocol.sendMessage(PlayerMessage.LOG, logMessage); 854 | } 855 | } -------------------------------------------------------------------------------- /examples/simid_protocol.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains logic for sending mesages between the SIMID creative and the player. 3 | * Note: Some browsers do not support promises and a more complete implementation 4 | * should consider using a polyfill. 5 | */ 6 | class SimidProtocol { 7 | 8 | constructor() { 9 | /* 10 | * A map of messsage type to an array of callbacks. 11 | * @private {Map>} 12 | */ 13 | this.listeners_ = {}; 14 | 15 | /* 16 | * The session ID for this protocol. 17 | * @private {String} 18 | */ 19 | this.sessionId_ = ''; 20 | 21 | /** 22 | * The next message ID to use when sending a message. 23 | * @private {number} 24 | */ 25 | this.nextMessageId_ = 1; 26 | 27 | /** 28 | * The window where the message should be posted to. 29 | * @private {!Element} 30 | */ 31 | this.target_ = window.parent; 32 | 33 | this.resolutionListeners_ = {}; 34 | 35 | window.addEventListener('message', 36 | this.receiveMessage.bind(this), false); 37 | } 38 | 39 | /* Reverts this protocol to its original state */ 40 | reset() { 41 | this.listeners_ = {}; 42 | this.sessionId_ = ''; 43 | this.nextMessageId_ = 1; 44 | // TODO: Perhaps we should reject all associated promises. 45 | this.resolutionListeners_ = {}; 46 | } 47 | 48 | /** 49 | * Sends a message using post message. Returns a promise 50 | * that will resolve or reject after the message receives a response. 51 | * @param {string} messageType The name of the message 52 | * @param {?Object} messageArgs The arguments for the message, may be null. 53 | * @return {!Promise} Promise that will be fulfilled when client resolves or rejects. 54 | */ 55 | sendMessage(messageType, messageArgs) { 56 | // Incrementing between messages keeps each message id unique. 57 | const messageId = this.nextMessageId_ ++; 58 | // Only create session does not need to be in the SIMID name space 59 | // because it is part of the protocol. 60 | const nameSpacedMessage = 61 | messageType == ProtocolMessage.CREATE_SESSION ? 62 | messageType : 'SIMID:' + messageType; 63 | // The message object as defined by the SIMID spec. 64 | const message = { 65 | 'sessionId': this.sessionId_, 66 | 'messageId': messageId, 67 | 'type': nameSpacedMessage, 68 | 'timestamp': Date.now(), 69 | 'args': messageArgs 70 | } 71 | 72 | if (EventsThatRequireResponse.includes(messageType)) { 73 | // If the message requires a callback this code will set 74 | // up a promise that will call resolve or reject with its parameters. 75 | return new Promise((resolve, reject) => { 76 | this.addResolveRejectListener_(messageId, resolve, reject); 77 | this.target_.postMessage(JSON.stringify(message), '*'); 78 | }); 79 | } 80 | // A default promise will just resolve immediately. 81 | // It is assumed no one would listen to these promises, but if they do 82 | // it will "just work". 83 | return new Promise((resolve, reject) => { 84 | this.target_.postMessage(JSON.stringify(message), '*'); 85 | resolve(); 86 | }); 87 | } 88 | 89 | /** 90 | * Adds a listener for a given message. 91 | */ 92 | addListener(messageType, callback) { 93 | if (!this.listeners_[messageType]) { 94 | this.listeners_[messageType] = [callback]; 95 | } else { 96 | this.listeners_[messageType].push(callback); 97 | } 98 | } 99 | 100 | /** 101 | * Sets up a listener for resolve/reject messages. 102 | * @private 103 | */ 104 | addResolveRejectListener_(messageId, resolve, reject) { 105 | const listener = (data) => { 106 | const type = data['type']; 107 | const args = data['args']['value']; 108 | if (type == 'resolve') { 109 | resolve(args); 110 | } else if (type == 'reject') { 111 | reject(args); 112 | } 113 | } 114 | this.resolutionListeners_[messageId] = listener.bind(this); 115 | } 116 | 117 | /** 118 | * Recieves messages from either the player or creative. 119 | */ 120 | receiveMessage(event) { 121 | if (!event || !event.data) { 122 | return; 123 | } 124 | const data = JSON.parse(event.data); 125 | if (!data) { 126 | // If there is no data in the event this is not a SIMID message. 127 | return; 128 | } 129 | const sessionId = data['sessionId']; 130 | 131 | const type = data['type']; 132 | // A sessionId is valid in one of two cases: 133 | // 1. It is not set and the message type is createSession. 134 | // 2. The session ids match exactly. 135 | const isCreatingSession = this.sessionId_ == '' && type == ProtocolMessage.CREATE_SESSION; 136 | const isSessionIdMatch = this.sessionId_ == sessionId; 137 | const validSessionId = isCreatingSession || isSessionIdMatch; 138 | 139 | if (!validSessionId || type == null) { 140 | // Ignore invalid messages. 141 | return; 142 | } 143 | 144 | // There are 2 types of messages to handle: 145 | // 1. Protocol messages (like resolve, reject and createSession) 146 | // 2. Messages starting with SIMID: 147 | // All other messages are ignored. 148 | if (Object.values(ProtocolMessage).includes(type)) { 149 | this.handleProtocolMessage_(data); 150 | } else if (type.startsWith('SIMID:')) { 151 | // Remove SIMID: from the front of the message so we can compare them with the map. 152 | const specificType = type.substr(6); 153 | const listeners = this.listeners_[specificType]; 154 | if (listeners) { 155 | listeners.forEach((listener) => listener(data)); 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * Handles incoming messages specifically for the protocol 162 | * @param {!Object} data Data passed back from the message 163 | * @private 164 | */ 165 | handleProtocolMessage_(data) { 166 | const type = data['type']; 167 | switch (type) { 168 | case ProtocolMessage.CREATE_SESSION: 169 | this.sessionId_ = data['sessionId']; 170 | this.resolve(data); 171 | const listeners = this.listeners_[type]; 172 | if (listeners) { 173 | // calls each of the listeners with the data. 174 | listeners.forEach((listener) => listener(data)); 175 | } 176 | break; 177 | case ProtocolMessage.RESOLVE: 178 | // intentional fallthrough 179 | case ProtocolMessage.REJECT: 180 | const args = data['args']; 181 | const correlatingId = args['messageId']; 182 | const resolutionFunction = this.resolutionListeners_[correlatingId]; 183 | if (resolutionFunction) { 184 | // If the listener exists call it once only. 185 | resolutionFunction(data); 186 | delete this.resolutionListeners_[correlatingId]; 187 | } 188 | break; 189 | } 190 | } 191 | 192 | /** 193 | * Resolves an incoming message. 194 | * @param {!Object} incomingMessage the message that is being resolved. 195 | * @param {!Object} outgoingArgs Any arguments that are part of the resolution. 196 | */ 197 | resolve(incomingMessage, outgoingArgs) { 198 | const messageId = this.nextMessageId_ ++; 199 | const resolveMessageArgs = { 200 | 'messageId': incomingMessage['messageId'], 201 | 'value': outgoingArgs, 202 | }; 203 | const message = { 204 | 'sessionId': this.sessionId_, 205 | 'messageId': messageId, 206 | 'type': ProtocolMessage.RESOLVE, 207 | 'timestamp': Date.now(), 208 | 'args': resolveMessageArgs 209 | } 210 | this.target_.postMessage(JSON.stringify(message), '*'); 211 | } 212 | 213 | /** 214 | * Rejects an incoming message. 215 | * @param {!Object} incomingMessage the message that is being resolved. 216 | * @param {!Object} outgoingArgs Any arguments that are part of the resolution. 217 | */ 218 | reject(incomingMessage, outgoingArgs) { 219 | const messageId = this.nextMessageId_ ++; 220 | const rejectMessageArgs = { 221 | 'messageId': incomingMessage['messageId'], 222 | 'value': outgoingArgs, 223 | }; 224 | const message = { 225 | 'sessionId': this.sessionId_, 226 | 'messageId': messageId, 227 | 'type': ProtocolMessage.REJECT, 228 | 'timestamp': Date.now(), 229 | 'args': rejectMessageArgs 230 | } 231 | this.target_.postMessage(JSON.stringify(message), '*'); 232 | } 233 | 234 | /** 235 | * Creates a new session. 236 | * @param {String} sessionId 237 | * @return {!Promise} The promise from the create session message. 238 | */ 239 | createSession() { 240 | const sessionCreationResolved = () => { 241 | console.log('Session created.'); 242 | } 243 | const sessionCreationRejected = () => { 244 | // If this ever happens, it may be impossible for the ad 245 | // to ever communicate with the player. 246 | console.log('Session creation was rejected.'); 247 | } 248 | this.generateSessionId_(); 249 | this.sendMessage(ProtocolMessage.CREATE_SESSION).then( 250 | sessionCreationResolved, sessionCreationRejected); 251 | } 252 | 253 | /** 254 | * Sets the session ID, this should only be used on session creation. 255 | * @private 256 | */ 257 | generateSessionId_() { 258 | // This function generates a random v4 UUID. In a v4 UUID, of the format 259 | // xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx, all bits are selected randomly, 260 | // except the bits of 'M', which must be equal to 4, and 'N', whose first 2 261 | // most significant bits must be set to 10b. So in total only 122 of the 128 262 | // bits are random. See 263 | // https://en.wikipedia.org/wiki/Universally_unique_identifier for more. 264 | 265 | // crypto.getRandomValues is preferred over crypto.randomUUID since it 266 | // supports much older browsers including IE, and doesn't require a secure 267 | // context. 268 | 269 | // Create 128 random bits (8-bit * 16). 270 | const random16Uint8s = new Uint8Array(16); 271 | window.crypto.getRandomValues(random16Uint8s); 272 | // Split each 8-bit int into two 4-bit ints (4-bit * 32). 273 | const random32Uint4s = Array.from(Array(32).keys()).map(index => { 274 | const isEven = index % 2 == 0; 275 | const randomUint8 = random16Uint8s[Math.floor(index/2)]; 276 | // Pick the high 4 bits for even indices, the low 4 bits for odd. 277 | return isEven ? (randomUint8 >> 4) : (randomUint8 & 15); 278 | }); 279 | 280 | // Fix the 12th digit to 4 for the UUID version. 281 | random32Uint4s[12] = 4; 282 | // Fix the 16th digit's 2 high bits to 10b for UUID variant 1. 283 | random32Uint4s[16] = 0b1000 | (random32Uint4s[16] & 0b0011); 284 | 285 | const hexDigits = random32Uint4s.map(v => v.toString(16)); 286 | const uuidComponents = [ 287 | hexDigits.slice(0, 8).join(''), 288 | hexDigits.slice(8, 12).join(''), 289 | hexDigits.slice(12, 16).join(''), 290 | hexDigits.slice(16, 20).join(''), 291 | hexDigits.slice(20).join(''), 292 | ]; 293 | const uuid = uuidComponents.join('-'); 294 | this.sessionId_ = uuid; 295 | } 296 | 297 | setMessageTarget(target) { 298 | this.target_ = target; 299 | } 300 | } 301 | 302 | 303 | ProtocolMessage = { 304 | CREATE_SESSION: 'createSession', 305 | RESOLVE: 'resolve', 306 | REJECT: 'reject' 307 | } 308 | 309 | /** Contains all constants common across SIMID */ 310 | 311 | MediaMessage = { 312 | DURATION_CHANGE: 'Media:durationchange', 313 | ENDED: 'Media:ended', 314 | ERROR: 'Media:error', 315 | PAUSE: 'Media:pause', 316 | PLAY: 'Media:play', 317 | PLAYING: 'Media:playing', 318 | SEEKED: 'Media:seeked', 319 | SEEKING: 'Media:seeking', 320 | TIME_UPDATE: 'Media:timeupdate', 321 | VOLUME_CHANGE: 'Media:volumechange', 322 | }; 323 | 324 | PlayerMessage = { 325 | RESIZE: 'Player:resize', 326 | INIT: 'Player:init', 327 | LOG: 'Player:log', 328 | START_CREATIVE: 'Player:startCreative', 329 | AD_SKIPPED: 'Player:adSkipped', 330 | AD_STOPPED: 'Player:adStopped', 331 | FATAL_ERROR: 'Player:fatalError', 332 | }; 333 | 334 | /** Messages from the creative */ 335 | CreativeMessage = { 336 | CLICK_THRU: 'Creative:clickThru', 337 | EXPAND_NONLINEAR: 'Creative:expandNonlinear', 338 | COLLAPSE_NONLINEAR: 'Creative:collapseNonlinear', 339 | FATAL_ERROR: 'Creative:fatalError', 340 | GET_MEDIA_STATE: 'Creative:getMediaState', 341 | LOG: 'Creative:log', 342 | REQUEST_FULL_SCREEN: 'Creative:requestFullScreen', 343 | REQUEST_EXIT_FULL_SCREEN: 'Creative:requestExitFullScreen', 344 | REQUEST_SKIP: 'Creative:requestSkip', 345 | REQUEST_STOP: 'Creative:requestStop', 346 | REQUEST_PAUSE: 'Creative:requestPause', 347 | REQUEST_PLAY: 'Creative:requestPlay', 348 | REQUEST_RESIZE: 'Creative:requestResize', 349 | REQUEST_VOLUME: 'Creative:requestVolume', 350 | REQUEST_TRACKING: 'Creative:reportTracking', 351 | REQUEST_CHANGE_AD_DURATION: 'Creative:requestChangeAdDuration', 352 | REQUEST_RESIZE: 'Creative:requestResize', 353 | }; 354 | 355 | /** 356 | * These messages require a response (either resolve or reject). 357 | * All other messages do not require a response and are information only. 358 | */ 359 | EventsThatRequireResponse = [ 360 | CreativeMessage.GET_MEDIA_STATE, 361 | CreativeMessage.REQUEST_VIDEO_LOCATION, 362 | CreativeMessage.READY, 363 | CreativeMessage.CLICK_THRU, 364 | CreativeMessage.REQUEST_SKIP, 365 | CreativeMessage.REQUEST_STOP, 366 | CreativeMessage.REQUEST_PAUSE, 367 | CreativeMessage.REQUEST_PLAY, 368 | CreativeMessage.REQUEST_FULL_SCREEN, 369 | CreativeMessage.REQUEST_EXIT_FULL_SCREEN, 370 | CreativeMessage.REQUEST_VOLUME, 371 | CreativeMessage.REQUEST_RESIZE, 372 | CreativeMessage.REQUEST_CHANGE_AD_DURATION, 373 | CreativeMessage.REPORT_TRACKING, 374 | PlayerMessage.INIT, 375 | PlayerMessage.START_CREATIVE, 376 | PlayerMessage.AD_SKIPPED, 377 | PlayerMessage.AD_STOPPED, 378 | PlayerMessage.FATAL_ERROR, 379 | ProtocolMessage.CREATE_SESSION, 380 | ]; 381 | 382 | // A list of errors the creative might send to the player. 383 | CreativeErrorCode = { 384 | UNSPECIFIED: 1100, 385 | CANNOT_LOAD_RESOURCE: 1101, 386 | PLAYBACK_AREA_UNUSABLE: 1102, 387 | INCORRECT_VERSION: 1103, 388 | TECHNICAL_ERROR: 1104, 389 | EXPAND_NOT_POSSIBLE: 1105, 390 | PAUSE_NOT_HONORED: 1106, 391 | PLAYMODE_NOT_ADEQUATE: 1107, 392 | CREATIVE_INTERNAL_ERROR: 1108, 393 | DEVICE_NOT_SUPPORTED: 1109, 394 | MESSAGES_NOT_FOLLOWING_SPEC: 1110, 395 | PLAYER_RESPONSE_TIMEOUT: 1111, 396 | }; 397 | 398 | // A list of errors the player might send to the creative. 399 | PlayerErrorCode = { 400 | UNSPECIFIED: 1200, 401 | WRONG_VERSION: 1201, 402 | UNSUPPORTED_TIME: 1202, 403 | UNSUPPORTED_FUNCTIONALITY_REQUEST: 1203, 404 | UNSUPPORTED_ACTIONS: 1204, 405 | POSTMESSAGE_CHANNEL_OVERLOADED: 1205, 406 | VIDEO_COULD_NOT_LOAD: 1206, 407 | VIDEO_TIME_OUT: 1207, 408 | RESPONSE_TIMEOUT: 1208, 409 | MEDIA_NOT_SUPPORTED: 1209, 410 | SPEC_NOT_FOLLOWED_ON_INIT: 1210, 411 | SPEC_NOT_FOLLOWED_ON_MESSAGES: 1211, 412 | }; 413 | 414 | // A list of reasons a player could stop the ad. 415 | StopCode = { 416 | UNSPECIFIED: 0, 417 | USER_INITIATED: 1, 418 | MEDIA_PLAYBACK_COMPLETE: 2, 419 | PLAYER_INITATED: 3, 420 | CREATIVE_INITIATED: 4, 421 | NON_LINEAR_DURATION_COMPLETE: 5, 422 | }; 423 | -------------------------------------------------------------------------------- /images/IABTechLabLogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/IABTechLabLogo.jpg -------------------------------------------------------------------------------- /images/collapse-non-linear-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/collapse-non-linear-workflow.png -------------------------------------------------------------------------------- /images/dgrm-Creative-clickThru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-Creative-clickThru.png -------------------------------------------------------------------------------- /images/dgrm-Creative-collapseNonlinear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-Creative-collapseNonlinear.png -------------------------------------------------------------------------------- /images/dgrm-Creative-expandNonlinear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-Creative-expandNonlinear.png -------------------------------------------------------------------------------- /images/dgrm-Creative-requestNavigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-Creative-requestNavigation.png -------------------------------------------------------------------------------- /images/dgrm-creative-requestNavigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-creative-requestNavigation.png -------------------------------------------------------------------------------- /images/dgrm-init-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-init-normal.png -------------------------------------------------------------------------------- /images/dgrm-init-reject-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-init-reject-timeout.png -------------------------------------------------------------------------------- /images/dgrm-init-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-init-reject.png -------------------------------------------------------------------------------- /images/dgrm-init-resolve-delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-init-resolve-delay.png -------------------------------------------------------------------------------- /images/dgrm-init-resolve-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-init-resolve-timeout.png -------------------------------------------------------------------------------- /images/dgrm-init-special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-init-special.png -------------------------------------------------------------------------------- /images/dgrm-requestChangeAdDuration-known.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-requestChangeAdDuration-known.png -------------------------------------------------------------------------------- /images/dgrm-requestChangeAdDuration-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-requestChangeAdDuration-unknown.png -------------------------------------------------------------------------------- /images/dgrm-requestSkip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-requestSkip.png -------------------------------------------------------------------------------- /images/dgrm-requestStop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-requestStop.png -------------------------------------------------------------------------------- /images/dgrm-session-init-sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-session-init-sequence.png -------------------------------------------------------------------------------- /images/dgrm-simid-initialization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-simid-initialization.png -------------------------------------------------------------------------------- /images/dgrm-startCreative-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-startCreative-normal.png -------------------------------------------------------------------------------- /images/dgrm-startCreative-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/dgrm-startCreative-reject.png -------------------------------------------------------------------------------- /images/expand-non-linear-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/expand-non-linear-workflow.png -------------------------------------------------------------------------------- /images/infinity-duration-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/infinity-duration-value.png -------------------------------------------------------------------------------- /images/init-normal-flow-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/init-normal-flow-gray.png -------------------------------------------------------------------------------- /images/init-normal-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/init-normal-flow.png -------------------------------------------------------------------------------- /images/init-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/init-reject.png -------------------------------------------------------------------------------- /images/init-resolve-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/init-resolve-timeout.png -------------------------------------------------------------------------------- /images/init-special-cases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/init-special-cases.png -------------------------------------------------------------------------------- /images/non-linear-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/non-linear-intro.png -------------------------------------------------------------------------------- /images/nonlinear-user-experience.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/nonlinear-user-experience.png -------------------------------------------------------------------------------- /images/positive-duration-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/positive-duration-value.png -------------------------------------------------------------------------------- /images/simid_diagram_1_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/simid_diagram_1_overlay.png -------------------------------------------------------------------------------- /images/simid_diagram_2_api_models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/simid_diagram_2_api_models.png -------------------------------------------------------------------------------- /images/simid_diagram_3_assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/simid_diagram_3_assets.png -------------------------------------------------------------------------------- /images/simid_diagram_4_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/simid_diagram_4_loading.png -------------------------------------------------------------------------------- /images/source/simid_diagram_1_overlay.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/source/simid_diagram_1_overlay.ai -------------------------------------------------------------------------------- /images/source/simid_diagram_2_api_models.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/source/simid_diagram_2_api_models.ai -------------------------------------------------------------------------------- /images/source/simid_diagram_3_assets.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/source/simid_diagram_3_assets.ai -------------------------------------------------------------------------------- /images/source/simid_diagram_4_loading.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/source/simid_diagram_4_loading.ai -------------------------------------------------------------------------------- /images/startCreative-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/startCreative-normal.png -------------------------------------------------------------------------------- /images/startCreative-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/images/startCreative-reject.png -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | INPUT=index.bs 6 | OUTPUT=index.html 7 | 8 | cd "$( dirname "$0" )/.." 9 | 10 | echo -n "Linting $INPUT ..." 11 | errors="$( curl https://api.csswg.org/bikeshed/ -F file=@"$INPUT" -F force=1 -F output=err --silent )" 12 | 13 | if [[ -n $errors ]]; then 14 | echo " Errors found." 15 | echo -e "\n=== LINTER OUTPUT ===\n$errors\n=== END OF LINTER OUTPUT ===\n" 16 | else 17 | echo " No errors found." 18 | fi 19 | 20 | echo -n "Building $OUTPUT ..." 21 | curl https://api.csswg.org/bikeshed/ -F file=@"$INPUT" -F force=1 --silent >"$OUTPUT" 22 | echo " Done." 23 | -------------------------------------------------------------------------------- /scripts/watch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | INPUT=index.bs 6 | 7 | dir="$( dirname "$0" )" 8 | cd "$dir/.." 9 | 10 | # If we can't invoke `stat` as GNU stat, fall back to BSD-style (incl. macOS) 11 | statopt='-c %Z' 12 | stat $statopt "$INPUT" >/dev/null 2>&1 || statopt='-f %Sm' 13 | 14 | prev=$( stat $statopt "$INPUT" ) 15 | "$dir/build.sh" 16 | while true; do 17 | curr=$( stat $statopt "$INPUT" ) 18 | if [[ $curr != $prev ]]; then 19 | echo "Change to $INPUT detected." 20 | "$dir/build.sh" 21 | prev=$curr 22 | fi 23 | sleep 1 24 | done 25 | -------------------------------------------------------------------------------- /simid-1.0.1.images/IABTechLabLogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/IABTechLabLogo.jpg -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-init-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-init-normal.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-init-reject-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-init-reject-timeout.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-init-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-init-reject.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-init-resolve-delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-init-resolve-delay.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-init-special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-init-special.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-requestChangeAdDuration-known.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-requestChangeAdDuration-known.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-requestChangeAdDuration-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-requestChangeAdDuration-unknown.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-requestSkip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-requestSkip.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-requestStop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-requestStop.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-session-init-sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-session-init-sequence.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-simid-initialization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-simid-initialization.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-startCreative-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-startCreative-normal.png -------------------------------------------------------------------------------- /simid-1.0.1.images/dgrm-startCreative-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/dgrm-startCreative-reject.png -------------------------------------------------------------------------------- /simid-1.0.1.images/infinity-duration-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/infinity-duration-value.png -------------------------------------------------------------------------------- /simid-1.0.1.images/init-normal-flow-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/init-normal-flow-gray.png -------------------------------------------------------------------------------- /simid-1.0.1.images/init-normal-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/init-normal-flow.png -------------------------------------------------------------------------------- /simid-1.0.1.images/init-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/init-reject.png -------------------------------------------------------------------------------- /simid-1.0.1.images/init-resolve-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/init-resolve-timeout.png -------------------------------------------------------------------------------- /simid-1.0.1.images/init-special-cases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/init-special-cases.png -------------------------------------------------------------------------------- /simid-1.0.1.images/positive-duration-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/positive-duration-value.png -------------------------------------------------------------------------------- /simid-1.0.1.images/simid_diagram_1_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/simid_diagram_1_overlay.png -------------------------------------------------------------------------------- /simid-1.0.1.images/simid_diagram_2_api_models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/simid_diagram_2_api_models.png -------------------------------------------------------------------------------- /simid-1.0.1.images/simid_diagram_3_assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/simid_diagram_3_assets.png -------------------------------------------------------------------------------- /simid-1.0.1.images/simid_diagram_4_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/simid_diagram_4_loading.png -------------------------------------------------------------------------------- /simid-1.0.1.images/startCreative-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/startCreative-normal.png -------------------------------------------------------------------------------- /simid-1.0.1.images/startCreative-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.1.images/startCreative-reject.png -------------------------------------------------------------------------------- /simid-1.0.images/IABTechLabLogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/IABTechLabLogo.jpg -------------------------------------------------------------------------------- /simid-1.0.images/simid_diagram_1_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/simid_diagram_1_overlay.png -------------------------------------------------------------------------------- /simid-1.0.images/simid_diagram_2_api_models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/simid_diagram_2_api_models.png -------------------------------------------------------------------------------- /simid-1.0.images/simid_diagram_3_assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/simid_diagram_3_assets.png -------------------------------------------------------------------------------- /simid-1.0.images/simid_diagram_4_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/simid_diagram_4_loading.png -------------------------------------------------------------------------------- /simid-1.0.images/source/simid_diagram_1_overlay.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/source/simid_diagram_1_overlay.ai -------------------------------------------------------------------------------- /simid-1.0.images/source/simid_diagram_2_api_models.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/source/simid_diagram_2_api_models.ai -------------------------------------------------------------------------------- /simid-1.0.images/source/simid_diagram_3_assets.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/source/simid_diagram_3_assets.ai -------------------------------------------------------------------------------- /simid-1.0.images/source/simid_diagram_4_loading.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.0.images/source/simid_diagram_4_loading.ai -------------------------------------------------------------------------------- /simid-1.1.0.images/IABTechLabLogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/IABTechLabLogo.jpg -------------------------------------------------------------------------------- /simid-1.1.0.images/collapse-non-linear-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/collapse-non-linear-workflow.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-Creative-clickThru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-Creative-clickThru.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-Creative-collapseNonlinear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-Creative-collapseNonlinear.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-Creative-expandNonlinear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-Creative-expandNonlinear.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-Creative-requestNavigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-Creative-requestNavigation.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-init-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-init-normal.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-init-reject-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-init-reject-timeout.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-init-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-init-reject.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-init-resolve-delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-init-resolve-delay.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-init-resolve-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-init-resolve-timeout.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-init-special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-init-special.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-requestChangeAdDuration-known.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-requestChangeAdDuration-known.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-requestChangeAdDuration-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-requestChangeAdDuration-unknown.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-requestSkip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-requestSkip.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-requestStop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-requestStop.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-session-init-sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-session-init-sequence.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-simid-initialization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-simid-initialization.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-startCreative-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-startCreative-normal.png -------------------------------------------------------------------------------- /simid-1.1.0.images/dgrm-startCreative-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/dgrm-startCreative-reject.png -------------------------------------------------------------------------------- /simid-1.1.0.images/expand-non-linear-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/expand-non-linear-workflow.png -------------------------------------------------------------------------------- /simid-1.1.0.images/infinity-duration-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/infinity-duration-value.png -------------------------------------------------------------------------------- /simid-1.1.0.images/init-normal-flow-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/init-normal-flow-gray.png -------------------------------------------------------------------------------- /simid-1.1.0.images/init-normal-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/init-normal-flow.png -------------------------------------------------------------------------------- /simid-1.1.0.images/init-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/init-reject.png -------------------------------------------------------------------------------- /simid-1.1.0.images/init-resolve-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/init-resolve-timeout.png -------------------------------------------------------------------------------- /simid-1.1.0.images/init-special-cases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/init-special-cases.png -------------------------------------------------------------------------------- /simid-1.1.0.images/non-linear-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/non-linear-intro.png -------------------------------------------------------------------------------- /simid-1.1.0.images/nonlinear-user-experience.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/nonlinear-user-experience.png -------------------------------------------------------------------------------- /simid-1.1.0.images/positive-duration-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/positive-duration-value.png -------------------------------------------------------------------------------- /simid-1.1.0.images/simid_diagram_1_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/simid_diagram_1_overlay.png -------------------------------------------------------------------------------- /simid-1.1.0.images/simid_diagram_2_api_models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/simid_diagram_2_api_models.png -------------------------------------------------------------------------------- /simid-1.1.0.images/simid_diagram_3_assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/simid_diagram_3_assets.png -------------------------------------------------------------------------------- /simid-1.1.0.images/simid_diagram_4_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/simid_diagram_4_loading.png -------------------------------------------------------------------------------- /simid-1.1.0.images/source/simid_diagram_1_overlay.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/source/simid_diagram_1_overlay.ai -------------------------------------------------------------------------------- /simid-1.1.0.images/source/simid_diagram_2_api_models.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/source/simid_diagram_2_api_models.ai -------------------------------------------------------------------------------- /simid-1.1.0.images/source/simid_diagram_3_assets.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/source/simid_diagram_3_assets.ai -------------------------------------------------------------------------------- /simid-1.1.0.images/source/simid_diagram_4_loading.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/source/simid_diagram_4_loading.ai -------------------------------------------------------------------------------- /simid-1.1.0.images/startCreative-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/startCreative-normal.png -------------------------------------------------------------------------------- /simid-1.1.0.images/startCreative-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InteractiveAdvertisingBureau/SIMID/897a2a738c5310871c1a3fe67410ba74a96f5b97/simid-1.1.0.images/startCreative-reject.png --------------------------------------------------------------------------------