From 6166c15c867421b57f7da00d21e52e85add2bd65 Mon Sep 17 00:00:00 2001 From: rclations Date: Thu, 22 Jun 2017 17:04:23 -0400 Subject: [PATCH 01/41] working on boilerplate in subfolder --- .bowerrc | 3 - .gitignore | 3 - boilerplate/.bowerrc | 3 + boilerplate/.editorconfig | 24 + boilerplate/.eslintrc.js | 78 +++ boilerplate/.eslintrc.json | 171 +++++ boilerplate/.gitignore | 80 +++ boilerplate/.sass-lint.yml | 142 ++++ boilerplate/.travis.yml | 23 + boilerplate/.yo-rc.json | 19 + boilerplate/Dockunit.json | 157 +++++ boilerplate/Gulpfile.js | 360 ++++++++++ readme.txt => boilerplate/README.md | 43 +- boilerplate/assets/README.md | 6 + boilerplate/assets/css/sass/styles.scss | 22 + boilerplate/assets/js/jquery.dfp.js | 653 ++++++++++++++++++ boilerplate/assets/js/jquery.dfp.min.js | 8 + boilerplate/assets/repo/README.md | 6 + boilerplate/bin/install-wp-tests.sh | 113 +++ bower.json => boilerplate/bower.json | 0 {docs => boilerplate/docs}/img/create-key.png | Bin .../docs}/img/custom-criteria.png | Bin {docs => boilerplate/docs}/img/dfw.png | Bin {docs => boilerplate/docs}/img/key-values.png | Bin {docs => boilerplate/docs}/img/name-key.png | Bin .../docs}/img/network-code.png | Bin {docs => boilerplate/docs}/img/targeting.png | Bin {docs => boilerplate/docs}/img/widget.png | Bin boilerplate/doubleclick-for-wordpress.php | 373 ++++++++++ boilerplate/includes/README.md | 6 + boilerplate/languages | 21 + boilerplate/package.json | 57 ++ boilerplate/phpcs.xml | 16 + boilerplate/phpunit.xml | 14 + release.sh => boilerplate/release.sh | 0 boilerplate/tests/bootstrap.php | 35 + boilerplate/tests/test-base.php | 36 + css/dfp.css | 15 - docs/Colophon.md | 9 - docs/Developer-API.md | 105 --- docs/Targeting.md | 91 --- docs/index.md | 51 -- readme.md | 5 - 43 files changed, 2451 insertions(+), 297 deletions(-) delete mode 100644 .bowerrc delete mode 100644 .gitignore create mode 100644 boilerplate/.bowerrc create mode 100644 boilerplate/.editorconfig create mode 100644 boilerplate/.eslintrc.js create mode 100644 boilerplate/.eslintrc.json create mode 100644 boilerplate/.gitignore create mode 100644 boilerplate/.sass-lint.yml create mode 100644 boilerplate/.travis.yml create mode 100644 boilerplate/.yo-rc.json create mode 100644 boilerplate/Dockunit.json create mode 100644 boilerplate/Gulpfile.js rename readme.txt => boilerplate/README.md (67%) create mode 100644 boilerplate/assets/README.md create mode 100644 boilerplate/assets/css/sass/styles.scss create mode 100644 boilerplate/assets/js/jquery.dfp.js create mode 100644 boilerplate/assets/js/jquery.dfp.min.js create mode 100644 boilerplate/assets/repo/README.md create mode 100644 boilerplate/bin/install-wp-tests.sh rename bower.json => boilerplate/bower.json (100%) rename {docs => boilerplate/docs}/img/create-key.png (100%) rename {docs => boilerplate/docs}/img/custom-criteria.png (100%) rename {docs => boilerplate/docs}/img/dfw.png (100%) rename {docs => boilerplate/docs}/img/key-values.png (100%) rename {docs => boilerplate/docs}/img/name-key.png (100%) rename {docs => boilerplate/docs}/img/network-code.png (100%) rename {docs => boilerplate/docs}/img/targeting.png (100%) rename {docs => boilerplate/docs}/img/widget.png (100%) create mode 100644 boilerplate/doubleclick-for-wordpress.php create mode 100644 boilerplate/includes/README.md create mode 100644 boilerplate/languages create mode 100644 boilerplate/package.json create mode 100644 boilerplate/phpcs.xml create mode 100644 boilerplate/phpunit.xml rename release.sh => boilerplate/release.sh (100%) create mode 100644 boilerplate/tests/bootstrap.php create mode 100644 boilerplate/tests/test-base.php delete mode 100644 css/dfp.css delete mode 100644 docs/Colophon.md delete mode 100644 docs/Developer-API.md delete mode 100644 docs/Targeting.md delete mode 100644 docs/index.md delete mode 100644 readme.md diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 107d1e8..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "js/vendor" -} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8006366..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -site/* -release/* -js/vendor/* diff --git a/boilerplate/.bowerrc b/boilerplate/.bowerrc new file mode 100644 index 0000000..0eac082 --- /dev/null +++ b/boilerplate/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "assets/bower" +} \ No newline at end of file diff --git a/boilerplate/.editorconfig b/boilerplate/.editorconfig new file mode 100644 index 0000000..8a9fde0 --- /dev/null +++ b/boilerplate/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{html,js,php,css,scss}] +charset = utf-8 + +# 4 space indentation +[*.{html,js,php,css,scss}] +indent_style = tab +indent_size = 4 + +# Matches the exact files +[{package.json,bower.json,.bowerrc,.eslintrc,.travis.yml,.sass-lint.yml,phpcs.xml}] +indent_style = space +indent_size = 2 diff --git a/boilerplate/.eslintrc.js b/boilerplate/.eslintrc.js new file mode 100644 index 0000000..c1e5082 --- /dev/null +++ b/boilerplate/.eslintrc.js @@ -0,0 +1,78 @@ +module.exports = { + "env": { + "browser": true, + "jquery": true, + "es6": true, + }, + "extends": "wordpress", + "installedESLint": true, + "plugins": [], + "rules": { + // Enforce spacing inside array brackets + 'array-bracket-spacing': ['error', 'always'], + // Enforce one true brace style + 'brace-style': 'error', + // Require camel case names + 'camelcase': ['error', { + properties: 'always' + }], + // Disallow or enforce trailing commas + 'comma-dangle': ['error', 'never'], + // Enforce spacing before and after comma + 'comma-spacing': 'error', + // Enforce one true comma style + 'comma-style': ['error', 'last'], + // Encourages use of dot notation whenever possible + 'dot-notation': ['error', { + allowKeywords: true, + allowPattern: '^[a-z]+(_[a-z]+)+$' + }], + // Enforce newline at the end of file, with no multiple empty lines + 'eol-last': 'error', + // Require or disallow spacing between function identifiers and their invocations + 'func-call-spacing': 'off', + // Enforces spacing between keys and values in object literal properties + 'key-spacing': ['error', { + beforeColon: false, + afterColon: true + }], + // Enforce spacing before and after keywords + 'keyword-spacing': 'error', + // Disallow mixed "LF" and "CRLF" as linebreaks + 'linebreak-style': ['error', 'unix'], + // Enforces empty lines around comments + 'lines-around-comment': ['error', { + beforeLineComment: true + }], + // Disallow mixed spaces and tabs for indentation + 'no-mixed-spaces-and-tabs': 'error', + // Disallow use of multiline strings + 'no-multi-str': 'error', + // Disallow multiple empty lines + 'no-multiple-empty-lines': 'error', + // Disallow use of the with statement + 'no-with': 'error', + // Require or disallow an newline around variable declarations + 'one-var-declaration-per-line': ['error', 'initializations'], + // Enforce operators to be placed before or after line breaks + 'operator-linebreak': ['error', 'after'], + // Require or disallow use of semicolons instead of ASI + 'semi': ['error', 'always'], + // Require or disallow space before blocks + 'space-before-blocks': ['error', 'always'], + // Require or disallow space before function opening parenthesis + 'space-before-function-paren': ['error', 'never'], + // Require or disallow space before blocks + 'space-in-parens': ['error', 'always', {exceptions: ['{}', '[]']}], + // Require spaces around operators + 'space-infix-ops': 'error', + // Require or disallow spaces before/after unary operators (words on by default, nonwords) + 'space-unary-ops': ['error', { + overrides: {'!': true} + }], + // Requires to declare all vars on top of their containing scope + 'vars-on-top': 'error', + // Require or disallow Yoda conditions + 'yoda': ['error', 'always'] + } +} diff --git a/boilerplate/.eslintrc.json b/boilerplate/.eslintrc.json new file mode 100644 index 0000000..2d2951e --- /dev/null +++ b/boilerplate/.eslintrc.json @@ -0,0 +1,171 @@ +{ + "globals": { + }, + + "env": { + "browser": true, + "commonjs": true, + "jquery": true + }, + + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "impliedStrict": true + } + }, + + "rules": { + // Possible Errors + "comma-dangle": 2, + "no-cond-assign": 2, + "no-console": 1, + "no-constant-condition": 2, + "no-control-regex": 2, + "no-debugger": 1, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty-character-class": 2, + "no-empty": 2, + "no-ex-assign": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 2, + "no-extra-semi": 2, + "no-func-assign": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-negated-in-lhs": 2, + "no-obj-calls": 2, + "no-regex-spaces": 2, + "no-sparse-arrays": 2, + "no-unreachable": 2, + "use-isnan": 2, + "valid-jsdoc": 2, + "valid-typeof": 2, + "no-unexpected-multiline": 2, + // Best Practices + "accessor-pairs": 2, + "block-scoped-var": 2, + "complexity": 2, + "consistent-return": 2, + "curly": 2, + "default-case": 2, + "dot-notation": 2, + "dot-location": [2, "property"], + "eqeqeq": 2, + "guard-for-in": 2, + "no-alert": 2, + "no-caller": 2, + "no-div-regex": 2, + "no-else-return": 2, + "no-eq-null": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-loop-func": 2, + "no-multi-spaces": [2, { "exceptions": { "VariableDeclarator": true } }], + "no-multi-str": 2, + "no-native-reassign": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-new": 2, + "no-octal-escape": 2, + "no-octal": 2, + "no-process-env": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-throw-literal": 2, + "no-unused-expressions": 2, + "no-useless-call": 2, + "no-useless-concat": 2, + "no-void": 2, + "no-warning-comments": 2, + "no-with": 2, + "radix": 2, + "vars-on-top": 2, + "wrap-iife": 2, + "yoda": [1, "always"], + // Strict Mode + "strict": 2, + // Variables + "no-catch-shadow": 2, + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow-restricted-names": 2, + "no-shadow": 2, + "no-undef-init": 2, + "no-undef": 2, + "no-undefined": 2, + "no-unused-vars": 2, + "no-use-before-define": 2, + // Stylistic Issues + "array-bracket-spacing": [2, "always"], + "block-spacing": 2, + "brace-style": 2, + "camelcase": 2, + "comma-spacing": [2, {"before": false, "after": true}], + "comma-style": [2, "last"], + "consistent-this": 2, + "eol-last": 2, + "func-style": 2, + "id-match": 2, + "indent": [2, "tab", {"SwitchCase": 1} ], + "key-spacing": [1, { "align": "value" }], + "lines-around-comment": [2, { "beforeBlockComment": true, "allowBlockStart": true , "allowObjectStart": true }], + "linebreak-style": 2, + "max-nested-callbacks": 2, + "new-cap": 2, + "new-parens": 2, + "newline-after-var": 2, + "no-array-constructor": 2, + "no-continue": 2, + "no-inline-comments": 2, + "no-lonely-if": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multiple-empty-lines": 2, + "no-nested-ternary": 2, + "no-new-object": 2, + "no-spaced-func": 2, + "no-trailing-spaces": 2, + "no-underscore-dangle": 2, + "no-unneeded-ternary": 2, + "object-curly-spacing": [2, "always"], + "one-var": 2, + "operator-assignment": 2, + "operator-linebreak": 2, + "padded-blocks": [2, "never"], + "quote-props": [2, "as-needed"], + "quotes": [2, "single"], + "semi-spacing": 2, + "semi": [2, "always"], + "keyword-spacing": 1, + "space-before-blocks": 2, + "space-before-function-paren": [2, "never"], + "space-in-parens": [2, "always"], + "space-infix-ops": 2, + "space-unary-ops": [1, { "words": true, "nonwords": true }], + "spaced-comment": 2, + "wrap-regex": 2, + // Legacy + "max-depth": 2, + "max-len": [1, 100, 4], + "max-params": [2, 4], + "max-statements": 1, + "no-bitwise": 2, + "no-plusplus": 2 + } +} diff --git a/boilerplate/.gitignore b/boilerplate/.gitignore new file mode 100644 index 0000000..ddbb21f --- /dev/null +++ b/boilerplate/.gitignore @@ -0,0 +1,80 @@ +# Node/npm +node_modules/ +npm-debug.log +.grunt +.sass-cache + +# OSX +.DS_Store +.AppleDouble +.LSOverride +.Spotlight-V100 +.Trashes +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + + +# Vim +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + +### Bower ### +bower_components +.bower-cache +.bower-registry +.bower-tmp + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + + + +# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock + + +# Other, add your own ignored things here. +.idea/ +release +site +js/vendor diff --git a/boilerplate/.sass-lint.yml b/boilerplate/.sass-lint.yml new file mode 100644 index 0000000..c1fc5f3 --- /dev/null +++ b/boilerplate/.sass-lint.yml @@ -0,0 +1,142 @@ +# Sample Configs +# - https://github.com/sasstools/sass-lint/blob/develop/docs/sass-lint.yml +# - https://github.com/sasstools/gulp-sass-lint/blob/master/tests/.sass-lint.yml + +# Sass Lint Rules +# - https://github.com/sasstools/sass-lint/tree/develop/docs/rules + +# Severity +# - 0: turns rule off +# - 1: set as a warning +# - 2: set to error + +# File Options +files: + ignore: + - 'sass/vendor/**/*.scss' + - 'sass/tests/**/*.scss' + - 'bourbon/app/**/*.scss' + - 'bourbon-neat/app/**/*.scss' + +# Rule Configuration +rules: + # Extends + placeholder-in-extend: 0 + + # Mixins + mixins-before-declarations: + - 1 + - + exclude: ['media'] # except @include media(); + + # Line Spacing + empty-line-between-blocks: + - 1 + - + allow-single-line-rulesets: false + + # Disallows + no-color-literals: + - 1 + - + allow-rgba: true + no-debug: 0 + no-ids: 0 + no-mergeable-selectors: 0 + no-qualifying-elements: + - 1 + - + allow-element-with-attribute: true + allow-element-with-class: false + allow-element-with-id: false + no-vendor-prefixes: 0 + no-warn: 0 + + # Nesting + force-attribute-nesting: 0 + force-element-nesting: 0 + force-pseudo-nesting: 0 + + # Name Formats + function-name-format: + - 1 + - + convention-explanation: Please use hyphenated lowercase for function names. Also, you may use a leading underscore if you prefer. + mixin-name-format: + - 1 + - + convention-explanation: Please use hyphenated lowercase for mixin names. Also, you may use a leading underscore if you prefer. + placeholder-name-format: + - 1 + - + convention-explanation: Please use hyphenated lowercase for placeholder names. Also, you may use a leading underscore if you prefer. + variable-name-format: + - 1 + - + allow-leading-underscore: false + convention-explanation: Please use hyphenated lowercase for variable names. No leading underscore is allowed. + + # Style Guide + attribute-quotes: 1 + border-zero: 0 + brace-style: + - 1 + - + style: 1tbs + allow-single-line: false + class-name-format: + - 1 + - + allow-leading-underscore: false + convention-explanation: Please use hypenated lowercase for class names, and without a leading underscore. If you find a WordPress core condition that conflicts with this convention feel free to modify and extend the ignore option. + ignore: ['current_page_item', 'widget_search'] # ignore selectors declared by WordPress + + empty-args: 0 + hex-length: 1 + hex-notation: 1 + id-name-format: + - 1 + - + convention-explanation: Please try to use hyphenated lowercase ID name format. If overriding a plugin that does not use this format then please ignore or extend the list of ignore option. + indentation: + - 1 + - + size: tab + leading-zero: + - 1 + - + include: true + + nesting-depth: + - 1 + - + max-depth: 3 + + property-units: + - 1 + - + per-property: { width: ['rem', 'vw'], height: ['rem', 'vh'], margin: ['rem'], padding: ['rem'] } + + quotes: + - 1 + - + style: double + + shorthand-values: + - 1 + - + allowed-shorthands: + - 1 + - 2 + + # Inner Spacing + space-between-parens: 0 + + # Final Items + final-newline: 0 + + no-misspelled-properties: + - 1 + - + 'extra-properties': + - '-webkit-overflow-scrolling' diff --git a/boilerplate/.travis.yml b/boilerplate/.travis.yml new file mode 100644 index 0000000..cbef1ee --- /dev/null +++ b/boilerplate/.travis.yml @@ -0,0 +1,23 @@ +language: php + +notifications: + email: + on_success: never + on_failure: change + +php: + - 5.3 + - 5.5 + +env: + - WP_VERSION=latest WP_MULTISITE=0 + +matrix: + include: + - php: 5.3 + env: WP_VERSION=latest WP_MULTISITE=1 + +before_script: + - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION + +script: phpunit diff --git a/boilerplate/.yo-rc.json b/boilerplate/.yo-rc.json new file mode 100644 index 0000000..08fb816 --- /dev/null +++ b/boilerplate/.yo-rc.json @@ -0,0 +1,19 @@ +{ + "generator-plugin-wp": { + "name": "DoubleClick for WordPress", + "homepage": "https://labs.inn.org", + "description": "Serve DoubleClick ads natively in WordPress. Built to make serving and targeting responsive ads easy.", + "version": "0.2.1", + "author": "innlabs, willhaynes24", + "authoremail": "labs@inn.org", + "authorurl": "https://labs.inn.org", + "license": "GPLv2", + "slug": "doubleclick-for-wordpress", + "classname": "DoubleClick_For_WordPress", + "mainclassname": "DoubleClick_For_WordPress", + "classprefix": "DCFWP_", + "prefix": "doubleclick_for_wordpress", + "year": 2017, + "currentVersionWP": "4.7.2" + } +} \ No newline at end of file diff --git a/boilerplate/Dockunit.json b/boilerplate/Dockunit.json new file mode 100644 index 0000000..047a815 --- /dev/null +++ b/boilerplate/Dockunit.json @@ -0,0 +1,157 @@ +{ + "containers":[ + { + "prettyName":"PHP 7.0 FPM WordPress Latest", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-7.0-rc-4-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test root '' localhost" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 7.0 FPM WordPress 4.7", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-7.0-rc-4-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.7" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 7.0 FPM WordPress 4.6", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-7.0-rc-4-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.6" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 7.0 FPM WordPress 4.5", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-7.0-rc-4-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.5" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 7.0 FPM WordPress 4.4", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-7.0-rc-4-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.4" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 7.0 FPM WordPress 4.3", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-7.0-rc-4-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.3" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.6 FPM WordPress Latest", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.6-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test2 root '' localhost" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.6 FPM WordPress 4.7", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.6-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test2 root '' localhost 4.7" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.6 FPM WordPress 4.6", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.6-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test2 root '' localhost 4.6" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.6 FPM WordPress 4.5", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.6-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test2 root '' localhost 4.5" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.6 FPM WordPress 4.4", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.6-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test2 root '' localhost 4.4" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.6 FPM WordPress 4.3", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.6-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test2 root '' localhost 4.3" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.2 FPM WordPress 4.7", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.2-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test3 root '' localhost 4.7" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.2 FPM WordPress 4.6", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.2-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test3 root '' localhost 4.6" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.2 FPM WordPress 4.5", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.2-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test3 root '' localhost 4.5" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.2 FPM WordPress 4.4", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.2-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test3 root '' localhost 4.4" + ], + "testCommand":"phpunit" + }, + { + "prettyName":"PHP 5.2 FPM WordPress 4.3", + "image":"dockunit/prebuilt-images:php-mysql-phpunit-wordpress-5.2-fpm", + "beforeScripts":[ + "service mysql start", + "bash bin/install-wp-tests.sh wordpress_test3 root '' localhost 4.3" + ], + "testCommand":"phpunit" + } + ] +} diff --git a/boilerplate/Gulpfile.js b/boilerplate/Gulpfile.js new file mode 100644 index 0000000..608f7f7 --- /dev/null +++ b/boilerplate/Gulpfile.js @@ -0,0 +1,360 @@ +// Require our dependencies +const autoprefixer = require( 'autoprefixer' ); +const babel = require( 'gulp-babel' ); +const bourbon = require( 'bourbon' ).includePaths; +const browserSync = require( 'browser-sync' ); +const cheerio = require( 'gulp-cheerio' ); +const concat = require( 'gulp-concat' ); +const cssnano = require( 'gulp-cssnano' ); +const del = require( 'del' ); +const eslint = require( 'gulp-eslint' ); +const gulp = require( 'gulp' ); +const gutil = require( 'gulp-util' ); +const imagemin = require( 'gulp-imagemin' ); +const mqpacker = require( 'css-mqpacker' ); +const neat = require( 'bourbon-neat' ).includePaths; +const notify = require( 'gulp-notify' ); +const plumber = require( 'gulp-plumber' ); +const postcss = require( 'gulp-postcss' ); +const reload = browserSync.reload; +const rename = require( 'gulp-rename' ); +const sass = require( 'gulp-sass' ); +const sassLint = require( 'gulp-sass-lint' ); +const sort = require( 'gulp-sort' ); +const sourcemaps = require( 'gulp-sourcemaps' ); +const spritesmith = require( 'gulp.spritesmith' ); +const svgmin = require( 'gulp-svgmin' ); +const svgstore = require( 'gulp-svgstore' ); +const uglify = require( 'gulp-uglify' ); +const wpPot = require( 'gulp-wp-pot' ); + +// Set assets paths. +const paths = { + 'css': [ './*.css', '!*.min.css' ], + 'icons': 'assets/images/svg-icons/*.svg', + 'images': [ 'assets/images/*', '!assets/images/*.svg' ], + 'php': [ './*.php', './**/*.php' ], + 'sass': 'assets/css/sass/*.scss', + 'concat_scripts': 'assets/scripts/concat/*.js', + 'scripts': [ 'assets/scripts/*.js', 'assets/js/*.js', '!assets/scripts/*.min.js' ], + 'sprites': 'assets/images/sprites/*.png' +}; + +/** + * Handle errors and alert the user. + */ +function handleErrors () { + const args = Array.prototype.slice.call( arguments ); + + notify.onError( { + 'title': 'Task Failed [<%= error.message %>', + 'message': 'See console.', + 'sound': 'Sosumi' // See: https://github.com/mikaelbr/node-notifier#all-notification-options-with-their-defaults + } ).apply( this, args ); + + gutil.beep(); // Beep 'sosumi' again. + + // Prevent the 'watch' task from stopping. + this.emit( 'end' ); +} + +/** + * Delete style.css and style.min.css before we minify and optimize + */ +gulp.task( 'clean:styles', () => + del( [ 'style.css', 'style.min.css' ] ) +); + +/** + * Compile Sass and run stylesheet through PostCSS. + * + * https://www.npmjs.com/package/gulp-sass + * https://www.npmjs.com/package/gulp-postcss + * https://www.npmjs.com/package/gulp-autoprefixer + * https://www.npmjs.com/package/css-mqpacker + */ +gulp.task( 'postcss', [ 'clean:styles' ], () => + gulp.src( 'assets/sass/*.scss', paths.css ) + + // Deal with errors. + .pipe( plumber( {'errorHandler': handleErrors} ) ) + + // Wrap tasks in a sourcemap. + .pipe( sourcemaps.init() ) + + // Compile Sass using LibSass. + .pipe( sass( { + 'includePaths': [].concat( bourbon, neat ), + 'errLogToConsole': true, + 'outputStyle': 'expanded' // Options: nested, expanded, compact, compressed + } ) ) + + // Parse with PostCSS plugins. + .pipe( postcss( [ + autoprefixer( { + 'browsers': [ 'last 2 version' ] + } ), + mqpacker( { + 'sort': true + } ) + ] ) ) + + // Create sourcemap. + .pipe( sourcemaps.write() ) + + // Create style.css. + .pipe( gulp.dest( './' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Minify and optimize style.css. + * + * https://www.npmjs.com/package/gulp-cssnano + */ +gulp.task( 'cssnano', [ 'postcss' ], () => + gulp.src( 'style.css' ) + .pipe( plumber( {'errorHandler': handleErrors} ) ) + .pipe( cssnano( { + 'safe': true // Use safe optimizations. + } ) ) + .pipe( rename( 'style.min.css' ) ) + .pipe( gulp.dest( './' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Delete the svg-icons.svg before we minify, concat. + */ +gulp.task( 'clean:icons', () => + del( [ 'assets/images/svg-icons.svg' ] ) +); + +/** + * Minify, concatenate, and clean SVG icons. + * + * https://www.npmjs.com/package/gulp-svgmin + * https://www.npmjs.com/package/gulp-svgstore + * https://www.npmjs.com/package/gulp-cheerio + */ +gulp.task( 'svg', [ 'clean:icons' ], () => + gulp.src( paths.icons ) + + // Deal with errors. + .pipe( plumber( {'errorHandler': handleErrors} ) ) + + // Minify SVGs. + .pipe( svgmin() ) + + // Add a prefix to SVG IDs. + .pipe( rename( {'prefix': 'icon-'} ) ) + + // Combine all SVGs into a single + .pipe( svgstore( {'inlineSvg': true} ) ) + + // Clean up the by removing the following cruft... + .pipe( cheerio( { + 'run': function ( $, file ) { + $( 'svg' ).attr( 'style', 'display:none' ); + $( '[fill]' ).removeAttr( 'fill' ); + $( 'path' ).removeAttr( 'class' ); + }, + 'parserOptions': {'xmlMode': true} + } ) ) + + // Save svg-icons.svg. + .pipe( gulp.dest( 'assets/images/' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Optimize images. + * + * https://www.npmjs.com/package/gulp-imagemin + */ +gulp.task( 'imagemin', () => + gulp.src( paths.images ) + .pipe( plumber( {'errorHandler': handleErrors} ) ) + .pipe( imagemin( { + 'optimizationLevel': 5, + 'progressive': true, + 'interlaced': true + } ) ) + .pipe( gulp.dest( 'assets/images' ) ) +); + +/** + * Delete the sprites.png before rebuilding sprite. + */ +gulp.task( 'clean:sprites', () => { + del( [ 'assets/images/sprites.png' ] ) +} ); + +/** + * Concatenate images into a single PNG sprite. + * + * https://www.npmjs.com/package/gulp.spritesmith + */ +gulp.task( 'spritesmith', [ 'clean:sprites' ], () => + gulp.src( paths.sprites ) + .pipe( plumber( {'errorHandler': handleErrors} ) ) + .pipe( spritesmith( { + 'imgName': 'sprites.png', + 'cssName': '../../assets/sass/base/_sprites.scss', + 'imgPath': 'assets/images/sprites.png', + 'algorithm': 'binary-tree' + } ) ) + .pipe( gulp.dest( 'assets/images/' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Concatenate and transform JavaScript. + * + * https://www.npmjs.com/package/gulp-concat + * https://github.com/babel/gulp-babel + * https://www.npmjs.com/package/gulp-sourcemaps + */ +gulp.task( 'concat', () => + gulp.src( paths.concat_scripts ) + + // Deal with errors. + .pipe( plumber( + {'errorHandler': handleErrors} + ) ) + + // Start a sourcemap. + .pipe( sourcemaps.init() ) + + // Convert ES6+ to ES2015. + .pipe( babel( { + presets: [ 'es2015' ] + } ) ) + + // Concatenate partials into a single script. + .pipe( concat( 'project.js' ) ) + + // Append the sourcemap to project.js. + .pipe( sourcemaps.write() ) + + // Save project.js + .pipe( gulp.dest( 'assets/scripts' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Minify compiled JavaScript. + * + * https://www.npmjs.com/package/gulp-uglify + */ +gulp.task( 'uglify', [ 'concat' ], () => + gulp.src( paths.scripts ) + .pipe( rename( {'suffix': '.min'} ) ) + .pipe( plumber( {'errorHandler': handleErrors} ) ) + .pipe( uglify( { + 'mangle': false + } ) ) + .pipe( gulp.dest( 'assets/js' ) ) +); + +/** + * Delete the theme's .pot before we create a new one. + */ +gulp.task( 'clean:pot', () => + del( [ 'languages/_s.pot' ] ) +); + +/** + * Scan the theme and create a POT file. + * + * https://www.npmjs.com/package/gulp-wp-pot + */ +gulp.task( 'wp-pot', [ 'clean:pot' ], () => + gulp.src( paths.php ) + .pipe( plumber( {'errorHandler': handleErrors} ) ) + .pipe( sort() ) + .pipe( wpPot( { + 'domain': '_s', + 'destFile': '_s.pot', + 'package': '_s', + 'bugReport': 'http://_s.com', + 'lastTranslator': 'John Doe ', + 'team': 'Team ' + } ) ) + .pipe( gulp.dest( 'languages/' ) ) +); + +/** + * Sass linting. + * + * https://www.npmjs.com/package/sass-lint + */ +gulp.task( 'sass:lint', () => + gulp.src( [ + 'assets/sass/**/*.scss', + '!assets/sass/base/_normalize.scss', + '!assets/sass/base/_sprites.scss', + '!node_modules/**' + ] ) + .pipe( sassLint() ) + .pipe( sassLint.format() ) + .pipe( sassLint.failOnError() ) +); + +/** + * JavaScript linting. + * + * https://www.npmjs.com/package/gulp-eslint + */ +gulp.task( 'js:lint', () => + gulp.src( [ + 'assets/scripts/concat/*.js', + 'assets/scripts/*.js', + '!assets/scripts/project.js', + '!assets/scripts/*.min.js', + '!Gruntfile.js', + '!Gulpfile.js', + '!node_modules/**' + ] ) + .pipe( eslint() ) + .pipe( eslint.format() ) + .pipe( eslint.failAfterError() ) +); + +/** + * Process tasks and reload browsers on file changes. + * + * https://www.npmjs.com/package/browser-sync + */ +gulp.task( 'watch', function () { + + // Kick off BrowserSync. + browserSync( { + 'open': false, // Open project in a new tab? + 'injectChanges': true, // Auto inject changes instead of full reload. + 'proxy': 'testing.dev', // Use http://_s.com:3000 to use BrowserSync. + 'watchOptions': { + 'debounceDelay': 1000 // Wait 1 second before injecting. + } + } ); + + // Run tasks when files change. + gulp.watch( paths.icons, [ 'icons' ] ); + gulp.watch( paths.sass, [ 'styles' ] ); + gulp.watch( paths.scripts, [ 'scripts' ] ); + gulp.watch( paths.concat_scripts, [ 'scripts' ] ); + gulp.watch( paths.sprites, [ 'sprites' ] ); + gulp.watch( paths.php, [ 'markup' ] ); +} ); + +/** + * Create individual tasks. + */ +gulp.task( 'markup', browserSync.reload ); +gulp.task( 'i18n', [ 'wp-pot' ] ); +gulp.task( 'icons', [ 'svg' ] ); +gulp.task( 'scripts', [ 'uglify' ] ); +gulp.task( 'styles', [ 'cssnano' ] ); +gulp.task( 'sprites', [ 'spritesmith' ] ); +gulp.task( 'lint', [ 'sass:lint', 'js:lint' ] ); +gulp.task( 'default', [ 'sprites', 'i18n', 'icons', 'styles', 'scripts', 'imagemin'] ); \ No newline at end of file diff --git a/readme.txt b/boilerplate/README.md similarity index 67% rename from readme.txt rename to boilerplate/README.md index 1548d18..252e1d0 100644 --- a/readme.txt +++ b/boilerplate/README.md @@ -1,16 +1,16 @@ -=== DoubleClick for WordPress === -Contributors: inn_nerds, willhaynes24 -Donate link: https://inn.org/donate -Tags: ads, doubleclick, publishers, news -Requires at least: 4.0.0 -Tested up to: 4.8 -Stable tag: 0.2.1 -License: GPLv2 or later -License URI: http://www.gnu.org/licenses/gpl-2.0.html +# DoubleClick for WordPress # +**Contributors:** innlabs, willhaynes24 +**Donate link:** https://labs.inn.org +**Tags:** ads, doubleclick, publishers, news +**Requires at least:** 4.0 +**Tested up to:** 4.8 +**Stable tag:** 0.2.1 +**License:** GPLv2 +**License URI:** http://www.gnu.org/licenses/gpl-2.0.html Serve DoubleClick ads natively in WordPress. Built to make serving and targeting responsive ads easy. -== Description == +## Description ## This WordPress plugin gives site administrators an easy way to serve DFP inventory on their WordPress site. @@ -18,8 +18,7 @@ Implementing is simple. Configure your network code and input your identifiers. For more advanced documentation for developers and advanced users see [the official plugin docs](https://github.com/INN/DoubleClick-for-WordPress/blob/master/docs/index.md). - -== Installation == +## Installation ## 1. Upload the plugin directory to your `/wp-content/plugins/` directory 2. Activate the plugin through the 'Plugins' menu in WordPress @@ -28,11 +27,20 @@ For more advanced documentation for developers and advanced users see [the offic For more advanced documentation for developers and advanced users see [the official plugin docs](https://github.com/INN/DoubleClick-for-WordPress/tree/master/docs/index.md). +### Manual Installation ### + +1. Upload the entire `/doubleclick-for-wordpress` directory to the `/wp-content/plugins/` directory. +2. Activate DoubleClick for WordPress through the 'Plugins' menu in WordPress. + +## Frequently Asked Questions ## + -== Changelog == +## Screenshots ## -= 0.2.1 = +## Changelog ## + +### 0.2.1 ### - Widget now includes a default stylesheet: - setting `.display-none` to `display: none;` to support `jQuery.dfp.js`' utility styles - centering ad units within the ad widgets. @@ -42,8 +50,13 @@ For more advanced documentation for developers and advanced users see [the offic - Numerous small bugfixes - Tested up to WordPress 4.6 -= 0.1 = +### 0.1 ### - Initial beta release. - Add support for displaying different sizes of ad unit based on the size of the viewport + +## Upgrade Notice ## + +### 0.2.1 ### +First Release diff --git a/boilerplate/assets/README.md b/boilerplate/assets/README.md new file mode 100644 index 0000000..164ee50 --- /dev/null +++ b/boilerplate/assets/README.md @@ -0,0 +1,6 @@ +# DoubleClick for WordPress Assets # +https://labs.inn.org +Copyright (c) 2017 innlabs, willhaynes24 +Licensed under the GPLv2 license. + +Assets such as styles, javascript, and images. \ No newline at end of file diff --git a/boilerplate/assets/css/sass/styles.scss b/boilerplate/assets/css/sass/styles.scss new file mode 100644 index 0000000..a2ad07f --- /dev/null +++ b/boilerplate/assets/css/sass/styles.scss @@ -0,0 +1,22 @@ +/** + * DoubleClick for WordPress + * https://labs.inn.org + * + * Copyright (c) 2017 innlabs, willhaynes24 + * Licensed under the GPLv2+ license. + */ + .display-none { + display: none; + } + .widget_doubleclick_widget { + text-align: center; + } + .dfw-unit.display-block:before { + speak: auto; + text-transform: uppercase; + content: "Advertisement"; + display: block; + text-align: center; + font-size: 14px; + color: #aaa; + } diff --git a/boilerplate/assets/js/jquery.dfp.js b/boilerplate/assets/js/jquery.dfp.js new file mode 100644 index 0000000..be68354 --- /dev/null +++ b/boilerplate/assets/js/jquery.dfp.js @@ -0,0 +1,653 @@ +/*! + * jQuery DFP v2.4.2 + * http://github.com/coop182/jquery.dfp.js + * + * Copyright 2016 Matt Cooper + * Released under the MIT license + */ + +(function (window, undefined) { + "use strict"; + + /** + * Make plugin UMD compatible + * Uses CommonJS, AMD or browser globals to create plugin. + */ + (function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(window.jQuery || window.Zepto); + } + }(function ($) { + + var + + // Save Scope + dfpScript = this || {}; + + var + // DFP account ID + dfpID = '', + + // Init counters + count = 0, + uid = 0, + rendered = 0, + + // Default DFP selector + dfpSelector = '.adunit', + + adsCouldNeverBeInitilized = false, + + // Keep track of if we've already tried to load gpt.js before + dfpIsLoaded = false, + + // Store adunit on div as: + storeAs = 'googleAdUnit', + + /** + * Init function sets required params and loads Google's DFP script + * @param String id The DFP account ID + * @param String selector The adunit selector + * @param Object options Custom options to apply + */ + init = function (id, selector, options) { + var $adCollection; + + // Reset counters on each call + count = 0; + rendered = 0; + + dfpID = id; + $adCollection = $(selector); + + /** + * @returns {boolean} + */ + dfpScript.shouldCheckForAdBlockers = function(){ + return options ? typeof options.afterAdBlocked === 'function' : false; + }; + + // explicitly wait for loader to be completed, otherwise the googletag might not be available + dfpLoader(options, $adCollection).then(function(){ + options = setOptions(options); + dfpScript.dfpOptions = options; + + $(function () { + createAds(options, $adCollection); + displayAds(options, $adCollection); + }); + }); + + }, + + /** + * Set the options for DFP + * @param Object options Custom options to apply + * @return Object extended options + */ + setOptions = function (options) { + + // Set default options + var dfpOptions = { + setTargeting: {}, + setCategoryExclusion: '', + setLocation: '', + enableSingleRequest: true, + collapseEmptyDivs: 'original', + refreshExisting: true, + disablePublisherConsole: false, + disableInitialLoad: false, + setCentering: false, + noFetch: false, + namespace: undefined, + sizeMapping: {} + }; + + if (typeof options.setUrlTargeting === 'undefined' || options.setUrlTargeting) { + // Get URL Targeting + var urlTargeting = getUrlTargeting(options.url); + $.extend(true, dfpOptions.setTargeting, { + UrlHost: urlTargeting.Host, + UrlPath: urlTargeting.Path, + UrlQuery: urlTargeting.Query + }); + } + + // Merge options objects + $.extend(true, dfpOptions, options); + + // If a custom googletag is specified, use it. + if (dfpOptions.googletag) { + window.googletag.cmd.push(function () { + $.extend(true, window.googletag, dfpOptions.googletag); + }); + } + + return dfpOptions; + }, + + /** + * Find and create all Ads + * @param Object dfpOptions options related to ad instantiation + * @param jQuery $adCollection collection of ads + * @return Array an array of ad units that have been created. + */ + createAds = function (dfpOptions, $adCollection) { + var googletag = window.googletag; + // Loops through on page Ad units and gets ads for them. + $adCollection.each(function () { + var $adUnit = $(this); + + count++; + + // adUnit name + var adUnitName = getName($adUnit, dfpOptions); + + // adUnit id - this will use an existing id or an auto generated one. + var adUnitID = getID($adUnit, adUnitName); + + // get dimensions of the adUnit + var dimensions = getDimensions($adUnit); + + // set existing content + $adUnit.data('existingContent', $adUnit.html()); + + // wipe html clean ready for ad and set the default display class. + $adUnit.html('').addClass('display-none'); + + // Push commands to DFP to create ads + googletag.cmd.push(function () { + + var googleAdUnit, + $adUnitData = $adUnit.data(storeAs); + + if ($adUnitData) { + + // Get existing ad unit + googleAdUnit = $adUnitData; + + } else { + + // Build slotName for loading + var slotName; + if (dfpID === '') { + slotName = adUnitName; + } else { + slotName = '/' + dfpID + '/' + adUnitName; + } + + // Create the ad - out of page or normal + if ($adUnit.data('outofpage')) { + googleAdUnit = googletag.defineOutOfPageSlot(slotName, adUnitID); + } else { + googleAdUnit = googletag.defineSlot(slotName, dimensions, adUnitID); + if ($adUnit.data('companion')) { + googleAdUnit = googleAdUnit.addService(googletag.companionAds()); + } + } + + googleAdUnit = googleAdUnit.addService(googletag.pubads()); + + } + + // Sets custom targeting for just THIS ad unit if it has been specified + var targeting = $adUnit.data('targeting'); + if (targeting) { + $.each(targeting, function (k, v) { + googleAdUnit.setTargeting(k, v); + }); + } + + // Sets custom exclusions for just THIS ad unit if it has been specified + var exclusions = $adUnit.data('exclusions'); + if (exclusions) { + var exclusionsGroup = exclusions.split(','); + var valueTrimmed; + $.each(exclusionsGroup, function (k, v) { + valueTrimmed = $.trim(v); + if (valueTrimmed.length > 0) { + googleAdUnit.setCategoryExclusion(valueTrimmed); + } + }); + } + + // Sets responsive size mapping for just THIS ad unit if it has been specified + var mapping = $adUnit.data('size-mapping'); + if (mapping && dfpOptions.sizeMapping[mapping]) { + // Convert verbose to DFP format + var map = googletag.sizeMapping(); + $.each(dfpOptions.sizeMapping[mapping], function (k, v) { + map.addSize(v.browser, v.ad_sizes); + }); + googleAdUnit.defineSizeMapping(map.build()); + } + + // Store googleAdUnit reference + $adUnit.data(storeAs, googleAdUnit); + + // Allow altering of the ad slot before ad load + if (typeof dfpOptions.beforeEachAdLoaded === 'function') { + dfpOptions.beforeEachAdLoaded.call(this, $adUnit); + } + }); + + }); + + // Push DFP config options + googletag.cmd.push(function () { + + var pubadsService = googletag.pubads(); + + if (dfpOptions.enableSingleRequest) { + pubadsService.enableSingleRequest(); + } + + $.each(dfpOptions.setTargeting, function (k, v) { + pubadsService.setTargeting(k, v); + }); + + var setLocation = dfpOptions.setLocation; + if (typeof setLocation === 'object') { + if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number' && + typeof setLocation.precision === 'number') { + pubadsService.setLocation(setLocation.latitude, setLocation.longitude, setLocation.precision); + } else if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number') { + pubadsService.setLocation(setLocation.latitude, setLocation.longitude); + } + } + + if (dfpOptions.setCategoryExclusion.length > 0) { + var exclusionsGroup = dfpOptions.setCategoryExclusion.split(','); + var valueTrimmed; + + $.each(exclusionsGroup, function (k, v) { + valueTrimmed = $.trim(v); + if (valueTrimmed.length > 0) { + pubadsService.setCategoryExclusion(valueTrimmed); + } + }); + } + + if (dfpOptions.collapseEmptyDivs) { + pubadsService.collapseEmptyDivs(); + } + + if (dfpOptions.disablePublisherConsole) { + pubadsService.disablePublisherConsole(); + } + + if (dfpOptions.companionAds) { + googletag.companionAds().setRefreshUnfilledSlots(true); + + if (!dfpOptions.disableInitialLoad) { + pubadsService.enableVideoAds(); + } + } + + if (dfpOptions.disableInitialLoad) { + pubadsService.disableInitialLoad(); + } + + if (dfpOptions.noFetch) { + pubadsService.noFetch(); + } + + if (dfpOptions.setCentering) { + pubadsService.setCentering(true); + } + + // Setup event listener to listen for renderEnded event and fire callbacks. + pubadsService.addEventListener('slotRenderEnded', function (event) { + + rendered++; + + var $adUnit = $('#' + event.slot.getSlotId().getDomId()); + + var display = event.isEmpty ? 'none' : 'block'; + + // if the div has been collapsed but there was existing content expand the + // div and reinsert the existing content. + var $existingContent = $adUnit.data('existingContent'); + if (display === 'none' && $.trim($existingContent).length > 0 && + dfpOptions.collapseEmptyDivs === 'original') { + $adUnit.show().html($existingContent); + display = 'block display-original'; + } + + $adUnit.removeClass('display-none').addClass('display-' + display); + + // Excute afterEachAdLoaded callback if provided + if (typeof dfpOptions.afterEachAdLoaded === 'function') { + dfpOptions.afterEachAdLoaded.call(this, $adUnit, event); + } + + // Excute afterAllAdsLoaded callback if provided + if (typeof dfpOptions.afterAllAdsLoaded === 'function' && rendered === count) { + dfpOptions.afterAllAdsLoaded.call(this, $adCollection); + } + + }); + + // this will work with AdblockPlus + if(dfpScript.shouldCheckForAdBlockers() && !googletag._adBlocked_) { + setTimeout(function () { + var slots = pubadsService.getSlots ? pubadsService.getSlots() : []; + if (slots.length > 0) { + $.get(slots[0].getContentUrl()).always(function (r) { + if (r.status !== 200) { + $.each(slots, function () { + var $adUnit = $('#' + this.getSlotId().getDomId()); + dfpOptions.afterAdBlocked.call(dfpScript, $adUnit, this); + }); + } + }); + } + }, 0); + } + + googletag.enableServices(); + + }); + + }, + + /** + * Display all created Ads + * @param {Object} dfpOptions options related to ad instantiation + * @param {jQuery} $adCollection collection of ads + */ + displayAds = function (dfpOptions, $adCollection) { + + var googletag = window.googletag; + // Check if google adLoader can be loaded, this will work with AdBlock + if(dfpScript.shouldCheckForAdBlockers() && !googletag._adBlocked_) { + if (googletag.getVersion) { + var script = '//partner.googleadservices.com/gpt/pubads_impl_' + + googletag.getVersion() + '.js'; + $.getScript(script).always(function (r) { + if (r && r.statusText === 'error') { + $.each($adCollection, function () { + dfpOptions.afterAdBlocked.call(dfpScript, $(this)); + }); + } + }); + } + + } + + $adCollection.each(function () { + + var $adUnit = $(this), + $adUnitData = $adUnit.data(storeAs); + + if (googletag._adBlocked_) { + if(dfpScript.shouldCheckForAdBlockers()) { + dfpOptions.afterAdBlocked.call(dfpScript, $adUnit); + } + } + if (dfpOptions.refreshExisting && $adUnitData && $adUnit.hasClass('display-block')) { + + googletag.cmd.push(function () { googletag.pubads().refresh([$adUnitData]); }); + + } else { + googletag.cmd.push(function () { googletag.display($adUnit.attr('id')); }); + } + + }); + + }, + + /** + * Create an array of paths so that we can target DFP ads to Page URI's + * @return Array an array of URL parts that can be targeted. + */ + getUrlTargeting = function (url) { + + // Get the url and parse it to its component parts using regex from RFC2396 Appendix-B (https://tools.ietf.org/html/rfc2396#appendix-B) + var urlMatches = (url || window.location.toString()).match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/); + var matchedAuthority = urlMatches[4] || ''; + var matchedPath = (urlMatches[5] || '').replace(/(.)\/$/, '$1'); + var matchedQuery = urlMatches[7] || ''; + + // Get the query params for targeting against + var params = matchedQuery.replace(/\=/ig, ':').split('&'); + + return { + Host: matchedAuthority, + Path: matchedPath, + Query: params + }; + + }, + + /** + * Get the id of the adUnit div or generate a unique one. + * @param Object $adUnit The adunit to work with + * @param String adUnitName The name of the adunit + * @return String The ID of the adunit or a unique autogenerated ID + */ + getID = function ($adUnit, adUnitName) { + + uid++; + return $adUnit.attr('id') || $adUnit.attr('id', adUnitName.replace(/[^A-z0-9]/g, '_') + '-auto-gen-id-' + uid).attr('id'); + + }, + + /** + * Get the name of the Ad unit, either use the div id or + * check for the optional attribute data-adunit + * @param Object $adUnit The adunit to work with + * @param Object dfpOptions options related to ad instantiation + * @return String The name of the adunit, will be the same as inside DFP + */ + getName = function ($adUnit, dfpOptions) { + + var adUnitName = $adUnit.data('adunit') || dfpOptions.namespace || $adUnit.attr('id') || ''; + if (typeof dfpOptions.alterAdUnitName === 'function') { + adUnitName = dfpOptions.alterAdUnitName.call(this, adUnitName, $adUnit); + } + return adUnitName; + + }, + + /** + * Get the dimensions of the ad unit using the container div dimensions or + * check for the optional attribute data-dimensions + * @param Object $adUnit The adunit to work with + * @return Array The dimensions of the adunit (width, height) + */ + getDimensions = function ($adUnit) { + + var dimensions = [], + dimensionsData = $adUnit.data('dimensions'); + + // Check if data-dimensions are specified. If they aren't, use the dimensions of the ad unit div. + if (dimensionsData) { + + var dimensionGroups = dimensionsData.split(','); + + $.each(dimensionGroups, function (k, v) { + + var dimensionSet = v.split('x'); + dimensions.push([parseInt(dimensionSet[0], 10), parseInt(dimensionSet[1], 10)]); + + }); + + } else { + + dimensions.push([$adUnit.width(), $adUnit.height()]); + + } + + return dimensions; + + }, + + /** + * Call the google DFP script - there is a little bit of error detection in here to detect + * if the dfp script has failed to load either through an error or it being blocked by an ad + * blocker... if it does not load we execute a dummy script to replace the real DFP. + * + * @param {Object} options + * @param {Array} $adCollection + */ + dfpLoader = function (options, $adCollection) { + + function execBlockEvents() { + if(dfpScript.shouldCheckForAdBlockers()) { + $.each($adCollection, function () { + options.afterAdBlocked.call(dfpScript, $(this)); + }); + } + } + + // make sure we don't load gpt.js multiple times + dfpIsLoaded = dfpIsLoaded || $('script[src*="googletagservices.com/tag/js/gpt.js"]').length; + if (dfpIsLoaded) { + if(adsCouldNeverBeInitilized) { + execBlockEvents(); + } + return $.Deferred().resolve(); + } + + var loaded = $.Deferred(); + + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + + var gads = document.createElement('script'); + gads.async = true; + gads.type = 'text/javascript'; + + // Adblock blocks the load of Ad scripts... so we check for that + gads.onerror = function () { + dfpBlocked(); + loaded.resolve(); + adsCouldNeverBeInitilized = true; + execBlockEvents(); + }; + + gads.onload = function() { + // this will work with ghostery: + if (!googletag._loadStarted_) { + googletag._adBlocked_ = true; + execBlockEvents(); + } + loaded.resolve(); + }; + + var useSSL = 'https:' === document.location.protocol; + gads.src = (useSSL ? 'https:' : 'http:') + + '//www.googletagservices.com/tag/js/gpt.js'; + var node = document.getElementsByTagName('script')[0]; + node.parentNode.insertBefore(gads, node); + + // Adblock plus seems to hide blocked scripts... so we check for that + if (gads.style.display === 'none') { + dfpBlocked(); + } + + return loaded; + + }, + + /** + * This function gets called if DFP has been blocked by an adblocker + * it implements a dummy version of the dfp object and allows the script to excute its callbacks + * regardless of whether DFP is actually loaded or not... it is basically only useful for situations + * where you are laying DFP over existing content and need to init things like slide shows after the loading + * is completed. + */ + dfpBlocked = function () { + var googletag = window.googletag; + // Get the stored dfp commands + var commands = googletag.cmd; + + var _defineSlot = function (name, dimensions, id, oop) { + googletag.ads.push(id); + googletag.ads[id] = { + renderEnded: function () { }, + addService: function () { return this; } + }; + + return googletag.ads[id]; + }; + + // overwrite the dfp object - replacing the command array with a function and defining missing functions + googletag = { + cmd: { + push: function (callback) { + callback.call(dfpScript); + } + }, + ads: [], + pubads: function () { return this; }, + noFetch: function () { return this; }, + disableInitialLoad: function () { return this; }, + disablePublisherConsole: function () { return this; }, + enableSingleRequest: function () { return this; }, + setTargeting: function () { return this; }, + collapseEmptyDivs: function () { return this; }, + enableServices: function () { return this; }, + defineSlot: function (name, dimensions, id) { + return _defineSlot(name, dimensions, id, false); + }, + defineOutOfPageSlot: function (name, id) { + return _defineSlot(name, [], id, true); + }, + display: function (id) { + googletag.ads[id].renderEnded.call(dfpScript); + return this; + } + + }; + + // Execute any stored commands + $.each(commands, function (k, v) { + googletag.cmd.push(v); + }); + + + }; + + + /** + * Add function to the jQuery / Zepto namespace + * @param String id (Optional) The DFP account ID + * @param Object options (Optional) Custom options to apply + */ + $.dfp = $.fn.dfp = function (id, options) { + + options = options || {}; + + if (id === undefined) { + id = dfpID; + } + + if (typeof id === 'object') { + options = id; + id = options.dfpID || dfpID; + } + + var selector = this; + + if (typeof this === 'function') { + selector = dfpSelector; + } + + init(id, selector, options); + + return this; + + }; + + })); + +})(window); diff --git a/boilerplate/assets/js/jquery.dfp.min.js b/boilerplate/assets/js/jquery.dfp.min.js new file mode 100644 index 0000000..1cea571 --- /dev/null +++ b/boilerplate/assets/js/jquery.dfp.min.js @@ -0,0 +1,8 @@ +/** + * jQuery DFP v2.4.2 + * http://github.com/coop182/jquery.dfp.js + * + * Copyright 2016 Matt Cooper + * Released under the MIT license + */ +!function(a,b){"use strict";!function(b){"function"==typeof define&&define.amd?define(["jquery"],b):b("object"==typeof exports?require("jquery"):a.jQuery||a.Zepto)}(function(c){var d=this||{},e="",f=0,g=0,h=0,i=".adunit",j=!1,k=!1,l="googleAdUnit",m=function(a,b,g){var i;f=0,h=0,e=a,i=c(b),d.shouldCheckForAdBlockers=function(){return g?"function"==typeof g.afterAdBlocked:!1},u(g,i).then(function(){g=n(g),d.dfpOptions=g,c(function(){o(g,i),p(g,i)})})},n=function(d){var e={setTargeting:{},setCategoryExclusion:"",setLocation:"",enableSingleRequest:!0,collapseEmptyDivs:"original",refreshExisting:!0,disablePublisherConsole:!1,disableInitialLoad:!1,setCentering:!1,noFetch:!1,namespace:b,sizeMapping:{}};if("undefined"==typeof d.setUrlTargeting||d.setUrlTargeting){var f=q(d.url);c.extend(!0,e.setTargeting,{UrlHost:f.Host,UrlPath:f.Path,UrlQuery:f.Query})}return c.extend(!0,e,d),e.googletag&&a.googletag.cmd.push(function(){c.extend(!0,a.googletag,e.googletag)}),e},o=function(b,g){var i=a.googletag;g.each(function(){var a=c(this);f++;var d=s(a,b),g=r(a,d),h=t(a);a.data("existingContent",a.html()),a.html("").addClass("display-none"),i.cmd.push(function(){var f,j=a.data(l);if(j)f=j;else{var k;k=""===e?d:"/"+e+"/"+d,a.data("outofpage")?f=i.defineOutOfPageSlot(k,g):(f=i.defineSlot(k,h,g),a.data("companion")&&(f=f.addService(i.companionAds()))),f=f.addService(i.pubads())}var m=a.data("targeting");m&&c.each(m,function(a,b){f.setTargeting(a,b)});var n=a.data("exclusions");if(n){var o,p=n.split(",");c.each(p,function(a,b){o=c.trim(b),o.length>0&&f.setCategoryExclusion(o)})}var q=a.data("size-mapping");if(q&&b.sizeMapping[q]){var r=i.sizeMapping();c.each(b.sizeMapping[q],function(a,b){r.addSize(b.browser,b.ad_sizes)}),f.defineSizeMapping(r.build())}a.data(l,f),"function"==typeof b.beforeEachAdLoaded&&b.beforeEachAdLoaded.call(this,a)})}),i.cmd.push(function(){var a=i.pubads();b.enableSingleRequest&&a.enableSingleRequest(),c.each(b.setTargeting,function(b,c){a.setTargeting(b,c)});var e=b.setLocation;if("object"==typeof e&&("number"==typeof e.latitude&&"number"==typeof e.longitude&&"number"==typeof e.precision?a.setLocation(e.latitude,e.longitude,e.precision):"number"==typeof e.latitude&&"number"==typeof e.longitude&&a.setLocation(e.latitude,e.longitude)),b.setCategoryExclusion.length>0){var j,k=b.setCategoryExclusion.split(",");c.each(k,function(b,d){j=c.trim(d),j.length>0&&a.setCategoryExclusion(j)})}b.collapseEmptyDivs&&a.collapseEmptyDivs(),b.disablePublisherConsole&&a.disablePublisherConsole(),b.companionAds&&(i.companionAds().setRefreshUnfilledSlots(!0),b.disableInitialLoad||a.enableVideoAds()),b.disableInitialLoad&&a.disableInitialLoad(),b.noFetch&&a.noFetch(),b.setCentering&&a.setCentering(!0),a.addEventListener("slotRenderEnded",function(a){h++;var d=c("#"+a.slot.getSlotId().getDomId()),e=a.isEmpty?"none":"block",i=d.data("existingContent");"none"===e&&c.trim(i).length>0&&"original"===b.collapseEmptyDivs&&(d.show().html(i),e="block display-original"),d.removeClass("display-none").addClass("display-"+e),"function"==typeof b.afterEachAdLoaded&&b.afterEachAdLoaded.call(this,d,a),"function"==typeof b.afterAllAdsLoaded&&h===f&&b.afterAllAdsLoaded.call(this,g)}),d.shouldCheckForAdBlockers()&&!i._adBlocked_&&setTimeout(function(){var e=a.getSlots?a.getSlots():[];e.length>0&&c.get(e[0].getContentUrl()).always(function(a){200!==a.status&&c.each(e,function(){var a=c("#"+this.getSlotId().getDomId());b.afterAdBlocked.call(d,a,this)})})},0),i.enableServices()})},p=function(b,e){var f=a.googletag;if(d.shouldCheckForAdBlockers()&&!f._adBlocked_&&f.getVersion){var g="//partner.googleadservices.com/gpt/pubads_impl_"+f.getVersion()+".js";c.getScript(g).always(function(a){a&&"error"===a.statusText&&c.each(e,function(){b.afterAdBlocked.call(d,c(this))})})}e.each(function(){var a=c(this),e=a.data(l);f._adBlocked_&&d.shouldCheckForAdBlockers()&&b.afterAdBlocked.call(d,a),f.cmd.push(b.refreshExisting&&e&&a.hasClass("display-block")?function(){f.pubads().refresh([e])}:function(){f.display(a.attr("id"))})})},q=function(b){var c=(b||a.location.toString()).match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/),d=c[4]||"",e=(c[5]||"").replace(/(.)\/$/,"$1"),f=c[7]||"",g=f.replace(/\=/gi,":").split("&");return{Host:d,Path:e,Query:g}},r=function(a,b){return g++,a.attr("id")||a.attr("id",b.replace(/[^A-z0-9]/g,"_")+"-auto-gen-id-"+g).attr("id")},s=function(a,b){var c=a.data("adunit")||b.namespace||a.attr("id")||"";return"function"==typeof b.alterAdUnitName&&(c=b.alterAdUnitName.call(this,c,a)),c},t=function(a){var b=[],d=a.data("dimensions");if(d){var e=d.split(",");c.each(e,function(a,c){var d=c.split("x");b.push([parseInt(d[0],10),parseInt(d[1],10)])})}else b.push([a.width(),a.height()]);return b},u=function(b,e){function f(){d.shouldCheckForAdBlockers()&&c.each(e,function(){b.afterAdBlocked.call(d,c(this))})}if(k=k||c('script[src*="googletagservices.com/tag/js/gpt.js"]').length)return j&&f(),c.Deferred().resolve();var g=c.Deferred();a.googletag=a.googletag||{},a.googletag.cmd=a.googletag.cmd||[];var h=document.createElement("script");h.async=!0,h.type="text/javascript",h.onerror=function(){v(),g.resolve(),j=!0,f()},h.onload=function(){googletag._loadStarted_||(googletag._adBlocked_=!0,f()),g.resolve()};var i="https:"===document.location.protocol;h.src=(i?"https:":"http:")+"//www.googletagservices.com/tag/js/gpt.js";var l=document.getElementsByTagName("script")[0];return l.parentNode.insertBefore(h,l),"none"===h.style.display&&v(),g},v=function(){var b=a.googletag,e=b.cmd,f=function(a,c,d,e){return b.ads.push(d),b.ads[d]={renderEnded:function(){},addService:function(){return this}},b.ads[d]};b={cmd:{push:function(a){a.call(d)}},ads:[],pubads:function(){return this},noFetch:function(){return this},disableInitialLoad:function(){return this},disablePublisherConsole:function(){return this},enableSingleRequest:function(){return this},setTargeting:function(){return this},collapseEmptyDivs:function(){return this},enableServices:function(){return this},defineSlot:function(a,b,c){return f(a,b,c,!1)},defineOutOfPageSlot:function(a,b){return f(a,[],b,!0)},display:function(a){return b.ads[a].renderEnded.call(d),this}},c.each(e,function(a,c){b.cmd.push(c)})};c.dfp=c.fn.dfp=function(a,c){c=c||{},a===b&&(a=e),"object"==typeof a&&(c=a,a=c.dfpID||e);var d=this;return"function"==typeof this&&(d=i),m(a,d,c),this}})}(window); \ No newline at end of file diff --git a/boilerplate/assets/repo/README.md b/boilerplate/assets/repo/README.md new file mode 100644 index 0000000..a7f6ccc --- /dev/null +++ b/boilerplate/assets/repo/README.md @@ -0,0 +1,6 @@ +# DoubleClick for WordPress Repo Assets # +https://labs.inn.org +Copyright (c) 2017 innlabs, willhaynes24 +Licensed under the GPLv2 license. + +Assets such as screenshots and banner for WordPress.org plugin repository listing. \ No newline at end of file diff --git a/boilerplate/bin/install-wp-tests.sh b/boilerplate/bin/install-wp-tests.sh new file mode 100644 index 0000000..fdf3542 --- /dev/null +++ b/boilerplate/bin/install-wp-tests.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} + +WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then + WP_TESTS_TAG="tags/$WP_VERSION" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi + +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz + tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i .bak' + else + local ioption='-i' + fi + + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + fi + + cd $WP_TESTS_DIR + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s:define( 'WP_DEBUG', true );:define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true );:" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + +} + +install_db() { + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_wp +install_test_suite +install_db diff --git a/bower.json b/boilerplate/bower.json similarity index 100% rename from bower.json rename to boilerplate/bower.json diff --git a/docs/img/create-key.png b/boilerplate/docs/img/create-key.png similarity index 100% rename from docs/img/create-key.png rename to boilerplate/docs/img/create-key.png diff --git a/docs/img/custom-criteria.png b/boilerplate/docs/img/custom-criteria.png similarity index 100% rename from docs/img/custom-criteria.png rename to boilerplate/docs/img/custom-criteria.png diff --git a/docs/img/dfw.png b/boilerplate/docs/img/dfw.png similarity index 100% rename from docs/img/dfw.png rename to boilerplate/docs/img/dfw.png diff --git a/docs/img/key-values.png b/boilerplate/docs/img/key-values.png similarity index 100% rename from docs/img/key-values.png rename to boilerplate/docs/img/key-values.png diff --git a/docs/img/name-key.png b/boilerplate/docs/img/name-key.png similarity index 100% rename from docs/img/name-key.png rename to boilerplate/docs/img/name-key.png diff --git a/docs/img/network-code.png b/boilerplate/docs/img/network-code.png similarity index 100% rename from docs/img/network-code.png rename to boilerplate/docs/img/network-code.png diff --git a/docs/img/targeting.png b/boilerplate/docs/img/targeting.png similarity index 100% rename from docs/img/targeting.png rename to boilerplate/docs/img/targeting.png diff --git a/docs/img/widget.png b/boilerplate/docs/img/widget.png similarity index 100% rename from docs/img/widget.png rename to boilerplate/docs/img/widget.png diff --git a/boilerplate/doubleclick-for-wordpress.php b/boilerplate/doubleclick-for-wordpress.php new file mode 100644 index 0000000..e358ae7 --- /dev/null +++ b/boilerplate/doubleclick-for-wordpress.php @@ -0,0 +1,373 @@ +basename = plugin_basename( __FILE__ ); + $this->url = plugin_dir_url( __FILE__ ); + $this->path = plugin_dir_path( __FILE__ ); + } + + /** + * Attach other plugin classes to the base plugin class. + * + * @since 0.2.1 + */ + public function plugin_classes() { + // $this->plugin_class = new DCFWP_Plugin_Class( $this ); + + } // END OF PLUGIN CLASSES FUNCTION + + /** + * Add hooks and filters. + * Priority needs to be + * < 10 for CPT_Core, + * < 5 for Taxonomy_Core, + * and 0 for Widgets because widgets_init runs at init priority 1. + * + * @since 0.2.1 + */ + public function hooks() { + add_action( 'init', array( $this, 'init' ), 0 ); + do_action( 'dfw_setup' ); + } + + /** + * Activate the plugin. + * + * @since 0.2.1 + */ + public function _activate() { + // Bail early if requirements aren't met. + if ( ! $this->check_requirements() ) { + return; + } + + // Make sure any rewrite functionality has been loaded. + flush_rewrite_rules(); + } + + /** + * Deactivate the plugin. + * Uninstall routines should be in uninstall.php. + * + * @since 0.2.1 + */ + public function _deactivate() { + // Add deactivation cleanup functionality here. + } + + /** + * Init hooks + * + * @since 0.2.1 + */ + public function init() { + + // Bail early if requirements aren't met. + if ( ! $this->check_requirements() ) { + return; + } + + // Load translated strings for plugin. + load_plugin_textdomain( 'doubleclick-for-wordpress', false, dirname( $this->basename ) . '/languages/' ); + + // Initialize plugin classes. + $this->plugin_classes(); + } + + /** + * Check if the plugin meets requirements and + * disable it if they are not present. + * + * @since 0.2.1 + * + * @return boolean True if requirements met, false if not. + */ + public function check_requirements() { + + // Bail early if plugin meets requirements. + if ( $this->meets_requirements() ) { + return true; + } + + // Add a dashboard notice. + add_action( 'all_admin_notices', array( $this, 'requirements_not_met_notice' ) ); + + // Deactivate our plugin. + add_action( 'admin_init', array( $this, 'deactivate_me' ) ); + + // Didn't meet the requirements. + return false; + } + + /** + * Deactivates this plugin, hook this function on admin_init. + * + * @since 0.2.1 + */ + public function deactivate_me() { + + // We do a check for deactivate_plugins before calling it, to protect + // any developers from accidentally calling it too early and breaking things. + if ( function_exists( 'deactivate_plugins' ) ) { + deactivate_plugins( $this->basename ); + } + } + + /** + * Check that all plugin requirements are met. + * + * @since 0.2.1 + * + * @return boolean True if requirements are met. + */ + public function meets_requirements() { + + // Do checks for required classes / functions or similar. + // Add detailed messages to $this->activation_errors array. + return true; + } + + /** + * Adds a notice to the dashboard if the plugin requirements are not met. + * + * @since 0.2.1 + */ + public function requirements_not_met_notice() { + + // Compile default message. + $default_message = sprintf( __( 'DoubleClick for WordPress is missing requirements and has been deactivated. Please make sure all requirements are available.', 'doubleclick-for-wordpress' ), admin_url( 'plugins.php' ) ); + + // Default details to null. + $details = null; + + // Add details if any exist. + if ( $this->activation_errors && is_array( $this->activation_errors ) ) { + $details = '' . implode( '
', $this->activation_errors ) . ''; + } + + // Output errors. + ?> +
+

+ +
+ $field; + default: + throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field ); + } + } + + /** + * Include a file from the includes directory. + * + * @since 0.2.1 + * + * @param string $filename Name of the file to be included. + * @return boolean Result of include call. + */ + public static function include_file( $filename ) { + $file = self::dir( $filename . '.php' ); + if ( file_exists( $file ) ) { + return include_once( $file ); + } + return false; + } + + /** + * This plugin's directory. + * + * @since 0.2.1 + * + * @param string $path (optional) appended path. + * @return string Directory and path. + */ + public static function dir( $path = '' ) { + static $dir; + $dir = $dir ? $dir : trailingslashit( dirname( __FILE__ ) ); + return $dir . $path; + } + + /** + * This plugin's url. + * + * @since 0.2.1 + * + * @param string $path (optional) appended path. + * @return string URL and path. + */ + public static function url( $path = '' ) { + static $url; + $url = $url ? $url : trailingslashit( plugin_dir_url( __FILE__ ) ); + return $url . $path; + } +} + +/** + * Grab the DoubleClick_For_WordPress object and return it. + * Wrapper for DoubleClick_For_WordPress::get_instance(). + * + * @since 0.2.1 + * @return DoubleClick_For_WordPress Singleton instance of plugin class. + */ +function doubleclick_for_wordpress() { + return DoubleClick_For_WordPress::get_instance(); +} + +// Kick it off. +add_action( 'plugins_loaded', array( doubleclick_for_wordpress(), 'hooks' ) ); + +// Activation and deactivation. +register_activation_hook( __FILE__, array( doubleclick_for_wordpress(), '_activate' ) ); +register_deactivation_hook( __FILE__, array( doubleclick_for_wordpress(), '_deactivate' ) ); diff --git a/boilerplate/includes/README.md b/boilerplate/includes/README.md new file mode 100644 index 0000000..0d7a1a6 --- /dev/null +++ b/boilerplate/includes/README.md @@ -0,0 +1,6 @@ +# DoubleClick for WordPress Includes # +https://labs.inn.org +Copyright (c) 2017 innlabs, willhaynes24 +Licensed under the GPLv2 license. + +Additional PHP functionality goes here. \ No newline at end of file diff --git a/boilerplate/languages b/boilerplate/languages new file mode 100644 index 0000000..462a40f --- /dev/null +++ b/boilerplate/languages @@ -0,0 +1,21 @@ +# Copyright (C) 2017 _s +# This file is distributed under the same license as the _s package. +msgid "" +msgstr "" +"Project-Id-Version: _s\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language-Team: Team \n" +"Last-Translator: John Doe \n" +"Report-Msgid-Bugs-To: http://_s.com\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-KeywordsList: __;_e;_ex:1,2c;_n:1,2;_n_noop:1,2;_nx:1,2,4c;_nx_noop:1,2,3c;_x:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" +"X-Poedit-SearchPath-0: .\n" +"X-Poedit-SearchPathExcluded-0: *.js\n" +"X-Poedit-SourceCharset: UTF-8\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: node_modules/grunt-wp-i18n/test/fixtures/basic-theme/page-templates/full-width.php:2 +msgid "Full Width" +msgstr "" diff --git a/boilerplate/package.json b/boilerplate/package.json new file mode 100644 index 0000000..905d952 --- /dev/null +++ b/boilerplate/package.json @@ -0,0 +1,57 @@ +{ + "name": "doubleclick-for-wordpress", + "title": "DoubleClick for WordPress", + "version": "0.2.1", + "description": "Serve DoubleClick ads natively in WordPress. Built to make serving and targeting responsive ads easy.", + "author": { + "name": "innlabs, willhaynes24", + "url": "https://labs.inn.org" + }, + "license": "GPLv2", + "devDependencies": { + "grunt": "latest", + "grunt-contrib-watch": "latest", + "grunt-wp-i18n": "latest", + "grunt-contrib-copy": "latest", + "grunt-text-replace": "latest", + "grunt-contrib-clean": "latest", + "grunt-contrib-compress": "latest", + "grunt-wp-deploy": "latest", + "load-grunt-tasks": "latest", + "autoprefixer": "latest", + "babel-preset-es2015": "latest", + "bourbon": "latest", + "bourbon-neat": "latest", + "browser-sync": "latest", + "css-mqpacker": "latest", + "del": "latest", + "eslint-config-wordpress": "latest", + "glob": "latest", + "gulp": "latest", + "gulp-babel": "latest", + "gulp-cheerio": "latest", + "gulp-concat": "latest", + "gulp-cssnano": "latest", + "gulp-eslint": "latest", + "gulp-imagemin": "latest", + "gulp-notify": "latest", + "gulp-plumber": "latest", + "gulp-postcss": "latest", + "gulp-rename": "latest", + "gulp-sass": "latest", + "gulp-sass-lint": "latest", + "gulp-sort": "latest", + "gulp-sourcemaps": "latest", + "gulp-svgmin": "latest", + "gulp-svgstore": "latest", + "gulp-uglify": "latest", + "gulp-util": "latest", + "gulp-wp-pot": "latest", + "gulp.spritesmith": "latest" + }, + "scripts": { + "test": "dockunit", + "preversion": "npm test", + "version": "grunt version && git add ." + } +} diff --git a/boilerplate/phpcs.xml b/boilerplate/phpcs.xml new file mode 100644 index 0000000..405ccd7 --- /dev/null +++ b/boilerplate/phpcs.xml @@ -0,0 +1,16 @@ + + + + + + + A modified set of rules, based on the WordPress Coding Standards. + + + + + + + + + diff --git a/boilerplate/phpunit.xml b/boilerplate/phpunit.xml new file mode 100644 index 0000000..44f0fdb --- /dev/null +++ b/boilerplate/phpunit.xml @@ -0,0 +1,14 @@ + + + + ./tests/ + + + diff --git a/release.sh b/boilerplate/release.sh similarity index 100% rename from release.sh rename to boilerplate/release.sh diff --git a/boilerplate/tests/bootstrap.php b/boilerplate/tests/bootstrap.php new file mode 100644 index 0000000..f007ab6 --- /dev/null +++ b/boilerplate/tests/bootstrap.php @@ -0,0 +1,35 @@ +assertTrue( class_exists( 'DoubleClick_For_WordPress') ); + } + + /** + * Test that our main helper function is an instance of our class. + * + * @since 0.2.1 + */ + function test_get_instance() { + $this->assertInstanceOf( 'DoubleClick_For_WordPress', doubleclick_for_wordpress() ); + } + + /** + * Replace this with some actual testing code. + * + * @since 0.2.1 + */ + function test_sample() { + $this->assertTrue( true ); + } +} diff --git a/css/dfp.css b/css/dfp.css deleted file mode 100644 index 26f3fc4..0000000 --- a/css/dfp.css +++ /dev/null @@ -1,15 +0,0 @@ -.display-none { - display: none; -} -.widget_doubleclick_widget { - text-align: center; -} -.dfw-unit.display-block:before { - speak: auto; - text-transform: uppercase; - content: "Advertisement"; - display: block; - text-align: center; - font-size: 14px; - color: #aaa; -} diff --git a/docs/Colophon.md b/docs/Colophon.md deleted file mode 100644 index 5904420..0000000 --- a/docs/Colophon.md +++ /dev/null @@ -1,9 +0,0 @@ -# Colophon - -#### Credits - -Built by [__Will Haynes__](https://github.com/willhaynes) ([@willhaynes](https://twitter.com/willhaynes)) for the [Institute for Nonprofit News](https://github.com/inn). - -#### License - -Released under [__GPLv2__](http://www.gnu.org/licenses/gpl-2.0.html). \ No newline at end of file diff --git a/docs/Developer-API.md b/docs/Developer-API.md deleted file mode 100644 index c30bf21..0000000 --- a/docs/Developer-API.md +++ /dev/null @@ -1,105 +0,0 @@ -# Developer API - -A `global $DoubleClick` variable is defined on plugin init, making it possible to define breakpoints -and place ads directly within your theme. - -__Quick Function Links:__ - - * [$DoubleClick->register_breakpoint($identifer,$args)](#doubleclick-register_breakpointidentiferargs) - * [$DoubleClick->place_ad($identifer,$size,$breakpoints)](#doubleclick-place_adidentifersizebreakpoints) - -* * * - -## 1. Define Breakpoints - -##### $DoubleClick->register_breakpoint($identifer,$args) - -You can make it easier for users to target breakpoints by defining them in `functions.php` - -##### Example: - -```php -function ad_setup() { - - global $DoubleClick; - - // Optionally define the network code directly in functions.php. - // $DoubleClick->networkCode = "xxxxxxx"; - - /* Define Breakpoints */ - $DoubleClick->register_breakpoint('phone', array('minWidth'=> 0,'maxWidth'=>720)); - $DoubleClick->register_breakpoint('tablet', array('minWidth'=>760,'maxWidth'=>1040)); - $DoubleClick->register_breakpoint('desktop', array('minWidth'=>1040,'maxWidth'=>1220)); - $DoubleClick->register_breakpoint('xl', array('minWidth'=>1220,'maxWidth'=>9999)); - -} -add_action('dfw_setup','ad_setup'); -``` - -##### Paramaters: - -__$identifer__ - -`String` A unique identifier for this breakpoint - -__$args__ - -`Array` An array of properties about the breakpoint. Currently the only keys supported are minWidth and maxWidth. - -* * * - -## 2. Place Ads - -### $DoubleClick->place_ad($identifer,$sizes,$args) - -Prints DOM to display an ad at the given breakpoint. - -##### Example: - -```php - -global $DoubleClick; - -// simple call: -$DoubleClick->place_ad('my-identifer','300x250'); - -// more options: -$sizes = array( - 'phone' => '300x50' // show a medium rectangle for phone and up. - 'tablet' => '728x90' // show a leaderboard for tablet and up. - 'desktop' => '' // show no ad for desktop and up. - ); -$args = array( - 'lazyLoad' => false // if set to true, the ad will load only once its within view on screen. - ); - -$DoubleClick->place_ad('my-identifier',$sizes,$args); -``` - - -##### Paramaters: - -__$identifer__ - -`String` The DFP identifier for an ad unit (DFP does not require you to create an identifier. If this does not match a value defined in DFP, a network-wide ad will still be requested). - -__$sizes__ - -`Array|String` The size for the ad. Either a string for all breakpoints, or an array of sizes for each breakpoint. - -__$args__ - -`Array` (optional) An array of additional arguments. Values: - - - __lazyLoad__: (true/false) setting this to true and the ad will be loaded only once it's within view on the page. Default is false. - -* * * - -## 3. Troubleshooting - -On any page with DoubleClick ads enabled (namely with DoubleClick js enqueued), load the included developer console to confirm data about advertisement delivery for each placement. - -#### Append the URL with the parameter: -``` -?google_force_console -``` diff --git a/docs/Targeting.md b/docs/Targeting.md deleted file mode 100644 index 437202a..0000000 --- a/docs/Targeting.md +++ /dev/null @@ -1,91 +0,0 @@ -# Targeting - -This plugin implements advanced DFP targeting criteria to allow trafficers to specify pages or groups of pages where line items should serve. - -* * * - -## 1. Example - -A local restaurant might wish to only have their ads shown a landing page and related article pages for a publisher's food category. - -![Screenshot](img/targeting.png) - -_In the above example, this line item is targeting all posts in the Category "food"._ - -* * * - -## 2. Available targeting criteria - -The following WordPress and general targeting criteria are defined for each ad call: - -#### WordPress Targeting — - -Targeting criteria specific to WordPress variables. - - - `Page` → Target the [type of page](http://codex.wordpress.org/Conditional_Tags). - - > Takes the values of 'home', 'front-page', 'admin', 'admin-bar-showing', 'single', 'archive', 'author', 'date' or 'search'. - - - `Category` → On single pages, target based on WordPress category. The value(s) passed are the WordPress slug for the category. - - > - - - `Tag` → On single pages, target based on WordPress tags. The value(s) passed are the WordPress slug for the tag. - -#### URL Targeting — - -Additional targeting criteria set by the URL of the page. - - - `inURL` → Target a piece of the page path. - - > __eg__. targeting the string '__/dvds__' would match [example.com**/dvds**/](http://example.com/dvds/), [example.com**/dvds**/page1](http://example.com/dvds/page1) and [example.com**/dvds**/page2](http://example.com/dvds/page2) - - - `URLIs` → Target the entire page path. - - > __eg__. targeting the string '__/books__' will **only** match [example.com**/books**](http://example.com/books/) and not [example.com**/books/page1**](http://example.com/books/page1). (Note: Any trailing '/' is removed.) - - - `Domain` → Target based on the domain. - - > eg. run different advertising on [staging.example.com](http://staging.example.com) and [example.com](http://example.com). - - - `Query` → Target a ?query var. - - > eg. target the url [example.com/?**movie=12**](http://example.com/news/) with the targeting string 'p:12' - -__Warning__: Targeting strings are limited to 40 characters long by DFP. Targeting URLIs or domains longer than that will result in error. - -* * * - -## 3. Defining targeting criteria in DFP - -DFP needs to be told what filter keys exist before they can be used. - -#### Example - -In Inventory > Custom Targeting, hit "New Key," - -![screenshot](img/create-key.png) - -Enter a name for the key and "values type" to indicate whether this key is static or dynamic. - -For the purposes of this tutorial, we will use the `Category` targeting key. Also for our purposes, we will assume the site has a small number of predefined categories, such as "sports", "arts", "news" and "opinion". This is better suited for a value type of _"Users will select from predefined targeting values"_ (__static__). - -![screenshot](img/name-key.png) - -If a key is more __dynamic__, such as a blog that uses hundreds of categories, "values type" should be set it _"Users will enter targeting values when creating line items or checking inventory"_ - -On the next screen enter values the key could take. This will be a list of categories, unique domains, url segments, tags, &c. - -![screenshot](img/key-values.png) - -#### Using the targeting criteria - -Now, when creating a line item, select "custom criteria" from the left hand menu under targeting. After entering a targeting key, select one or more options: - -![screenshot](img/custom-criteria.png) - -This line item will show only on article pages that belong in the news category. - -The [available targeting criteria](#2-available-targeting-criteria) section above contains more information about what is possible with different criteria. - - diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 12831d5..0000000 --- a/docs/index.md +++ /dev/null @@ -1,51 +0,0 @@ -# ![Screenshot](img/dfw.png) DoubleClick for WordPress - -Serve DoubleClick ads natively in WordPress. Built to make serving responsive ads easy. - -Built by the [Institute for Nonprofit News](http://inn.org/), DoubleClick for WordPress works with [Project Largo](http://largoproject.org/) or any standalone WordPress blog. - -* * * - -## 1. What is this thing? - -This WordPress plugin serves inventory from a DoubleClick for Publisher account on a WordPress site. - -No need to copy and paste ad codes or header tags — the plugin generates all of this on its own. - -* * * - -## 2. How do I use it? - -For the most basic integration, only two steps are necessary. - -#### __2.1. Define Settings__ — - -Under _Settings > DoubleClick for WordPress_ update the field with your network code from DFP, and a place to define breakpoints to serve ads to. - -**NOTE:** Both Network Code and Breakpoints may be defined by a developer within the WordPress theme. If a developer already defined these values within the theme, a user will know based on their display in the plugin Settings page. - -![Screenshot](img/network-code.png) - -#### __2.2 Placing Advertisements__ — - -Advertisements are placed in two ways. - -##### 1. Via Reusable Widget - -![Screenshot](img/widget.png) - - * The __identifier__ field is the name of a line item in DoubleClick for Publishers. If an line item with the same identifier in DoubleClick matches the identifier set in the widget, the ad will load (subject to conditional targeting set in DFP admin). If the identifier is blank or doesn't match a line item in DFP, the plugin attempts to load inventory from anywhere in the DFP account. - - * Based on breakpoints defined in plugin settings or theme, an __Inventory Size__ can be defined for each breakpoint set either in the plugin settings or via theme function. - * For an ad the same __Inventory Size__ at every breakpoint, use that size in each breakpoint (i.e. `300x250`). - * For an ad with multiple __Inventory Sizes__ (and corresponding creatives), define the appropriate Inventory Size for each breakpoint (i.e. `320x50` for a mobile breakpoint, `728x90` for tablet and desktop breakpoints). - -* * * - -##### 2. Via Theme Function - -See [Developer API](Developer-API.md) documentation. - -## 3. Is that it? - -Nope! Next [configure targeting](Targeting.md) and advanced users may look at the [developer api](developer-api/). diff --git a/readme.md b/readme.md deleted file mode 100644 index 44979cd..0000000 --- a/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# DoubleClick for WordPress - -Serve DoubleClick ads natively in WordPress. Built to make serving responsive ads easy. - -[Read the documentation on GitHub](https://github.com/INN/DoubleClick-for-WordPress/tree/master/docs). From 3b95655129cb955dc0053b291926816b79e00f7d Mon Sep 17 00:00:00 2001 From: rclations Date: Thu, 22 Jun 2017 17:46:19 -0400 Subject: [PATCH 02/41] remove cmb2, rename options file/class --- boilerplate/.bowerrc => .bowerrc | 0 boilerplate/.editorconfig => .editorconfig | 0 boilerplate/.eslintrc.js => .eslintrc.js | 0 boilerplate/.eslintrc.json => .eslintrc.json | 0 boilerplate/.gitignore => .gitignore | 0 boilerplate/.sass-lint.yml => .sass-lint.yml | 0 boilerplate/.travis.yml => .travis.yml | 0 boilerplate/.yo-rc.json => .yo-rc.json | 0 boilerplate/Dockunit.json => Dockunit.json | 0 boilerplate/Gulpfile.js => Gulpfile.js | 0 boilerplate/README.md => README.md | 0 {boilerplate/assets => assets}/README.md | 0 .../assets => assets}/css/sass/styles.scss | 0 .../assets => assets}/js/jquery.dfp.js | 0 .../assets => assets}/js/jquery.dfp.min.js | 0 {boilerplate/assets => assets}/repo/README.md | 0 {boilerplate/bin => bin}/install-wp-tests.sh | 0 boilerplate/bower.json => bower.json | 0 dfw.php | 12 - {boilerplate/docs => docs}/img/create-key.png | Bin .../docs => docs}/img/custom-criteria.png | Bin {boilerplate/docs => docs}/img/dfw.png | Bin {boilerplate/docs => docs}/img/key-values.png | Bin {boilerplate/docs => docs}/img/name-key.png | Bin .../docs => docs}/img/network-code.png | Bin {boilerplate/docs => docs}/img/targeting.png | Bin {boilerplate/docs => docs}/img/widget.png | Bin ...press.php => doubleclick-for-wordpress.php | 78 +-- {boilerplate/includes => includes}/README.md | 0 includes/class-options.php | 119 ++++ dfw-init.php => includes/dfw-init.php | 0 dfw-options.php => includes/dfw-options.php | 0 dfw-widget.php => includes/dfw-widget.php | 0 js/jquery.dfp.min.js | 8 - js/jquery.dfw.js | 51 -- js/vendor/.keep | 0 js/vendor/jquery.dfp.js/.bower.json | 25 - js/vendor/jquery.dfp.js/MIT-License.txt | 7 - js/vendor/jquery.dfp.js/Readme.markdown | 360 ---------- js/vendor/jquery.dfp.js/bower.json | 15 - js/vendor/jquery.dfp.js/dfp.jquery.json | 29 - js/vendor/jquery.dfp.js/jquery.dfp.js | 646 ------------------ js/vendor/jquery.dfp.js/jquery.dfp.min.js | 8 - js/vendor/jquery.dfp.js/package.json | 18 - js/vendor/jquery.dfp.min.js | 8 - boilerplate/languages => languages | 0 license.txt | 280 -------- mkdocs.yml | 2 - boilerplate/package.json => package.json | 0 boilerplate/phpcs.xml => phpcs.xml | 0 boilerplate/phpunit.xml => phpunit.xml | 0 boilerplate/release.sh => release.sh | 0 {boilerplate/tests => tests}/bootstrap.php | 0 {boilerplate/tests => tests}/test-base.php | 0 tests/test-doubleclick.php | 36 + 55 files changed, 167 insertions(+), 1535 deletions(-) rename boilerplate/.bowerrc => .bowerrc (100%) rename boilerplate/.editorconfig => .editorconfig (100%) rename boilerplate/.eslintrc.js => .eslintrc.js (100%) rename boilerplate/.eslintrc.json => .eslintrc.json (100%) rename boilerplate/.gitignore => .gitignore (100%) rename boilerplate/.sass-lint.yml => .sass-lint.yml (100%) rename boilerplate/.travis.yml => .travis.yml (100%) rename boilerplate/.yo-rc.json => .yo-rc.json (100%) rename boilerplate/Dockunit.json => Dockunit.json (100%) rename boilerplate/Gulpfile.js => Gulpfile.js (100%) rename boilerplate/README.md => README.md (100%) rename {boilerplate/assets => assets}/README.md (100%) rename {boilerplate/assets => assets}/css/sass/styles.scss (100%) rename {boilerplate/assets => assets}/js/jquery.dfp.js (100%) rename {boilerplate/assets => assets}/js/jquery.dfp.min.js (100%) rename {boilerplate/assets => assets}/repo/README.md (100%) rename {boilerplate/bin => bin}/install-wp-tests.sh (100%) rename boilerplate/bower.json => bower.json (100%) delete mode 100644 dfw.php rename {boilerplate/docs => docs}/img/create-key.png (100%) rename {boilerplate/docs => docs}/img/custom-criteria.png (100%) rename {boilerplate/docs => docs}/img/dfw.png (100%) rename {boilerplate/docs => docs}/img/key-values.png (100%) rename {boilerplate/docs => docs}/img/name-key.png (100%) rename {boilerplate/docs => docs}/img/network-code.png (100%) rename {boilerplate/docs => docs}/img/targeting.png (100%) rename {boilerplate/docs => docs}/img/widget.png (100%) rename boilerplate/doubleclick-for-wordpress.php => doubleclick-for-wordpress.php (81%) rename {boilerplate/includes => includes}/README.md (100%) create mode 100644 includes/class-options.php rename dfw-init.php => includes/dfw-init.php (100%) rename dfw-options.php => includes/dfw-options.php (100%) rename dfw-widget.php => includes/dfw-widget.php (100%) delete mode 100644 js/jquery.dfp.min.js delete mode 100644 js/jquery.dfw.js delete mode 100644 js/vendor/.keep delete mode 100644 js/vendor/jquery.dfp.js/.bower.json delete mode 100644 js/vendor/jquery.dfp.js/MIT-License.txt delete mode 100644 js/vendor/jquery.dfp.js/Readme.markdown delete mode 100644 js/vendor/jquery.dfp.js/bower.json delete mode 100644 js/vendor/jquery.dfp.js/dfp.jquery.json delete mode 100644 js/vendor/jquery.dfp.js/jquery.dfp.js delete mode 100644 js/vendor/jquery.dfp.js/jquery.dfp.min.js delete mode 100644 js/vendor/jquery.dfp.js/package.json delete mode 100644 js/vendor/jquery.dfp.min.js rename boilerplate/languages => languages (100%) delete mode 100644 license.txt delete mode 100644 mkdocs.yml rename boilerplate/package.json => package.json (100%) rename boilerplate/phpcs.xml => phpcs.xml (100%) rename boilerplate/phpunit.xml => phpunit.xml (100%) rename boilerplate/release.sh => release.sh (100%) rename {boilerplate/tests => tests}/bootstrap.php (100%) rename {boilerplate/tests => tests}/test-base.php (100%) create mode 100644 tests/test-doubleclick.php diff --git a/boilerplate/.bowerrc b/.bowerrc similarity index 100% rename from boilerplate/.bowerrc rename to .bowerrc diff --git a/boilerplate/.editorconfig b/.editorconfig similarity index 100% rename from boilerplate/.editorconfig rename to .editorconfig diff --git a/boilerplate/.eslintrc.js b/.eslintrc.js similarity index 100% rename from boilerplate/.eslintrc.js rename to .eslintrc.js diff --git a/boilerplate/.eslintrc.json b/.eslintrc.json similarity index 100% rename from boilerplate/.eslintrc.json rename to .eslintrc.json diff --git a/boilerplate/.gitignore b/.gitignore similarity index 100% rename from boilerplate/.gitignore rename to .gitignore diff --git a/boilerplate/.sass-lint.yml b/.sass-lint.yml similarity index 100% rename from boilerplate/.sass-lint.yml rename to .sass-lint.yml diff --git a/boilerplate/.travis.yml b/.travis.yml similarity index 100% rename from boilerplate/.travis.yml rename to .travis.yml diff --git a/boilerplate/.yo-rc.json b/.yo-rc.json similarity index 100% rename from boilerplate/.yo-rc.json rename to .yo-rc.json diff --git a/boilerplate/Dockunit.json b/Dockunit.json similarity index 100% rename from boilerplate/Dockunit.json rename to Dockunit.json diff --git a/boilerplate/Gulpfile.js b/Gulpfile.js similarity index 100% rename from boilerplate/Gulpfile.js rename to Gulpfile.js diff --git a/boilerplate/README.md b/README.md similarity index 100% rename from boilerplate/README.md rename to README.md diff --git a/boilerplate/assets/README.md b/assets/README.md similarity index 100% rename from boilerplate/assets/README.md rename to assets/README.md diff --git a/boilerplate/assets/css/sass/styles.scss b/assets/css/sass/styles.scss similarity index 100% rename from boilerplate/assets/css/sass/styles.scss rename to assets/css/sass/styles.scss diff --git a/boilerplate/assets/js/jquery.dfp.js b/assets/js/jquery.dfp.js similarity index 100% rename from boilerplate/assets/js/jquery.dfp.js rename to assets/js/jquery.dfp.js diff --git a/boilerplate/assets/js/jquery.dfp.min.js b/assets/js/jquery.dfp.min.js similarity index 100% rename from boilerplate/assets/js/jquery.dfp.min.js rename to assets/js/jquery.dfp.min.js diff --git a/boilerplate/assets/repo/README.md b/assets/repo/README.md similarity index 100% rename from boilerplate/assets/repo/README.md rename to assets/repo/README.md diff --git a/boilerplate/bin/install-wp-tests.sh b/bin/install-wp-tests.sh similarity index 100% rename from boilerplate/bin/install-wp-tests.sh rename to bin/install-wp-tests.sh diff --git a/boilerplate/bower.json b/bower.json similarity index 100% rename from boilerplate/bower.json rename to bower.json diff --git a/dfw.php b/dfw.php deleted file mode 100644 index cf86c10..0000000 --- a/dfw.php +++ /dev/null @@ -1,12 +0,0 @@ -plugin_class = new DCFWP_Plugin_Class( $this ); + $this->options = new DCWP_Options( $this ); } // END OF PLUGIN CLASSES FUNCTION /** @@ -303,55 +292,12 @@ public function __get( $field ) { case 'basename': case 'url': case 'path': + case 'doubleclick': return $this->$field; default: throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field ); } } - - /** - * Include a file from the includes directory. - * - * @since 0.2.1 - * - * @param string $filename Name of the file to be included. - * @return boolean Result of include call. - */ - public static function include_file( $filename ) { - $file = self::dir( $filename . '.php' ); - if ( file_exists( $file ) ) { - return include_once( $file ); - } - return false; - } - - /** - * This plugin's directory. - * - * @since 0.2.1 - * - * @param string $path (optional) appended path. - * @return string Directory and path. - */ - public static function dir( $path = '' ) { - static $dir; - $dir = $dir ? $dir : trailingslashit( dirname( __FILE__ ) ); - return $dir . $path; - } - - /** - * This plugin's url. - * - * @since 0.2.1 - * - * @param string $path (optional) appended path. - * @return string URL and path. - */ - public static function url( $path = '' ) { - static $url; - $url = $url ? $url : trailingslashit( plugin_dir_url( __FILE__ ) ); - return $url . $path; - } } /** diff --git a/boilerplate/includes/README.md b/includes/README.md similarity index 100% rename from boilerplate/includes/README.md rename to includes/README.md diff --git a/includes/class-options.php b/includes/class-options.php new file mode 100644 index 0000000..f1853c0 --- /dev/null +++ b/includes/class-options.php @@ -0,0 +1,119 @@ +plugin = $plugin; + $this->hooks(); + + // Set our title. + $this->title = esc_attr__( 'DoubleClick for WordPress Doubleclick', 'doubleclick-for-wordpress' ); + } + + /** + * Initiate our hooks. + * + * @since 0.2.1 + */ + public function hooks() { + + // Hook in our actions to the admin. + add_action( 'admin_init', array( $this, 'admin_init' ) ); + add_action( 'admin_menu', array( $this, 'add_options_page' ) ); + + } + + /** + * Register our setting to WP. + * + * @since 0.2.1 + */ + public function admin_init() { + register_setting( $this->key, $this->key ); + } + + /** + * Add menu options page. + * + * @since 0.2.1 + */ + public function add_options_page() { + $this->options_page = add_menu_page( + $this->title, + $this->title, + 'manage_options', + $this->key, + array( $this, 'admin_page_display' ) + ); + + } + + /** + * Admin page markup. Mostly handled by CMB2. + * + * @since 0.2.1 + */ + public function admin_page_display() { + ?> +
+

+
+ 0&&g.setCategoryExclusion(n)})}var p=c.data("size-mapping");if(p&&k.sizeMapping[p]){var q=b.googletag.sizeMapping();a.each(k.sizeMapping[p],function(a,b){q.addSize(b.browser,b.ad_sizes)}),g.defineSizeMapping(q.build())}c.data(m,g),"function"==typeof k.beforeEachAdLoaded&&k.beforeEachAdLoaded.call(this,c)})}),b.googletag.cmd.push(function(){if(k.enableSingleRequest&&b.googletag.pubads().enableSingleRequest(),a.each(k.setTargeting,function(a,c){b.googletag.pubads().setTargeting(a,c)}),"object"==typeof k.setLocation&&("number"==typeof k.setLocation.latitude&&"number"==typeof k.setLocation.longitude&&"number"==typeof k.setLocation.precision?b.googletag.pubads().setLocation(k.setLocation.latitude,k.setLocation.longitude,k.setLocation.precision):"number"==typeof k.setLocation.latitude&&"number"==typeof k.setLocation.longitude&&b.googletag.pubads().setLocation(k.setLocation.latitude,k.setLocation.longitude)),k.setCategoryExclusion.length>0){var c,e=k.setCategoryExclusion.split(",");a.each(e,function(d,e){c=a.trim(e),c.length>0&&b.googletag.pubads().setCategoryExclusion(c)})}k.collapseEmptyDivs&&b.googletag.pubads().collapseEmptyDivs(),k.disablePublisherConsole&&b.googletag.pubads().disablePublisherConsole(),k.companionAds&&(b.googletag.companionAds().setRefreshUnfilledSlots(!0),k.disableInitialLoad||b.googletag.pubads().enableVideoAds()),k.disableInitialLoad&&b.googletag.pubads().disableInitialLoad(),k.noFetch&&b.googletag.pubads().noFetch(),b.googletag.pubads().addEventListener("slotRenderEnded",function(b){i++;var c=a("#"+b.slot.getSlotId().getDomId()),e=b.isEmpty?"none":"block",f=c.data("existingContent");"none"===e&&a.trim(f).length>0&&"original"===k.collapseEmptyDivs&&(c.show().html(f),e="block display-original"),c.removeClass("display-none").addClass("display-"+e),"function"==typeof k.afterEachAdLoaded&&k.afterEachAdLoaded.call(this,c,b),"function"==typeof k.afterAllAdsLoaded&&i===g&&k.afterAllAdsLoaded.call(this,d)}),b.googletag.enableServices()})},q=function(){d.each(function(){var c=a(this),d=c.data(m);b.googletag.cmd.push(k.refreshExisting&&d&&c.hasClass("display-block")?function(){b.googletag.pubads().refresh([d])}:function(){b.googletag.display(c.attr("id"))})})},r=function(){var a=b.location.pathname.replace(/\/$/,""),c=new RegExp("/([^/]*)","ig"),d=a.match(c),e=["/"],f="";if(d&&"/"!==a){var g="",h=d.length;if(h>0)for(var i=0;h>i;i++){g=d[i],e.push(g);for(var j=i+1;h>j;j++)g+=d[j],e.push(g);0===i&&(e.splice(-1,1),f=g)}e.push(f)}e=e.reverse();var k=(b.location.toString().replace(/\=/gi,":").match(/\?(.+)$/),RegExp.$1.split("&"));return{inURL:e,URLIs:e[0],Query:k}},s=function(a,b){return h++,a.attr("id")||a.attr("id",b.replace(/[^A-z0-9]/g,"_")+"-auto-gen-id-"+h).attr("id")},t=function(a){var b=a.data("adunit")||k.namespace||a.attr("id")||"";return"function"==typeof k.alterAdUnitName&&(b=k.alterAdUnitName.call(this,b,a)),b},u=function(b){var c=[],d=b.data("dimensions");if(d){var e=d.split(",");a.each(e,function(a,b){var d=b.split("x");c.push([parseInt(d[0],10),parseInt(d[1],10)])})}else c.push([b.width(),b.height()]);return c},v=function(){if(l=l||a('script[src*="googletagservices.com/tag/js/gpt.js"]').length,!l){b.googletag=b.googletag||{},b.googletag.cmd=b.googletag.cmd||[];var c=document.createElement("script");c.async=!0,c.type="text/javascript",c.onerror=function(){w()};var d="https:"===document.location.protocol;c.src=(d?"https:":"http:")+"//www.googletagservices.com/tag/js/gpt.js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(c,e),"none"===c.style.display&&w()}},w=function(){var c=b.googletag.cmd;setTimeout(function(){var d=function(a,c,d){return b.googletag.ads.push(d),b.googletag.ads[d]={renderEnded:function(){},addService:function(){return this}},b.googletag.ads[d]};b.googletag={cmd:{push:function(a){a.call(e)}},ads:[],pubads:function(){return this},noFetch:function(){return this},disableInitialLoad:function(){return this},disablePublisherConsole:function(){return this},enableSingleRequest:function(){return this},setTargeting:function(){return this},collapseEmptyDivs:function(){return this},enableServices:function(){return this},defineSlot:function(a,b,c){return d(a,b,c,!1)},defineOutOfPageSlot:function(a,b){return d(a,[],b,!0)},display:function(a){return b.googletag.ads[a].renderEnded.call(e),this}},a.each(c,function(a,c){b.googletag.cmd.push(c)})},50)};a.dfp=a.fn.dfp=function(a,b){b=b||{},a===c&&(a=f),"object"==typeof a&&(b=a,a=b.dfpID||f);var d=this;return"function"==typeof this&&(d=j),n(a,d,b),this}}(window.jQuery||window.Zepto||window.tire,window); \ No newline at end of file diff --git a/js/jquery.dfw.js b/js/jquery.dfw.js deleted file mode 100644 index 523bd50..0000000 --- a/js/jquery.dfw.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * A few lines of javascript to help the DoubleClick for WordPress plugin. - * Extends jquery.dfw.min.js with some event listeners specific to us. - */ -(function() { - var $ = jQuery; - - // On window scroll check if there are any new ads to load. - var lazyLoad = function() { - // all the lazy load ads on the page. - var lazyLoaders = $( '.dfw-lazy-load:not(.dfw-loaded)' ), - // jQuery object to hold ads that actually need loading. - toLoad = $( [] ), - // window scrolltop. - st = $( document ).scrollTop(), - // window height. - wh = $( window ).height(); - - // determind with lazyLoaders needs toLoad. - lazyLoaders.each(function () { - var top = $( this ).offset().top; - - // how much futher down the page to load - // the ad (so it's at least loading before it's on screen). - var buffer = 200; - if (top < st + wh + buffer) { - toLoad = toLoad.add( $( this ) ); - } - }); - - // if we have some ads, then load 'em! - if (toLoad.length > 0) { - $( toLoad ).dfp({ - "dfpID": dfw.networkCode, - "collapseEmptyDivs": false, - "sizeMapping": dfw.mappings, - "setTargeting": dfw.targeting - }).addClass( 'dfw-loaded' ); - } - }; - - $( document ).ready(function(){ - // Watch for scroll events, and determine whether any ads - // need lazy loading. Wait a few seconds for images and text to render - // and units to reach their "true" depth on the page. - $( window ).on( 'scroll', lazyLoad ); - - // Run lazyLoad once on DOM ready to load any ads in view - lazyLoad(); - }); -})(); diff --git a/js/vendor/.keep b/js/vendor/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/js/vendor/jquery.dfp.js/.bower.json b/js/vendor/jquery.dfp.js/.bower.json deleted file mode 100644 index 4bde70c..0000000 --- a/js/vendor/jquery.dfp.js/.bower.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "jquery.dfp", - "version": "2.4.1", - "main": "jquery.dfp.js", - "ignore": [ - "img/", - "tests/", - ".gitattributes", - ".gitignore", - ".travis.yml", - ".vimrc", - "Gruntfile.js", - "example-bootstrap.js" - ], - "homepage": "https://github.com/coop182/jquery.dfp.js", - "_release": "2.4.1", - "_resolution": { - "type": "version", - "tag": "2.4.1", - "commit": "75d2b1cc513f5a04d93637ca61cf4b3ed605acc1" - }, - "_source": "git://github.com/coop182/jquery.dfp.js.git", - "_target": "~2.4.1", - "_originalSource": "jquery.dfp.js" -} \ No newline at end of file diff --git a/js/vendor/jquery.dfp.js/MIT-License.txt b/js/vendor/jquery.dfp.js/MIT-License.txt deleted file mode 100644 index 4282de9..0000000 --- a/js/vendor/jquery.dfp.js/MIT-License.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2015 Matt Cooper - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/js/vendor/jquery.dfp.js/Readme.markdown b/js/vendor/jquery.dfp.js/Readme.markdown deleted file mode 100644 index af53a4b..0000000 --- a/js/vendor/jquery.dfp.js/Readme.markdown +++ /dev/null @@ -1,360 +0,0 @@ -jQuery DFP - A jQuery implementation for Google DFP -====================================================== - -[![Build Status](https://travis-ci.org/coop182/jquery.dfp.js.png?branch=master)](https://travis-ci.org/coop182/jquery.dfp.js) - -This script is a drop in solution for getting Double Click for Publishers (DFP) by Google working on your page. By including this script on your page and then initialising it in the ways described below you should find it very easy to get DFP working. - -Do not include any of the generated DFP script tags from the DFP admin on your page, this script replaces them. - -This script also works with [Zepto.js](http://zeptojs.com/) - -Demo / Ad unit tester ---------------------- - -You can use [this page](http://coop182.github.io/jquery.dfp.js/dfptests/test.html?google_console=1&networkID=15572793&adunitID=Leader&dimensions=728x90) to test your DFP ads using the jquery.dfp.js script. There is some debug code included to help debug the ad delivery. - -You can also use the [Google Console](https://support.google.com/dfp_sb/answer/181070?hl=en-GB) to debug your ad units. This is done by by adding a "google_console=1" or "google_debug=1" to the url, and toggling the console by pressing CTRL + F10. Subsequent pagerequests will not require the parameters, and the console can be toggled. Adding the querystring "googfc" to an url, will also load the console, but also show it, without having to press CTRL + F10. - -Setup ------ - -You can add ad units to your page in any location that you would like to display an ad. - -By default this script will look for ad units with a class of `adunit` but you can of course use jQuery selectors as well. - -The minimum information required for an ad unit to function is having the ad unit specified. To do this you can use the id parameter of the element, for example: - -```html -
-``` - -In the example above the ID of the div element will be used to look up a corresponding ad unit in DFP and the dimensions of the adunit will be set to the same dimensions of the div which could be defined in your CSS. - -You can optionally specify the adunit name and dimensions in the following way: - -```html -
-``` - -This method can be useful for including multiple copies of an ad unit with the same name which when part of a DFP placement will then pull in as many different creatives as possible. - -You can also specify multiple dimensions sets: - -```html -
-``` - -Also you can optionally specify custom targeting on a per ad unit basis in the following way: - -```html -
-``` - -Also you can optionally specify custom exclusion category on a per ad unit basis in the following way: - -```html -
-``` - -To create an out of page ad unit set the data-outofpage property on the ad unit. Dimensions are not required for out of page ad units. - -```html -
-``` - -In order to identify an ad unit on the page that is a video companion ad, set the data-companion attribute on that unit. - -```html -
-``` - -Usage ------ - -Calling the script: - -```html - - - DFP TEST - - - - - -
- - - - - -``` - -Using a bootstrap file (take a look at [example-bootstrap.js](https://github.com/coop182/jquery.dfp.js/blob/master/example-bootstrap.js)): - -```html - - - DFP TEST - - - - - -
- - - -``` - -You can init the script in the following ways: - -```javascript -$.dfp('xxxxxxxxx'); -``` -```javascript -$.dfp({ - dfpID:'xxxxxxxxx' -}); -``` -```javascript -$('selector').dfp({ - dfpID:'xxxxxxxxx' -}); -``` -```javascript -$('selector').dfp({ - dfpID:'xxxxxxxxx', - setCategoryExclusion: 'firstcategory, secondcategory' -}); -``` -```javascript -$('selector').dfp({ - dfpID:'xxxxxxxxx', - setLocation: { latitude: 34, longitude: -45.12, precision: 1000 } -}); -``` - -```javascript -$('selector').dfp({ - dfpID:'xxxxxxxxx', - sizeMapping: { - 'my-default': [ - {browser: [1024, 768], ad_sizes: [980, 185]}, - {browser: [ 980, 600], ad_sizes: [[728, 90], [640, 480]]} - {browser: [ 0, 0], ad_sizes: [88, 31]} - ], - } -}); -``` - -Available Options ------------------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
dfpIDThis string is your unique DFP account ID.
setTargetingThis object is where you set custom targeting key value pairs. Also see the Default Targeting options that are set further down the page.
urlThis string is the url used by the URL Targeting feature. The default value of this option is the value found by calling window.location.
setUrlTargetingThis boolean specifies whether the targeting should include information found in the url of the current page. The default value of this option is true.
setCategoryExclusionThis comma separated list sets category exclusions globally (page level).
setLocationThis object sets geolocalization. String values are not valid.
enableSingleRequestThis boolean sets whether the page ads are fetched with a single request or not, you will need to set this to false it you want to call $.dfp() more than once, typically you would do this if you are loading ad units into the page after the initial load.
collapseEmptyDivsThis can be set to true, false or 'original'. If its set to true the divs will be set to display:none if no line item is found. False means that the ad unit div will stay visible no matter what. Setting this to 'original' (the default option) means that the ad unit div will be hidden if no line items are found UNLESS there is some existing content inside the ad unit div tags. This allows you to have fall back content in the ad unit in the event that no ads are found.
refreshExistingThis boolean controls what happens when dfp is called multiple times on ad units. By default it is set to true which means that if an already initialised ad is initialised again it will instead be refreshed.
sizeMappingDefines named size maps that can be used with in combination with the data-size-mapping attribute to enable responsive ad sizing (https://support.google.com/dfp_premium/answer/3423562?hl=en).
companionAdsIf adding companion ads to accompany videos using the IMA SDK to serve video ads, then pass this parameter as true to identify the units being used for that purpose. (https://support.google.com/dfp_premium/answer/1191131)
disableInitialLoadThis allows for serving companion ad units when the video on the page auto plays. You'll need to include this setting with companionAds as true to avoid possible double impressions. (https://support.google.com/dfp_premium/answer/1191131)
afterEachAdLoadedThis is a call back function, see below for more information.
afterAllAdsLoadedThis is a call back function, see below for more information.
beforeEachAdLoadedThis is a call back function, see below for more information.
- -Callbacks ---------- - -This script provides two callbacks which you can use to make working with DFP a little easier. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CallbackParametersDescription
afterEachAdLoaded(adUnit) -
    -
  • adUnit - jQuery Object - the jQuery object
  • -
-
This is called after each ad unit has finished rendering.
afterAllAdsLoaded(adUnits) -
    -
  • adUnits - jQuery Object - the jQuery object containing all selected ad units
  • -
-
This is called after all ad units have finished rendering.
alterAdUnitName(adUnitName, adUnit) -
    -
  • adUnitName - String - the default ad unit name
  • -
  • adUnit - jQuery Object - the jQuery object
  • -
-
Return the modified or overrided ad unit name. This function is called once per ad unit.
beforeEachAdLoaded(adUnit) -
    -
  • adUnit - jQuery Object - the jQuery object
  • -
-
This is called before each ad unit has started rendering.
afterAdBlocked(adUnit) -
    -
  • adUnit - jQuery Object - the jQuery object
  • -
-
This is called after each AdUnit has been blocked.
- -Please see the [example-bootstrap.js](https://github.com/coop182/jquery.dfp.js/blob/master/example-bootstrap.js) file for an example of how to use these. - -Default URL Targeting ---------------------- - -The following targeting options are built into this script and should be setup in your DFP account ([within Inventory/Custom Targeting](https://support.google.com/dfp_sb/bin/answer.py?hl=en&answer=2983838)) to make full use of them. These targeting-parameters can be turned on/off with the setUrlTargeting option. - -**Beware: The Targeting string has a 40 character limit!** - - - - - - - - - - - - - - - - - - -
KeyDescription
UrlHostThis allows you to target different host names, for example you could test your ads on your staging environment before pushing them live by specifying a host of staging.yourdomain.com within DFP, this script will take care of the rest.
UrlPathThis allows you to target the path of the users browser, for example if you set UrlPath to '/page1' on the targeting options of the DFP line item it would match http://www.yourdomain.com/page1 only and not http://www.yourdomain.com/page1/segment2.
UrlQueryThis allows you to target the query parameters of a page. For example if the URL was http://www.yourdomain.com/page1?param1=value1 you could target it with a DFP ad by specifying a UrlQuery targeting string of param1:value1
- -DFP now supports both a "begins with" and a "contains" operator when specifying the custom criteria value. Furthermore the value when using free-form-key-value custom criterias, is no longer subject to a 40 character limit. Read more about custom criteria in the [DFP help](https://support.google.com/dfp_premium/answer/188092). - -![URL Targeting](https://raw.github.com/coop182/jquery.dfp.js/master/img/url-targetting.png) - -**IMPORTANT: Regarding user-identifiable information in url targeting** - -If your url contains user-identifiable information you have to anonymize the url when using URL targeting. - -From the [DFP docs](https://support.google.com/dfp_premium/answer/177383): - -> You may not pass any user-identifiable data (including names, addresses, or user IDs) in the targeting. Please mask this information using the encoding of your choice, and ensure your ad trafficker knows how to decode the values when setting up a line item. - -From the [DFP Terms & Conditions](http://www.google.dk/doubleclick/publishers/small-business/terms.html): - -> **2.3 Prohibited Actions.** You will not, and will not allow any third party to: ... (h) utilize any feature or functionality of the Program, or include anything in Program Data or Program Ads, that could be so utilized, to personally identify and/or personally track individual end users or any other persons - -Ignoring this rule can result in Google shutting down your network! - -You can anonymize the url by providing an anonymized version in the 'url' option. This example shows how to replace email occurances in the url with an empty string: - -```javascript -$('selector').dfp({ - dfpID: 'xxxxxxxxx', - url: window.location.toString().replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/gi, '') -}); -``` - -Contributing ------------- - -Any and all contributions will be greatly appreciated. - -If you wish to you can use [Grunt](http://gruntjs.com/) to enable a smooth contributing and build process. - -Install Node.js by running `sudo apt-get install nodejs` - -Install Grunt using: `npm install -g grunt-cli` - -Once installed run `npm install` from inside the cloned repo directory. - -You should now be able to make your changes to `jquery.dfp.js` and once you are finished simply run `grunt` if there are no errors then you can commit your changes and make a pull request and your feature/bug fix will be merged as soon as possible. - -Please feel free to write tests which will test your new code, [Travis CI](https://travis-ci.org/) is used to test the code automatically once a pull request is generated. - -Thanks a lot to these [contributors](https://github.com/coop182/jquery.dfp.js/graphs/contributors). diff --git a/js/vendor/jquery.dfp.js/bower.json b/js/vendor/jquery.dfp.js/bower.json deleted file mode 100644 index 3cd7690..0000000 --- a/js/vendor/jquery.dfp.js/bower.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "jquery.dfp", - "version": "2.4.1", - "main": "jquery.dfp.js", - "ignore": [ - "img/", - "tests/", - ".gitattributes", - ".gitignore", - ".travis.yml", - ".vimrc", - "Gruntfile.js", - "example-bootstrap.js" - ] -} diff --git a/js/vendor/jquery.dfp.js/dfp.jquery.json b/js/vendor/jquery.dfp.js/dfp.jquery.json deleted file mode 100644 index ec7c9e3..0000000 --- a/js/vendor/jquery.dfp.js/dfp.jquery.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "dfp", - "title": "jQuery DFP", - "description": "A jQuery implementation for Google DoubleClick for Publishers (DFP)", - "keywords": [ - "DFP", - "google", - "ads", - "advertising" - ], - "version": "2.4.1", - "author": { - "name": "Matt Cooper", - "email": "matt@matthewcooper.net", - "url": "http://matthewcooper.net" - }, - "homepage": "https://github.com/coop182/jquery.dfp.js", - "bugs": "https://github.com/coop182/jquery.dfp.js/issues", - "docs": "https://github.com/coop182/jquery.dfp.js", - "licenses": [ - { - "type": "MIT", - "url": "https://github.com/coop182/jquery.dfp.js/blob/master/MIT-License.txt" - } - ], - "dependencies": { - "jquery": ">= 1.7" - } -} diff --git a/js/vendor/jquery.dfp.js/jquery.dfp.js b/js/vendor/jquery.dfp.js/jquery.dfp.js deleted file mode 100644 index 89762f1..0000000 --- a/js/vendor/jquery.dfp.js/jquery.dfp.js +++ /dev/null @@ -1,646 +0,0 @@ -/** - * jQuery DFP v2.4.1 - * http://github.com/coop182/jquery.dfp.js - * - * Copyright 2015 Matt Cooper - * Released under the MIT license - */ - -(function (window, undefined) { - "use strict"; - - /** - * Make plugin UMD compatible - * Uses CommonJS, AMD or browser globals to create plugin. - */ - (function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define( ['jquery'], factory ); - } else if (typeof exports === 'object') { - // Node/CommonJS - factory( require( 'jquery' ) ); - } else { - // Browser globals - factory( window.jQuery || window.Zepto ); - } - }(function ($) { - - var - - // Save Scope - dfpScript = this || {}; - - var - // DFP account ID - dfpID = '', - - // Init counters - count = 0, - uid = 0, - rendered = 0, - - // Default DFP selector - dfpSelector = '.adunit', - - adsCouldNeverBeInitilized = false, - - // Keep track of if we've already tried to load gpt.js before - dfpIsLoaded = false, - - // Store adunit on div as: - storeAs = 'googleAdUnit', - - /** - * Init function sets required params and loads Google's DFP script - * @param String id The DFP account ID - * @param String selector The adunit selector - * @param Object options Custom options to apply - */ - init = function (id, selector, options) { - var $adCollection; - - // Reset counters on each call - count = 0; - rendered = 0; - - dfpID = id; - $adCollection = $( selector ); - - /** - * @returns {boolean} - */ - dfpScript.shouldCheckForAdBlockers = function(){ - return options ? typeof options.afterAdBlocked === 'function' : false; - }; - - // explicitly wait for loader to be completed, otherwise the googletag might not be available - dfpLoader( options, $adCollection ).then(function(){ - options = setOptions( options ); - dfpScript.dfpOptions = options; - - $(function () { - createAds( options, $adCollection ); - displayAds( options, $adCollection ); - }); - }); - - }, - - /** - * Set the options for DFP - * @param Object options Custom options to apply - * @return Object extended options - */ - setOptions = function (options) { - - // Set default options - var dfpOptions = { - setTargeting: {}, - setCategoryExclusion: '', - setLocation: '', - enableSingleRequest: true, - collapseEmptyDivs: 'original', - refreshExisting: true, - disablePublisherConsole: false, - disableInitialLoad: false, - noFetch: false, - namespace: undefined, - sizeMapping: {} - }; - - if (typeof options.setUrlTargeting === 'undefined' || options.setUrlTargeting) { - // Get URL Targeting - var urlTargeting = getUrlTargeting( options.url ); - $.extend(true, dfpOptions.setTargeting, { - UrlHost: urlTargeting.Host, - UrlPath: urlTargeting.Path, - UrlQuery: urlTargeting.Query - }); - } - - // Merge options objects - $.extend( true, dfpOptions, options ); - - // If a custom googletag is specified, use it. - if (dfpOptions.googletag) { - window.googletag.cmd.push(function () { - $.extend( true, window.googletag, dfpOptions.googletag ); - }); - } - - return dfpOptions; - }, - - /** - * Find and create all Ads - * @param Object dfpOptions options related to ad instantiation - * @param jQuery $adCollection collection of ads - * @return Array an array of ad units that have been created. - */ - createAds = function (dfpOptions, $adCollection) { - var googletag = window.googletag; - // Loops through on page Ad units and gets ads for them. - $adCollection.each(function () { - var $adUnit = $( this ); - - count++; - - // adUnit name - var adUnitName = getName( $adUnit, dfpOptions ); - - // adUnit id - this will use an existing id or an auto generated one. - var adUnitID = getID( $adUnit, adUnitName ); - - // get dimensions of the adUnit - var dimensions = getDimensions( $adUnit ); - - // set existing content - $adUnit.data( 'existingContent', $adUnit.html() ); - - // wipe html clean ready for ad and set the default display class. - $adUnit.html( '' ).addClass( 'display-none' ); - - // Push commands to DFP to create ads - googletag.cmd.push(function () { - - var googleAdUnit, - $adUnitData = $adUnit.data( storeAs ); - - if ($adUnitData) { - - // Get existing ad unit - googleAdUnit = $adUnitData; - - } else { - - // Build slotName for loading - var slotName; - if (dfpID === '') { - slotName = adUnitName; - } else { - slotName = '/' + dfpID + '/' + adUnitName; - } - - // Create the ad - out of page or normal - if ($adUnit.data( 'outofpage' )) { - googleAdUnit = googletag.defineOutOfPageSlot( slotName, adUnitID ); - } else { - googleAdUnit = googletag.defineSlot( slotName, dimensions, adUnitID ); - if ($adUnit.data( 'companion' )) { - googleAdUnit = googleAdUnit.addService( googletag.companionAds() ); - } - } - - googleAdUnit = googleAdUnit.addService( googletag.pubads() ); - - } - - // Sets custom targeting for just THIS ad unit if it has been specified - var targeting = $adUnit.data( 'targeting' ); - if (targeting) { - $.each(targeting, function (k, v) { - googleAdUnit.setTargeting( k, v ); - }); - } - - // Sets custom exclusions for just THIS ad unit if it has been specified - var exclusions = $adUnit.data( 'exclusions' ); - if (exclusions) { - var exclusionsGroup = exclusions.split( ',' ); - var valueTrimmed; - $.each(exclusionsGroup, function (k, v) { - valueTrimmed = $.trim( v ); - if (valueTrimmed.length > 0) { - googleAdUnit.setCategoryExclusion( valueTrimmed ); - } - }); - } - - // Sets responsive size mapping for just THIS ad unit if it has been specified - var mapping = $adUnit.data( 'size-mapping' ); - if (mapping && dfpOptions.sizeMapping[mapping]) { - // Convert verbose to DFP format - var map = googletag.sizeMapping(); - $.each(dfpOptions.sizeMapping[mapping], function (k, v) { - map.addSize( v.browser, v.ad_sizes ); - }); - googleAdUnit.defineSizeMapping( map.build() ); - } - - // Store googleAdUnit reference - $adUnit.data( storeAs, googleAdUnit ); - - // Allow altering of the ad slot before ad load - if (typeof dfpOptions.beforeEachAdLoaded === 'function') { - dfpOptions.beforeEachAdLoaded.call( this, $adUnit ); - } - }); - - }); - - // Push DFP config options - googletag.cmd.push(function () { - - var pubadsService = googletag.pubads(); - - if (dfpOptions.enableSingleRequest) { - pubadsService.enableSingleRequest(); - } - - $.each(dfpOptions.setTargeting, function (k, v) { - pubadsService.setTargeting( k, v ); - }); - - var setLocation = dfpOptions.setLocation; - if (typeof setLocation === 'object') { - if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number' && - typeof setLocation.precision === 'number') { - pubadsService.setLocation( setLocation.latitude, setLocation.longitude, setLocation.precision ); - } else if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number') { - pubadsService.setLocation( setLocation.latitude, setLocation.longitude ); - } - } - - if (dfpOptions.setCategoryExclusion.length > 0) { - var exclusionsGroup = dfpOptions.setCategoryExclusion.split( ',' ); - var valueTrimmed; - - $.each(exclusionsGroup, function (k, v) { - valueTrimmed = $.trim( v ); - if (valueTrimmed.length > 0) { - pubadsService.setCategoryExclusion( valueTrimmed ); - } - }); - } - - if (dfpOptions.collapseEmptyDivs) { - pubadsService.collapseEmptyDivs(); - } - - if (dfpOptions.disablePublisherConsole) { - pubadsService.disablePublisherConsole(); - } - - if (dfpOptions.companionAds) { - googletag.companionAds().setRefreshUnfilledSlots( true ); - - if ( ! dfpOptions.disableInitialLoad) { - pubadsService.enableVideoAds(); - } - } - - if (dfpOptions.disableInitialLoad) { - pubadsService.disableInitialLoad(); - } - - if (dfpOptions.noFetch) { - pubadsService.noFetch(); - } - - // Setup event listener to listen for renderEnded event and fire callbacks. - pubadsService.addEventListener('slotRenderEnded', function (event) { - - rendered++; - - var $adUnit = $( '#' + event.slot.getSlotId().getDomId() ); - - var display = event.isEmpty ? 'none' : 'block'; - - // if the div has been collapsed but there was existing content expand the - // div and reinsert the existing content. - var $existingContent = $adUnit.data( 'existingContent' ); - if (display === 'none' && $.trim( $existingContent ).length > 0 && - dfpOptions.collapseEmptyDivs === 'original') { - $adUnit.show().html( $existingContent ); - display = 'block display-original'; - } - - $adUnit.removeClass( 'display-none' ).addClass( 'display-' + display ); - - // Excute afterEachAdLoaded callback if provided - if (typeof dfpOptions.afterEachAdLoaded === 'function') { - dfpOptions.afterEachAdLoaded.call( this, $adUnit, event ); - } - - // Excute afterAllAdsLoaded callback if provided - if (typeof dfpOptions.afterAllAdsLoaded === 'function' && rendered === count) { - dfpOptions.afterAllAdsLoaded.call( this, $adCollection ); - } - - }); - - // this will work with AdblockPlus - if (dfpScript.shouldCheckForAdBlockers() && ! googletag._adBlocked_) { - setTimeout(function () { - var slots = pubadsService.getSlots ? pubadsService.getSlots() : []; - if (slots.length > 0) { - $.get( slots[0].getContentUrl() ).always(function (r) { - if (r.status !== 200) { - $.each(slots, function () { - var $adUnit = $( '#' + this.getSlotId().getDomId() ); - dfpOptions.afterAdBlocked.call( dfpScript, $adUnit, this ); - }); - } - }); - } - }, 0); - } - - googletag.enableServices(); - - }); - - }, - - /** - * Display all created Ads - * @param {Object} dfpOptions options related to ad instantiation - * @param {jQuery} $adCollection collection of ads - */ - displayAds = function (dfpOptions, $adCollection) { - - var googletag = window.googletag; - // Check if google adLoader can be loaded, this will work with AdBlock - if (dfpScript.shouldCheckForAdBlockers() && ! googletag._adBlocked_) { - if (googletag.getVersion) { - var script = '//partner.googleadservices.com/gpt/pubads_impl_' + - googletag.getVersion() + '.js'; - $.getScript( script ).always(function (r) { - if (r && r.statusText === 'error') { - $.each($adCollection, function () { - dfpOptions.afterAdBlocked.call( dfpScript, $( this ) ); - }); - } - }); - } - - } - - $adCollection.each(function () { - - var $adUnit = $( this ), - $adUnitData = $adUnit.data( storeAs ); - - if (googletag._adBlocked_) { - if (dfpScript.shouldCheckForAdBlockers()) { - dfpOptions.afterAdBlocked.call( dfpScript, $adUnit ); - } - } - if (dfpOptions.refreshExisting && $adUnitData && $adUnit.hasClass( 'display-block' )) { - - googletag.cmd.push( function () { googletag.pubads().refresh( [$adUnitData] ); } ); - - } else { - googletag.cmd.push( function () { googletag.display( $adUnit.attr( 'id' ) ); } ); - } - - }); - - }, - - /** - * Create an array of paths so that we can target DFP ads to Page URI's - * @return Array an array of URL parts that can be targeted. - */ - getUrlTargeting = function (url) { - - // Get the url and parse it to its component parts using regex from RFC2396 Appendix-B (https://tools.ietf.org/html/rfc2396#appendix-B) - var urlMatches = (url || window.location.toString()).match( / ^(([^: / ?#] + ):)?(\ / \ / ([^ / ?#] * ))?([^?#] * )(\?([^#] * ))?(#( .* ))? / ); - var matchedAuthority = urlMatches[4] || ''; - var matchedPath = (urlMatches[5] || '').replace( /(.)\/$/, '$1' ); - var matchedQuery = urlMatches[7] || ''; - - // Get the query params for targeting against - var params = matchedQuery.replace( /\=/ig, ':' ).split( '&' ); - - return { - Host: matchedAuthority, - Path: matchedPath, - Query: params - }; - - }, - - /** - * Get the id of the adUnit div or generate a unique one. - * @param Object $adUnit The adunit to work with - * @param String adUnitName The name of the adunit - * @return String The ID of the adunit or a unique autogenerated ID - */ - getID = function ($adUnit, adUnitName) { - - uid++; - return $adUnit.attr( 'id' ) || $adUnit.attr( 'id', adUnitName.replace( /[^A-z0-9]/g, '_' ) + '-auto-gen-id-' + uid ).attr( 'id' ); - - }, - - /** - * Get the name of the Ad unit, either use the div id or - * check for the optional attribute data-adunit - * @param Object $adUnit The adunit to work with - * @param Object dfpOptions options related to ad instantiation - * @return String The name of the adunit, will be the same as inside DFP - */ - getName = function ($adUnit, dfpOptions) { - - var adUnitName = $adUnit.data( 'adunit' ) || dfpOptions.namespace || $adUnit.attr( 'id' ) || ''; - if (typeof dfpOptions.alterAdUnitName === 'function') { - adUnitName = dfpOptions.alterAdUnitName.call( this, adUnitName, $adUnit ); - } - return adUnitName; - - }, - - /** - * Get the dimensions of the ad unit using the container div dimensions or - * check for the optional attribute data-dimensions - * @param Object $adUnit The adunit to work with - * @return Array The dimensions of the adunit (width, height) - */ - getDimensions = function ($adUnit) { - - var dimensions = [], - dimensionsData = $adUnit.data( 'dimensions' ); - - // Check if data-dimensions are specified. If they aren't, use the dimensions of the ad unit div. - if (dimensionsData) { - - var dimensionGroups = dimensionsData.split( ',' ); - - $.each(dimensionGroups, function (k, v) { - - var dimensionSet = v.split( 'x' ); - dimensions.push( [parseInt( dimensionSet[0], 10 ), parseInt( dimensionSet[1], 10 )] ); - - }); - - } else { - - dimensions.push( [$adUnit.width(), $adUnit.height()] ); - - } - - return dimensions; - - }, - - /** - * Call the google DFP script - there is a little bit of error detection in here to detect - * if the dfp script has failed to load either through an error or it being blocked by an ad - * blocker... if it does not load we execute a dummy script to replace the real DFP. - * - * @param {Object} options - * @param {Array} $adCollection - */ - dfpLoader = function (options, $adCollection) { - - function execBlockEvents() { - if (dfpScript.shouldCheckForAdBlockers()) { - $.each($adCollection, function () { - options.afterAdBlocked.call( dfpScript, $( this ) ); - }); - } - } - - // make sure we don't load gpt.js multiple times - dfpIsLoaded = dfpIsLoaded || $( 'script[src*="googletagservices.com/tag/js/gpt.js"]' ).length; - if (dfpIsLoaded) { - if (adsCouldNeverBeInitilized) { - execBlockEvents(); - } - return $.Deferred().resolve(); - } - - var loaded = $.Deferred(); - - window.googletag = window.googletag || {}; - window.googletag.cmd = window.googletag.cmd || []; - - var gads = document.createElement( 'script' ); - gads.async = true; - gads.type = 'text/javascript'; - - // Adblock blocks the load of Ad scripts... so we check for that - gads.onerror = function () { - dfpBlocked(); - loaded.resolve(); - adsCouldNeverBeInitilized = true; - execBlockEvents(); - }; - - gads.onload = function() { - // this will work with ghostery: - if ( ! googletag._loadStarted_) { - googletag._adBlocked_ = true; - execBlockEvents(); - } - loaded.resolve(); - }; - - var useSSL = 'https:' === document.location.protocol; - gads.src = (useSSL ? 'https:' : 'http:') + - '//www.googletagservices.com/tag/js/gpt.js'; - var node = document.getElementsByTagName( 'script' )[0]; - node.parentNode.insertBefore( gads, node ); - - // Adblock plus seems to hide blocked scripts... so we check for that - if (gads.style.display === 'none') { - dfpBlocked(); - } - - return loaded; - - }, - - /** - * This function gets called if DFP has been blocked by an adblocker - * it implements a dummy version of the dfp object and allows the script to excute its callbacks - * regardless of whether DFP is actually loaded or not... it is basically only useful for situations - * where you are laying DFP over existing content and need to init things like slide shows after the loading - * is completed. - */ - dfpBlocked = function () { - var googletag = window.googletag; - // Get the stored dfp commands - var commands = googletag.cmd; - - var _defineSlot = function (name, dimensions, id, oop) { - googletag.ads.push( id ); - googletag.ads[id] = { - renderEnded: function () { }, - addService: function () { return this; } - }; - - return googletag.ads[id]; - }; - - // overwrite the dfp object - replacing the command array with a function and defining missing functions - googletag = { - cmd: { - push: function (callback) { - callback.call( dfpScript ); - } - }, - ads: [], - pubads: function () { return this; }, - noFetch: function () { return this; }, - disableInitialLoad: function () { return this; }, - disablePublisherConsole: function () { return this; }, - enableSingleRequest: function () { return this; }, - setTargeting: function () { return this; }, - collapseEmptyDivs: function () { return this; }, - enableServices: function () { return this; }, - defineSlot: function (name, dimensions, id) { - return _defineSlot( name, dimensions, id, false ); - }, - defineOutOfPageSlot: function (name, id) { - return _defineSlot( name, [], id, true ); - }, - display: function (id) { - googletag.ads[id].renderEnded.call( dfpScript ); - return this; - } - - }; - - // Execute any stored commands - $.each(commands, function (k, v) { - googletag.cmd.push( v ); - }); - - }; - - /** - * Add function to the jQuery / Zepto namespace - * @param String id (Optional) The DFP account ID - * @param Object options (Optional) Custom options to apply - */ - $.dfp = $.fn.dfp = function (id, options) { - - options = options || {}; - - if (id === undefined) { - id = dfpID; - } - - if (typeof id === 'object') { - options = id; - id = options.dfpID || dfpID; - } - - var selector = this; - - if (typeof this === 'function') { - selector = dfpSelector; - } - - init( id, selector, options ); - - return this; - - }; - - })); - -})(window); diff --git a/js/vendor/jquery.dfp.js/jquery.dfp.min.js b/js/vendor/jquery.dfp.js/jquery.dfp.min.js deleted file mode 100644 index ec7fbda..0000000 --- a/js/vendor/jquery.dfp.js/jquery.dfp.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * jQuery DFP v2.4.1 - * http://github.com/coop182/jquery.dfp.js - * - * Copyright 2015 Matt Cooper - * Released under the MIT license - */ -!function(a,b){"use strict";!function(b){"function"==typeof define&&define.amd?define(["jquery"],b):b("object"==typeof exports?require("jquery"):a.jQuery||a.Zepto)}(function(c){var d=this||{},e="",f=0,g=0,h=0,i=".adunit",j=!1,k=!1,l="googleAdUnit",m=function(a,b,g){var i;f=0,h=0,e=a,i=c(b),d.shouldCheckForAdBlockers=function(){return g?"function"==typeof g.afterAdBlocked:!1},u(g,i).then(function(){g=n(g),d.dfpOptions=g,c(function(){o(g,i),p(g,i)})})},n=function(d){var e={setTargeting:{},setCategoryExclusion:"",setLocation:"",enableSingleRequest:!0,collapseEmptyDivs:"original",refreshExisting:!0,disablePublisherConsole:!1,disableInitialLoad:!1,noFetch:!1,namespace:b,sizeMapping:{}};if("undefined"==typeof d.setUrlTargeting||d.setUrlTargeting){var f=q(d.url);c.extend(!0,e.setTargeting,{UrlHost:f.Host,UrlPath:f.Path,UrlQuery:f.Query})}return c.extend(!0,e,d),e.googletag&&a.googletag.cmd.push(function(){c.extend(!0,a.googletag,e.googletag)}),e},o=function(b,g){var i=a.googletag;g.each(function(){var a=c(this);f++;var d=s(a,b),g=r(a,d),h=t(a);a.data("existingContent",a.html()),a.html("").addClass("display-none"),i.cmd.push(function(){var f,j=a.data(l);if(j)f=j;else{var k;k=""===e?d:"/"+e+"/"+d,a.data("outofpage")?f=i.defineOutOfPageSlot(k,g):(f=i.defineSlot(k,h,g),a.data("companion")&&(f=f.addService(i.companionAds()))),f=f.addService(i.pubads())}var m=a.data("targeting");m&&c.each(m,function(a,b){f.setTargeting(a,b)});var n=a.data("exclusions");if(n){var o,p=n.split(",");c.each(p,function(a,b){o=c.trim(b),o.length>0&&f.setCategoryExclusion(o)})}var q=a.data("size-mapping");if(q&&b.sizeMapping[q]){var r=i.sizeMapping();c.each(b.sizeMapping[q],function(a,b){r.addSize(b.browser,b.ad_sizes)}),f.defineSizeMapping(r.build())}a.data(l,f),"function"==typeof b.beforeEachAdLoaded&&b.beforeEachAdLoaded.call(this,a)})}),i.cmd.push(function(){var a=i.pubads();b.enableSingleRequest&&a.enableSingleRequest(),c.each(b.setTargeting,function(b,c){a.setTargeting(b,c)});var e=b.setLocation;if("object"==typeof e&&("number"==typeof e.latitude&&"number"==typeof e.longitude&&"number"==typeof e.precision?a.setLocation(e.latitude,e.longitude,e.precision):"number"==typeof e.latitude&&"number"==typeof e.longitude&&a.setLocation(e.latitude,e.longitude)),b.setCategoryExclusion.length>0){var j,k=b.setCategoryExclusion.split(",");c.each(k,function(b,d){j=c.trim(d),j.length>0&&a.setCategoryExclusion(j)})}b.collapseEmptyDivs&&a.collapseEmptyDivs(),b.disablePublisherConsole&&a.disablePublisherConsole(),b.companionAds&&(i.companionAds().setRefreshUnfilledSlots(!0),b.disableInitialLoad||a.enableVideoAds()),b.disableInitialLoad&&a.disableInitialLoad(),b.noFetch&&a.noFetch(),a.addEventListener("slotRenderEnded",function(a){h++;var d=c("#"+a.slot.getSlotId().getDomId()),e=a.isEmpty?"none":"block",i=d.data("existingContent");"none"===e&&c.trim(i).length>0&&"original"===b.collapseEmptyDivs&&(d.show().html(i),e="block display-original"),d.removeClass("display-none").addClass("display-"+e),"function"==typeof b.afterEachAdLoaded&&b.afterEachAdLoaded.call(this,d,a),"function"==typeof b.afterAllAdsLoaded&&h===f&&b.afterAllAdsLoaded.call(this,g)}),d.shouldCheckForAdBlockers()&&!i._adBlocked_&&setTimeout(function(){var e=a.getSlots?a.getSlots():[];e.length>0&&c.get(e[0].getContentUrl()).always(function(a){200!==a.status&&c.each(e,function(){var a=c("#"+this.getSlotId().getDomId());b.afterAdBlocked.call(d,a,this)})})},0),i.enableServices()})},p=function(b,e){var f=a.googletag;if(d.shouldCheckForAdBlockers()&&!f._adBlocked_&&f.getVersion){var g="//partner.googleadservices.com/gpt/pubads_impl_"+f.getVersion()+".js";c.getScript(g).always(function(a){a&&"error"===a.statusText&&c.each(e,function(){b.afterAdBlocked.call(d,c(this))})})}e.each(function(){var a=c(this),e=a.data(l);f._adBlocked_&&d.shouldCheckForAdBlockers()&&b.afterAdBlocked.call(d,a),f.cmd.push(b.refreshExisting&&e&&a.hasClass("display-block")?function(){f.pubads().refresh([e])}:function(){f.display(a.attr("id"))})})},q=function(b){var c=(b||a.location.toString()).match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/),d=c[4]||"",e=(c[5]||"").replace(/(.)\/$/,"$1"),f=c[7]||"",g=f.replace(/\=/gi,":").split("&");return{Host:d,Path:e,Query:g}},r=function(a,b){return g++,a.attr("id")||a.attr("id",b.replace(/[^A-z0-9]/g,"_")+"-auto-gen-id-"+g).attr("id")},s=function(a,b){var c=a.data("adunit")||b.namespace||a.attr("id")||"";return"function"==typeof b.alterAdUnitName&&(c=b.alterAdUnitName.call(this,c,a)),c},t=function(a){var b=[],d=a.data("dimensions");if(d){var e=d.split(",");c.each(e,function(a,c){var d=c.split("x");b.push([parseInt(d[0],10),parseInt(d[1],10)])})}else b.push([a.width(),a.height()]);return b},u=function(b,e){function f(){d.shouldCheckForAdBlockers()&&c.each(e,function(){b.afterAdBlocked.call(d,c(this))})}if(k=k||c('script[src*="googletagservices.com/tag/js/gpt.js"]').length)return j&&f(),c.Deferred().resolve();var g=c.Deferred();a.googletag=a.googletag||{},a.googletag.cmd=a.googletag.cmd||[];var h=document.createElement("script");h.async=!0,h.type="text/javascript",h.onerror=function(){v(),g.resolve(),j=!0,f()},h.onload=function(){googletag._loadStarted_||(googletag._adBlocked_=!0,f()),g.resolve()};var i="https:"===document.location.protocol;h.src=(i?"https:":"http:")+"//www.googletagservices.com/tag/js/gpt.js";var l=document.getElementsByTagName("script")[0];return l.parentNode.insertBefore(h,l),"none"===h.style.display&&v(),g},v=function(){var b=a.googletag,e=b.cmd,f=function(a,c,d,e){return b.ads.push(d),b.ads[d]={renderEnded:function(){},addService:function(){return this}},b.ads[d]};b={cmd:{push:function(a){a.call(d)}},ads:[],pubads:function(){return this},noFetch:function(){return this},disableInitialLoad:function(){return this},disablePublisherConsole:function(){return this},enableSingleRequest:function(){return this},setTargeting:function(){return this},collapseEmptyDivs:function(){return this},enableServices:function(){return this},defineSlot:function(a,b,c){return f(a,b,c,!1)},defineOutOfPageSlot:function(a,b){return f(a,[],b,!0)},display:function(a){return b.ads[a].renderEnded.call(d),this}},c.each(e,function(a,c){b.cmd.push(c)})};c.dfp=c.fn.dfp=function(a,c){c=c||{},a===b&&(a=e),"object"==typeof a&&(c=a,a=c.dfpID||e);var d=this;return"function"==typeof this&&(d=i),m(a,d,c),this}})}(window); \ No newline at end of file diff --git a/js/vendor/jquery.dfp.js/package.json b/js/vendor/jquery.dfp.js/package.json deleted file mode 100644 index 0b6b72c..0000000 --- a/js/vendor/jquery.dfp.js/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "jquery.dfp", - "version": "2.4.1", - "devDependencies": { - "grunt": "~0.4", - "grunt-contrib-jasmine": "^0.8.2", - "grunt-contrib-jshint": "^0.11.1", - "grunt-contrib-uglify": "^0.8.0" - }, - "scripts": { - "test": "grunt travis --verbose" - }, - "repository": { - "type": "git", - "url": "https://github.com/coop182/jquery.dfp.js" - }, - "main": "jquery.dfp.js" -} diff --git a/js/vendor/jquery.dfp.min.js b/js/vendor/jquery.dfp.min.js deleted file mode 100644 index 9d6b7f9..0000000 --- a/js/vendor/jquery.dfp.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * jQuery DFP v1.1.5 - * http://github.com/coop182/jquery.dfp.js - * - * Copyright 2014 Matt Cooper - * Released under the MIT license - */ -!function(a,b,c){"use strict";var d,e=this,f="",g=0,h=0,i=0,j=".adunit",k={},l=!1,m="googleAdUnit",n=function(b,c,e){g=0,i=0,f=b,d=a(c),v(),o(e),a(function(){p(),q()})},o=function(d){if(k={setTargeting:{},setCategoryExclusion:"",setLocation:"",enableSingleRequest:!0,collapseEmptyDivs:"original",refreshExisting:!0,disablePublisherConsole:!1,disableInitialLoad:!1,noFetch:!1,namespace:c,sizeMapping:{}},"undefined"==typeof d.setUrlTargeting||d.setUrlTargeting){var e=r();a.extend(!0,k.setTargeting,{inURL:e.inURL,URLIs:e.URLIs,Query:e.Query,Domain:b.location.host})}a.extend(!0,k,d),k.googletag&&b.googletag.cmd.push(function(){a.extend(!0,b.googletag,k.googletag)})},p=function(){d.each(function(){var c=a(this);g++;var d=t(c),e=s(c,d),h=u(c);c.data("existingContent",c.html()),c.html("").addClass("display-none"),b.googletag.cmd.push(function(){var g,i=c.data(m);g=i?i:c.data("outofpage")?b.googletag.defineOutOfPageSlot("/"+f+"/"+d,e).addService(b.googletag.pubads()):c.data("companion")?b.googletag.defineSlot("/"+f+"/"+d,h,e).addService(b.googletag.companionAds()).addService(b.googletag.pubads()):b.googletag.defineSlot("/"+f+"/"+d,h,e).addService(b.googletag.pubads());var j=c.data("targeting");j&&a.each(j,function(a,b){g.setTargeting(a,b)});var l=c.data("exclusions");if(l){var n,o=l.split(",");a.each(o,function(b,c){n=a.trim(c),n.length>0&&g.setCategoryExclusion(n)})}var p=c.data("size-mapping");if(p&&k.sizeMapping[p]){var q=b.googletag.sizeMapping();a.each(k.sizeMapping[p],function(a,b){q.addSize(b.browser,b.ad_sizes)}),g.defineSizeMapping(q.build())}c.data(m,g),"function"==typeof k.beforeEachAdLoaded&&k.beforeEachAdLoaded.call(this,c)})}),b.googletag.cmd.push(function(){if(k.enableSingleRequest&&b.googletag.pubads().enableSingleRequest(),a.each(k.setTargeting,function(a,c){b.googletag.pubads().setTargeting(a,c)}),"object"==typeof k.setLocation&&("number"==typeof k.setLocation.latitude&&"number"==typeof k.setLocation.longitude&&"number"==typeof k.setLocation.precision?b.googletag.pubads().setLocation(k.setLocation.latitude,k.setLocation.longitude,k.setLocation.precision):"number"==typeof k.setLocation.latitude&&"number"==typeof k.setLocation.longitude&&b.googletag.pubads().setLocation(k.setLocation.latitude,k.setLocation.longitude)),k.setCategoryExclusion.length>0){var c,e=k.setCategoryExclusion.split(",");a.each(e,function(d,e){c=a.trim(e),c.length>0&&b.googletag.pubads().setCategoryExclusion(c)})}k.collapseEmptyDivs&&b.googletag.pubads().collapseEmptyDivs(),k.disablePublisherConsole&&b.googletag.pubads().disablePublisherConsole(),k.companionAds&&(b.googletag.companionAds().setRefreshUnfilledSlots(!0),k.disableInitialLoad||b.googletag.pubads().enableVideoAds()),k.disableInitialLoad&&b.googletag.pubads().disableInitialLoad(),k.noFetch&&b.googletag.pubads().noFetch(),b.googletag.pubads().addEventListener("slotRenderEnded",function(b){i++;var c=a("#"+b.slot.getSlotId().getDomId()),e=b.isEmpty?"none":"block",f=c.data("existingContent");"none"===e&&a.trim(f).length>0&&"original"===k.collapseEmptyDivs&&(c.show().html(f),e="block display-original"),c.removeClass("display-none").addClass("display-"+e),"function"==typeof k.afterEachAdLoaded&&k.afterEachAdLoaded.call(this,c,b),"function"==typeof k.afterAllAdsLoaded&&i===g&&k.afterAllAdsLoaded.call(this,d)}),b.googletag.enableServices()})},q=function(){d.each(function(){var c=a(this),d=c.data(m);b.googletag.cmd.push(k.refreshExisting&&d&&c.hasClass("display-block")?function(){b.googletag.pubads().refresh([d])}:function(){b.googletag.display(c.attr("id"))})})},r=function(){var a=b.location.pathname.replace(/\/$/,""),c=new RegExp("/([^/]*)","ig"),d=a.match(c),e=["/"],f="";if(d&&"/"!==a){var g="",h=d.length;if(h>0)for(var i=0;h>i;i++){g=d[i],e.push(g);for(var j=i+1;h>j;j++)g+=d[j],e.push(g);0===i&&(e.splice(-1,1),f=g)}e.push(f)}e=e.reverse();var k=(b.location.toString().replace(/\=/gi,":").match(/\?(.+)$/),RegExp.$1.split("&"));return{inURL:e,URLIs:e[0],Query:k}},s=function(a,b){return h++,a.attr("id")||a.attr("id",b.replace(/[^A-z0-9]/g,"_")+"-auto-gen-id-"+h).attr("id")},t=function(a){var b=a.data("adunit")||k.namespace||a.attr("id")||"";return"function"==typeof k.alterAdUnitName&&(b=k.alterAdUnitName.call(this,b,a)),b},u=function(b){var c=[],d=b.data("dimensions");if(d){var e=d.split(",");a.each(e,function(a,b){var d=b.split("x");c.push([parseInt(d[0],10),parseInt(d[1],10)])})}else c.push([b.width(),b.height()]);return c},v=function(){if(l=l||a('script[src*="googletagservices.com/tag/js/gpt.js"]').length,!l){b.googletag=b.googletag||{},b.googletag.cmd=b.googletag.cmd||[];var c=document.createElement("script");c.async=!0,c.type="text/javascript",c.onerror=function(){w()};var d="https:"===document.location.protocol;c.src=(d?"https:":"http:")+"//www.googletagservices.com/tag/js/gpt.js";var e=document.getElementsByTagName("script")[0];e.parentNode.insertBefore(c,e),"none"===c.style.display&&w()}},w=function(){var c=b.googletag.cmd;setTimeout(function(){var d=function(a,c,d){return b.googletag.ads.push(d),b.googletag.ads[d]={renderEnded:function(){},addService:function(){return this}},b.googletag.ads[d]};b.googletag={cmd:{push:function(a){a.call(e)}},ads:[],pubads:function(){return this},noFetch:function(){return this},disableInitialLoad:function(){return this},disablePublisherConsole:function(){return this},enableSingleRequest:function(){return this},setTargeting:function(){return this},collapseEmptyDivs:function(){return this},enableServices:function(){return this},defineSlot:function(a,b,c){return d(a,b,c,!1)},defineOutOfPageSlot:function(a,b){return d(a,[],b,!0)},display:function(a){return b.googletag.ads[a].renderEnded.call(e),this}},a.each(c,function(a,c){b.googletag.cmd.push(c)})},50)};a.dfp=a.fn.dfp=function(a,b){b=b||{},a===c&&(a=f),"object"==typeof a&&(b=a,a=b.dfpID||f);var d=this;return"function"==typeof this&&(d=j),n(a,d,b),this}}(window.jQuery||window.Zepto||window.tire,window); \ No newline at end of file diff --git a/boilerplate/languages b/languages similarity index 100% rename from boilerplate/languages rename to languages diff --git a/license.txt b/license.txt deleted file mode 100644 index fa02ff1..0000000 --- a/license.txt +++ /dev/null @@ -1,280 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110, USA - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 79c3e3c..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,2 +0,0 @@ -site_name: DoubleClick for WordPress -theme: readthedocs diff --git a/boilerplate/package.json b/package.json similarity index 100% rename from boilerplate/package.json rename to package.json diff --git a/boilerplate/phpcs.xml b/phpcs.xml similarity index 100% rename from boilerplate/phpcs.xml rename to phpcs.xml diff --git a/boilerplate/phpunit.xml b/phpunit.xml similarity index 100% rename from boilerplate/phpunit.xml rename to phpunit.xml diff --git a/boilerplate/release.sh b/release.sh similarity index 100% rename from boilerplate/release.sh rename to release.sh diff --git a/boilerplate/tests/bootstrap.php b/tests/bootstrap.php similarity index 100% rename from boilerplate/tests/bootstrap.php rename to tests/bootstrap.php diff --git a/boilerplate/tests/test-base.php b/tests/test-base.php similarity index 100% rename from boilerplate/tests/test-base.php rename to tests/test-base.php diff --git a/tests/test-doubleclick.php b/tests/test-doubleclick.php new file mode 100644 index 0000000..2277ace --- /dev/null +++ b/tests/test-doubleclick.php @@ -0,0 +1,36 @@ +assertTrue( class_exists( 'DCWP_Options') ); + } + + /** + * Test that we can access our class through our helper function. + * + * @since 0.2.1 + */ + function test_class_access() { + $this->assertInstanceOf( 'DCWP_Options', doubleclick_for_wordpress()->options ); + } + + /** + * Replace this with some actual testing code. + * + * @since 0.2.1 + */ + function test_sample() { + $this->assertTrue( true ); + } +} From 27e5e28065efc126db31c41a93e8fd2ac20928ba Mon Sep 17 00:00:00 2001 From: rclations Date: Thu, 22 Jun 2017 18:09:04 -0400 Subject: [PATCH 03/41] wip on migrating options page --- includes/class-options.php | 152 +++++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 5 deletions(-) diff --git a/includes/class-options.php b/includes/class-options.php index f1853c0..e32a940 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -26,7 +26,7 @@ class DCWP_Options { * @var string * @since 0.2.1 */ - protected $key = 'doubleclick_for_wordpress_doubleclick'; + protected $key = 'doubleclick_for_wordpress'; /** * Options page metabox ID. @@ -34,7 +34,7 @@ class DCWP_Options { * @var string * @since 0.2.1 */ - protected $metabox_id = 'doubleclick_for_wordpress_doubleclick_metabox'; + protected $metabox_id = 'doubleclick_for_wordpress_metabox'; /** * Options Page title. @@ -63,7 +63,7 @@ public function __construct( $plugin ) { $this->hooks(); // Set our title. - $this->title = esc_attr__( 'DoubleClick for WordPress Doubleclick', 'doubleclick-for-wordpress' ); + $this->title = esc_attr__( 'DoubleClick', 'doubleclick-for-wordpress' ); } /** @@ -76,7 +76,7 @@ public function hooks() { // Hook in our actions to the admin. add_action( 'admin_init', array( $this, 'admin_init' ) ); add_action( 'admin_menu', array( $this, 'add_options_page' ) ); - + add_action( 'admin_init', array( $this, 'register_options' ) ); } /** @@ -94,7 +94,8 @@ public function admin_init() { * @since 0.2.1 */ public function add_options_page() { - $this->options_page = add_menu_page( + $this->options_page = add_submenu_page( + 'options-general.php', $this->title, $this->title, 'manage_options', @@ -113,7 +114,148 @@ public function admin_page_display() { ?>

+
+ + + +
? )"; + } + + public function breakpoints_section_intro() { + echo 'Enter breakpoints below
'; + echo 'Example: phone: 0 to 480, tablet 480 to 768, desktop 768 to 9999.'; + } + + public function documentation_section_intro() { + echo 'Available on GitHub'; + } + + public function network_code_input() { + global $doubleclick; + + if ( isset( $doubleclick->network_code ) ) { + echo ''; + } else { + echo ''; + } + } + + public function breakpoints_input() { + global $doubleclick; + + foreach ( $doubleclick->breakpoints as $breakpoint ) { + if ( ! $breakpoint->option ) { + echo ''; + echo ''; + echo ' (set in theme)
'; + } + } + + $breakpoints = maybe_unserialize( get_option( 'breakpoints' ) ); + + $i = 0; + while ( $i < 5 ) { + $identifier = ( isset( $breakpoints[ $i ]['identifier'] ) )? $breakpoints[ $i ]['identifier'] : ''; + $min_width = ( isset( $breakpoints[ $i ]['min-width'] ) )? $breakpoints[ $i ]['min-width'] : ''; + $max_width = ( isset( $breakpoints[ $i ]['max-width'] ) )? $breakpoints[ $i ]['max-width'] : ''; ?> + + +
$group[0], + 'min-width' => $group[1], + 'max-width' => $group[2], + ); + } + } + + return $breakpoints; + } } From cb1a8694fb4a0c33a578671b3705e60441adf2b5 Mon Sep 17 00:00:00 2001 From: rclations Date: Fri, 23 Jun 2017 16:26:06 -0400 Subject: [PATCH 04/41] finished options page conversion --- includes/dfw-options.php | 177 --------------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 includes/dfw-options.php diff --git a/includes/dfw-options.php b/includes/dfw-options.php deleted file mode 100644 index 2132b8d..0000000 --- a/includes/dfw-options.php +++ /dev/null @@ -1,177 +0,0 @@ -'; - echo '

DoubleClick for WordPress Options

'; - echo '
'; - - settings_fields( 'doubleclick-for-wordpress' ); - do_settings_sections( 'doubleclick-for-wordpress' ); - submit_button(); - - echo '
'; - echo ''; // div.wrap -} - -/** - * Registers options for the plugin. - * - * @since v0.1 - */ -function dfw_register_options() { - // Add a section for network option - add_settings_section( - 'dfw_network_options', - 'Network Settings', - 'dfw_settings_section_intro', - 'doubleclick-for-wordpress' - ); - - // Add a section for network option - add_settings_section( - 'dfw_breakpoint_options', - 'Breakpoints', - 'dfw_breakpoints_section_intro', - 'doubleclick-for-wordpress' - ); - - // Add a section for network option - add_settings_section( - 'dfw_documentation_options', - 'Documentation', - 'dfw_documentation_section_intro', - 'doubleclick-for-wordpress' - ); - - // Network Code - add_settings_field( - 'dfw_network_code', - 'DoubleClick Network Code', - 'dfw_network_code_input', - 'doubleclick-for-wordpress', - 'dfw_network_options' - ); - - // Breakpoints - add_settings_field( - 'dfw_breakpoints', - 'Breakpoints', - 'dfw_breakpoints_input', - 'doubleclick-for-wordpress', - 'dfw_breakpoint_options' - ); - - register_setting( 'doubleclick-for-wordpress', 'dfw_network_code' ); - register_setting( 'doubleclick-for-wordpress', 'dfw_breakpoints', 'dfw_breakpoints_save' ); - -} -add_action( 'admin_init', 'dfw_register_options' ); - -function dfw_settings_section_intro() { - echo "Enter a DoubleClick for Publisher's Network Code ( ? )"; -} - -function dfw_breakpoints_section_intro() { - echo 'Enter breakpoints below
'; - echo 'Example: phone: 0 to 480, tablet 480 to 768, desktop 768 to 9999.'; -} - -function dfw_documentation_section_intro() { - echo 'Available on GitHub'; -} - -function dfw_network_code_input() { - global $doubleclick; - - if ( isset( $doubleclick->network_code ) ) { - echo ''; - } else { - echo ''; - } -} - -function dfw_breakpoints_input() { - global $doubleclick; - - foreach ( $doubleclick->breakpoints as $breakpoint ) { - if ( ! $breakpoint->option ) { - echo ''; - echo ''; - echo ' (set in theme)
'; - } - } - - $breakpoints = maybe_unserialize( get_option( 'dfw_breakpoints' ) ); - - $i = 0; - while ( $i < 5 ) { - $identifier = ( isset( $breakpoints[ $i ]['identifier'] ) )? $breakpoints[ $i ]['identifier'] : ''; - $min_width = ( isset( $breakpoints[ $i ]['min-width'] ) )? $breakpoints[ $i ]['min-width'] : ''; - $max_width = ( isset( $breakpoints[ $i ]['max-width'] ) )? $breakpoints[ $i ]['max-width'] : ''; ?> - - -
$group[0], - 'min-width' => $group[1], - 'max-width' => $group[2], - ); - } - } - - return $breakpoints; -} From d7d1aac3b0f58ebcbf3779e63e18ffbe40053ffa Mon Sep 17 00:00:00 2001 From: rclations Date: Fri, 23 Jun 2017 17:00:56 -0400 Subject: [PATCH 05/41] migrate widget into new structure --- doubleclick-for-wordpress.php | 2 + includes/class-widget.php | 293 ++++++++++++++++++++++++++++++++++ includes/dfw-widget.php | 162 ------------------- tests/test-widget.php | 36 +++++ 4 files changed, 331 insertions(+), 162 deletions(-) create mode 100644 includes/class-widget.php delete mode 100644 includes/dfw-widget.php create mode 100644 tests/test-widget.php diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index b41b140..77369a5 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -39,6 +39,7 @@ // Include additional php files here. require 'includes/class-options.php'; + require 'includes/class-widget.php'; /** * Main initiation class. @@ -136,6 +137,7 @@ protected function __construct() { public function plugin_classes() { $this->options = new DCWP_Options( $this ); + $this->widget = new DCWP_Widget( $this ); } // END OF PLUGIN CLASSES FUNCTION /** diff --git a/includes/class-widget.php b/includes/class-widget.php new file mode 100644 index 0000000..45a3468 --- /dev/null +++ b/includes/class-widget.php @@ -0,0 +1,293 @@ +widget_name = esc_html__( 'DoubleClick Ad', 'doubleclick-for-wordpress' ); + $this->default_widget_title = esc_html__( 'DoubleClick Ad', 'doubleclick-for-wordpress' ); + + parent::__construct( + $this->widget_slug, + $this->widget_name, + array( + 'classname' => $this->widget_slug, + 'description' => esc_html__( 'Serve ads from DFP.', 'doubleclick-for-wordpress' ), + ) + ); + + // Clear cache on save. + add_action( 'switch_theme', array( $this, 'flush_widget_cache' ) ); + + // Add a shortcode for our widget. + add_shortcode( self::$shortcode, array( __CLASS__, 'get_widget' ) ); + } + + /** + * Delete this widget's cache. + * + * Note: Could also delete any transients + * delete_transient( 'some-transient-generated-by-this-widget' ); + * + * @since 0.2.1 + */ + public function flush_widget_cache() { + wp_cache_delete( $this->widget_slug, 'widget' ); + } + + /** + * Front-end display of widget. + * + * @since 0.2.1 + * + * @param array $args The widget arguments set up when a sidebar is registered. + * @param array $instance The widget settings as set by user. + */ + public function widget( $args, $instance ) { + + // Set widget attributes. + $atts = array( + 'before_widget' => $args['before_widget'], + 'after_widget' => $args['after_widget'], + 'before_title' => $args['before_title'], + 'after_title' => $args['after_title'], + 'title' => $instance['title'], + 'text' => $instance['text'], + ); + + // Display the widget. + echo self::get_widget( $atts ); // WPCS XSS OK. + } + + /** + * Return the widget/shortcode output + * + * @since 0.2.1 + * + * @param array $atts Array of widget/shortcode attributes/args. + * @return string Widget output + */ + public static function get_widget( $atts ) { + + global $doubleclick; + + $defaults = array( + 'before_widget' => '', + 'after_widget' => '', + 'before_title' => '', + 'after_title' => '', + 'title' => '', + 'text' => '', + ); + + // Parse defaults and create a shortcode. + $atts = shortcode_atts( $defaults, (array) $atts, self::$shortcode ); + + // prepare identifier parameter. + $identifier = ! empty( $instance['identifier'] ) ? $instance['identifier'] : 'ident'; + + // prepare size parameter. + $sizes = $instance['sizes']; + if ( ! empty( $sizes ) ) { + foreach ( $sizes as $breakpoint => $size ) { + if ( empty( $sizes[ $breakpoint ] ) ) { + unset( $sizes[ $breakpoint ] ); + } + } + } else { + printf( + '', + esc_html__( 'This DoubleClick for WordPress widget is not appearing because the widget has no sizes set for its breakpoints.', 'dfw' ) + ); + return; + } + + // bugfix: replace $args with $dfw_args to prevent widget interference + // prepare dfw_args parameter. + $dfw_args = null; + if ( $instance['lazyLoad'] ) { + $dfw_args = array( 'lazyLoad' => true ); + } + + // Start an output buffer. + ob_start(); + + // Start widget markup. + echo $atts['before_widget']; // WPCS XSS OK. + + // Maybe display widget title. + echo ( $atts['title'] ) ? $atts['before_title'] . esc_html( $atts['title'] ) . $atts['after_title'] : '' ; // WPCS XSS OK. + + // and finally, place the ad. + $doubleclick->place_ad( $identifier, $sizes, $dfw_args ); + + // End the widget markup. + echo $atts['after_widget']; // WPCS XSS OK. + + // Return the output buffer. + return ob_get_clean(); + + } + + /** + * Update form values as they are saved. + * + * @since 0.2.1 + * + * @param array $new_instance New settings for this instance as input by the user. + * @param array $old_instance Old settings for this instance. + * @return array Settings to save or bool false to cancel saving. + */ + public function update( $new_instance, $old_instance ) { + + // Previously saved values. + $instance = $old_instance; + + // @TODO add sanitization + $instance['identifier'] = ( ! empty( $new_instance['identifier'] ) ) ? strip_tags( $new_instance['identifier'] ) : ''; + $instance['lazyLoad'] = ( ! empty( $new_instance['lazyLoad'] ) ) ? $new_instance['lazyLoad'] : 0 ; + $instance['breakpoints'] = $new_instance['breakpoints']; + $instance['sizes'] = $new_instance['sizes']; + $instance['size'] = $new_instance['size']; + + // Flush cache. + $this->flush_widget_cache(); + + return $instance; + } + + /** + * Back-end widget form with defaults. + * + * @since 0.2.1 + * + * @param array $instance Current settings. + */ + public function form( $instance ) { + global $doubleclick; + + // Set defaults. + $defaults = array( + 'title' => $this->default_widget_title, + 'text' => '', + ); + + // Parse args. + $instance = wp_parse_args( (array) $instance, $defaults ); + + $identifier = ! empty( $instance['identifier'] ) ? $instance['identifier'] : ''; + ?> +

+ + +

+ + breakpoints ) > 0 ) : $i = 0; ?> + +

Size for breakpoints:

+ + breakpoints as $breakpoint ) : ?> +

+
+ +

+ + + +


+ + + +

+
+ +

+ + + +

Lazy Load?

+

+ + >
+

+

+ __( 'Serve ads from DFP.', 'dfw' ) ) // Args - ); - } - - /** - * Front-end display of widget. - * - * @see WP_Widget::widget() - * - * @param array $args Widget arguments. - * @param array $instance Saved values from database. - */ - public function widget( $args, $instance ) { - - global $doubleclick; - - // prepare identifier parameter. - $identifier = ! empty( $instance['identifier'] ) ? $instance['identifier'] : 'ident'; - - // prepare size parameter. - $sizes = $instance['sizes']; - if ( ! empty( $sizes ) ) { - foreach ( $sizes as $breakpoint => $size ) { - if ( empty( $sizes[ $breakpoint ] ) ) { - unset( $sizes[ $breakpoint ] ); - } - } - } else { - printf( - '', - esc_html__( 'This DoubleClick for WordPress widget is not appearing because the widget has no sizes set for its breakpoints.', 'dfw' ) - ); - return; - } - - // bugfix: replace $args with $dfw_args to prevent widget interference - // prepare dfw_args parameter. - $dfw_args = null; - if ( $instance['lazyLoad'] ) { - $dfw_args = array( 'lazyLoad' => true ); - } - - // begin actual widget output - echo wp_kses_post( $args['before_widget'] ); - - // print (optional) title. - if ( ! empty( $instance['title'] ) ) { - echo wp_kses_post( $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'] ); - } - - // and finally, place the ad. - $doubleclick->place_ad( $identifier, $sizes, $dfw_args ); - - echo wp_kses_post( $args['after_widget'] ); - } - - /** - * Back-end widget form. - * - * @see WP_Widget::form() - * - * @param array $instance Previously saved values from database. - */ - public function form( $instance ) { - - global $doubleclick; - - $identifier = ! empty( $instance['identifier'] ) ? $instance['identifier'] : ''; - ?> -

- - -

- - breakpoints ) > 0 ) : $i = 0; ?> - -

Size for breakpoints:

- - breakpoints as $breakpoint ) : ?> -

-
- -

- - - -


- - - -

-
- -

- - - -

Lazy Load?

-

- - >
-

-

- assertTrue( class_exists( 'DCWP_Widget') ); + } + + /** + * Test that we can access our class through our helper function. + * + * @since 0.2.1 + */ + function test_class_access() { + $this->assertInstanceOf( 'DCWP_Widget', doubleclick_for_wordpress()->widget ); + } + + /** + * Replace this with some actual testing code. + * + * @since 0.2.1 + */ + function test_sample() { + $this->assertTrue( true ); + } +} From 82d133bc764bad6e03db122b80d96d0de8223a2f Mon Sep 17 00:00:00 2001 From: aschweigert Date: Mon, 10 Jul 2017 10:00:49 -0400 Subject: [PATCH 06/41] re-run VIP code standards, fix whitespace --- assets/js/jquery.dfp.js | 1282 ++++++++++++++++----------------- doubleclick-for-wordpress.php | 6 +- tests/test-base.php | 4 +- tests/test-doubleclick.php | 2 +- tests/test-widget.php | 2 +- 5 files changed, 647 insertions(+), 649 deletions(-) diff --git a/assets/js/jquery.dfp.js b/assets/js/jquery.dfp.js index be68354..8cc7791 100644 --- a/assets/js/jquery.dfp.js +++ b/assets/js/jquery.dfp.js @@ -7,647 +7,645 @@ */ (function (window, undefined) { - "use strict"; - - /** - * Make plugin UMD compatible - * Uses CommonJS, AMD or browser globals to create plugin. - */ - (function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS - factory(require('jquery')); - } else { - // Browser globals - factory(window.jQuery || window.Zepto); - } - }(function ($) { - - var - - // Save Scope - dfpScript = this || {}; - - var - // DFP account ID - dfpID = '', - - // Init counters - count = 0, - uid = 0, - rendered = 0, - - // Default DFP selector - dfpSelector = '.adunit', - - adsCouldNeverBeInitilized = false, - - // Keep track of if we've already tried to load gpt.js before - dfpIsLoaded = false, - - // Store adunit on div as: - storeAs = 'googleAdUnit', - - /** - * Init function sets required params and loads Google's DFP script - * @param String id The DFP account ID - * @param String selector The adunit selector - * @param Object options Custom options to apply - */ - init = function (id, selector, options) { - var $adCollection; - - // Reset counters on each call - count = 0; - rendered = 0; - - dfpID = id; - $adCollection = $(selector); - - /** - * @returns {boolean} - */ - dfpScript.shouldCheckForAdBlockers = function(){ - return options ? typeof options.afterAdBlocked === 'function' : false; - }; - - // explicitly wait for loader to be completed, otherwise the googletag might not be available - dfpLoader(options, $adCollection).then(function(){ - options = setOptions(options); - dfpScript.dfpOptions = options; - - $(function () { - createAds(options, $adCollection); - displayAds(options, $adCollection); - }); - }); - - }, - - /** - * Set the options for DFP - * @param Object options Custom options to apply - * @return Object extended options - */ - setOptions = function (options) { - - // Set default options - var dfpOptions = { - setTargeting: {}, - setCategoryExclusion: '', - setLocation: '', - enableSingleRequest: true, - collapseEmptyDivs: 'original', - refreshExisting: true, - disablePublisherConsole: false, - disableInitialLoad: false, - setCentering: false, - noFetch: false, - namespace: undefined, - sizeMapping: {} - }; - - if (typeof options.setUrlTargeting === 'undefined' || options.setUrlTargeting) { - // Get URL Targeting - var urlTargeting = getUrlTargeting(options.url); - $.extend(true, dfpOptions.setTargeting, { - UrlHost: urlTargeting.Host, - UrlPath: urlTargeting.Path, - UrlQuery: urlTargeting.Query - }); - } - - // Merge options objects - $.extend(true, dfpOptions, options); - - // If a custom googletag is specified, use it. - if (dfpOptions.googletag) { - window.googletag.cmd.push(function () { - $.extend(true, window.googletag, dfpOptions.googletag); - }); - } - - return dfpOptions; - }, - - /** - * Find and create all Ads - * @param Object dfpOptions options related to ad instantiation - * @param jQuery $adCollection collection of ads - * @return Array an array of ad units that have been created. - */ - createAds = function (dfpOptions, $adCollection) { - var googletag = window.googletag; - // Loops through on page Ad units and gets ads for them. - $adCollection.each(function () { - var $adUnit = $(this); - - count++; - - // adUnit name - var adUnitName = getName($adUnit, dfpOptions); - - // adUnit id - this will use an existing id or an auto generated one. - var adUnitID = getID($adUnit, adUnitName); - - // get dimensions of the adUnit - var dimensions = getDimensions($adUnit); - - // set existing content - $adUnit.data('existingContent', $adUnit.html()); - - // wipe html clean ready for ad and set the default display class. - $adUnit.html('').addClass('display-none'); - - // Push commands to DFP to create ads - googletag.cmd.push(function () { - - var googleAdUnit, - $adUnitData = $adUnit.data(storeAs); - - if ($adUnitData) { - - // Get existing ad unit - googleAdUnit = $adUnitData; - - } else { - - // Build slotName for loading - var slotName; - if (dfpID === '') { - slotName = adUnitName; - } else { - slotName = '/' + dfpID + '/' + adUnitName; - } - - // Create the ad - out of page or normal - if ($adUnit.data('outofpage')) { - googleAdUnit = googletag.defineOutOfPageSlot(slotName, adUnitID); - } else { - googleAdUnit = googletag.defineSlot(slotName, dimensions, adUnitID); - if ($adUnit.data('companion')) { - googleAdUnit = googleAdUnit.addService(googletag.companionAds()); - } - } - - googleAdUnit = googleAdUnit.addService(googletag.pubads()); - - } - - // Sets custom targeting for just THIS ad unit if it has been specified - var targeting = $adUnit.data('targeting'); - if (targeting) { - $.each(targeting, function (k, v) { - googleAdUnit.setTargeting(k, v); - }); - } - - // Sets custom exclusions for just THIS ad unit if it has been specified - var exclusions = $adUnit.data('exclusions'); - if (exclusions) { - var exclusionsGroup = exclusions.split(','); - var valueTrimmed; - $.each(exclusionsGroup, function (k, v) { - valueTrimmed = $.trim(v); - if (valueTrimmed.length > 0) { - googleAdUnit.setCategoryExclusion(valueTrimmed); - } - }); - } - - // Sets responsive size mapping for just THIS ad unit if it has been specified - var mapping = $adUnit.data('size-mapping'); - if (mapping && dfpOptions.sizeMapping[mapping]) { - // Convert verbose to DFP format - var map = googletag.sizeMapping(); - $.each(dfpOptions.sizeMapping[mapping], function (k, v) { - map.addSize(v.browser, v.ad_sizes); - }); - googleAdUnit.defineSizeMapping(map.build()); - } - - // Store googleAdUnit reference - $adUnit.data(storeAs, googleAdUnit); - - // Allow altering of the ad slot before ad load - if (typeof dfpOptions.beforeEachAdLoaded === 'function') { - dfpOptions.beforeEachAdLoaded.call(this, $adUnit); - } - }); - - }); - - // Push DFP config options - googletag.cmd.push(function () { - - var pubadsService = googletag.pubads(); - - if (dfpOptions.enableSingleRequest) { - pubadsService.enableSingleRequest(); - } - - $.each(dfpOptions.setTargeting, function (k, v) { - pubadsService.setTargeting(k, v); - }); - - var setLocation = dfpOptions.setLocation; - if (typeof setLocation === 'object') { - if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number' && - typeof setLocation.precision === 'number') { - pubadsService.setLocation(setLocation.latitude, setLocation.longitude, setLocation.precision); - } else if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number') { - pubadsService.setLocation(setLocation.latitude, setLocation.longitude); - } - } - - if (dfpOptions.setCategoryExclusion.length > 0) { - var exclusionsGroup = dfpOptions.setCategoryExclusion.split(','); - var valueTrimmed; - - $.each(exclusionsGroup, function (k, v) { - valueTrimmed = $.trim(v); - if (valueTrimmed.length > 0) { - pubadsService.setCategoryExclusion(valueTrimmed); - } - }); - } - - if (dfpOptions.collapseEmptyDivs) { - pubadsService.collapseEmptyDivs(); - } - - if (dfpOptions.disablePublisherConsole) { - pubadsService.disablePublisherConsole(); - } - - if (dfpOptions.companionAds) { - googletag.companionAds().setRefreshUnfilledSlots(true); - - if (!dfpOptions.disableInitialLoad) { - pubadsService.enableVideoAds(); - } - } - - if (dfpOptions.disableInitialLoad) { - pubadsService.disableInitialLoad(); - } - - if (dfpOptions.noFetch) { - pubadsService.noFetch(); - } - - if (dfpOptions.setCentering) { - pubadsService.setCentering(true); - } - - // Setup event listener to listen for renderEnded event and fire callbacks. - pubadsService.addEventListener('slotRenderEnded', function (event) { - - rendered++; - - var $adUnit = $('#' + event.slot.getSlotId().getDomId()); - - var display = event.isEmpty ? 'none' : 'block'; - - // if the div has been collapsed but there was existing content expand the - // div and reinsert the existing content. - var $existingContent = $adUnit.data('existingContent'); - if (display === 'none' && $.trim($existingContent).length > 0 && - dfpOptions.collapseEmptyDivs === 'original') { - $adUnit.show().html($existingContent); - display = 'block display-original'; - } - - $adUnit.removeClass('display-none').addClass('display-' + display); - - // Excute afterEachAdLoaded callback if provided - if (typeof dfpOptions.afterEachAdLoaded === 'function') { - dfpOptions.afterEachAdLoaded.call(this, $adUnit, event); - } - - // Excute afterAllAdsLoaded callback if provided - if (typeof dfpOptions.afterAllAdsLoaded === 'function' && rendered === count) { - dfpOptions.afterAllAdsLoaded.call(this, $adCollection); - } - - }); - - // this will work with AdblockPlus - if(dfpScript.shouldCheckForAdBlockers() && !googletag._adBlocked_) { - setTimeout(function () { - var slots = pubadsService.getSlots ? pubadsService.getSlots() : []; - if (slots.length > 0) { - $.get(slots[0].getContentUrl()).always(function (r) { - if (r.status !== 200) { - $.each(slots, function () { - var $adUnit = $('#' + this.getSlotId().getDomId()); - dfpOptions.afterAdBlocked.call(dfpScript, $adUnit, this); - }); - } - }); - } - }, 0); - } - - googletag.enableServices(); - - }); - - }, - - /** - * Display all created Ads - * @param {Object} dfpOptions options related to ad instantiation - * @param {jQuery} $adCollection collection of ads - */ - displayAds = function (dfpOptions, $adCollection) { - - var googletag = window.googletag; - // Check if google adLoader can be loaded, this will work with AdBlock - if(dfpScript.shouldCheckForAdBlockers() && !googletag._adBlocked_) { - if (googletag.getVersion) { - var script = '//partner.googleadservices.com/gpt/pubads_impl_' + - googletag.getVersion() + '.js'; - $.getScript(script).always(function (r) { - if (r && r.statusText === 'error') { - $.each($adCollection, function () { - dfpOptions.afterAdBlocked.call(dfpScript, $(this)); - }); - } - }); - } - - } - - $adCollection.each(function () { - - var $adUnit = $(this), - $adUnitData = $adUnit.data(storeAs); - - if (googletag._adBlocked_) { - if(dfpScript.shouldCheckForAdBlockers()) { - dfpOptions.afterAdBlocked.call(dfpScript, $adUnit); - } - } - if (dfpOptions.refreshExisting && $adUnitData && $adUnit.hasClass('display-block')) { - - googletag.cmd.push(function () { googletag.pubads().refresh([$adUnitData]); }); - - } else { - googletag.cmd.push(function () { googletag.display($adUnit.attr('id')); }); - } - - }); - - }, - - /** - * Create an array of paths so that we can target DFP ads to Page URI's - * @return Array an array of URL parts that can be targeted. - */ - getUrlTargeting = function (url) { - - // Get the url and parse it to its component parts using regex from RFC2396 Appendix-B (https://tools.ietf.org/html/rfc2396#appendix-B) - var urlMatches = (url || window.location.toString()).match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/); - var matchedAuthority = urlMatches[4] || ''; - var matchedPath = (urlMatches[5] || '').replace(/(.)\/$/, '$1'); - var matchedQuery = urlMatches[7] || ''; - - // Get the query params for targeting against - var params = matchedQuery.replace(/\=/ig, ':').split('&'); - - return { - Host: matchedAuthority, - Path: matchedPath, - Query: params - }; - - }, - - /** - * Get the id of the adUnit div or generate a unique one. - * @param Object $adUnit The adunit to work with - * @param String adUnitName The name of the adunit - * @return String The ID of the adunit or a unique autogenerated ID - */ - getID = function ($adUnit, adUnitName) { - - uid++; - return $adUnit.attr('id') || $adUnit.attr('id', adUnitName.replace(/[^A-z0-9]/g, '_') + '-auto-gen-id-' + uid).attr('id'); - - }, - - /** - * Get the name of the Ad unit, either use the div id or - * check for the optional attribute data-adunit - * @param Object $adUnit The adunit to work with - * @param Object dfpOptions options related to ad instantiation - * @return String The name of the adunit, will be the same as inside DFP - */ - getName = function ($adUnit, dfpOptions) { - - var adUnitName = $adUnit.data('adunit') || dfpOptions.namespace || $adUnit.attr('id') || ''; - if (typeof dfpOptions.alterAdUnitName === 'function') { - adUnitName = dfpOptions.alterAdUnitName.call(this, adUnitName, $adUnit); - } - return adUnitName; - - }, - - /** - * Get the dimensions of the ad unit using the container div dimensions or - * check for the optional attribute data-dimensions - * @param Object $adUnit The adunit to work with - * @return Array The dimensions of the adunit (width, height) - */ - getDimensions = function ($adUnit) { - - var dimensions = [], - dimensionsData = $adUnit.data('dimensions'); - - // Check if data-dimensions are specified. If they aren't, use the dimensions of the ad unit div. - if (dimensionsData) { - - var dimensionGroups = dimensionsData.split(','); - - $.each(dimensionGroups, function (k, v) { - - var dimensionSet = v.split('x'); - dimensions.push([parseInt(dimensionSet[0], 10), parseInt(dimensionSet[1], 10)]); - - }); - - } else { - - dimensions.push([$adUnit.width(), $adUnit.height()]); - - } - - return dimensions; - - }, - - /** - * Call the google DFP script - there is a little bit of error detection in here to detect - * if the dfp script has failed to load either through an error or it being blocked by an ad - * blocker... if it does not load we execute a dummy script to replace the real DFP. - * - * @param {Object} options - * @param {Array} $adCollection - */ - dfpLoader = function (options, $adCollection) { - - function execBlockEvents() { - if(dfpScript.shouldCheckForAdBlockers()) { - $.each($adCollection, function () { - options.afterAdBlocked.call(dfpScript, $(this)); - }); - } - } - - // make sure we don't load gpt.js multiple times - dfpIsLoaded = dfpIsLoaded || $('script[src*="googletagservices.com/tag/js/gpt.js"]').length; - if (dfpIsLoaded) { - if(adsCouldNeverBeInitilized) { - execBlockEvents(); - } - return $.Deferred().resolve(); - } - - var loaded = $.Deferred(); - - window.googletag = window.googletag || {}; - window.googletag.cmd = window.googletag.cmd || []; - - var gads = document.createElement('script'); - gads.async = true; - gads.type = 'text/javascript'; - - // Adblock blocks the load of Ad scripts... so we check for that - gads.onerror = function () { - dfpBlocked(); - loaded.resolve(); - adsCouldNeverBeInitilized = true; - execBlockEvents(); - }; - - gads.onload = function() { - // this will work with ghostery: - if (!googletag._loadStarted_) { - googletag._adBlocked_ = true; - execBlockEvents(); - } - loaded.resolve(); - }; - - var useSSL = 'https:' === document.location.protocol; - gads.src = (useSSL ? 'https:' : 'http:') + - '//www.googletagservices.com/tag/js/gpt.js'; - var node = document.getElementsByTagName('script')[0]; - node.parentNode.insertBefore(gads, node); - - // Adblock plus seems to hide blocked scripts... so we check for that - if (gads.style.display === 'none') { - dfpBlocked(); - } - - return loaded; - - }, - - /** - * This function gets called if DFP has been blocked by an adblocker - * it implements a dummy version of the dfp object and allows the script to excute its callbacks - * regardless of whether DFP is actually loaded or not... it is basically only useful for situations - * where you are laying DFP over existing content and need to init things like slide shows after the loading - * is completed. - */ - dfpBlocked = function () { - var googletag = window.googletag; - // Get the stored dfp commands - var commands = googletag.cmd; - - var _defineSlot = function (name, dimensions, id, oop) { - googletag.ads.push(id); - googletag.ads[id] = { - renderEnded: function () { }, - addService: function () { return this; } - }; - - return googletag.ads[id]; - }; - - // overwrite the dfp object - replacing the command array with a function and defining missing functions - googletag = { - cmd: { - push: function (callback) { - callback.call(dfpScript); - } - }, - ads: [], - pubads: function () { return this; }, - noFetch: function () { return this; }, - disableInitialLoad: function () { return this; }, - disablePublisherConsole: function () { return this; }, - enableSingleRequest: function () { return this; }, - setTargeting: function () { return this; }, - collapseEmptyDivs: function () { return this; }, - enableServices: function () { return this; }, - defineSlot: function (name, dimensions, id) { - return _defineSlot(name, dimensions, id, false); - }, - defineOutOfPageSlot: function (name, id) { - return _defineSlot(name, [], id, true); - }, - display: function (id) { - googletag.ads[id].renderEnded.call(dfpScript); - return this; - } - - }; - - // Execute any stored commands - $.each(commands, function (k, v) { - googletag.cmd.push(v); - }); - - - }; - - - /** - * Add function to the jQuery / Zepto namespace - * @param String id (Optional) The DFP account ID - * @param Object options (Optional) Custom options to apply - */ - $.dfp = $.fn.dfp = function (id, options) { - - options = options || {}; - - if (id === undefined) { - id = dfpID; - } - - if (typeof id === 'object') { - options = id; - id = options.dfpID || dfpID; - } - - var selector = this; - - if (typeof this === 'function') { - selector = dfpSelector; - } - - init(id, selector, options); - - return this; - - }; - - })); + "use strict"; + + /** + * Make plugin UMD compatible + * Uses CommonJS, AMD or browser globals to create plugin. + */ + (function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define( ['jquery'], factory ); + } else if (typeof exports === 'object') { + // Node/CommonJS + factory( require( 'jquery' ) ); + } else { + // Browser globals + factory( window.jQuery || window.Zepto ); + } + }(function ($) { + + var + + // Save Scope + dfpScript = this || {}; + + var + // DFP account ID + dfpID = '', + + // Init counters + count = 0, + uid = 0, + rendered = 0, + + // Default DFP selector + dfpSelector = '.adunit', + + adsCouldNeverBeInitilized = false, + + // Keep track of if we've already tried to load gpt.js before + dfpIsLoaded = false, + + // Store adunit on div as: + storeAs = 'googleAdUnit', + + /** + * Init function sets required params and loads Google's DFP script + * @param String id The DFP account ID + * @param String selector The adunit selector + * @param Object options Custom options to apply + */ + init = function (id, selector, options) { + var $adCollection; + + // Reset counters on each call + count = 0; + rendered = 0; + + dfpID = id; + $adCollection = $( selector ); + + /** + * @returns {boolean} + */ + dfpScript.shouldCheckForAdBlockers = function(){ + return options ? typeof options.afterAdBlocked === 'function' : false; + }; + + // explicitly wait for loader to be completed, otherwise the googletag might not be available + dfpLoader( options, $adCollection ).then(function(){ + options = setOptions( options ); + dfpScript.dfpOptions = options; + + $(function () { + createAds( options, $adCollection ); + displayAds( options, $adCollection ); + }); + }); + + }, + + /** + * Set the options for DFP + * @param Object options Custom options to apply + * @return Object extended options + */ + setOptions = function (options) { + + // Set default options + var dfpOptions = { + setTargeting: {}, + setCategoryExclusion: '', + setLocation: '', + enableSingleRequest: true, + collapseEmptyDivs: 'original', + refreshExisting: true, + disablePublisherConsole: false, + disableInitialLoad: false, + setCentering: false, + noFetch: false, + namespace: undefined, + sizeMapping: {} + }; + + if (typeof options.setUrlTargeting === 'undefined' || options.setUrlTargeting) { + // Get URL Targeting + var urlTargeting = getUrlTargeting( options.url ); + $.extend(true, dfpOptions.setTargeting, { + UrlHost: urlTargeting.Host, + UrlPath: urlTargeting.Path, + UrlQuery: urlTargeting.Query + }); + } + + // Merge options objects + $.extend( true, dfpOptions, options ); + + // If a custom googletag is specified, use it. + if (dfpOptions.googletag) { + window.googletag.cmd.push(function () { + $.extend( true, window.googletag, dfpOptions.googletag ); + }); + } + + return dfpOptions; + }, + + /** + * Find and create all Ads + * @param Object dfpOptions options related to ad instantiation + * @param jQuery $adCollection collection of ads + * @return Array an array of ad units that have been created. + */ + createAds = function (dfpOptions, $adCollection) { + var googletag = window.googletag; + // Loops through on page Ad units and gets ads for them. + $adCollection.each(function () { + var $adUnit = $( this ); + + count++; + + // adUnit name + var adUnitName = getName( $adUnit, dfpOptions ); + + // adUnit id - this will use an existing id or an auto generated one. + var adUnitID = getID( $adUnit, adUnitName ); + + // get dimensions of the adUnit + var dimensions = getDimensions( $adUnit ); + + // set existing content + $adUnit.data( 'existingContent', $adUnit.html() ); + + // wipe html clean ready for ad and set the default display class. + $adUnit.html( '' ).addClass( 'display-none' ); + + // Push commands to DFP to create ads + googletag.cmd.push(function () { + + var googleAdUnit, + $adUnitData = $adUnit.data( storeAs ); + + if ($adUnitData) { + + // Get existing ad unit + googleAdUnit = $adUnitData; + + } else { + + // Build slotName for loading + var slotName; + if (dfpID === '') { + slotName = adUnitName; + } else { + slotName = '/' + dfpID + '/' + adUnitName; + } + + // Create the ad - out of page or normal + if ($adUnit.data( 'outofpage' )) { + googleAdUnit = googletag.defineOutOfPageSlot( slotName, adUnitID ); + } else { + googleAdUnit = googletag.defineSlot( slotName, dimensions, adUnitID ); + if ($adUnit.data( 'companion' )) { + googleAdUnit = googleAdUnit.addService( googletag.companionAds() ); + } + } + + googleAdUnit = googleAdUnit.addService( googletag.pubads() ); + + } + + // Sets custom targeting for just THIS ad unit if it has been specified + var targeting = $adUnit.data( 'targeting' ); + if (targeting) { + $.each(targeting, function (k, v) { + googleAdUnit.setTargeting( k, v ); + }); + } + + // Sets custom exclusions for just THIS ad unit if it has been specified + var exclusions = $adUnit.data( 'exclusions' ); + if (exclusions) { + var exclusionsGroup = exclusions.split( ',' ); + var valueTrimmed; + $.each(exclusionsGroup, function (k, v) { + valueTrimmed = $.trim( v ); + if (valueTrimmed.length > 0) { + googleAdUnit.setCategoryExclusion( valueTrimmed ); + } + }); + } + + // Sets responsive size mapping for just THIS ad unit if it has been specified + var mapping = $adUnit.data( 'size-mapping' ); + if (mapping && dfpOptions.sizeMapping[mapping]) { + // Convert verbose to DFP format + var map = googletag.sizeMapping(); + $.each(dfpOptions.sizeMapping[mapping], function (k, v) { + map.addSize( v.browser, v.ad_sizes ); + }); + googleAdUnit.defineSizeMapping( map.build() ); + } + + // Store googleAdUnit reference + $adUnit.data( storeAs, googleAdUnit ); + + // Allow altering of the ad slot before ad load + if (typeof dfpOptions.beforeEachAdLoaded === 'function') { + dfpOptions.beforeEachAdLoaded.call( this, $adUnit ); + } + }); + + }); + + // Push DFP config options + googletag.cmd.push(function () { + + var pubadsService = googletag.pubads(); + + if (dfpOptions.enableSingleRequest) { + pubadsService.enableSingleRequest(); + } + + $.each(dfpOptions.setTargeting, function (k, v) { + pubadsService.setTargeting( k, v ); + }); + + var setLocation = dfpOptions.setLocation; + if (typeof setLocation === 'object') { + if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number' && + typeof setLocation.precision === 'number') { + pubadsService.setLocation( setLocation.latitude, setLocation.longitude, setLocation.precision ); + } else if (typeof setLocation.latitude === 'number' && typeof setLocation.longitude === 'number') { + pubadsService.setLocation( setLocation.latitude, setLocation.longitude ); + } + } + + if (dfpOptions.setCategoryExclusion.length > 0) { + var exclusionsGroup = dfpOptions.setCategoryExclusion.split( ',' ); + var valueTrimmed; + + $.each(exclusionsGroup, function (k, v) { + valueTrimmed = $.trim( v ); + if (valueTrimmed.length > 0) { + pubadsService.setCategoryExclusion( valueTrimmed ); + } + }); + } + + if (dfpOptions.collapseEmptyDivs) { + pubadsService.collapseEmptyDivs(); + } + + if (dfpOptions.disablePublisherConsole) { + pubadsService.disablePublisherConsole(); + } + + if (dfpOptions.companionAds) { + googletag.companionAds().setRefreshUnfilledSlots( true ); + + if ( ! dfpOptions.disableInitialLoad) { + pubadsService.enableVideoAds(); + } + } + + if (dfpOptions.disableInitialLoad) { + pubadsService.disableInitialLoad(); + } + + if (dfpOptions.noFetch) { + pubadsService.noFetch(); + } + + if (dfpOptions.setCentering) { + pubadsService.setCentering( true ); + } + + // Setup event listener to listen for renderEnded event and fire callbacks. + pubadsService.addEventListener('slotRenderEnded', function (event) { + + rendered++; + + var $adUnit = $( '#' + event.slot.getSlotId().getDomId() ); + + var display = event.isEmpty ? 'none' : 'block'; + + // if the div has been collapsed but there was existing content expand the + // div and reinsert the existing content. + var $existingContent = $adUnit.data( 'existingContent' ); + if (display === 'none' && $.trim( $existingContent ).length > 0 && + dfpOptions.collapseEmptyDivs === 'original') { + $adUnit.show().html( $existingContent ); + display = 'block display-original'; + } + + $adUnit.removeClass( 'display-none' ).addClass( 'display-' + display ); + + // Excute afterEachAdLoaded callback if provided + if (typeof dfpOptions.afterEachAdLoaded === 'function') { + dfpOptions.afterEachAdLoaded.call( this, $adUnit, event ); + } + + // Excute afterAllAdsLoaded callback if provided + if (typeof dfpOptions.afterAllAdsLoaded === 'function' && rendered === count) { + dfpOptions.afterAllAdsLoaded.call( this, $adCollection ); + } + + }); + + // this will work with AdblockPlus + if (dfpScript.shouldCheckForAdBlockers() && ! googletag._adBlocked_) { + setTimeout(function () { + var slots = pubadsService.getSlots ? pubadsService.getSlots() : []; + if (slots.length > 0) { + $.get( slots[0].getContentUrl() ).always(function (r) { + if (r.status !== 200) { + $.each(slots, function () { + var $adUnit = $( '#' + this.getSlotId().getDomId() ); + dfpOptions.afterAdBlocked.call( dfpScript, $adUnit, this ); + }); + } + }); + } + }, 0); + } + + googletag.enableServices(); + + }); + + }, + + /** + * Display all created Ads + * @param {Object} dfpOptions options related to ad instantiation + * @param {jQuery} $adCollection collection of ads + */ + displayAds = function (dfpOptions, $adCollection) { + + var googletag = window.googletag; + // Check if google adLoader can be loaded, this will work with AdBlock + if (dfpScript.shouldCheckForAdBlockers() && ! googletag._adBlocked_) { + if (googletag.getVersion) { + var script = '//partner.googleadservices.com/gpt/pubads_impl_' + + googletag.getVersion() + '.js'; + $.getScript( script ).always(function (r) { + if (r && r.statusText === 'error') { + $.each($adCollection, function () { + dfpOptions.afterAdBlocked.call( dfpScript, $( this ) ); + }); + } + }); + } + + } + + $adCollection.each(function () { + + var $adUnit = $( this ), + $adUnitData = $adUnit.data( storeAs ); + + if (googletag._adBlocked_) { + if (dfpScript.shouldCheckForAdBlockers()) { + dfpOptions.afterAdBlocked.call( dfpScript, $adUnit ); + } + } + if (dfpOptions.refreshExisting && $adUnitData && $adUnit.hasClass( 'display-block' )) { + + googletag.cmd.push( function () { googletag.pubads().refresh( [$adUnitData] ); } ); + + } else { + googletag.cmd.push( function () { googletag.display( $adUnit.attr( 'id' ) ); } ); + } + + }); + + }, + + /** + * Create an array of paths so that we can target DFP ads to Page URI's + * @return Array an array of URL parts that can be targeted. + */ + getUrlTargeting = function (url) { + + // Get the url and parse it to its component parts using regex from RFC2396 Appendix-B (https://tools.ietf.org/html/rfc2396#appendix-B) + var urlMatches = (url || window.location.toString()).match( / ^(([^: / ?#] + ):)?(\ / \ / ([^ / ?#] * ))?([^?#] * )(\?([^#] * ))?(#( .* ))? / ); + var matchedAuthority = urlMatches[4] || ''; + var matchedPath = (urlMatches[5] || '').replace( /(.)\/$/, '$1' ); + var matchedQuery = urlMatches[7] || ''; + + // Get the query params for targeting against + var params = matchedQuery.replace( /\=/ig, ':' ).split( '&' ); + + return { + Host: matchedAuthority, + Path: matchedPath, + Query: params + }; + + }, + + /** + * Get the id of the adUnit div or generate a unique one. + * @param Object $adUnit The adunit to work with + * @param String adUnitName The name of the adunit + * @return String The ID of the adunit or a unique autogenerated ID + */ + getID = function ($adUnit, adUnitName) { + + uid++; + return $adUnit.attr( 'id' ) || $adUnit.attr( 'id', adUnitName.replace( /[^A-z0-9]/g, '_' ) + '-auto-gen-id-' + uid ).attr( 'id' ); + + }, + + /** + * Get the name of the Ad unit, either use the div id or + * check for the optional attribute data-adunit + * @param Object $adUnit The adunit to work with + * @param Object dfpOptions options related to ad instantiation + * @return String The name of the adunit, will be the same as inside DFP + */ + getName = function ($adUnit, dfpOptions) { + + var adUnitName = $adUnit.data( 'adunit' ) || dfpOptions.namespace || $adUnit.attr( 'id' ) || ''; + if (typeof dfpOptions.alterAdUnitName === 'function') { + adUnitName = dfpOptions.alterAdUnitName.call( this, adUnitName, $adUnit ); + } + return adUnitName; + + }, + + /** + * Get the dimensions of the ad unit using the container div dimensions or + * check for the optional attribute data-dimensions + * @param Object $adUnit The adunit to work with + * @return Array The dimensions of the adunit (width, height) + */ + getDimensions = function ($adUnit) { + + var dimensions = [], + dimensionsData = $adUnit.data( 'dimensions' ); + + // Check if data-dimensions are specified. If they aren't, use the dimensions of the ad unit div. + if (dimensionsData) { + + var dimensionGroups = dimensionsData.split( ',' ); + + $.each(dimensionGroups, function (k, v) { + + var dimensionSet = v.split( 'x' ); + dimensions.push( [parseInt( dimensionSet[0], 10 ), parseInt( dimensionSet[1], 10 )] ); + + }); + + } else { + + dimensions.push( [$adUnit.width(), $adUnit.height()] ); + + } + + return dimensions; + + }, + + /** + * Call the google DFP script - there is a little bit of error detection in here to detect + * if the dfp script has failed to load either through an error or it being blocked by an ad + * blocker... if it does not load we execute a dummy script to replace the real DFP. + * + * @param {Object} options + * @param {Array} $adCollection + */ + dfpLoader = function (options, $adCollection) { + + function execBlockEvents() { + if (dfpScript.shouldCheckForAdBlockers()) { + $.each($adCollection, function () { + options.afterAdBlocked.call( dfpScript, $( this ) ); + }); + } + } + + // make sure we don't load gpt.js multiple times + dfpIsLoaded = dfpIsLoaded || $( 'script[src*="googletagservices.com/tag/js/gpt.js"]' ).length; + if (dfpIsLoaded) { + if (adsCouldNeverBeInitilized) { + execBlockEvents(); + } + return $.Deferred().resolve(); + } + + var loaded = $.Deferred(); + + window.googletag = window.googletag || {}; + window.googletag.cmd = window.googletag.cmd || []; + + var gads = document.createElement( 'script' ); + gads.async = true; + gads.type = 'text/javascript'; + + // Adblock blocks the load of Ad scripts... so we check for that + gads.onerror = function () { + dfpBlocked(); + loaded.resolve(); + adsCouldNeverBeInitilized = true; + execBlockEvents(); + }; + + gads.onload = function() { + // this will work with ghostery: + if ( ! googletag._loadStarted_) { + googletag._adBlocked_ = true; + execBlockEvents(); + } + loaded.resolve(); + }; + + var useSSL = 'https:' === document.location.protocol; + gads.src = (useSSL ? 'https:' : 'http:') + + '//www.googletagservices.com/tag/js/gpt.js'; + var node = document.getElementsByTagName( 'script' )[0]; + node.parentNode.insertBefore( gads, node ); + + // Adblock plus seems to hide blocked scripts... so we check for that + if (gads.style.display === 'none') { + dfpBlocked(); + } + + return loaded; + + }, + + /** + * This function gets called if DFP has been blocked by an adblocker + * it implements a dummy version of the dfp object and allows the script to excute its callbacks + * regardless of whether DFP is actually loaded or not... it is basically only useful for situations + * where you are laying DFP over existing content and need to init things like slide shows after the loading + * is completed. + */ + dfpBlocked = function () { + var googletag = window.googletag; + // Get the stored dfp commands + var commands = googletag.cmd; + + var _defineSlot = function (name, dimensions, id, oop) { + googletag.ads.push( id ); + googletag.ads[id] = { + renderEnded: function () { }, + addService: function () { return this; } + }; + + return googletag.ads[id]; + }; + + // overwrite the dfp object - replacing the command array with a function and defining missing functions + googletag = { + cmd: { + push: function (callback) { + callback.call( dfpScript ); + } + }, + ads: [], + pubads: function () { return this; }, + noFetch: function () { return this; }, + disableInitialLoad: function () { return this; }, + disablePublisherConsole: function () { return this; }, + enableSingleRequest: function () { return this; }, + setTargeting: function () { return this; }, + collapseEmptyDivs: function () { return this; }, + enableServices: function () { return this; }, + defineSlot: function (name, dimensions, id) { + return _defineSlot( name, dimensions, id, false ); + }, + defineOutOfPageSlot: function (name, id) { + return _defineSlot( name, [], id, true ); + }, + display: function (id) { + googletag.ads[id].renderEnded.call( dfpScript ); + return this; + } + + }; + + // Execute any stored commands + $.each(commands, function (k, v) { + googletag.cmd.push( v ); + }); + + }; + + /** + * Add function to the jQuery / Zepto namespace + * @param String id (Optional) The DFP account ID + * @param Object options (Optional) Custom options to apply + */ + $.dfp = $.fn.dfp = function (id, options) { + + options = options || {}; + + if (id === undefined) { + id = dfpID; + } + + if (typeof id === 'object') { + options = id; + id = options.dfpID || dfpID; + } + + var selector = this; + + if (typeof this === 'function') { + selector = dfpSelector; + } + + init( id, selector, options ); + + return this; + + }; + + })); })(window); diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 77369a5..5aec2b7 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -37,9 +37,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - // Include additional php files here. - require 'includes/class-options.php'; - require 'includes/class-widget.php'; + // Include additional php files here. + require 'includes/class-options.php'; + require 'includes/class-widget.php'; /** * Main initiation class. diff --git a/tests/test-base.php b/tests/test-base.php index b68bd61..da5ecdc 100644 --- a/tests/test-base.php +++ b/tests/test-base.php @@ -13,7 +13,7 @@ class DoubleClick_For_WordPress_Test extends WP_UnitTestCase { * @since 0.2.1 */ function test_class_exists() { - $this->assertTrue( class_exists( 'DoubleClick_For_WordPress') ); + $this->assertTrue( class_exists( 'DoubleClick_For_WordPress' ) ); } /** @@ -22,7 +22,7 @@ function test_class_exists() { * @since 0.2.1 */ function test_get_instance() { - $this->assertInstanceOf( 'DoubleClick_For_WordPress', doubleclick_for_wordpress() ); + $this->assertInstanceOf( 'DoubleClick_For_WordPress', doubleclick_for_wordpress() ); } /** diff --git a/tests/test-doubleclick.php b/tests/test-doubleclick.php index 2277ace..d740470 100644 --- a/tests/test-doubleclick.php +++ b/tests/test-doubleclick.php @@ -13,7 +13,7 @@ class DCWP_Options_Test extends WP_UnitTestCase { * @since 0.2.1 */ function test_class_exists() { - $this->assertTrue( class_exists( 'DCWP_Options') ); + $this->assertTrue( class_exists( 'DCWP_Options' ) ); } /** diff --git a/tests/test-widget.php b/tests/test-widget.php index fee0778..e3c5ab8 100644 --- a/tests/test-widget.php +++ b/tests/test-widget.php @@ -13,7 +13,7 @@ class DCWP_Widget_Test extends WP_UnitTestCase { * @since 0.2.1 */ function test_class_exists() { - $this->assertTrue( class_exists( 'DCWP_Widget') ); + $this->assertTrue( class_exists( 'DCWP_Widget' ) ); } /** From c0af412e3c02eeec2e9c4b00c0bcb9dddea77793 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Mon, 10 Jul 2017 10:21:19 -0400 Subject: [PATCH 07/41] remove spaces in size strings on save --- includes/class-widget.php | 4 ++-- includes/dfw-init.php | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/includes/class-widget.php b/includes/class-widget.php index 45a3468..63b85f3 100644 --- a/includes/class-widget.php +++ b/includes/class-widget.php @@ -201,8 +201,8 @@ public function update( $new_instance, $old_instance ) { $instance['identifier'] = ( ! empty( $new_instance['identifier'] ) ) ? strip_tags( $new_instance['identifier'] ) : ''; $instance['lazyLoad'] = ( ! empty( $new_instance['lazyLoad'] ) ) ? $new_instance['lazyLoad'] : 0 ; $instance['breakpoints'] = $new_instance['breakpoints']; - $instance['sizes'] = $new_instance['sizes']; - $instance['size'] = $new_instance['size']; + $instance['sizes'] = str_replace( ' ', '', $new_instance['sizes'] ); + $instance['size'] = str_replace( ' ', '', $new_instance['size'] ); // Flush cache. $this->flush_widget_cache(); diff --git a/includes/dfw-init.php b/includes/dfw-init.php index e8aa577..6290c68 100644 --- a/includes/dfw-init.php +++ b/includes/dfw-init.php @@ -419,7 +419,12 @@ public function mapping() { $browser_height = 1; $browser_width = (int) $breakpoint->min_width; - $size_strings = explode( ',', $size ); // eg. 300x250,336x300 + // Remove any extra spaces in the list of sizes. + // (needs to be just a comma-separated list of values) + $size = str_replace( ' ', '', $size ); + + // eg. 300x250,336x300 + $size_strings = explode( ',', $size ); $size_array = array(); foreach ( $size_strings as $size ) { From 0e5d47ae04f0881b25583bd46375356e05b6692f Mon Sep 17 00:00:00 2001 From: aschweigert Date: Mon, 10 Jul 2017 11:17:31 -0400 Subject: [PATCH 08/41] fix broken callback --- includes/class-options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-options.php b/includes/class-options.php index e32a940..533ac97 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -172,7 +172,7 @@ public function register_options() { ); register_setting( 'doubleclick-for-wordpress', 'network_code' ); - register_setting( 'doubleclick-for-wordpress', 'breakpoints', 'breakpoints_save' ); + register_setting( 'doubleclick-for-wordpress', 'breakpoints', array( $this, 'breakpoints_save' ) ); } From f9cb6a75dc6d8eb29a115e5a465a723bd85f2dd9 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Mon, 10 Jul 2017 11:20:14 -0400 Subject: [PATCH 09/41] fix invalid foreach warning when breakpoints are not yet defined --- includes/class-options.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/includes/class-options.php b/includes/class-options.php index 533ac97..aedcfa3 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -202,11 +202,13 @@ public function network_code_input() { public function breakpoints_input() { global $doubleclick; - foreach ( $doubleclick->breakpoints as $breakpoint ) { - if ( ! $breakpoint->option ) { - echo ''; - echo ''; - echo ' (set in theme)
'; + if ( isset( $doubleclick->breakpoints ) ) { + foreach ( $doubleclick->breakpoints as $breakpoint ) { + if ( ! $breakpoint->option ) { + echo ''; + echo ''; + echo ' (set in theme)
'; + } } } From a5f2c26e7cd7608a7c120ad69227b941e5853f83 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Mon, 10 Jul 2017 12:13:40 -0400 Subject: [PATCH 10/41] add some error conditions --- includes/class-options.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/includes/class-options.php b/includes/class-options.php index aedcfa3..74bc790 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -245,11 +245,35 @@ class="small-text" /> } public function breakpoints_save( $value ) { + global $dfw_options_errors; + $dfw_options_errors = new WP_Error; + $breakpoints = array(); $groups = array_chunk( $value, 3 ); foreach ( $groups as $group ) { if ( isset( $group[0] ) && $group[0] ) { + + // Make sure the min is, in fact, smaller than the max. + if ( $group[1] > $group[2] ) { + $dfw_options_errors->add( 'compare', __( 'The max value must be greater than the min.', 'dfw' ) ); + } + + // Compare with previous item in the array and make sure breakpoints don't overlap. + if ( $last = end( $breakpoints ) ) { + if ( $group[2] <= $last['max-width'] ) { + $dfw_options_errors->add( 'overlap', __( 'Breakpoints cannot overlap.', 'dfw' ) ); + } + } + + if ( is_wp_error( $dfw_options_errors ) && 1 < count( $dfw_options_errors->get_error_messages() ) ) { + foreach ( $dfw_options_errors->get_error_messages() as $error ) { + echo $error; + } + continue; // don't add this one to the array to save. + } + + // OK we're good, add it to the array to save. $breakpoints[] = array( 'identifier' => $group[0], 'min-width' => $group[1], From 5c2c119705c081a05a3611a8358d55a3e5f8cc58 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Mon, 10 Jul 2017 12:47:07 -0400 Subject: [PATCH 11/41] add settings link, closes #36 --- doubleclick-for-wordpress.php | 5 ++++- includes/class-options.php | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 77369a5..b19a260 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -192,10 +192,13 @@ public function init() { } // Load translated strings for plugin. - load_plugin_textdomain( 'doubleclick-for-wordpress', false, dirname( $this->basename ) . '/languages/' ); + load_plugin_textdomain( 'dfw', false, dirname( $this->basename ) . '/languages/' ); // Initialize plugin classes. $this->plugin_classes(); + + // Add a settings link to the plugins page. + add_filter( 'plugin_action_links_' . $this->basename, array( 'DCWP_Options', 'add_settings_link' ) ); } /** diff --git a/includes/class-options.php b/includes/class-options.php index e32a940..ce04831 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -63,7 +63,7 @@ public function __construct( $plugin ) { $this->hooks(); // Set our title. - $this->title = esc_attr__( 'DoubleClick', 'doubleclick-for-wordpress' ); + $this->title = esc_attr__( 'DoubleClick', 'dfw' ); } /** @@ -79,6 +79,14 @@ public function hooks() { add_action( 'admin_init', array( $this, 'register_options' ) ); } + public function add_settings_link( $links ) { + error_log( 'foo' ); + $mylinks = array( + '' . __( 'Settings', 'dfw' ) . '', + ); + return array_merge( $links, $mylinks ); + } + /** * Register our setting to WP. * @@ -102,7 +110,6 @@ public function add_options_page() { $this->key, array( $this, 'admin_page_display' ) ); - } /** From ab2c6ef347f3510fbf736e355d09597544979513 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Tue, 11 Jul 2017 21:24:16 -0400 Subject: [PATCH 12/41] remove debugging code --- includes/class-options.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/class-options.php b/includes/class-options.php index 3f1670a..7f9f6ff 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -80,7 +80,6 @@ public function hooks() { } public function add_settings_link( $links ) { - error_log( 'foo' ); $mylinks = array( '' . __( 'Settings', 'dfw' ) . '', ); From 98c6c873b098dcf3c885ba80545489391e0ba825 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Tue, 11 Jul 2017 21:32:21 -0400 Subject: [PATCH 13/41] do error handling more good --- includes/class-options.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/includes/class-options.php b/includes/class-options.php index 7f9f6ff..99c585e 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -251,9 +251,8 @@ class="small-text" /> } public function breakpoints_save( $value ) { - global $dfw_options_errors; - $dfw_options_errors = new WP_Error; - + $message = null; + $type = null; $breakpoints = array(); $groups = array_chunk( $value, 3 ); @@ -262,20 +261,19 @@ public function breakpoints_save( $value ) { // Make sure the min is, in fact, smaller than the max. if ( $group[1] > $group[2] ) { - $dfw_options_errors->add( 'compare', __( 'The max value must be greater than the min.', 'dfw' ) ); + $message = __( 'The max value must be greater than the min.', 'dfw' ); + $type = 'error'; } // Compare with previous item in the array and make sure breakpoints don't overlap. if ( $last = end( $breakpoints ) ) { if ( $group[2] <= $last['max-width'] ) { - $dfw_options_errors->add( 'overlap', __( 'Breakpoints cannot overlap.', 'dfw' ) ); + $message = __( 'Breakpoints cannot overlap.', 'dfw' ); + $type = 'error'; } } - if ( is_wp_error( $dfw_options_errors ) && 1 < count( $dfw_options_errors->get_error_messages() ) ) { - foreach ( $dfw_options_errors->get_error_messages() as $error ) { - echo $error; - } + if ( $message ) { continue; // don't add this one to the array to save. } @@ -288,6 +286,12 @@ public function breakpoints_save( $value ) { } } + if ( ! empty( $breakpoints ) && ! $message ) { + $message = __( 'Breakpoints updated.', 'dfw' ); + $type = 'updated'; + } + add_settings_error( 'breakpoint_save_notice', 'breakpoint_save_notice', $message, $type ); + return $breakpoints; } } From 0756967835184adecae93a2efc66eef437301fe3 Mon Sep 17 00:00:00 2001 From: rclations Date: Tue, 11 Jul 2017 22:05:23 -0400 Subject: [PATCH 14/41] Implementing solution from #24 --- includes/dfw-init.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/includes/dfw-init.php b/includes/dfw-init.php index 6290c68..addb06a 100644 --- a/includes/dfw-init.php +++ b/includes/dfw-init.php @@ -249,7 +249,9 @@ private function targeting() { } if ( is_single() ) { - $cats = get_the_category(); + global $wp_query; + $current_page_id = $wp_query->get_queried_object_id(); + $cats = get_the_category( $current_page_id ); $targeting['Category'] = array(); if ( $cats ) { @@ -260,7 +262,9 @@ private function targeting() { } if ( is_single() ) { - $tags = get_the_tags(); + global $wp_query; + $current_page_id = $wp_query->get_queried_object_id(); + $tags = get_the_tags( $current_page_id ); if ( $tags ) { $targeting['Tag'] = array(); foreach ( $tags as $t ) { From b3f2961679ced169f4a57abd6982c3e116ca423b Mon Sep 17 00:00:00 2001 From: rclations Date: Tue, 11 Jul 2017 22:09:20 -0400 Subject: [PATCH 15/41] little phpcs fixes --- includes/dfw-init.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/includes/dfw-init.php b/includes/dfw-init.php index addb06a..9237b79 100644 --- a/includes/dfw-init.php +++ b/includes/dfw-init.php @@ -1,10 +1,11 @@ network_code ) ? $this->network_code : get_option( 'dfw_network_code','xxxxxx' ); } + /** + * Add DFP script to the footer + */ public function footer_script() { if ( ! $this->debug ) { $mappings = array(); @@ -209,7 +213,7 @@ private function targeting() { $targeting = array(); $targeting['Page'] = array(); - // Homepage + // Homepage. if ( is_home() ) { $targeting['Page'][] = 'home'; } @@ -218,7 +222,7 @@ private function targeting() { $targeting['Page'][] = 'front-page'; } - // Admin + // Admin. if ( is_admin() ) { $targeting['Page'][] = 'admin'; } @@ -227,7 +231,7 @@ private function targeting() { $targeting['Page'][] = 'admin-bar-showing'; } - // Templates + // Templates. if ( is_single() ) { $targeting['Page'][] = 'single'; } @@ -378,9 +382,11 @@ public function __construct( $identifer, $size ) { global $doubleclick; - // doubleclick escapes '/' with '//' for some odd reason. - // currently we don't try to fix this, but could with this line: - // $this->identifier = str_replace('/','//',$identifier); + /** + * doubleclick escapes '/' with '//' for some odd reason. + * currently we don't try to fix this, but could with this line: + * $this->identifier = str_replace('/','//',$identifier); + */ $this->identifier = $identifer; $this->sizes = $size; $this->id = ++ DoubleClick::$count; From 5e6b6cc420e55cf0e9302e73fb5c6b6c340dd05d Mon Sep 17 00:00:00 2001 From: rclations Date: Wed, 12 Jul 2017 12:11:47 -0400 Subject: [PATCH 16/41] add empty boilerplate include to migrate dfw-init.php into --- doubleclick-for-wordpress.php | 11 +++++++++ includes/class-functions.php | 44 +++++++++++++++++++++++++++++++++++ tests/test-functions.php | 36 ++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 includes/class-functions.php create mode 100644 tests/test-functions.php diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 7cf52c9..31b4185 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -38,6 +38,7 @@ */ // Include additional php files here. + require 'includes/class-functions.php'; require 'includes/class-options.php'; require 'includes/class-widget.php'; @@ -104,6 +105,14 @@ final class DoubleClick_For_WordPress { */ protected $options; + /** + * Instance of DCWP_Functions + * + * @since0.2.1 + * @var DCWP_Functions + */ + protected $functions; + /** * Creates or returns an instance of this class. * @@ -138,6 +147,7 @@ public function plugin_classes() { $this->options = new DCWP_Options( $this ); $this->widget = new DCWP_Widget( $this ); + $this->functions = new DCWP_Functions( $this ); } // END OF PLUGIN CLASSES FUNCTION /** @@ -298,6 +308,7 @@ public function __get( $field ) { case 'url': case 'path': case 'doubleclick': + case 'functions': return $this->$field; default: throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field ); diff --git a/includes/class-functions.php b/includes/class-functions.php new file mode 100644 index 0000000..f964626 --- /dev/null +++ b/includes/class-functions.php @@ -0,0 +1,44 @@ +plugin = $plugin; + $this->hooks(); + } + + /** + * Initiate our hooks. + * + * @since 0.2.1 + */ + public function hooks() { + + } +} diff --git a/tests/test-functions.php b/tests/test-functions.php new file mode 100644 index 0000000..ab2599b --- /dev/null +++ b/tests/test-functions.php @@ -0,0 +1,36 @@ +assertTrue( class_exists( 'DCWP_Functions' ) ); + } + + /** + * Test that we can access our class through our helper function. + * + * @since 0.2.1 + */ + function test_class_access() { + $this->assertInstanceOf( 'DCWP_Functions', doubleclick_for_wordpress()->functions ); + } + + /** + * Replace this with some actual testing code. + * + * @since 0.2.1 + */ + function test_sample() { + $this->assertTrue( true ); + } +} From 1003147158c6b61e3587975c8315df8223681c1a Mon Sep 17 00:00:00 2001 From: rclations Date: Wed, 12 Jul 2017 12:16:07 -0400 Subject: [PATCH 17/41] adding classes for breakpoint and ad slot --- doubleclick-for-wordpress.php | 22 ++++++++++++++++++ includes/class-ad-slot.php | 44 +++++++++++++++++++++++++++++++++++ includes/class-breakpoint.php | 44 +++++++++++++++++++++++++++++++++++ tests/test-ad-slot.php | 36 ++++++++++++++++++++++++++++ tests/test-breakpoint.php | 36 ++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+) create mode 100644 includes/class-ad-slot.php create mode 100644 includes/class-breakpoint.php create mode 100644 tests/test-ad-slot.php create mode 100644 tests/test-breakpoint.php diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 31b4185..55d0ec5 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -39,6 +39,8 @@ // Include additional php files here. require 'includes/class-functions.php'; + require 'includes/class-breakpoint.php'; + require 'includes/class-ad-slot.php'; require 'includes/class-options.php'; require 'includes/class-widget.php'; @@ -113,6 +115,22 @@ final class DoubleClick_For_WordPress { */ protected $functions; + /** + * Instance of DCWP_Breakpoint + * + * @since0.2.1 + * @var DCWP_Breakpoint + */ + protected $breakpoint; + + /** + * Instance of DCWP_Ad_Slot + * + * @since0.2.1 + * @var DCWP_Ad_Slot + */ + protected $ad_slot; + /** * Creates or returns an instance of this class. * @@ -148,6 +166,8 @@ public function plugin_classes() { $this->options = new DCWP_Options( $this ); $this->widget = new DCWP_Widget( $this ); $this->functions = new DCWP_Functions( $this ); + $this->breakpoint = new DCWP_Breakpoint( $this ); + $this->ad_slot = new DCWP_Ad_Slot( $this ); } // END OF PLUGIN CLASSES FUNCTION /** @@ -309,6 +329,8 @@ public function __get( $field ) { case 'path': case 'doubleclick': case 'functions': + case 'breakpoint': + case 'ad_slot': return $this->$field; default: throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field ); diff --git a/includes/class-ad-slot.php b/includes/class-ad-slot.php new file mode 100644 index 0000000..62b5eba --- /dev/null +++ b/includes/class-ad-slot.php @@ -0,0 +1,44 @@ +plugin = $plugin; + $this->hooks(); + } + + /** + * Initiate our hooks. + * + * @since 0.2.1 + */ + public function hooks() { + + } +} diff --git a/includes/class-breakpoint.php b/includes/class-breakpoint.php new file mode 100644 index 0000000..9190d29 --- /dev/null +++ b/includes/class-breakpoint.php @@ -0,0 +1,44 @@ +plugin = $plugin; + $this->hooks(); + } + + /** + * Initiate our hooks. + * + * @since 0.2.1 + */ + public function hooks() { + + } +} diff --git a/tests/test-ad-slot.php b/tests/test-ad-slot.php new file mode 100644 index 0000000..a694f2e --- /dev/null +++ b/tests/test-ad-slot.php @@ -0,0 +1,36 @@ +assertTrue( class_exists( 'DCWP_Ad_Slot' ) ); + } + + /** + * Test that we can access our class through our helper function. + * + * @since 0.2.1 + */ + function test_class_access() { + $this->assertInstanceOf( 'DCWP_Ad_Slot', doubleclick_for_wordpress()->ad-slot ); + } + + /** + * Replace this with some actual testing code. + * + * @since 0.2.1 + */ + function test_sample() { + $this->assertTrue( true ); + } +} diff --git a/tests/test-breakpoint.php b/tests/test-breakpoint.php new file mode 100644 index 0000000..b49620d --- /dev/null +++ b/tests/test-breakpoint.php @@ -0,0 +1,36 @@ +assertTrue( class_exists( 'DCWP_Breakpoint' ) ); + } + + /** + * Test that we can access our class through our helper function. + * + * @since 0.2.1 + */ + function test_class_access() { + $this->assertInstanceOf( 'DCWP_Breakpoint', doubleclick_for_wordpress()->breakpoint ); + } + + /** + * Replace this with some actual testing code. + * + * @since 0.2.1 + */ + function test_sample() { + $this->assertTrue( true ); + } +} From 22964a6b4e2227a758260d9f332f74bd811d7036 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 12 Jul 2017 15:22:25 -0400 Subject: [PATCH 18/41] load the other files --- doubleclick-for-wordpress.php | 61 ++++++++++++++--------------------- includes/class-widget.php | 7 ++-- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 55d0ec5..b504dbb 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -37,13 +37,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - // Include additional php files here. - require 'includes/class-functions.php'; - require 'includes/class-breakpoint.php'; - require 'includes/class-ad-slot.php'; - require 'includes/class-options.php'; - require 'includes/class-widget.php'; - /** * Main initiation class. * @@ -107,30 +100,6 @@ final class DoubleClick_For_WordPress { */ protected $options; - /** - * Instance of DCWP_Functions - * - * @since0.2.1 - * @var DCWP_Functions - */ - protected $functions; - - /** - * Instance of DCWP_Breakpoint - * - * @since0.2.1 - * @var DCWP_Breakpoint - */ - protected $breakpoint; - - /** - * Instance of DCWP_Ad_Slot - * - * @since0.2.1 - * @var DCWP_Ad_Slot - */ - protected $ad_slot; - /** * Creates or returns an instance of this class. * @@ -165,9 +134,6 @@ public function plugin_classes() { $this->options = new DCWP_Options( $this ); $this->widget = new DCWP_Widget( $this ); - $this->functions = new DCWP_Functions( $this ); - $this->breakpoint = new DCWP_Breakpoint( $this ); - $this->ad_slot = new DCWP_Ad_Slot( $this ); } // END OF PLUGIN CLASSES FUNCTION /** @@ -224,6 +190,9 @@ public function init() { // Load translated strings for plugin. load_plugin_textdomain( 'dfw', false, dirname( $this->basename ) . '/languages/' ); + // Load the includes. + $this->load_files(); + // Initialize plugin classes. $this->plugin_classes(); @@ -256,6 +225,27 @@ public function check_requirements() { return false; } + /** + * Load all of the stuff in the includes folder. + * + * @since 0.3 + */ + public function load_files() { + $includes = array( + 'includes/class-functions.php', + 'includes/class-breakpoint.php', + 'includes/class-ad-slot.php', + 'includes/class-options.php', + 'includes/class-widget.php', + ); + + foreach ( $includes as $include ) { + if ( 0 === validate_file( $this->path . $include ) ) { + require_once( $this->path . $include ); + } + } + } + /** * Deactivates this plugin, hook this function on admin_init. * @@ -328,9 +318,6 @@ public function __get( $field ) { case 'url': case 'path': case 'doubleclick': - case 'functions': - case 'breakpoint': - case 'ad_slot': return $this->$field; default: throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field ); diff --git a/includes/class-widget.php b/includes/class-widget.php index 63b85f3..eff292b 100644 --- a/includes/class-widget.php +++ b/includes/class-widget.php @@ -156,7 +156,6 @@ public static function get_widget( $atts ) { return; } - // bugfix: replace $args with $dfw_args to prevent widget interference // prepare dfw_args parameter. $dfw_args = null; if ( $instance['lazyLoad'] ) { @@ -167,16 +166,16 @@ public static function get_widget( $atts ) { ob_start(); // Start widget markup. - echo $atts['before_widget']; // WPCS XSS OK. + echo wp_kses_post( $atts['before_widget'] ); // Maybe display widget title. - echo ( $atts['title'] ) ? $atts['before_title'] . esc_html( $atts['title'] ) . $atts['after_title'] : '' ; // WPCS XSS OK. + echo ( $atts['title'] ) ? $atts['before_title'] . esc_html( $atts['title'] ) . $atts['after_title'] : '' ; // and finally, place the ad. $doubleclick->place_ad( $identifier, $sizes, $dfw_args ); // End the widget markup. - echo $atts['after_widget']; // WPCS XSS OK. + echo wp_kses_post( $atts['after_widget'] ); // Return the output buffer. return ob_get_clean(); From d0449d4d854ebc658428a97df74009790e32dd72 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 12 Jul 2017 15:23:46 -0400 Subject: [PATCH 19/41] proper sanitization --- includes/class-widget.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-widget.php b/includes/class-widget.php index eff292b..ae0b343 100644 --- a/includes/class-widget.php +++ b/includes/class-widget.php @@ -169,7 +169,7 @@ public static function get_widget( $atts ) { echo wp_kses_post( $atts['before_widget'] ); // Maybe display widget title. - echo ( $atts['title'] ) ? $atts['before_title'] . esc_html( $atts['title'] ) . $atts['after_title'] : '' ; + echo ( $atts['title'] ) ? wp_kses_post( $atts['before_title'] ) . esc_html( $atts['title'] ) . wp_kses_post( $atts['after_title'] ) : '' ; // and finally, place the ad. $doubleclick->place_ad( $identifier, $sizes, $dfw_args ); From 8e3e77f0d99205347837ced795f12def33e3426e Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 12 Jul 2017 15:29:15 -0400 Subject: [PATCH 20/41] put the ad slot class here --- includes/class-ad-slot.php | 98 ++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/includes/class-ad-slot.php b/includes/class-ad-slot.php index 62b5eba..f59fc08 100644 --- a/includes/class-ad-slot.php +++ b/includes/class-ad-slot.php @@ -6,20 +6,27 @@ * @package DoubleClick_For_WordPress */ -/** - * DoubleClick for WordPress Ad Slot. - * - * @since 0.2.1 - */ class DCWP_Ad_Slot { /** - * Parent plugin class. + * DFP ad code. * - * @since 0.2.1 + * @var String. + */ + public $identifer; + + /** + * Either a string of sizes, or a size mapping. * - * @var DoubleClick_For_WordPress + * @var Array|String */ - protected $plugin = null; + public $sizes; + + /** + * Each ad gets a unique number to identify it. + * + * @var int + */ + public $id; /** * Constructor. @@ -28,17 +35,78 @@ class DCWP_Ad_Slot { * * @param DoubleClick_For_WordPress $plugin Main plugin object. */ - public function __construct( $plugin ) { - $this->plugin = $plugin; - $this->hooks(); + public function __construct( $identifier, $size ) { + global $doubleclick; + /** + * doubleclick escapes '/' with '//' for some odd reason. + * currently we don't try to fix this, but could with this line: + * $this->identifier = str_replace('/','//',$identifier); + */ + $this->identifier = $identifer; + $this->sizes = $size; + $this->id = ++ DoubleClick::$count; + } + + public function breakpoint_identifier() { + return null; } /** - * Initiate our hooks. + * If this ad unit has a size mapping. * - * @since 0.2.1 */ - public function hooks() { + public function has_mapping() { + if ( is_string( $this->sizes ) ) { + return false; + } else { + return true; + } + } + + public function mapping() { + global $doubleclick; + + // Return false if there is no mapping + if ( ! $this->has_mapping() ) { + return false; + } + + $mapping = array(); + + if ( empty( $this->sizes ) ) { + return $mapping; + } + + foreach ( $this->sizes as $breakpoint_identifier => $size ) { + $breakpoint = $doubleclick->breakpoints[ $breakpoint_identifier ]; + + // The minimum browser width/height for this sizemapping. + $browser_height = 1; + $browser_width = (int) $breakpoint->min_width; + + // Remove any extra spaces in the list of sizes. + // (needs to be just a comma-separated list of values) + $size = str_replace( ' ', '', $size ); + + // eg. 300x250,336x300 + $size_strings = explode( ',', $size ); + $size_array = array(); + + foreach ( $size_strings as $size ) { + if ( ! empty( $size ) ) { + $arr = explode( 'x', $size ); // eg. 300x250 + $width = (int) $arr[0]; + $height = (int) $arr[1]; + $size_array[] = array( $width, $height ); + } + } + + $mapping[] = array( + 'browser' => array( $browser_width, $browser_height ), + 'ad_sizes' => $size_array, + ); + } + return $mapping; } } From 905d9789e8a45eba53aa694cded1157c07cfb8d8 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 12 Jul 2017 15:36:13 -0400 Subject: [PATCH 21/41] put the ad-slot class here --- includes/class-ad-slot.php | 5 +-- includes/class-breakpoint.php | 66 ++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/includes/class-ad-slot.php b/includes/class-ad-slot.php index f59fc08..5855663 100644 --- a/includes/class-ad-slot.php +++ b/includes/class-ad-slot.php @@ -6,7 +6,7 @@ * @package DoubleClick_For_WordPress */ -class DCWP_Ad_Slot { +class DoubleClickAdSlot { /** * DFP ad code. * @@ -33,7 +33,8 @@ class DCWP_Ad_Slot { * * @since 0.2.1 * - * @param DoubleClick_For_WordPress $plugin Main plugin object. + * @param string $identifier the identifier for the ad slot + * @param string $size the size of the placement */ public function __construct( $identifier, $size ) { global $doubleclick; diff --git a/includes/class-breakpoint.php b/includes/class-breakpoint.php index 9190d29..cd16222 100644 --- a/includes/class-breakpoint.php +++ b/includes/class-breakpoint.php @@ -6,39 +6,65 @@ * @package DoubleClick_For_WordPress */ -/** - * DoubleClick for WordPress Breakpoint. - * - * @since 0.2.1 - */ -class DCWP_Breakpoint { +class DoubleClickBreakpoint { /** - * Parent plugin class. + * Slug of the breakpoint * - * @since 0.2.1 + * @var string + */ + public $identifier = ''; + + /** + * Minimum width for the breakpoint * - * @var DoubleClick_For_WordPress + * @var integer */ - protected $plugin = null; + public $min_width; /** - * Constructor. + * Maximum width for the breakpoint * - * @since 0.2.1 + * @var integer + */ + public $max_width; + + /** + * Was this breakpoint added by a theme or + * through an option? * - * @param DoubleClick_For_WordPress $plugin Main plugin object. + * @var boolean */ - public function __construct( $plugin ) { - $this->plugin = $plugin; - $this->hooks(); + public $option; + + public function __construct( $identifier, $args = null ) { + if ( isset( $args['min_width'] ) ) { + $this->min_width = $args['min_width']; + } + + if ( isset( $args['max_width'] ) ) { + $this->max_width = $args['max_width']; + } + + if ( isset( $args['_option'] ) && $args['_option'] ) { + $this->option = true; + } + + $this->identifier = $identifier; } /** - * Initiate our hooks. - * - * @since 0.2.1 + * Prints a javascript boolean statement for this breakpoint */ - public function hooks() { + public function js_logic() { + echo esc_js( $this->get_js_logic() ); + } + /** + * Returns a string with the boolean logic for the breakpoint. + * + * @return String boolean logic for breakpoint. + */ + public function get_js_logic() { + return "($this->min_width <= document.documentElement.clientWidth && document.documentElement.clientWidth < $this->max_width)"; } } From 99f7ab7b50402b1f7a462c08a17ccb75caa0b118 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 12 Jul 2017 16:03:14 -0400 Subject: [PATCH 22/41] ok this seems to work --- doubleclick-for-wordpress.php | 6 +- includes/class-ad-slot.php | 2 +- includes/class-breakpoint.php | 2 +- .../{dfw-init.php => class-doubleclick.php} | 218 ++---------------- includes/class-functions.php | 44 ---- 5 files changed, 18 insertions(+), 254 deletions(-) rename includes/{dfw-init.php => class-doubleclick.php} (61%) delete mode 100644 includes/class-functions.php diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index b504dbb..1827fdf 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -131,9 +131,10 @@ protected function __construct() { * @since 0.2.1 */ public function plugin_classes() { - + global $doubleclick; $this->options = new DCWP_Options( $this ); $this->widget = new DCWP_Widget( $this ); + $doubleclick = new DCWP_DoubleClick( $this ); } // END OF PLUGIN CLASSES FUNCTION /** @@ -181,7 +182,6 @@ public function _deactivate() { * @since 0.2.1 */ public function init() { - // Bail early if requirements aren't met. if ( ! $this->check_requirements() ) { return; @@ -232,11 +232,11 @@ public function check_requirements() { */ public function load_files() { $includes = array( - 'includes/class-functions.php', 'includes/class-breakpoint.php', 'includes/class-ad-slot.php', 'includes/class-options.php', 'includes/class-widget.php', + 'includes/class-doubleclick.php', ); foreach ( $includes as $include ) { diff --git a/includes/class-ad-slot.php b/includes/class-ad-slot.php index 5855663..cb83160 100644 --- a/includes/class-ad-slot.php +++ b/includes/class-ad-slot.php @@ -6,7 +6,7 @@ * @package DoubleClick_For_WordPress */ -class DoubleClickAdSlot { +class DCWP_AdSlot { /** * DFP ad code. * diff --git a/includes/class-breakpoint.php b/includes/class-breakpoint.php index cd16222..f518927 100644 --- a/includes/class-breakpoint.php +++ b/includes/class-breakpoint.php @@ -6,7 +6,7 @@ * @package DoubleClick_For_WordPress */ -class DoubleClickBreakpoint { +class DCWP_Breakpoint { /** * Slug of the breakpoint * diff --git a/includes/dfw-init.php b/includes/class-doubleclick.php similarity index 61% rename from includes/dfw-init.php rename to includes/class-doubleclick.php index 9237b79..acf2dc6 100644 --- a/includes/dfw-init.php +++ b/includes/class-doubleclick.php @@ -1,35 +1,25 @@ network_code = $network_code; @@ -122,7 +112,7 @@ public function __construct( $network_code = null ) { * @param string|array $args additional args. */ public function register_breakpoint( $identifier, $args = null ) { - $this->breakpoints[ $identifier ] = new DoubleClickBreakpoint( $identifier, $args ); + $this->breakpoints[ $identifier ] = new DCWP_Breakpoint( $identifier, $args ); } /** @@ -322,7 +312,7 @@ public function get_ad_placement( $identifier, $sizes, $args = null ) { $args = wp_parse_args( $args, $defaults ); - $ad_object = new DoubleClickAdSlot( $identifier, $sizes ); + $ad_object = new DCWP_AdSlot( $identifier, $sizes ); $this->ad_slots[] = $ad_object; // Print the ad tag. @@ -349,185 +339,3 @@ class='$classes' return $ad; } } - - -class DoubleClickAdSlot { - - /** - * DFP ad code. - * - * @var String. - */ - public $identifer; - - /** - * Either a string of sizes, or a size mapping. - * - * @var Array|String - */ - public $sizes; - - /** - * Each ad gets a unique number to identify it. - * - * @var int - */ - public $id; - - /** - * @param String $identifier - * @param Mixed $size - */ - public function __construct( $identifer, $size ) { - - global $doubleclick; - - /** - * doubleclick escapes '/' with '//' for some odd reason. - * currently we don't try to fix this, but could with this line: - * $this->identifier = str_replace('/','//',$identifier); - */ - $this->identifier = $identifer; - $this->sizes = $size; - $this->id = ++ DoubleClick::$count; - } - - public function breakpoint_identifier() { - return null; - } - - /** - * If this ad unit has a size mapping. - * - */ - public function has_mapping() { - if ( is_string( $this->sizes ) ) { - return false; - } else { - return true; - } - } - - public function mapping() { - global $doubleclick; - - // Return false if there is no mapping - if ( ! $this->has_mapping() ) { - return false; - } - - $mapping = array(); - - if ( empty( $this->sizes ) ) { - return $mapping; - } - - foreach ( $this->sizes as $breakpoint_identifier => $size ) { - $breakpoint = $doubleclick->breakpoints[ $breakpoint_identifier ]; - - // The minimum browser width/height for this sizemapping. - $browser_height = 1; - $browser_width = (int) $breakpoint->min_width; - - // Remove any extra spaces in the list of sizes. - // (needs to be just a comma-separated list of values) - $size = str_replace( ' ', '', $size ); - - // eg. 300x250,336x300 - $size_strings = explode( ',', $size ); - $size_array = array(); - - foreach ( $size_strings as $size ) { - if ( ! empty( $size ) ) { - $arr = explode( 'x', $size ); // eg. 300x250 - $width = (int) $arr[0]; - $height = (int) $arr[1]; - $size_array[] = array( $width, $height ); - } - } - - $mapping[] = array( - 'browser' => array( $browser_width, $browser_height ), - 'ad_sizes' => $size_array, - ); - } - - return $mapping; - } -} - -class DoubleClickBreakpoint { - - /** - * Slug of the breakpoint - * - * @var string - */ - public $identifier = ''; - - /** - * Minimum width for the breakpoint - * - * @var integer - */ - public $min_width; - - /** - * Maximum width for the breakpoint - * - * @var integer - */ - public $max_width; - - /** - * Was this breakpoint added by a theme or - * through an option? - * - * @var boolean - */ - public $option; - - public function __construct( $identifier, $args = null ) { - if ( isset( $args['min_width'] ) ) { - $this->min_width = $args['min_width']; - } - - if ( isset( $args['max_width'] ) ) { - $this->max_width = $args['max_width']; - } - - if ( isset( $args['_option'] ) && $args['_option'] ) { - $this->option = true; - } - - $this->identifier = $identifier; - } - - /** - * Prints a javascript boolean statement for this breakpoint - */ - public function js_logic() { - echo esc_js( $this->get_js_logic() ); - } - - /** - * Returns a string with the boolean logic for the breakpoint. - * - * @return String boolean logic for breakpoint. - */ - public function get_js_logic() { - return "($this->min_width <= document.documentElement.clientWidth && document.documentElement.clientWidth < $this->max_width)"; - } - -} - - -/** - * The dfp front end widget. - */ -include plugin_dir_path( __FILE__ ) . '/dfw-widget.php'; - -/** - * Front end options for the widget. - */ -include plugin_dir_path( __FILE__ ) . '/dfw-options.php'; diff --git a/includes/class-functions.php b/includes/class-functions.php deleted file mode 100644 index f964626..0000000 --- a/includes/class-functions.php +++ /dev/null @@ -1,44 +0,0 @@ -plugin = $plugin; - $this->hooks(); - } - - /** - * Initiate our hooks. - * - * @since 0.2.1 - */ - public function hooks() { - - } -} From a714954ce174a31dca9f267792e28cc0497deb6e Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 12 Jul 2017 17:15:19 -0400 Subject: [PATCH 23/41] mostly working? --- doubleclick-for-wordpress.php | 2 +- includes/class-doubleclick.php | 6 +++--- includes/class-options.php | 2 +- includes/class-widget.php | 2 ++ 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 1827fdf..1e31aa7 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -8,7 +8,7 @@ * Author URI: https://labs.inn.org * Donate link: https://labs.inn.org * License: GPLv2 - * Text Domain: doubleclick-for-wordpress + * Text Domain: dfw * Domain Path: /languages * * @link https://labs.inn.org diff --git a/includes/class-doubleclick.php b/includes/class-doubleclick.php index acf2dc6..8babc72 100644 --- a/includes/class-doubleclick.php +++ b/includes/class-doubleclick.php @@ -80,7 +80,7 @@ class DCWP_DoubleClick { */ public function __construct( $plugin, $network_code = null ) { - $this->network_code = $network_code; + $this->network_code = $this->network_code(); // Script enqueue is static because we only ever want to print it once. if ( ! $this::$enqueued ) { @@ -90,7 +90,7 @@ public function __construct( $plugin, $network_code = null ) { add_action( 'wp_print_footer_scripts', array( $this, 'footer_script' ) ); - $breakpoints = maybe_unserialize( get_option( 'dfw_breakpoints' ) ); + $breakpoints = maybe_unserialize( get_option( 'breakpoints' ) ); if ( ! empty( $breakpoints ) ) : foreach ( $breakpoints as $breakpoint ) { @@ -172,7 +172,7 @@ public function enqueue_scripts() { * @return String network code. */ private function network_code() { - return isset( $this->network_code ) ? $this->network_code : get_option( 'dfw_network_code','xxxxxx' ); + return isset( $this->network_code ) ? $this->network_code : get_option( 'network_code','xxxxxx' ); } /** diff --git a/includes/class-options.php b/includes/class-options.php index 99c585e..99220d0 100644 --- a/includes/class-options.php +++ b/includes/class-options.php @@ -132,7 +132,7 @@ public function admin_page_display() { /** * Registers options for the plugin. * - * @since v0.1 + * @since 0.1 */ public function register_options() { // Add a section for network option diff --git a/includes/class-widget.php b/includes/class-widget.php index ae0b343..8db3521 100644 --- a/includes/class-widget.php +++ b/includes/class-widget.php @@ -219,6 +219,8 @@ public function update( $new_instance, $old_instance ) { public function form( $instance ) { global $doubleclick; + var_dump( $doubleclick ); + // Set defaults. $defaults = array( 'title' => $this->default_widget_title, From e923a0a00b2a51d51dd1709b9caebec88d04331f Mon Sep 17 00:00:00 2001 From: aschweigert Date: Thu, 13 Jul 2017 16:50:32 -0400 Subject: [PATCH 24/41] fix most of the js stuff --- Gulpfile.js | 26 ++++---- assets/js/jquery.dfp.min.js | 9 +-- assets/js/jquery.dfw.js | 51 +++++++++++++++ assets/js/jquery.dfw.min.js | 1 + bower.json | 2 +- includes/class-doubleclick.php | 6 +- package.json | 111 +++++++++++++++++---------------- 7 files changed, 127 insertions(+), 79 deletions(-) create mode 100644 assets/js/jquery.dfw.js create mode 100644 assets/js/jquery.dfw.min.js diff --git a/Gulpfile.js b/Gulpfile.js index 608f7f7..541ad7d 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -16,6 +16,7 @@ const neat = require( 'bourbon-neat' ).includePaths; const notify = require( 'gulp-notify' ); const plumber = require( 'gulp-plumber' ); const postcss = require( 'gulp-postcss' ); +const pump = require( 'pump' ); const reload = browserSync.reload; const rename = require( 'gulp-rename' ); const sass = require( 'gulp-sass' ); @@ -36,7 +37,7 @@ const paths = { 'php': [ './*.php', './**/*.php' ], 'sass': 'assets/css/sass/*.scss', 'concat_scripts': 'assets/scripts/concat/*.js', - 'scripts': [ 'assets/scripts/*.js', 'assets/js/*.js', '!assets/scripts/*.min.js' ], + 'scripts': [ 'assets/js/*.js', '!assets/scripts/*.min.js' ], 'sprites': 'assets/images/sprites/*.png' }; @@ -238,7 +239,7 @@ gulp.task( 'concat', () => .pipe( sourcemaps.write() ) // Save project.js - .pipe( gulp.dest( 'assets/scripts' ) ) + .pipe( gulp.dest( 'assets/js' ) ) .pipe( browserSync.stream() ) ); @@ -247,15 +248,16 @@ gulp.task( 'concat', () => * * https://www.npmjs.com/package/gulp-uglify */ -gulp.task( 'uglify', [ 'concat' ], () => - gulp.src( paths.scripts ) - .pipe( rename( {'suffix': '.min'} ) ) - .pipe( plumber( {'errorHandler': handleErrors} ) ) - .pipe( uglify( { - 'mangle': false - } ) ) - .pipe( gulp.dest( 'assets/js' ) ) -); +gulp.task( 'uglify', [ 'concat' ], function (cb) { + pump([ + gulp.src( paths.scripts ), + rename( {'suffix': '.min'} ), + uglify(), + gulp.dest('assets/js') + ], + cb + ); +}); /** * Delete the theme's .pot before we create a new one. @@ -357,4 +359,4 @@ gulp.task( 'scripts', [ 'uglify' ] ); gulp.task( 'styles', [ 'cssnano' ] ); gulp.task( 'sprites', [ 'spritesmith' ] ); gulp.task( 'lint', [ 'sass:lint', 'js:lint' ] ); -gulp.task( 'default', [ 'sprites', 'i18n', 'icons', 'styles', 'scripts', 'imagemin'] ); \ No newline at end of file +gulp.task( 'default', [ 'sprites', 'i18n', 'icons', 'styles', 'scripts', 'imagemin'] ); diff --git a/assets/js/jquery.dfp.min.js b/assets/js/jquery.dfp.min.js index 1cea571..3729771 100644 --- a/assets/js/jquery.dfp.min.js +++ b/assets/js/jquery.dfp.min.js @@ -1,8 +1 @@ -/** - * jQuery DFP v2.4.2 - * http://github.com/coop182/jquery.dfp.js - * - * Copyright 2016 Matt Cooper - * Released under the MIT license - */ -!function(a,b){"use strict";!function(b){"function"==typeof define&&define.amd?define(["jquery"],b):b("object"==typeof exports?require("jquery"):a.jQuery||a.Zepto)}(function(c){var d=this||{},e="",f=0,g=0,h=0,i=".adunit",j=!1,k=!1,l="googleAdUnit",m=function(a,b,g){var i;f=0,h=0,e=a,i=c(b),d.shouldCheckForAdBlockers=function(){return g?"function"==typeof g.afterAdBlocked:!1},u(g,i).then(function(){g=n(g),d.dfpOptions=g,c(function(){o(g,i),p(g,i)})})},n=function(d){var e={setTargeting:{},setCategoryExclusion:"",setLocation:"",enableSingleRequest:!0,collapseEmptyDivs:"original",refreshExisting:!0,disablePublisherConsole:!1,disableInitialLoad:!1,setCentering:!1,noFetch:!1,namespace:b,sizeMapping:{}};if("undefined"==typeof d.setUrlTargeting||d.setUrlTargeting){var f=q(d.url);c.extend(!0,e.setTargeting,{UrlHost:f.Host,UrlPath:f.Path,UrlQuery:f.Query})}return c.extend(!0,e,d),e.googletag&&a.googletag.cmd.push(function(){c.extend(!0,a.googletag,e.googletag)}),e},o=function(b,g){var i=a.googletag;g.each(function(){var a=c(this);f++;var d=s(a,b),g=r(a,d),h=t(a);a.data("existingContent",a.html()),a.html("").addClass("display-none"),i.cmd.push(function(){var f,j=a.data(l);if(j)f=j;else{var k;k=""===e?d:"/"+e+"/"+d,a.data("outofpage")?f=i.defineOutOfPageSlot(k,g):(f=i.defineSlot(k,h,g),a.data("companion")&&(f=f.addService(i.companionAds()))),f=f.addService(i.pubads())}var m=a.data("targeting");m&&c.each(m,function(a,b){f.setTargeting(a,b)});var n=a.data("exclusions");if(n){var o,p=n.split(",");c.each(p,function(a,b){o=c.trim(b),o.length>0&&f.setCategoryExclusion(o)})}var q=a.data("size-mapping");if(q&&b.sizeMapping[q]){var r=i.sizeMapping();c.each(b.sizeMapping[q],function(a,b){r.addSize(b.browser,b.ad_sizes)}),f.defineSizeMapping(r.build())}a.data(l,f),"function"==typeof b.beforeEachAdLoaded&&b.beforeEachAdLoaded.call(this,a)})}),i.cmd.push(function(){var a=i.pubads();b.enableSingleRequest&&a.enableSingleRequest(),c.each(b.setTargeting,function(b,c){a.setTargeting(b,c)});var e=b.setLocation;if("object"==typeof e&&("number"==typeof e.latitude&&"number"==typeof e.longitude&&"number"==typeof e.precision?a.setLocation(e.latitude,e.longitude,e.precision):"number"==typeof e.latitude&&"number"==typeof e.longitude&&a.setLocation(e.latitude,e.longitude)),b.setCategoryExclusion.length>0){var j,k=b.setCategoryExclusion.split(",");c.each(k,function(b,d){j=c.trim(d),j.length>0&&a.setCategoryExclusion(j)})}b.collapseEmptyDivs&&a.collapseEmptyDivs(),b.disablePublisherConsole&&a.disablePublisherConsole(),b.companionAds&&(i.companionAds().setRefreshUnfilledSlots(!0),b.disableInitialLoad||a.enableVideoAds()),b.disableInitialLoad&&a.disableInitialLoad(),b.noFetch&&a.noFetch(),b.setCentering&&a.setCentering(!0),a.addEventListener("slotRenderEnded",function(a){h++;var d=c("#"+a.slot.getSlotId().getDomId()),e=a.isEmpty?"none":"block",i=d.data("existingContent");"none"===e&&c.trim(i).length>0&&"original"===b.collapseEmptyDivs&&(d.show().html(i),e="block display-original"),d.removeClass("display-none").addClass("display-"+e),"function"==typeof b.afterEachAdLoaded&&b.afterEachAdLoaded.call(this,d,a),"function"==typeof b.afterAllAdsLoaded&&h===f&&b.afterAllAdsLoaded.call(this,g)}),d.shouldCheckForAdBlockers()&&!i._adBlocked_&&setTimeout(function(){var e=a.getSlots?a.getSlots():[];e.length>0&&c.get(e[0].getContentUrl()).always(function(a){200!==a.status&&c.each(e,function(){var a=c("#"+this.getSlotId().getDomId());b.afterAdBlocked.call(d,a,this)})})},0),i.enableServices()})},p=function(b,e){var f=a.googletag;if(d.shouldCheckForAdBlockers()&&!f._adBlocked_&&f.getVersion){var g="//partner.googleadservices.com/gpt/pubads_impl_"+f.getVersion()+".js";c.getScript(g).always(function(a){a&&"error"===a.statusText&&c.each(e,function(){b.afterAdBlocked.call(d,c(this))})})}e.each(function(){var a=c(this),e=a.data(l);f._adBlocked_&&d.shouldCheckForAdBlockers()&&b.afterAdBlocked.call(d,a),f.cmd.push(b.refreshExisting&&e&&a.hasClass("display-block")?function(){f.pubads().refresh([e])}:function(){f.display(a.attr("id"))})})},q=function(b){var c=(b||a.location.toString()).match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/),d=c[4]||"",e=(c[5]||"").replace(/(.)\/$/,"$1"),f=c[7]||"",g=f.replace(/\=/gi,":").split("&");return{Host:d,Path:e,Query:g}},r=function(a,b){return g++,a.attr("id")||a.attr("id",b.replace(/[^A-z0-9]/g,"_")+"-auto-gen-id-"+g).attr("id")},s=function(a,b){var c=a.data("adunit")||b.namespace||a.attr("id")||"";return"function"==typeof b.alterAdUnitName&&(c=b.alterAdUnitName.call(this,c,a)),c},t=function(a){var b=[],d=a.data("dimensions");if(d){var e=d.split(",");c.each(e,function(a,c){var d=c.split("x");b.push([parseInt(d[0],10),parseInt(d[1],10)])})}else b.push([a.width(),a.height()]);return b},u=function(b,e){function f(){d.shouldCheckForAdBlockers()&&c.each(e,function(){b.afterAdBlocked.call(d,c(this))})}if(k=k||c('script[src*="googletagservices.com/tag/js/gpt.js"]').length)return j&&f(),c.Deferred().resolve();var g=c.Deferred();a.googletag=a.googletag||{},a.googletag.cmd=a.googletag.cmd||[];var h=document.createElement("script");h.async=!0,h.type="text/javascript",h.onerror=function(){v(),g.resolve(),j=!0,f()},h.onload=function(){googletag._loadStarted_||(googletag._adBlocked_=!0,f()),g.resolve()};var i="https:"===document.location.protocol;h.src=(i?"https:":"http:")+"//www.googletagservices.com/tag/js/gpt.js";var l=document.getElementsByTagName("script")[0];return l.parentNode.insertBefore(h,l),"none"===h.style.display&&v(),g},v=function(){var b=a.googletag,e=b.cmd,f=function(a,c,d,e){return b.ads.push(d),b.ads[d]={renderEnded:function(){},addService:function(){return this}},b.ads[d]};b={cmd:{push:function(a){a.call(d)}},ads:[],pubads:function(){return this},noFetch:function(){return this},disableInitialLoad:function(){return this},disablePublisherConsole:function(){return this},enableSingleRequest:function(){return this},setTargeting:function(){return this},collapseEmptyDivs:function(){return this},enableServices:function(){return this},defineSlot:function(a,b,c){return f(a,b,c,!1)},defineOutOfPageSlot:function(a,b){return f(a,[],b,!0)},display:function(a){return b.ads[a].renderEnded.call(d),this}},c.each(e,function(a,c){b.cmd.push(c)})};c.dfp=c.fn.dfp=function(a,c){c=c||{},a===b&&(a=e),"object"==typeof a&&(c=a,a=c.dfpID||e);var d=this;return"function"==typeof this&&(d=i),m(a,d,c),this}})}(window); \ No newline at end of file +!function(e,t){"use strict";!function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof exports?require("jquery"):e.jQuery||e.Zepto)}(function(n){var o=this||{},a="",i=0,s=0,r=0,l=!1,d=!1,c="googleAdUnit",u=function(e,t,s){var l;i=0,r=0,a=e,l=n(t),o.shouldCheckForAdBlockers=function(){return!!s&&"function"==typeof s.afterAdBlocked},b(s,l).then(function(){s=g(s),o.dfpOptions=s,n(function(){f(s,l),p(s,l)})})},g=function(o){var a={setTargeting:{},setCategoryExclusion:"",setLocation:"",enableSingleRequest:!0,collapseEmptyDivs:"original",refreshExisting:!0,disablePublisherConsole:!1,disableInitialLoad:!1,setCentering:!1,noFetch:!1,namespace:t,sizeMapping:{}};if(void 0===o.setUrlTargeting||o.setUrlTargeting){var i=h(o.url);n.extend(!0,a.setTargeting,{UrlHost:i.Host,UrlPath:i.Path,UrlQuery:i.Query})}return n.extend(!0,a,o),a.googletag&&e.googletag.cmd.push(function(){n.extend(!0,e.googletag,a.googletag)}),a},f=function(t,s){var l=e.googletag;s.each(function(){var e=n(this);i++;var o=m(e,t),s=v(e,o),r=y(e);e.data("existingContent",e.html()),e.html("").addClass("display-none"),l.cmd.push(function(){var i,d=e.data(c);if(d)i=d;else{var u;u=""===a?o:"/"+a+"/"+o,e.data("outofpage")?i=l.defineOutOfPageSlot(u,s):(i=l.defineSlot(u,r,s),e.data("companion")&&(i=i.addService(l.companionAds()))),i=i.addService(l.pubads())}var g=e.data("targeting");g&&n.each(g,function(e,t){i.setTargeting(e,t)});var f=e.data("exclusions");if(f){var p,h=f.split(",");n.each(h,function(e,t){(p=n.trim(t)).length>0&&i.setCategoryExclusion(p)})}var v=e.data("size-mapping");if(v&&t.sizeMapping[v]){var m=l.sizeMapping();n.each(t.sizeMapping[v],function(e,t){m.addSize(t.browser,t.ad_sizes)}),i.defineSizeMapping(m.build())}e.data(c,i),"function"==typeof t.beforeEachAdLoaded&&t.beforeEachAdLoaded.call(this,e)})}),l.cmd.push(function(){var e=l.pubads();t.enableSingleRequest&&e.enableSingleRequest(),n.each(t.setTargeting,function(t,n){e.setTargeting(t,n)});var a=t.setLocation;if("object"==typeof a&&("number"==typeof a.latitude&&"number"==typeof a.longitude&&"number"==typeof a.precision?e.setLocation(a.latitude,a.longitude,a.precision):"number"==typeof a.latitude&&"number"==typeof a.longitude&&e.setLocation(a.latitude,a.longitude)),t.setCategoryExclusion.length>0){var d,c=t.setCategoryExclusion.split(",");n.each(c,function(t,o){(d=n.trim(o)).length>0&&e.setCategoryExclusion(d)})}t.collapseEmptyDivs&&e.collapseEmptyDivs(),t.disablePublisherConsole&&e.disablePublisherConsole(),t.companionAds&&(l.companionAds().setRefreshUnfilledSlots(!0),t.disableInitialLoad||e.enableVideoAds()),t.disableInitialLoad&&e.disableInitialLoad(),t.noFetch&&e.noFetch(),t.setCentering&&e.setCentering(!0),e.addEventListener("slotRenderEnded",function(e){r++;var o=n("#"+e.slot.getSlotId().getDomId()),a=e.isEmpty?"none":"block",l=o.data("existingContent");"none"===a&&n.trim(l).length>0&&"original"===t.collapseEmptyDivs&&(o.show().html(l),a="block display-original"),o.removeClass("display-none").addClass("display-"+a),"function"==typeof t.afterEachAdLoaded&&t.afterEachAdLoaded.call(this,o,e),"function"==typeof t.afterAllAdsLoaded&&r===i&&t.afterAllAdsLoaded.call(this,s)}),o.shouldCheckForAdBlockers()&&!l._adBlocked_&&setTimeout(function(){var a=e.getSlots?e.getSlots():[];a.length>0&&n.get(a[0].getContentUrl()).always(function(e){200!==e.status&&n.each(a,function(){var e=n("#"+this.getSlotId().getDomId());t.afterAdBlocked.call(o,e,this)})})},0),l.enableServices()})},p=function(t,a){var i=e.googletag;if(o.shouldCheckForAdBlockers()&&!i._adBlocked_&&i.getVersion){var s="//partner.googleadservices.com/gpt/pubads_impl_"+i.getVersion()+".js";n.getScript(s).always(function(e){e&&"error"===e.statusText&&n.each(a,function(){t.afterAdBlocked.call(o,n(this))})})}a.each(function(){var e=n(this),a=e.data(c);i._adBlocked_&&o.shouldCheckForAdBlockers()&&t.afterAdBlocked.call(o,e),i.cmd.push(t.refreshExisting&&a&&e.hasClass("display-block")?function(){i.pubads().refresh([a])}:function(){i.display(e.attr("id"))})})},h=function(t){var n=(t||e.location.toString()).match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/);return{Host:n[4]||"",Path:(n[5]||"").replace(/(.)\/$/,"$1"),Query:(n[7]||"").replace(/\=/gi,":").split("&")}},v=function(e,t){return s++,e.attr("id")||e.attr("id",t.replace(/[^A-z0-9]/g,"_")+"-auto-gen-id-"+s).attr("id")},m=function(e,t){var n=e.data("adunit")||t.namespace||e.attr("id")||"";return"function"==typeof t.alterAdUnitName&&(n=t.alterAdUnitName.call(this,n,e)),n},y=function(e){var t=[],o=e.data("dimensions");if(o){var a=o.split(",");n.each(a,function(e,n){var o=n.split("x");t.push([parseInt(o[0],10),parseInt(o[1],10)])})}else t.push([e.width(),e.height()]);return t},b=function(t,a){function i(){o.shouldCheckForAdBlockers()&&n.each(a,function(){t.afterAdBlocked.call(o,n(this))})}if(d=d||n('script[src*="googletagservices.com/tag/js/gpt.js"]').length)return l&&i(),n.Deferred().resolve();var s=n.Deferred();e.googletag=e.googletag||{},e.googletag.cmd=e.googletag.cmd||[];var r=document.createElement("script");r.async=!0,r.type="text/javascript",r.onerror=function(){A(),s.resolve(),l=!0,i()},r.onload=function(){googletag._loadStarted_||(googletag._adBlocked_=!0,i()),s.resolve()};var c="https:"===document.location.protocol;r.src=(c?"https:":"http:")+"//www.googletagservices.com/tag/js/gpt.js";var u=document.getElementsByTagName("script")[0];return u.parentNode.insertBefore(r,u),"none"===r.style.display&&A(),s},A=function(){var t=e.googletag,a=t.cmd,i=function(e,n,o,a){return t.ads.push(o),t.ads[o]={renderEnded:function(){},addService:function(){return this}},t.ads[o]};t={cmd:{push:function(e){e.call(o)}},ads:[],pubads:function(){return this},noFetch:function(){return this},disableInitialLoad:function(){return this},disablePublisherConsole:function(){return this},enableSingleRequest:function(){return this},setTargeting:function(){return this},collapseEmptyDivs:function(){return this},enableServices:function(){return this},defineSlot:function(e,t,n){return i(0,0,n)},defineOutOfPageSlot:function(e,t){return i(0,0,t)},display:function(e){return t.ads[e].renderEnded.call(o),this}},n.each(a,function(e,n){t.cmd.push(n)})};n.dfp=n.fn.dfp=function(e,n){n=n||{},e===t&&(e=a),"object"==typeof e&&(n=e,e=n.dfpID||a);var o=this;return"function"==typeof this&&(o=".adunit"),u(e,o,n),this}})}(window); \ No newline at end of file diff --git a/assets/js/jquery.dfw.js b/assets/js/jquery.dfw.js new file mode 100644 index 0000000..523bd50 --- /dev/null +++ b/assets/js/jquery.dfw.js @@ -0,0 +1,51 @@ +/** + * A few lines of javascript to help the DoubleClick for WordPress plugin. + * Extends jquery.dfw.min.js with some event listeners specific to us. + */ +(function() { + var $ = jQuery; + + // On window scroll check if there are any new ads to load. + var lazyLoad = function() { + // all the lazy load ads on the page. + var lazyLoaders = $( '.dfw-lazy-load:not(.dfw-loaded)' ), + // jQuery object to hold ads that actually need loading. + toLoad = $( [] ), + // window scrolltop. + st = $( document ).scrollTop(), + // window height. + wh = $( window ).height(); + + // determind with lazyLoaders needs toLoad. + lazyLoaders.each(function () { + var top = $( this ).offset().top; + + // how much futher down the page to load + // the ad (so it's at least loading before it's on screen). + var buffer = 200; + if (top < st + wh + buffer) { + toLoad = toLoad.add( $( this ) ); + } + }); + + // if we have some ads, then load 'em! + if (toLoad.length > 0) { + $( toLoad ).dfp({ + "dfpID": dfw.networkCode, + "collapseEmptyDivs": false, + "sizeMapping": dfw.mappings, + "setTargeting": dfw.targeting + }).addClass( 'dfw-loaded' ); + } + }; + + $( document ).ready(function(){ + // Watch for scroll events, and determine whether any ads + // need lazy loading. Wait a few seconds for images and text to render + // and units to reach their "true" depth on the page. + $( window ).on( 'scroll', lazyLoad ); + + // Run lazyLoad once on DOM ready to load any ads in view + lazyLoad(); + }); +})(); diff --git a/assets/js/jquery.dfw.min.js b/assets/js/jquery.dfw.min.js new file mode 100644 index 0000000..6473713 --- /dev/null +++ b/assets/js/jquery.dfw.min.js @@ -0,0 +1 @@ +!function(){var d=jQuery,o=function(){var o=d(".dfw-lazy-load:not(.dfw-loaded)"),n=d([]),t=d(document).scrollTop(),e=d(window).height();o.each(function(){d(this).offset().top0&&d(n).dfp({dfpID:dfw.networkCode,collapseEmptyDivs:!1,sizeMapping:dfw.mappings,setTargeting:dfw.targeting}).addClass("dfw-loaded")};d(document).ready(function(){d(window).on("scroll",o),o()})}(); \ No newline at end of file diff --git a/bower.json b/bower.json index 26cf07e..df0482a 100644 --- a/bower.json +++ b/bower.json @@ -6,7 +6,7 @@ "nerds@inn.org" ], "description": "WordPress plugin for serving DFP ads.", - "main": "dfw.php", + "main": "doubleclick-for-wordpress.php", "moduleType": [ "es6" ], diff --git a/includes/class-doubleclick.php b/includes/class-doubleclick.php index 8babc72..bbf83e0 100644 --- a/includes/class-doubleclick.php +++ b/includes/class-doubleclick.php @@ -80,7 +80,7 @@ class DCWP_DoubleClick { */ public function __construct( $plugin, $network_code = null ) { - $this->network_code = $this->network_code(); + $this->network_code = $network_code; // Script enqueue is static because we only ever want to print it once. if ( ! $this::$enqueued ) { @@ -125,14 +125,14 @@ public function enqueue_scripts() { wp_register_script( 'jquery.dfp.js', - plugins_url( 'js/vendor/jquery.dfp.js/jquery.dfp' . $suffix . '.js', __FILE__ ), + plugins_url( 'assets/js/jquery.dfp' . $suffix . '.js', __FILE__ ), array( 'jquery' ), DFP_VERSION, true ); wp_register_script( 'jquery.dfw.js', - plugins_url( 'js/jquery.dfw.js', __FILE__ ), + plugins_url( 'assets/js/jquery.dfw' . $suffix . '.js', __FILE__ ), array( 'jquery.dfp.js' ), DFP_VERSION, true diff --git a/package.json b/package.json index 905d952..dc27e85 100644 --- a/package.json +++ b/package.json @@ -1,57 +1,58 @@ { - "name": "doubleclick-for-wordpress", - "title": "DoubleClick for WordPress", - "version": "0.2.1", - "description": "Serve DoubleClick ads natively in WordPress. Built to make serving and targeting responsive ads easy.", - "author": { - "name": "innlabs, willhaynes24", - "url": "https://labs.inn.org" - }, - "license": "GPLv2", - "devDependencies": { - "grunt": "latest", - "grunt-contrib-watch": "latest", - "grunt-wp-i18n": "latest", - "grunt-contrib-copy": "latest", - "grunt-text-replace": "latest", - "grunt-contrib-clean": "latest", - "grunt-contrib-compress": "latest", - "grunt-wp-deploy": "latest", - "load-grunt-tasks": "latest", - "autoprefixer": "latest", - "babel-preset-es2015": "latest", - "bourbon": "latest", - "bourbon-neat": "latest", - "browser-sync": "latest", - "css-mqpacker": "latest", - "del": "latest", - "eslint-config-wordpress": "latest", - "glob": "latest", - "gulp": "latest", - "gulp-babel": "latest", - "gulp-cheerio": "latest", - "gulp-concat": "latest", - "gulp-cssnano": "latest", - "gulp-eslint": "latest", - "gulp-imagemin": "latest", - "gulp-notify": "latest", - "gulp-plumber": "latest", - "gulp-postcss": "latest", - "gulp-rename": "latest", - "gulp-sass": "latest", - "gulp-sass-lint": "latest", - "gulp-sort": "latest", - "gulp-sourcemaps": "latest", - "gulp-svgmin": "latest", - "gulp-svgstore": "latest", - "gulp-uglify": "latest", - "gulp-util": "latest", - "gulp-wp-pot": "latest", - "gulp.spritesmith": "latest" - }, - "scripts": { - "test": "dockunit", - "preversion": "npm test", - "version": "grunt version && git add ." - } + "name": "doubleclick-for-wordpress", + "title": "DoubleClick for WordPress", + "version": "0.2.1", + "description": "Serve DoubleClick ads natively in WordPress. Built to make serving and targeting responsive ads easy.", + "author": { + "name": "innlabs, willhaynes24", + "url": "https://labs.inn.org" + }, + "license": "GPLv2", + "devDependencies": { + "autoprefixer": "latest", + "babel-preset-es2015": "latest", + "bourbon": "latest", + "bourbon-neat": "latest", + "browser-sync": "latest", + "css-mqpacker": "latest", + "del": "latest", + "eslint-config-wordpress": "latest", + "glob": "latest", + "pump": "latest", + "grunt": "latest", + "grunt-contrib-clean": "latest", + "grunt-contrib-compress": "latest", + "grunt-contrib-copy": "latest", + "grunt-contrib-watch": "latest", + "grunt-text-replace": "latest", + "grunt-wp-deploy": "latest", + "grunt-wp-i18n": "latest", + "gulp": "^3.9.1", + "gulp-babel": "latest", + "gulp-cheerio": "latest", + "gulp-concat": "latest", + "gulp-cssnano": "latest", + "gulp-eslint": "latest", + "gulp-imagemin": "latest", + "gulp-notify": "latest", + "gulp-plumber": "latest", + "gulp-postcss": "latest", + "gulp-rename": "latest", + "gulp-sass": "latest", + "gulp-sass-lint": "latest", + "gulp-sort": "latest", + "gulp-sourcemaps": "latest", + "gulp-svgmin": "latest", + "gulp-svgstore": "latest", + "gulp-uglify": "latest", + "gulp-util": "latest", + "gulp-wp-pot": "latest", + "gulp.spritesmith": "latest", + "load-grunt-tasks": "latest" + }, + "scripts": { + "test": "dockunit", + "preversion": "npm test", + "version": "grunt version && git add ." + } } From fc20eb47052be6ee3d82fdeb0c8ed4dd4ba92099 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Fri, 14 Jul 2017 13:29:51 -0400 Subject: [PATCH 25/41] actually load the js --- doubleclick-for-wordpress.php | 2 +- includes/class-doubleclick.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php index 1e31aa7..bdcced1 100644 --- a/doubleclick-for-wordpress.php +++ b/doubleclick-for-wordpress.php @@ -50,7 +50,7 @@ final class DoubleClick_For_WordPress { * @var string * @since 0.2.1 */ - const VERSION = '0.2.1'; + const VERSION = '0.3.0'; /** * URL of plugin directory. diff --git a/includes/class-doubleclick.php b/includes/class-doubleclick.php index bbf83e0..e5c0b58 100644 --- a/includes/class-doubleclick.php +++ b/includes/class-doubleclick.php @@ -125,16 +125,16 @@ public function enqueue_scripts() { wp_register_script( 'jquery.dfp.js', - plugins_url( 'assets/js/jquery.dfp' . $suffix . '.js', __FILE__ ), + plugins_url( 'assets/js/jquery.dfp' . $suffix . '.js', dirname( __FILE__ ) ), array( 'jquery' ), - DFP_VERSION, + DoubleClick_For_WordPress::VERSION, true ); wp_register_script( 'jquery.dfw.js', - plugins_url( 'assets/js/jquery.dfw' . $suffix . '.js', __FILE__ ), + plugins_url( 'assets/js/jquery.dfw' . $suffix . '.js', dirname( __FILE__ ) ), array( 'jquery.dfp.js' ), - DFP_VERSION, + DoubleClick_For_WordPress::VERSION, true ); @@ -158,9 +158,9 @@ public function enqueue_scripts() { wp_enqueue_style( 'dfp', - plugins_url( 'css/dfp.css', __FILE__ ), + plugins_url( 'assets/css/dfp.css', dirname( __FILE__ ) ), array(), - DFP_VERSION, + DoubleClick_For_WordPress::VERSION, 'all' ); } From 3f8ff8b32293f27585ae0d3d92476d1cd3f61d7d Mon Sep 17 00:00:00 2001 From: aschweigert Date: Tue, 18 Jul 2017 16:37:21 -0400 Subject: [PATCH 26/41] no abbreviation --- includes/class-doubleclick.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/class-doubleclick.php b/includes/class-doubleclick.php index e5c0b58..ad49b22 100644 --- a/includes/class-doubleclick.php +++ b/includes/class-doubleclick.php @@ -249,8 +249,8 @@ private function targeting() { $targeting['Category'] = array(); if ( $cats ) { - foreach ( $cats as $c ) { - $targeting['Category'][] = $c->slug; + foreach ( $cats as $cat ) { + $targeting['Category'][] = $cat->slug; } } } @@ -261,8 +261,8 @@ private function targeting() { $tags = get_the_tags( $current_page_id ); if ( $tags ) { $targeting['Tag'] = array(); - foreach ( $tags as $t ) { - $targeting['Tag'][] = $t->slug; + foreach ( $tags as $tag ) { + $targeting['Tag'][] = $tag->slug; } } } From 0edef92eceee4ac2517a5356edcff03ce5d79540 Mon Sep 17 00:00:00 2001 From: aschweigert Date: Wed, 19 Jul 2017 11:15:33 -0400 Subject: [PATCH 27/41] use wp_json_encode here instead of esc_js --- includes/class-doubleclick.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-doubleclick.php b/includes/class-doubleclick.php index ad49b22..263c776 100644 --- a/includes/class-doubleclick.php +++ b/includes/class-doubleclick.php @@ -188,7 +188,7 @@ public function footer_script() { } ?>