diff --git a/.bowerrc b/.bowerrc index 107d1e8..0eac082 100644 --- a/.bowerrc +++ b/.bowerrc @@ -1,3 +1,3 @@ { - "directory": "js/vendor" -} + "directory": "assets/bower" +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8a9fde0 --- /dev/null +++ b/.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/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..c1e5082 --- /dev/null +++ b/.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/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..2d2951e --- /dev/null +++ b/.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/.gitignore b/.gitignore index 8006366..ddbb21f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,80 @@ -site/* -release/* -js/vendor/* +# 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/.sass-lint.yml b/.sass-lint.yml new file mode 100644 index 0000000..c1fc5f3 --- /dev/null +++ b/.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/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cbef1ee --- /dev/null +++ b/.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/.yo-rc.json b/.yo-rc.json new file mode 100644 index 0000000..08fb816 --- /dev/null +++ b/.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/Dockunit.json b/Dockunit.json new file mode 100644 index 0000000..047a815 --- /dev/null +++ b/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/Gulpfile.js b/Gulpfile.js new file mode 100644 index 0000000..6ffc7a9 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,362 @@ +// 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 pump = require( 'pump' ); +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': [ 'assets/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/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( 'assets/css' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Minify and optimize style.css. + * + * https://www.npmjs.com/package/gulp-cssnano + */ +gulp.task( 'cssnano', [ 'postcss' ], () => + gulp.src( 'assets/css/style.css' ) + .pipe( plumber( {'errorHandler': handleErrors} ) ) + .pipe( cssnano( { + 'safe': true // Use safe optimizations. + } ) ) + .pipe( rename( 'style.min.css' ) ) + .pipe( gulp.dest( 'assets/css/' ) ) + .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/js' ) ) + .pipe( browserSync.stream() ) +); + +/** + * Minify compiled JavaScript. + * + * https://www.npmjs.com/package/gulp-uglify + */ +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. + */ +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'] ); diff --git a/readme.txt b/README.md similarity index 67% rename from readme.txt rename to README.md index 1548d18..252e1d0 100644 --- a/readme.txt +++ b/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/assets/README.md b/assets/README.md new file mode 100644 index 0000000..164ee50 --- /dev/null +++ b/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/css/dfp.css b/assets/css/dfp.css similarity index 100% rename from css/dfp.css rename to assets/css/dfp.css diff --git a/assets/css/sass/styles.scss b/assets/css/sass/styles.scss new file mode 100644 index 0000000..a2ad07f --- /dev/null +++ b/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/js/vendor/jquery.dfp.js/jquery.dfp.js b/assets/js/jquery.dfp.js similarity index 99% rename from js/vendor/jquery.dfp.js/jquery.dfp.js rename to assets/js/jquery.dfp.js index 89762f1..8cc7791 100644 --- a/js/vendor/jquery.dfp.js/jquery.dfp.js +++ b/assets/js/jquery.dfp.js @@ -1,8 +1,8 @@ -/** - * jQuery DFP v2.4.1 +/*! + * jQuery DFP v2.4.2 * http://github.com/coop182/jquery.dfp.js * - * Copyright 2015 Matt Cooper + * Copyright 2016 Matt Cooper * Released under the MIT license */ @@ -104,6 +104,7 @@ refreshExisting: true, disablePublisherConsole: false, disableInitialLoad: false, + setCentering: false, noFetch: false, namespace: undefined, sizeMapping: {} @@ -298,6 +299,10 @@ pubadsService.noFetch(); } + if (dfpOptions.setCentering) { + pubadsService.setCentering( true ); + } + // Setup event listener to listen for renderEnded event and fire callbacks. pubadsService.addEventListener('slotRenderEnded', function (event) { diff --git a/assets/js/jquery.dfp.min.js b/assets/js/jquery.dfp.min.js new file mode 100644 index 0000000..3729771 --- /dev/null +++ b/assets/js/jquery.dfp.min.js @@ -0,0 +1 @@ +!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/js/jquery.dfw.js b/assets/js/jquery.dfw.js similarity index 100% rename from js/jquery.dfw.js rename to assets/js/jquery.dfw.js 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/assets/repo/README.md b/assets/repo/README.md new file mode 100644 index 0000000..a7f6ccc --- /dev/null +++ b/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/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100644 index 0000000..fdf3542 --- /dev/null +++ b/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/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/dfw-options.php b/dfw-options.php deleted file mode 100644 index 2132b8d..0000000 --- a/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; -} diff --git a/dfw-widget.php b/dfw-widget.php deleted file mode 100644 index 7fb2529..0000000 --- a/dfw-widget.php +++ /dev/null @@ -1,162 +0,0 @@ - __( '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?

-

- - >
-

-

- 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/doubleclick-for-wordpress.php b/doubleclick-for-wordpress.php new file mode 100644 index 0000000..e1032de --- /dev/null +++ b/doubleclick-for-wordpress.php @@ -0,0 +1,366 @@ +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() { + global $doubleclick; + $this->options = new DCWP_Options( $this ); + $this->widget = new DCWP_Widget( $this ); + $doubleclick = new DCWP_DoubleClick( $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( 'dfw', false, dirname( $this->basename ) . '/languages/' ); + + // Run the updates if needed. + $this->update_options(); + + // Load the includes. + $this->load_files(); + + // 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' ) ); + + } + + /** + * 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; + } + + /** + * Before v0.3 the options were not prefixed. + * Here, we update the option keys if needed. + * + * @since 0.3 + */ + public function update_options() { + $prefix = 'dfw_'; + $options = array( 'network_code', 'breakpoints' ); + foreach ( $options as $old_option ) { + $new_option = $prefix . $old_option; + if ( ! get_option( $new_option ) && $old_option_value = get_option( $old_option ) ) { + update_option( $new_option, $old_option_value ); + delete_option( $old_option ); + } + } + } + + /** + * Load all of the stuff in the includes folder. + * + * @since 0.3 + */ + public function load_files() { + $includes = array( + '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 ) { + if ( 0 === validate_file( $this->path . $include ) ) { + require_once( $this->path . $include ); + } + } + } + + /** + * 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 ); + } + } +} + +/** + * 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/includes/README.md b/includes/README.md new file mode 100644 index 0000000..0d7a1a6 --- /dev/null +++ b/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/includes/class-ad-slot.php b/includes/class-ad-slot.php new file mode 100644 index 0000000..26016a6 --- /dev/null +++ b/includes/class-ad-slot.php @@ -0,0 +1,124 @@ +identifier = str_replace('/','//',$identifier); + */ + $this->identifier = $identifer; + $this->sizes = $size; + $this->id = ++ DCWP_DoubleClick::$count; + } + + /** + * @TODO need a docbloc for this function + */ + 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; + } + } + + /** + * @TODO need a docblock for this function + */ + 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; + } +} diff --git a/includes/class-breakpoint.php b/includes/class-breakpoint.php new file mode 100644 index 0000000..73cf180 --- /dev/null +++ b/includes/class-breakpoint.php @@ -0,0 +1,82 @@ +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 wp_json_encode( $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)"; + } +} diff --git a/dfw-init.php b/includes/class-doubleclick.php similarity index 52% rename from dfw-init.php rename to includes/class-doubleclick.php index e8aa577..b3ffa6b 100644 --- a/dfw-init.php +++ b/includes/class-doubleclick.php @@ -1,34 +1,25 @@ 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 ) { @@ -99,19 +91,22 @@ public function __construct( $network_code = null ) { add_action( 'wp_print_footer_scripts', array( $this, 'footer_script' ) ); - $breakpoints = maybe_unserialize( get_option( 'dfw_breakpoints' ) ); + $breakpoints = apply_filters( 'dfw_breakpoints', maybe_unserialize( get_option( 'dfw_breakpoints' ) ) ); - if ( ! empty( $breakpoints ) ) : + if ( ! empty( $breakpoints ) ) { foreach ( $breakpoints as $breakpoint ) { + // if this is not set explicitly, it's coming from the dfw_breakpoints option. + if ( ! isset( $breakpoint['option'] ) ) { + $breakpoint['option'] = true; + } $args = array( 'min_width' => $breakpoint['min-width'], 'max_width' => $breakpoint['max-width'], - '_option' => true,// this breakpoint is set in WordPress options. - ); + '_option' => $breakpoint['option'], + ); $this->register_breakpoint( $breakpoint['identifier'], $args ); } - endif; - + } } /** @@ -121,7 +116,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 ); } /** @@ -134,16 +129,16 @@ 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', dirname( __FILE__ ) ), array( 'jquery' ), - DFP_VERSION, + DoubleClick_For_WordPress::VERSION, true ); wp_register_script( 'jquery.dfw.js', - plugins_url( 'js/jquery.dfw.js', __FILE__ ), + plugins_url( 'assets/js/jquery.dfw' . $suffix . '.js', dirname( __FILE__ ) ), array( 'jquery.dfp.js' ), - DFP_VERSION, + DoubleClick_For_WordPress::VERSION, true ); @@ -165,13 +160,15 @@ public function enqueue_scripts() { wp_localize_script( 'jquery.dfw.js', 'dfw', $data ); wp_enqueue_script( 'jquery.dfw.js' ); - wp_enqueue_style( - 'dfp', - plugins_url( 'css/dfp.css', __FILE__ ), - array(), - DFP_VERSION, - 'all' - ); + if ( ! empty( get_option( 'dfw_css' ) ) ) { + wp_enqueue_style( + 'dfp', + plugins_url( 'assets/css/dfp.css', dirname( __FILE__ ) ), + array(), + DoubleClick_For_WordPress::VERSION, + 'all' + ); + } } /** @@ -181,9 +178,13 @@ 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' ); + $network_code = isset( $this->network_code ) ? $this->network_code : get_option( 'dfw_network_code' ); + return apply_filters( 'dfw_network_code', $network_code ); } + /** + * Add DFP script to the footer + */ public function footer_script() { if ( ! $this->debug ) { $mappings = array(); @@ -194,7 +195,7 @@ public function footer_script() { } ?> - - - - -
- - - - - -``` - -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.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/languages b/languages new file mode 100644 index 0000000..462a40f --- /dev/null +++ b/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/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/package.json b/package.json new file mode 100644 index 0000000..dc27e85 --- /dev/null +++ b/package.json @@ -0,0 +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": { + "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 ." + } +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..405ccd7 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,16 @@ + + + + + + + A modified set of rules, based on the WordPress Coding Standards. + + + + + + + + + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..44f0fdb --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,14 @@ + + + + ./tests/ + + + 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). diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..f007ab6 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,35 @@ +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-base.php b/tests/test-base.php new file mode 100644 index 0000000..da5ecdc --- /dev/null +++ b/tests/test-base.php @@ -0,0 +1,36 @@ +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/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 ); + } +} diff --git a/tests/test-doubleclick.php b/tests/test-doubleclick.php new file mode 100644 index 0000000..d740470 --- /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 ); + } +} 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 ); + } +} diff --git a/tests/test-widget.php b/tests/test-widget.php new file mode 100644 index 0000000..e3c5ab8 --- /dev/null +++ b/tests/test-widget.php @@ -0,0 +1,36 @@ +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 ); + } +}