├── .gitattributes ├── generators ├── common │ ├── templates │ │ ├── gitkeep │ │ ├── browserslistrc │ │ └── gitignore │ └── index.js ├── page │ ├── templates │ │ ├── data.json │ │ ├── defaultImage.jpg │ │ ├── gulp │ │ │ ├── tasks │ │ │ │ ├── watchify.js │ │ │ │ ├── browserify.js │ │ │ │ ├── assets.js │ │ │ │ ├── templates.js │ │ │ │ ├── plain-images.js │ │ │ │ ├── scss.js │ │ │ │ ├── server.js │ │ │ │ ├── optimize-images.js │ │ │ │ ├── clear-test.js │ │ │ │ ├── aws-test.js │ │ │ │ ├── jsify.js │ │ │ │ ├── resize-images.js │ │ │ │ └── aws.js │ │ │ └── index.js │ │ ├── styles.scss │ │ ├── buttonLeft.svg │ │ ├── buttonRight.svg │ │ ├── img.html │ │ ├── gulpfile.js │ │ ├── package.json │ │ └── README.md │ ├── config.json │ └── index.js ├── graphic-module │ ├── templates │ │ ├── dist │ │ │ ├── data │ │ │ │ ├── create.json │ │ │ │ └── update.json │ │ │ └── index.html │ │ ├── src │ │ │ ├── scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── _chart-styles.scss │ │ │ │ └── styles.scss │ │ │ └── js │ │ │ │ ├── global-chart.js │ │ │ │ └── chart.js │ │ ├── preview.png │ │ ├── gulp │ │ │ ├── index.js │ │ │ └── tasks │ │ │ │ ├── server.js │ │ │ │ ├── sass.js │ │ │ │ └── browserify.js │ │ ├── gulpfile.js │ │ ├── package.json │ │ └── README │ ├── config.json │ └── index.js ├── embeddable-graphic │ ├── templates │ │ ├── gulp │ │ │ ├── tasks │ │ │ │ ├── watchify.js │ │ │ │ ├── browserify.js │ │ │ │ ├── html.js │ │ │ │ ├── data.js │ │ │ │ ├── server.js │ │ │ │ ├── sass.js │ │ │ │ ├── jsify.js │ │ │ │ └── aws.js │ │ │ └── index.js │ │ ├── gulpfile.js │ │ ├── src │ │ │ ├── sass │ │ │ │ ├── styles.scss │ │ │ │ ├── _ooyala.scss │ │ │ │ └── _base.scss │ │ │ ├── js │ │ │ │ └── scripts.js │ │ │ ├── embed.html │ │ │ └── index.html │ │ ├── package.json │ │ └── README.md │ ├── config.json │ └── index.js ├── nvm │ └── index.js ├── gitsecrets │ └── index.js ├── app │ └── index.js └── linters │ └── index.js ├── .eslintrc.json ├── package.json ├── .gitignore ├── README.md └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /generators/common/templates/gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/page/templates/data.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "root": true 4 | } 5 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/dist/data/create.json: -------------------------------------------------------------------------------- 1 | [20, 40, 60] 2 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/dist/data/update.json: -------------------------------------------------------------------------------- 1 | [40, 30, 20, 30] 2 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/src/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $<%= objName %>-container: '#chart'; 2 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/tasks/watchify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./jsify')(true); 4 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/tasks/browserify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./jsify')(false); 4 | -------------------------------------------------------------------------------- /generators/graphic-module/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "verboseName": "Graphic module", 3 | "description": "TK", 4 | "priority": 5 5 | } -------------------------------------------------------------------------------- /generators/graphic-module/templates/src/js/global-chart.js: -------------------------------------------------------------------------------- 1 | import chart from './chart.js'; 2 | 3 | 4 | window.<%= objName %> = chart; 5 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/src/scss/_chart-styles.scss: -------------------------------------------------------------------------------- 1 | #{$<%= objName %>-container} { 2 | 3 | /*Your chart styles here*/ 4 | 5 | } 6 | -------------------------------------------------------------------------------- /generators/common/templates/browserslistrc: -------------------------------------------------------------------------------- 1 | # Browsers that we support 2 | # see https://github.com/ai/browserslist 3 | 4 | > 2% in US 5 | Last 2 versions 6 | -------------------------------------------------------------------------------- /generators/page/templates/defaultImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DallasMorningNews/generator-dmninteractives/HEAD/generators/page/templates/defaultImage.jpg -------------------------------------------------------------------------------- /generators/graphic-module/templates/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DallasMorningNews/generator-dmninteractives/HEAD/generators/graphic-module/templates/preview.png -------------------------------------------------------------------------------- /generators/page/templates/gulp/tasks/watchify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | module.exports = require('./jsify')(true); 8 | -------------------------------------------------------------------------------- /generators/page/templates/gulp/tasks/browserify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | module.exports = require('./jsify')(false); 8 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "verboseName": "Embeddable graphic", 3 | "description": "An embeddable interactive region, to be inserted into stories.", 4 | "priority": 4 5 | } -------------------------------------------------------------------------------- /generators/graphic-module/templates/src/scss/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'chart-styles'; 3 | 4 | #{$<%= objName %>-container} { 5 | border: 1px dashed red; 6 | width: 50%; 7 | min-width: 290px; 8 | height:400px; 9 | } 10 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/gulp/index.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | 3 | module.exports = (tasks) => { 4 | tasks.forEach((name) => { 5 | gulp.task(name, require(`./tasks/${name}`)); // eslint-disable-line global-require 6 | }); 7 | 8 | return gulp; 9 | }; 10 | -------------------------------------------------------------------------------- /generators/page/templates/gulp/tasks/assets.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const gulp = require('gulp'); 8 | 9 | 10 | module.exports = () => 11 | gulp.src('./src/data/**/*') 12 | .pipe(gulp.dest('./dist/data')); 13 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/tasks/html.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const gulp = require('gulp'); 8 | 9 | 10 | module.exports = () => 11 | gulp.src('./src/*.html') 12 | .pipe(gulp.dest('./dist/')); 13 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/tasks/data.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const gulp = require('gulp'); 8 | 9 | 10 | module.exports = () => 11 | gulp.src('./src/data/**/*') 12 | .pipe(gulp.dest('./dist/data/')); 13 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const gulp = require('gulp'); 4 | 5 | 6 | module.exports = (tasks) => { 7 | tasks.forEach((name) => { 8 | gulp.task(name, require(`./tasks/${name}`)); // eslint-disable-line global-require 9 | }); 10 | 11 | return gulp; 12 | }; 13 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/gulpfile.js: -------------------------------------------------------------------------------- 1 | const util = require('gulp-util'); 2 | 3 | process.env.NODE_ENV = !!util.env.production ? 'production' : 'development'; 4 | 5 | const gulp = require('./gulp')([, 6 | 'sass', 7 | 'browserify', 8 | 'server', 9 | ]); 10 | 11 | gulp.task('build', ['sass', 'browserify', 'server']); 12 | gulp.task('default', ['build']); 13 | -------------------------------------------------------------------------------- /generators/page/templates/gulp/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const gulp = require('gulp'); 8 | 9 | module.exports = (tasks) => { 10 | tasks.forEach((name) => { 11 | gulp.task(name, require(`./tasks/${name}`)); // eslint-disable-line global-require 12 | }); 13 | 14 | return gulp; 15 | }; 16 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/gulp/tasks/server.js: -------------------------------------------------------------------------------- 1 | const browserSync = require('browser-sync').create(); 2 | const gulp = require('gulp'); 3 | 4 | module.exports = () => { 5 | browserSync.init({ 6 | files: ['./dist/**/*'], 7 | server: { 8 | baseDir: './dist/', 9 | }, 10 | ghostMode: false, 11 | }); 12 | 13 | gulp.watch(['./src/scss/**/*.scss'], ['sass']); 14 | }; 15 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const sass = require('gulp-sass'); 3 | const sourcemaps = require('gulp-sourcemaps'); 4 | 5 | module.exports = () => 6 | gulp.src('src/scss/*.scss') 7 | .pipe(sourcemaps.init()) 8 | .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError)) 9 | .pipe(sourcemaps.write()) 10 | .pipe(gulp.dest('dist/css')); 11 | -------------------------------------------------------------------------------- /generators/page/templates/styles.scss: -------------------------------------------------------------------------------- 1 | /* */ 2 | /* Default style imports */ 3 | /* */ 4 | 5 | @import 'base'; 6 | 7 | 8 | // Should get Hancock to embed these in base, at some point: 9 | 10 | @import 'header'; 11 | @import 'footer'; 12 | 13 | 14 | /* */ 15 | /* Project-specific styles */ 16 | /* */ 17 | 18 | // Enter your custom styles here. -------------------------------------------------------------------------------- /generators/nvm/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mix this into your generator by adding a composeWith('nvm') to your 3 | * generator's initializing() method to add auto-creation of .nvmrc 4 | */ 5 | const semver = require('semver'); 6 | const yeoman = require('yeoman-generator'); 7 | 8 | 9 | module.exports = yeoman.Base.extend({ 10 | writing() { 11 | if (semver.valid(process.version) !== null) { 12 | this.fs.write('.nvmrc', `${semver.major(process.version)}.${semver.minor(process.version)}`); 13 | } 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/tasks/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browserSync = require('browser-sync').create(); 4 | const gulp = require('gulp'); 5 | 6 | module.exports = () => { 7 | browserSync.init({ 8 | files: ['./dist/**/*'], 9 | server: { 10 | baseDir: './dist/', 11 | index: 'embed.html', 12 | }, 13 | ghostMode: false, 14 | }); 15 | 16 | gulp.watch(['./src/sass/*.scss'], ['sass']); 17 | gulp.watch(['./src/data/**/*'], ['data']); 18 | gulp.watch(['./src/*.html'], ['html']); 19 | }; 20 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const gulp = require('./gulp')([ 4 | 'sass', 5 | 'browserify', 6 | 'watchify', 7 | 'server', 8 | 'aws', 9 | 'html', 10 | 'data', 11 | ]); 12 | const runSequence = require('run-sequence'); 13 | 14 | 15 | gulp.task('build', ['data', 'html', 'sass', 'browserify']); 16 | gulp.task('publish', (cb) => { runSequence('build', 'aws', cb); }); 17 | gulp.task('dev-server', ['watchify', 'server']); 18 | gulp.task('default', (cb) => { runSequence('build', 'dev-server', cb); }); 19 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/src/sass/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'normalize/import-now'; 2 | 3 | @import 'mixins'; 4 | @import 'variables'; 5 | @import 'base'; 6 | 7 | html.embedded, html { 8 | // Applied when embedded using Pym.js 9 | font-size: 10px; 10 | } 11 | 12 | body { 13 | // This helps Pym.js more more accurately measure our
14 | &:before, 15 | &:after { 16 | content: " "; 17 | display: table; 18 | } 19 | } 20 | 21 | 22 | // NOTE!!!! If you need ooyala code, add @import 'ooyala'; to your list of imports above 23 | 24 | // Your styles go here 25 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/src/js/scripts.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import pym from 'pym.js'; 3 | 4 | const pymChild = new pym.Child(); 5 | 6 | // Your graphic code goes here 7 | // 8 | // | 9 | // + +--+ +--+ 10 | // | | +---+ | | 11 | // | | || | | | 12 | // + | || +----+ | 13 | // | | || || || | 14 | // | | || || || | 15 | // + | || || || | 16 | // | | || || || | 17 | // | | || || || | 18 | // +-------------------------+ 19 | 20 | // Call this every time you need to resize the iframe, after your 21 | // graphic is drawn, etc. 22 | pymChild.sendHeight(); 23 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const autoprefixer = require('autoprefixer'); 4 | const eyeglass = require('eyeglass'); 5 | const gulp = require('gulp'); 6 | const postcss = require('gulp-postcss'); 7 | const sass = require('gulp-sass'); 8 | const sourcemaps = require('gulp-sourcemaps'); 9 | 10 | 11 | module.exports = () => 12 | gulp.src('src/sass/*.scss') 13 | .pipe(sourcemaps.init()) 14 | .pipe(sass(eyeglass({ outputStyle: 'compressed' })).on('error', sass.logError)) 15 | .pipe(postcss([autoprefixer({ grid: true })])) 16 | .pipe(sourcemaps.write('./')) 17 | .pipe(gulp.dest('dist/css')); 18 | -------------------------------------------------------------------------------- /generators/page/templates/gulp/tasks/templates.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const gulp = require('gulp'); 8 | const nunjucksRender = require('gulp-nunjucks-render'); 9 | 10 | 11 | const meta = require('./../../meta.json'); 12 | 13 | 14 | module.exports = () => { 15 | nunjucksRender.nunjucks.configure(['src/templates/'], { watch: false }); 16 | 17 | return gulp.src([ 18 | './src/templates/**/*.html', 19 | '!./src/templates/base.html', 20 | '!./src/templates/partials/**/*.html', 21 | '!./src/templates/adblock*.html', 22 | ]) 23 | .pipe(nunjucksRender(meta)) 24 | .pipe(gulp.dest('./dist')); 25 | }; 26 | -------------------------------------------------------------------------------- /generators/page/templates/buttonLeft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | -------------------------------------------------------------------------------- /generators/page/templates/buttonRight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | -------------------------------------------------------------------------------- /generators/page/templates/gulp/tasks/plain-images.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const changed = require('gulp-changed'); 8 | const gulp = require('gulp'); 9 | const merge = require('merge-stream'); 10 | 11 | 12 | module.exports = () => { 13 | // Copies over SVGs or other image files 14 | const other = gulp.src([ 15 | './src/images/**/*', 16 | '!./src/images/**/*.{png,jpg,JPG}', 17 | '!./src/images/opt/**/*', 18 | ],{ nodir: true }) 19 | .pipe(changed('./dist/images')) 20 | .pipe(gulp.dest('./dist/images')); 21 | 22 | // Copy imgs that weren't resized 23 | const copied = gulp.src('./src/images/opt/**/_*.{png,jpg,JPG}') 24 | .pipe(changed('./dist/images')) 25 | .pipe(gulp.dest('./dist/images')); 26 | 27 | return merge(other, copied); 28 | }; 29 | -------------------------------------------------------------------------------- /generators/page/templates/gulp/tasks/scss.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable strict */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-enable strict */ 6 | 7 | const autoprefixer = require('autoprefixer'); 8 | const eyeglass = require('eyeglass'); 9 | const gulp = require('gulp'); 10 | const rename = require('gulp-rename'); 11 | const postcss = require('gulp-postcss'); 12 | const sass = require('gulp-sass'); 13 | const sourcemaps = require('gulp-sourcemaps'); 14 | 15 | 16 | module.exports = () => 17 | gulp.src(['./src/scss/**/*.scss', './src/scss/**/*.css']) 18 | .pipe(sourcemaps.init()) 19 | .pipe(sass(eyeglass({ outputStyle: 'compressed' })).on('error', sass.logError)) 20 | .pipe(postcss([autoprefixer({ grid: true })])) 21 | // eslint-disable-next-line no-param-reassign 22 | .pipe(rename((filePath) => { filePath.basename += '-bundle'; })) 23 | .pipe(sourcemaps.write()) 24 | .pipe(gulp.dest('./dist/css')); 25 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/src/embed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /generators/graphic-module/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appName %>", 3 | "version": "1.0.0", 4 | "description": "", 5 | "author": "", 6 | "main": "src/js/chart.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "license": "UNLICENSED", 11 | "devDependencies": { 12 | "babel-preset-env": "^1.6.1", 13 | "babelify": "^7.3.0", 14 | "browser-sync": "^2.17.5", 15 | "browserify": "^13.1.0", 16 | "event-stream": "<=3.3.4", 17 | "gulp": "^3.9.1", 18 | "gulp-rename": "^1.2.2", 19 | "gulp-sass": "^2.3.2", 20 | "gulp-sourcemaps": "^2.1.1", 21 | "gulp-uglify": "^2.0.0", 22 | "gulp-util": "^3.0.7", 23 | "vinyl-buffer": "^1.0.0", 24 | "vinyl-source-stream": "^1.1.0", 25 | "watchify": "^3.7.0" 26 | }, 27 | "dependencies": { 28 | "d3": "^4.2.8" 29 | }, 30 | "browserify": { 31 | "transform": [["babelify", { "presets": ["es2015"] }]] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /generators/embeddable-graphic/templates/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <% _.each(fonts, function( font ) { %> 7 | 8 | <% }); %> 9 | 10 | 11 | 12 | 13 |
12 | {% endmacro %}
13 |
14 |
15 | {% macro png(filename, alt, class='', id='', sizes='(min-width: 1px) 100vw, 100vw', srcSize='1800') %}
16 |
24 | {% endmacro %}
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/server.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const batch = require('gulp-batch');
8 | const browserSync = require('browser-sync').create();
9 | const gulp = require('gulp');
10 | const runSequence = require('run-sequence');
11 | const watch = require('gulp-watch');
12 |
13 |
14 | module.exports = () => {
15 | browserSync.init({
16 | files: ['./dist/**/*'],
17 | server: {
18 | baseDir: './dist/',
19 | },
20 | ghostMode: false,
21 | });
22 |
23 | watch('./src/data/**/*', () => { runSequence('assets'); });
24 |
25 | watch('./src/scss/**/*.scss', () => { runSequence('scss'); });
26 |
27 | watch('./src/templates/**/*.{html,svg}', () => { runSequence('templates'); });
28 |
29 | watch(
30 | [
31 | './src/images/**/*',
32 | '!./src/images/opt/**/*',
33 | '!**/*.crdownload', // Ignore chrome's temp file
34 | ],
35 | () => { runSequence('img'); } // eslint-disable-line comma-dangle
36 | // batch((e, cb) => { gulp.start('img', cb); })
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-dmninteractives",
3 | "version": "0.8.9",
4 | "description": "Yeoman generator for interactive pages at The Dallas Morning News",
5 | "license": "MIT",
6 | "main": "app/index.js",
7 | "repository": "DallasMorningNews/generator-dmninteractives",
8 | "author": {
9 | "name": "John Hancock",
10 | "email": "newsapps@dallasnews.com",
11 | "url": "https://github.com/DallasMorningNews"
12 | },
13 | "files": [
14 | "generators"
15 | ],
16 | "keywords": [
17 | "yeoman-generator"
18 | ],
19 | "dependencies": {
20 | "chalk": "^1.1.3",
21 | "google-url-helper": "^1.0.3",
22 | "lodash": "^4.17.11",
23 | "mkdirp": "^0.5.1",
24 | "octonode": "^0.9.3",
25 | "semver": "^5.5.0",
26 | "underscore.string": "^3.3.5",
27 | "valid-url": "^1.0.9",
28 | "yeoman-generator": "^0.19.2"
29 | },
30 | "devDependencies": {
31 | "eslint": "^3.14.1",
32 | "eslint-config-airbnb": "^14.0.0",
33 | "eslint-plugin-import": "^2.13.0",
34 | "eslint-plugin-jsx-a11y": "^3.0.2",
35 | "eslint-plugin-react": "^6.9.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= appName %>",
3 | "version": "1.0.0",
4 | "main": "src/js/scripts.js",
5 | "license": "UNLICENSED",
6 | "repository": "DallasMorningNews/<%= appName %>",
7 | "private": true,
8 | "dependencies": {
9 | "jquery": "^3",
10 | "normalize-scss": "*",
11 | "pym.js": "^1.x"
12 | },
13 | "devDependencies": {
14 | "autoprefixer": "^7.2.2",
15 | "babel-preset-env": "^1.6.1",
16 | "babelify": "^7.3.0",
17 | "browser-sync": "^2.24.0",
18 | "browserify": "^14.0.0",
19 | "event-stream": "3.3.4",
20 | "eyeglass": "^1.2.1",
21 | "gulp": "^3.9.1",
22 | "gulp-awspublish": "^3.3.0",
23 | "gulp-awspublish-router": "^0.1.5",
24 | "gulp-cloudfront-invalidate-aws-publish": "^0.2.0",
25 | "gulp-confirm": "^1.0.4",
26 | "gulp-postcss": "^7.0.0",
27 | "gulp-rename": "^1.2.2",
28 | "gulp-sass": "^4.0.1",
29 | "gulp-sourcemaps": "^2.1.1",
30 | "gulp-uglify": "^2.0.0",
31 | "run-sequence": "^1.2.2",
32 | "vinyl-buffer": "^1.0.0",
33 | "vinyl-source-stream": "^1.1.0",
34 | "watchify": "^3.7.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/generators/page/templates/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const runSequence = require('run-sequence');
8 | const S = require('string');
9 |
10 |
11 | const gulp = require('./gulp')([
12 | 'assets',
13 | 'aws',
14 | 'aws-test',
15 | 'browserify',
16 | 'clear-test',
17 | 'plain-images',
18 | 'optimize-images',
19 | 'resize-images',
20 | 'scss',
21 | 'templates',
22 | 'server',
23 | 'watchify',
24 | ]);
25 | const meta = require('./meta.json');
26 |
27 |
28 | const appName = S(meta.name).slugify().s;
29 |
30 |
31 | gulp.task('img', (cb) => {
32 | runSequence('optimize-images', 'resize-images', 'plain-images', cb);
33 | });
34 |
35 | gulp.task('default', [
36 | 'assets',
37 | 'img',
38 | 'scss',
39 | 'watchify',
40 | 'templates',
41 | 'server',
42 | ], () => {});
43 |
44 |
45 | gulp.task('build', ['assets', 'img', 'scss', 'templates', 'browserify']);
46 |
47 |
48 | gulp.task('publish', (cb) => { runSequence('build', 'aws', 'clear-test', cb); });
49 |
50 |
51 | gulp.task('publish-test', (cb) => { runSequence('build', 'aws-test', cb); });
52 |
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/optimize-images.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const changed = require('gulp-changed');
8 | const gulp = require('gulp');
9 | const imagemin = require('gulp-imagemin');
10 | const imageminJpegRecompress = require('imagemin-jpeg-recompress');
11 | const merge = require('merge-stream');
12 |
13 |
14 | module.exports = () => {
15 | const pngs = gulp.src([
16 | './src/images/**/*.png',
17 | '!./src/images/opt/**/*',
18 | ])
19 | .pipe(changed('./src/images/opt'))
20 | .pipe(
21 | imagemin({
22 | optimizationLevel: 4,
23 | progressive: true,
24 | svgoPlugins: [{removeViewBox: false}],
25 | })
26 | )
27 | .pipe(gulp.dest('./src/images/opt'));
28 |
29 | const jpgs = gulp.src([
30 | './src/images/**/*.{jpg,JPG}',
31 | '!./src/images/opt/**/*',
32 | ])
33 | .pipe(changed('./src/images/opt'))
34 | .pipe(imageminJpegRecompress({
35 | loops: 3,
36 | min: 50,
37 | max: 75,
38 | target: 0.9999,
39 | progressive: true
40 | })()
41 | )
42 | .pipe(gulp.dest('./src/images/opt'));
43 |
44 | return merge(pngs, jpgs);
45 | };
46 |
--------------------------------------------------------------------------------
/generators/common/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Mix this generator in to get common files that we should have in all of our
3 | * projects. For now it's just a .gitignore, but anything that lives in all
4 | * projects is a good fit for this subgenerator.
5 | */
6 |
7 | const mkdirp = require('mkdirp');
8 | const yeoman = require('yeoman-generator');
9 |
10 |
11 | module.exports = yeoman.Base.extend({
12 | writing: {
13 | git() {
14 | this.fs.copy(
15 | this.templatePath('gitignore'),
16 | this.destinationPath('.gitignore') // eslint-disable-line comma-dangle
17 | );
18 |
19 | this.fs.copy(
20 | this.templatePath('gitkeep'),
21 | this.destinationPath('./src/assets/.gitkeep') // eslint-disable-line comma-dangle
22 | );
23 |
24 | this.fs.copy(
25 | this.templatePath('gitkeep'),
26 | this.destinationPath('./src/data/.gitkeep') // eslint-disable-line comma-dangle
27 | );
28 | },
29 |
30 | directories() {
31 | mkdirp('./dist');
32 | },
33 |
34 | browserCompat() {
35 | this.fs.copy(
36 | this.templatePath('browserslistrc'),
37 | this.destinationPath('.browserslistrc') // eslint-disable-line comma-dangle
38 | );
39 | },
40 | },
41 | });
42 |
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/clear-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const aws = require('aws-sdk');
8 | const gulp = require('gulp');
9 | const S = require('string');
10 |
11 |
12 | const awsJson = require('./../../aws.json');
13 | const meta = require('./../../meta.json');
14 |
15 |
16 | const appName = S(meta.name).slugify().s;
17 |
18 |
19 | module.exports = () => {
20 | aws.config.update({
21 | accessKeyId: awsJson.accessKeyId,
22 | secretAccessKey: awsJson.secretAccessKey,
23 | region: 'us-east-1',
24 | });
25 |
26 | const s3 = new aws.S3();
27 |
28 | let params = {
29 | Bucket: 'interactives.dallasnews.com.test',
30 | // Do not change this!
31 | Prefix: `test/${appName}`,
32 | };
33 |
34 | s3.listObjects(params, (err, data) => {
35 | if (err) {
36 | console.log(err);
37 | } else {
38 | params = { Bucket: awsJson.params.Bucket };
39 | params.Delete = {};
40 | params.Delete.Objects = [];
41 |
42 | data.Contents.forEach((content) => {
43 | params.Delete.Objects.push({ Key: content.Key });
44 | });
45 |
46 | if (params.Delete.Objects.length > 0) {
47 | s3.deleteObjects(params, (errMsg, fileData) => console.log(fileData.Deleted.length));
48 | }
49 | }
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/generators/page/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= appName %>",
3 | "version": "1.0.0",
4 | "main": "app.js",
5 | "license": "UNLICENSED",
6 | "private": true,
7 | "dependencies": {
8 | "autoprefixer": "^7.2.2",
9 | "aws-sdk": "^2.10.0",
10 | "babel-preset-env": "^1.6.1",
11 | "babelify": "^7.3.0",
12 | "browserify": "^14.0.0",
13 | "browser-sync": "^2.24.0",
14 | "deline": "^1.0.4",
15 | "event-stream": "3.3.4",
16 | "eyeglass": "^1.2.1",
17 | "gulp": "^3.9.0",
18 | "gulp-awspublish": "^3.0.1",
19 | "gulp-awspublish-router": "^0.1.5",
20 | "gulp-batch": "^1.0.5",
21 | "gulp-changed": "^2.0.0",
22 | "gulp-cloudfront-invalidate-aws-publish": "^0.2.0",
23 | "gulp-confirm": "^1.0.4",
24 | "gulp-image-resize": "^0.12.0",
25 | "gulp-imagemin": "^3.1.1",
26 | "gulp-nunjucks-render": "^1.0.0",
27 | "gulp-postcss": "^7.0.0",
28 | "gulp-rename": "^1.2.2",
29 | "gulp-sass": "^4.0.1",
30 | "gulp-sourcemaps": "^1.6.0",
31 | "gulp-uglify": "^2.0.0",
32 | "gulp-util": "^3.0.8",
33 | "gulp-watch": "^4.3.5",
34 | "imagemin-jpeg-recompress": "^4.3.0",
35 | "jquery": "^3.2.1",
36 | "merge-stream": "^1.0.0",
37 | "run-sequence": "^1.1.5",
38 | "string": "^3.3.1",
39 | "vinyl-buffer": "^1.0.0",
40 | "vinyl-source-stream": "^1.1.0",
41 | "watchify": "^3.7.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/generators/page/templates/README.md:
--------------------------------------------------------------------------------
1 | # interactive_<%= slug %>
2 |
3 | This is an interactive presentation graphic built using the [`dmninteractives` Yeoman generator](https://github.com/DallasMorningNews/generator-dmninteractives).
4 |
5 | ## Requirements
6 |
7 | - Node - `brew install node`
8 | - Gulp - `npm install -g gulp-cli`
9 |
10 | ## Local development
11 |
12 | #### Installation
13 |
14 | 1. `npm install` to install development tooling
15 | 2. `gulp` to open a local development server
16 |
17 | #### What's inside
18 |
19 | - `src/index.html` - HTML markup, which gets processed by Nunjucks
20 | - `src/js/*.js` - Graphic scripts, written in ES2015 (it'll be transpiled with Babel)
21 | - `src/scss/*.scss` - Graphic styles in SCSS
22 | - `src/data/*` - files that should be both published and committed to the repository (probably CSVs, JSON, etc.); copied to `dist/data/*` by Gulp
23 | - `src/assets/*` - assets (probably media assets, such as Illustrator files) that don’t get copied by Gulp but are tracked by `git`
24 | - `dist/*` - All of the above, transpiled
25 |
26 | _Important caveat:_ Video, audio and ZIP files are ignored by `git` regardless of where they're saved. You'll need to manually alter the [`.gitignore`](.gitignore) file to have them committed to Github.
27 |
28 | #### Publishing
29 |
30 | `gulp publish` will upload your [`dist/`](dist/) folder to the `<%= year %>/<%= slug %>/` folder on our interactives S3 bucket.
31 |
32 | ## Copyright
33 |
34 | ©<%= year %> The Dallas Morning News
35 |
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/aws-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const awspublish = require('gulp-awspublish');
8 | const confirm = require('gulp-confirm');
9 | const deline = require('deline');
10 | const gulp = require('gulp');
11 | const gutil = require('gulp-util');
12 | const path = require('path');
13 | const rename = require('gulp-rename');
14 | const S = require('string');
15 |
16 |
17 | const awsJson = require('./../../aws.json');
18 | const meta = require('./../../meta.json');
19 |
20 |
21 | const appName = S(meta.name).slugify().s;
22 |
23 | const awsDirectory = `test/${appName}/`;
24 |
25 |
26 | module.exports = () => {
27 | const publisher = awspublish.create(Object.assign(awsJson, {
28 | params: {
29 | Bucket: 'interactives.dallasnews.com.test'
30 | }
31 | }));
32 |
33 | return gulp.src('./dist/**/*')
34 | .pipe(confirm({
35 | question: deline`You're about to publish this project to AWS
36 | under the directory '${awsDirectory}'.
37 | Are you sure you want to do this?`,
38 | input: '_key:y',
39 | }))
40 | .pipe(rename((filePath) => {
41 | // eslint-disable-next-line no-param-reassign
42 | filePath.dirname = awsDirectory + filePath.dirname.replace('.\\', '');
43 | }))
44 | .pipe(publisher.publish({}, { force: false, noAcl: true }))
45 | .pipe(publisher.cache())
46 | .pipe(awspublish.reporter());
47 | };
48 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/templates/src/sass/_ooyala.scss:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------------------
2 | // OOYALA CODE
3 | // -----------------------------------------------------------------------------
4 |
5 | .video-block {
6 | width: 55%;
7 | margin: 4.8rem 0;
8 | font-size: 15px;
9 | img {
10 | max-width: 170px !important;
11 | max-height: 18px !important;
12 | }
13 | }
14 | .video-wrapper {
15 | position: relative;
16 | padding-bottom: 56.25%;
17 | padding-top: 25px;
18 | height: 0;
19 | }
20 | .video-wrapper iframe, .video-wrapper embed {
21 | position: absolute;
22 | top: 0;
23 | left: 0;
24 | width: 100%;
25 | height: 100%;
26 | }
27 |
28 | @media (max-width: 900px) {
29 | .video-block {
30 | width: 100%;
31 | }
32 | }
33 |
34 | @media (max-width: 767px) {
35 | .video-block {
36 | margin: 2.4rem 0;
37 | }
38 | }
39 |
40 | @media (max-width: 681px) {
41 | .video-block {
42 | img {
43 | max-width: 130px !important;
44 | }
45 | }
46 | }
47 |
48 | @media (max-width: 500px) {
49 | .video-block {
50 | img {
51 | max-width: 80px !important;
52 | max-height: 14px !important;
53 | }
54 | }
55 | }
56 |
57 | .oo-state-screen-info {
58 | font-size: 1.6rem !important;
59 | }
60 |
61 | .oo-state-screen-description {
62 | font-size: 1.1rem !important;
63 | }
64 |
65 | .oo-player-container {
66 | min-width: 0px !important;
67 | }
68 |
69 | .oo-state-screen-title, .oo-state-screen-description {
70 | max-width: 90% !important;
71 | }
72 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/templates/src/sass/_base.scss:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | h1, h2, h3, h4 {
6 | margin-bottom: 1.2rem;
7 | }
8 | h5, h6 {
9 | margin-bottom: .6rem;
10 | }
11 | h1 {
12 | font-size: 6rem;
13 | line-height: 7.2rem;
14 | }
15 | h2 {
16 | font-size: 4.8rem;
17 | line-height: 6rem;
18 | }
19 | h3 {
20 | font-size: 3.6rem;
21 | line-height: 4.2rem;
22 | }
23 | h4 {
24 | font-size: 2.4rem;
25 | line-height: 3rem;
26 | }
27 | h5 {
28 | font-size: 2rem;
29 | line-height: 2.4rem;
30 | }
31 | h6 {
32 | font-size: 1.6rem;
33 | line-height: 1.8rem;
34 | }
35 |
36 | .clearfix:after {
37 | display: table;
38 | content: "";
39 | clear: both;
40 | }
41 |
42 | #embed__container {
43 | font-family: $sans;
44 | max-width: 900px;
45 | width: 100%;
46 | margin: 0 auto;
47 | background-color: $black245;
48 | padding: 2.4rem;
49 | display: block;
50 | font-weight: 300;
51 | a {
52 | color: $dmnblue;
53 | text-decoration: underline;
54 | font-weight: 700;
55 | }
56 | }
57 |
58 | .embed__chatter {
59 | margin-bottom: 2.4rem;
60 | p {
61 | @include type(1.4rem, 1.8rem, 1.2rem);
62 | &:last-of-type {
63 | margin-bottom: 0;
64 | }
65 | }
66 | h4 {
67 | margin-top: 0;
68 | }
69 | }
70 |
71 | .embed__content {
72 | p {
73 | @include type(1.4rem, 1.8rem, 1.2rem);
74 | &:last-of-type {
75 | margin-bottom: 0;
76 | }
77 | }
78 | .source, .credit {
79 | @include type(1.2rem, 1.5rem, .6rem);
80 | }
81 | .credit {
82 | text-align: right;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/templates/gulp/tasks/jsify.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const browserify = require('browserify');
4 | const gulp = require('gulp');
5 | const source = require('vinyl-source-stream');
6 | const uglify = require('gulp-uglify');
7 | const buffer = require('vinyl-buffer');
8 | const sourcemaps = require('gulp-sourcemaps');
9 | const watchify = require('watchify');
10 | const gutil = require('gulp-util');
11 | const babelify = require('babelify');
12 | const es = require('event-stream');
13 |
14 | module.exports = (watch) => {
15 | const wrapper = watch ? watchify : (b) => b;
16 |
17 | return () => {
18 | const files = [
19 | 'scripts.js',
20 | ];
21 |
22 | const tasks = files.map((entry) => {
23 | const props = {
24 | entries: `./src/js/${entry}`,
25 | extensions: ['.js'],
26 | cache: {},
27 | packageCache: {},
28 | debug: true,
29 | };
30 |
31 | const bundler = wrapper(browserify(props).transform(babelify, {
32 | presets: ['env'],
33 | }));
34 |
35 | function bundle() {
36 | return bundler.bundle()
37 | .on('error', gutil.log.bind(gutil, 'Browserify Error'))
38 | .pipe(source(entry))
39 | .pipe(buffer())
40 | .pipe(sourcemaps.init({ loadMaps: true }))
41 | .pipe(uglify({ mangle: false, compress: true }).on('error', gutil.log))
42 | .pipe(sourcemaps.write('./'))
43 | .pipe(gulp.dest('./dist/js/'));
44 | }
45 |
46 | bundler.on('log', gutil.log);
47 | bundler.on('update', bundle);
48 |
49 | return bundle();
50 | });
51 | return es.merge.apply(null, tasks);
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/generators/graphic-module/templates/gulp/tasks/browserify.js:
--------------------------------------------------------------------------------
1 | const browserify = require('browserify');
2 | const gulp = require('gulp');
3 | const source = require('vinyl-source-stream');
4 | const uglify = require('gulp-uglify');
5 | const buffer = require('vinyl-buffer');
6 | const sourcemaps = require('gulp-sourcemaps');
7 | const watchify = require('watchify');
8 | const gutil = require('gulp-util');
9 | const babelify = require('babelify');
10 | const util = require('gulp-util');
11 | const es = require('event-stream');
12 |
13 | module.exports = () => {
14 | const files = [
15 | 'chart.js',
16 | 'global-chart.js',
17 | ];
18 |
19 | const tasks = files.map((entry) => {
20 | const props = {
21 | entries: `./src/js/${entry}`,
22 | extensions: ['.js'],
23 | cache: {},
24 | packageCache: {},
25 | debug: true,
26 | };
27 |
28 | const bundler = watchify(browserify(props).transform(babelify, {
29 | presets: ['env'],
30 | }));
31 |
32 | function bundle() {
33 | return bundler.bundle()
34 | .on('error', gutil.log.bind(gutil, 'Browserify Error'))
35 | .pipe(source(entry))
36 | .pipe(buffer())
37 | .pipe(!!util.env.production ? sourcemaps.init({ loadMaps: true }) : util.noop())
38 | .pipe(!!util.env.production ?
39 | uglify({ mangle: false, compress: true }).on('error', gutil.log) : util.noop()
40 | )
41 | .pipe(!!util.env.production ? sourcemaps.write('./') : util.noop())
42 | .pipe(gulp.dest('./dist/js/'));
43 | }
44 |
45 | bundler.on('log', gutil.log);
46 | bundler.on('update', bundle);
47 |
48 | return bundle();
49 | });
50 | return es.merge.apply(null, tasks);
51 | };
52 |
--------------------------------------------------------------------------------
/generators/graphic-module/templates/README:
--------------------------------------------------------------------------------
1 | # <%= appName %>
2 |
3 | Chart module for _{your chart type here}_.
4 |
5 | 
6 |
7 | ### Install
8 | ```bash
9 | $ npm install --save <%= appName %>
10 | ```
11 |
12 | ##### Requirements
13 |
14 | This module uses ES6 syntax. To use as a pre-compiled module, you'll need a compiler like [babel](https://babeljs.io/).
15 |
16 | ### Use
17 |
18 | In the client, include the `global-chart.js` bundle, which defines a global chart object, `<%= objName %>`:
19 |
20 | ```html
21 |
22 | ```
23 |
24 | To use as a module, simply import the chart object:
25 | ```javascript
26 | import <%= objName %> from '<%= appName %>';
27 | ```
28 |
29 | The chart object has two methods, one to create the chart, initially, and another to update it.
30 |
31 | ```javascript
32 | var myChart = new <%= objName %>();
33 |
34 | // create needs a selection string and prefectched data
35 | myChart.create('#chart', data);
36 |
37 | // update needs only new data
38 | myChart.update(newData);
39 | ```
40 |
41 | To apply this chart's default styles when using SCSS, simply define the variable `$<%= objName %>-container` to represent the ID or class of the chart's container(s) and import the `_chart-styles.scss` partial.
42 |
43 | ```CSS
44 | $<%= objName %>-container: '#chart';
45 |
46 | @import 'path/to/<%= appName %>/src/scss/_chart-styles';
47 | ```
48 |
49 |
50 | ### Developing
51 |
52 | Write your chart code in `chart.js` and add custom styles to `_chart-styles.scss`.
53 |
54 | Then, just run gulp:
55 | ```bash
56 | $ gulp
57 | ```
58 |
59 | Or to minimize javascript before publishing:
60 | ```bash
61 | $ gulp --production
62 | ```
63 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/templates/README.md:
--------------------------------------------------------------------------------
1 | # <%= slug %>
2 |
3 | This is an embeddable graphic built using the [`dmninteractives` Yeoman generator](https://github.com/DallasMorningNews/generator-dmninteractives). It's designed to be embedded using [Pym.js](http://blog.apps.npr.org/pym.js/) as a responsive `iframe`.
4 |
5 | ## Requirements
6 |
7 | - Node - `brew install node`
8 | - Gulp - `npm install -g gulp-cli`
9 |
10 | ## Local development
11 |
12 | #### Installation
13 |
14 | 1. `npm install` to install development tooling
15 | 2. `gulp` to open a local development server
16 |
17 | #### What's inside
18 |
19 | - `src/index.html` - Graphic HTML markup; there's no Nunjucks, etc. so this is just straight HTML
20 | - `src/embed.html` - A page to test your embed
21 | - `src/js/*.js` - Graphic scripts, written in ES2015 (it'll be transpiled with Babel)
22 | - `src/sass/*.scss` - Graphic styles in SCSS
23 | - `dist/*` - All of the above, transpiled
24 |
25 | _Important caveat:_ Video, audio and ZIP files are ignored by `git` regardless of where they're saved. You'll need to manually alter the [`.gitignore`](.gitignore) file to have them committed to Github.
26 |
27 | #### Publishing
28 |
29 | `gulp publish` will upload your [`dist/`](dist/) folder to the `embeds/<%= year %>/<%= slug %>/` folder on our interactives S3 bucket.
30 |
31 | ## Usage
32 |
33 | #### Embedding in Serif
34 |
35 | The below embed code can be pasted into a Serif "code block":
36 |
37 | ```html
38 |
39 |
40 |
41 |
42 | ```
43 |
44 | ## Copyright
45 |
46 | ©<%= year %> The Dallas Morning News
47 |
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/jsify.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const babelify = require('babelify');
8 | const browserify = require('browserify');
9 | const buffer = require('vinyl-buffer');
10 | const es = require('event-stream');
11 | const gulp = require('gulp');
12 | const gutil = require('gulp-util');
13 | const rename = require('gulp-rename');
14 | const source = require('vinyl-source-stream');
15 | const sourcemaps = require('gulp-sourcemaps');
16 | const uglify = require('gulp-uglify');
17 | const watchify = require('watchify');
18 |
19 |
20 | module.exports = (watch) => {
21 | const wrapper = watch ? watchify : b => b;
22 |
23 | return () => {
24 | const files = [
25 | 'scripts.js',
26 | ];
27 |
28 | const tasks = files.map((entry) => {
29 | const props = {
30 | entries: `./src/js/${entry}`,
31 | extensions: ['.js'],
32 | cache: {},
33 | packageCache: {},
34 | debug: true,
35 | };
36 |
37 | const bundler = wrapper(browserify(props).transform(babelify, {
38 | presets: ['env'],
39 | }));
40 |
41 | function bundle() {
42 | return bundler.bundle()
43 | .on('error', gutil.log.bind(gutil, 'Browserify Error'))
44 | .pipe(source(entry))
45 | .pipe(buffer())
46 | // eslint-disable-next-line no-param-reassign
47 | .pipe(rename((filePath) => { filePath.basename += '-bundle'; }))
48 | .pipe(sourcemaps.init({ loadMaps: true }))
49 | .pipe(uglify({ mangle: false, compress: true }).on('error', gutil.log))
50 | .pipe(sourcemaps.write('./'))
51 | .pipe(gulp.dest('./dist/js/'));
52 | }
53 |
54 | bundler.on('log', gutil.log);
55 | bundler.on('update', bundle);
56 |
57 | return bundle();
58 | });
59 | return es.merge.apply(null, tasks);
60 | };
61 | };
62 |
--------------------------------------------------------------------------------
/generators/gitsecrets/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Mix this into your generator by adding a composeWith('gitsecrets') to your
3 | * generator's initializing() method to add optional run of `git init` and
4 | * secrets protection with git-secrets
5 | */
6 | const chalk = require('chalk');
7 | const yeoman = require('yeoman-generator');
8 |
9 |
10 | module.exports = yeoman.Base.extend({
11 | prompting() {
12 | const done = this.async();
13 |
14 | this.prompt([{
15 | type: 'confirm',
16 | name: 'doit',
17 | message: 'Would you like to try to prevent AWS credentials from being committed to this project\'s git repository (we\'ll create a new repo if one doesn\'t exist)?',
18 | }], (props) => {
19 | this.doit = props.doit;
20 | done();
21 | });
22 | },
23 |
24 | writing() {
25 | this.failed = false;
26 |
27 | if (!this.doit) {
28 | return;
29 | }
30 |
31 | const done = this.async();
32 |
33 | this.spawnCommand('git', ['init', '--quiet']).on('close', () => {
34 | this.spawnCommand('git', ['secrets', '--install']).on('close', (installCode) => {
35 | if (installCode !== 0) {
36 | this.failed = true;
37 | done();
38 | return;
39 | }
40 |
41 | this.spawnCommand('git', ['secrets', '--register-aws']).on('close', (awsInstallCode) => {
42 | if (awsInstallCode !== 0) {
43 | this.failed = true;
44 | }
45 | });
46 |
47 | done();
48 | });
49 | });
50 | },
51 |
52 | end() {
53 | if (this.failed === true) {
54 | this.log(`${chalk.bold(chalk.red('Enabling AWS secrets protection failed.'))}`);
55 | this.log(` (1) Ensure you have git-secrets installed on your computer by running with ${chalk.magenta('brew install git-secrets')}.`);
56 | this.log(` (2) Then run ${chalk.magenta('git secrets --install && git secrets --register-aws')} to manually protect this repo.`);
57 | }
58 | },
59 | });
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/yeoman,node,osx
3 |
4 | ### Node ###
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 |
24 | # nyc test coverage
25 | .nyc_output
26 |
27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
28 | .grunt
29 |
30 | # Bower dependency directory (https://bower.io/)
31 | bower_components
32 |
33 | # node-waf configuration
34 | .lock-wscript
35 |
36 | # Compiled binary addons (http://nodejs.org/api/addons.html)
37 | build/Release
38 |
39 | # Dependency directories
40 | node_modules/
41 | jspm_packages/
42 |
43 | # Typescript v1 declaration files
44 | typings/
45 |
46 | # Optional npm cache directory
47 | .npm
48 |
49 | # Optional eslint cache
50 | .eslintcache
51 |
52 | # Optional REPL history
53 | .node_repl_history
54 |
55 | # Output of 'npm pack'
56 | *.tgz
57 |
58 | # Yarn Integrity file
59 | .yarn-integrity
60 |
61 | # dotenv environment variables file
62 | .env
63 |
64 |
65 | ### OSX ###
66 | *.DS_Store
67 | .AppleDouble
68 | .LSOverride
69 |
70 | # Icon must end with two \r
71 | Icon
72 |
73 | # Thumbnails
74 | ._*
75 |
76 | # Files that might appear in the root of a volume
77 | .DocumentRevisions-V100
78 | .fseventsd
79 | .Spotlight-V100
80 | .TemporaryItems
81 | .Trashes
82 | .VolumeIcon.icns
83 | .com.apple.timemachine.donotpresent
84 |
85 | # Directories potentially created on remote AFP share
86 | .AppleDB
87 | .AppleDesktop
88 | Network Trash Folder
89 | Temporary Items
90 | .apdisk
91 |
92 | ### Yeoman ###
93 | bower_components/
94 |
95 | build/
96 | dist/
97 |
98 | # End of https://www.gitignore.io/api/yeoman,node,osx
99 |
100 | .yo-rc.json
101 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/templates/gulp/tasks/aws.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const confirm = require('gulp-confirm');
4 | const rename = require('gulp-rename');
5 | const awspublish = require('gulp-awspublish');
6 | const awspublishRouter = require("gulp-awspublish-router");
7 | const cloudfront = require('gulp-cloudfront-invalidate-aws-publish');
8 | const gulp = require('gulp');
9 |
10 | const awsJson = require('../../aws.json');
11 | const meta = require('../../meta.json');
12 |
13 | const cfSettings = {
14 | distribution: 'E3QK9W6AW5LAVH',
15 | accessKeyId: awsJson.accessKeyId,
16 | secretAccessKey: awsJson.secretAccessKey,
17 | wait: false,
18 | indexRootPath: true,
19 | };
20 |
21 | const oneDayInMS = 60 * 60 * 24;
22 |
23 | const routes = {
24 | routes: {
25 | // Cache video and audio for 1 week on user's computer, one month in CloudFront
26 | '^.*\\.(aif|iff|m3u|m4a|mid|mp3|mpa|ra|wav|wma|3g2|3gp|asf|asx|avi|flv|mov|mp4|mpg|rm|swf|vob|wmv)': {
27 | cacheTime: oneDayInMS * 7,
28 | sharedCacheTime: oneDayInMS * 30,
29 | },
30 | // Cache images 2 days on user's computer, one month in CloudFront
31 | '^.*\\.(jpg|jpeg|svg|bmp|png|tiff|gif)': {
32 | cacheTime: oneDayInMS * 2,
33 | sharedCacheTime: oneDayInMS * 30,
34 | },
35 | // Cache images 5 minutes on user's computer, one month in CloudFront
36 | '^.*\\.(html|js|css)': {
37 | cacheTime: 60 * 5,
38 | sharedCacheTime: oneDayInMS,
39 | },
40 | // Catch everything else in an empty route, or we'll get errors
41 | '.*': {},
42 | },
43 | };
44 |
45 | module.exports = () => {
46 | const publisher = awspublish.create(awsJson);
47 | const awsDirectory = `embeds/${meta.publishYear}/${meta.name}/`;
48 |
49 | return gulp.src('./dist/**/*')
50 | .pipe(confirm({
51 | question: `You're about to publish this project to AWS under directory ${
52 | awsDirectory
53 | }. Are you sure you want to do this?`,
54 | input: '_key:y',
55 | }))
56 | .pipe(rename((path) => {
57 | // eslint-disable-next-line no-param-reassign
58 | path.dirname = awsDirectory + path.dirname.replace('.\\', '');
59 | }))
60 | .pipe(awspublishRouter(routes))
61 | .pipe(publisher.publish({}, { force: false }))
62 | .pipe(cloudfront(cfSettings))
63 | .pipe(publisher.cache())
64 | .pipe(awspublish.reporter());
65 | };
66 |
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/resize-images.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const changed = require('gulp-changed');
8 | const fs = require('fs');
9 | const gulp = require('gulp');
10 | const imageResize = require('gulp-image-resize');
11 | const merge = require('merge-stream');
12 | const path = require('path');
13 | const rename = require('gulp-rename');
14 |
15 |
16 | module.exports = () => {
17 | function resize(size){
18 | /**
19 | * From gulp-changed source:
20 | * https://github.com/sindresorhus/gulp-changed/blob/master/index.js#L9
21 | */
22 | function fsOperationFailed(stream, sourceFile, err) {
23 | if (err) {
24 | if (err.code !== 'ENOENT') {
25 | stream.emit('error', new gutil.PluginError('gulp-changed', err, {
26 | fileName: sourceFile.path
27 | }));
28 | }
29 | stream.push(sourceFile);
30 | }
31 | return err;
32 | }
33 |
34 | /**
35 | * Custom comparator that takes into account filename changes.
36 | */
37 | function cacheCompare(stream, cb, sourceFile, targetPath) {
38 | const dir = path.dirname(targetPath);
39 | const ext = path.extname(targetPath);
40 |
41 | const base = path.basename(targetPath, ext);
42 |
43 | targetPath = path.join(dir, (base + ('-' + size.toString()) + ext.toLowerCase()));
44 |
45 | fs.stat(targetPath, (err, targetStat) => {
46 | if (!fsOperationFailed(stream, sourceFile, err)) {
47 | if (sourceFile.stat.mtime > targetStat.mtime) {
48 | stream.push(sourceFile);
49 | }
50 | }
51 | cb();
52 | });
53 | }
54 |
55 | return gulp.src([
56 | './src/images/opt/**/*.{png,jpg,JPG}',
57 | '!./src/images/opt/**/_*.{png,jpg,JPG}',
58 | ])
59 | .pipe(changed('./dist/images', { hasChanged: cacheCompare }))
60 | .pipe(imageResize({ width : size, upscale : false, imageMagick : true }))
61 | .pipe(rename(path => {
62 | path.basename += ('-' + size.toString());
63 | path.extname = path.extname.toLowerCase();
64 | return path;
65 | }))
66 | .pipe(gulp.dest('./dist/images'));
67 | }
68 |
69 | // Create and copy resized pngs
70 | const s1 = resize(3000);
71 | const s2 = resize(2400);
72 | const s3 = resize(1800);
73 | const s4 = resize(1200);
74 | const s5 = resize(600);
75 |
76 | return merge(s1, s2, s3, s4, s5);
77 | };
78 |
--------------------------------------------------------------------------------
/generators/app/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const _ = require('lodash');
8 | const fs = require('fs');
9 | const path = require('path');
10 | const yeoman = require('yeoman-generator');
11 |
12 |
13 | module.exports = yeoman.Base.extend({
14 | prompting() {
15 | const done = this.async();
16 |
17 | this.log(' ____ __ ____ _____\n');
18 | this.log(' / __ \\/ |/ / | / / | ____ ____ _____\n');
19 | this.log(' / / / / /|_/ / |/ / /| | / __ \\/ __ \\/ ___/\n');
20 | this.log(' / /_/ / / / / /| / ___ |/ /_/ / /_/ (__ )\n');
21 | this.log('/_____/_/ /_/_/ |_/_/ |_/ .___/ .___/____/\n');
22 | this.log(' /_/ /_/ \n');
23 |
24 | const generatorDir = path.join(this.sourceRoot(), '../..');
25 | const generatorSubdirs = _.filter(
26 | fs.readdirSync(generatorDir),
27 | // eslint-disable-next-line comma-dangle
28 | pathName => fs.statSync(path.join(generatorDir, pathName)).isDirectory()
29 | );
30 |
31 | let subGeneratorConfigs = [];
32 | _.each(generatorSubdirs, (dirName) => {
33 | const rawConfigPath = path.join(generatorDir, dirName, 'config.json');
34 |
35 | if (fs.existsSync(rawConfigPath)) {
36 | // eslint-disable-next-line import/no-dynamic-require,global-require
37 | const rawConfig = require(rawConfigPath);
38 |
39 | const finalConfig = _.clone(rawConfig);
40 |
41 | finalConfig.typeSlug = dirName;
42 |
43 | subGeneratorConfigs.push(finalConfig);
44 | }
45 | });
46 |
47 | subGeneratorConfigs = _.sortBy(subGeneratorConfigs, ['priority']);
48 |
49 | this.subGeneratorConfigs = subGeneratorConfigs;
50 |
51 | const prompts = [
52 | {
53 | type: 'list',
54 | name: 'module',
55 | message: 'Welcome. What would you like to make today?',
56 | choices: _.map(
57 | this.subGeneratorConfigs,
58 | // eslint-disable-next-line comma-dangle
59 | config => ({ name: config.verboseName, value: config.typeSlug })
60 | ),
61 | },
62 | ];
63 |
64 | this.prompt(prompts, (props) => {
65 | this.props = props;
66 | done();
67 | });
68 | },
69 |
70 | subgen() {
71 | if (_.includes(_.map(this.subGeneratorConfigs, 'typeSlug'), this.props.module)) {
72 | this.composeWith(
73 | `dmninteractives:${this.props.module}`,
74 | {
75 | options: {
76 | baseConfig: _.find(this.subGeneratorConfigs, { typeSlug: this.props.module }),
77 | },
78 | } // eslint-disable-line comma-dangle
79 | );
80 | }
81 | },
82 | });
83 |
--------------------------------------------------------------------------------
/generators/linters/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Mix this into your generator by adding a composeWith('linters') to your
3 | * generator's initializing() method to add ESLint configuration
4 | */
5 |
6 | const yeoman = require('yeoman-generator');
7 | const chalk = require('chalk');
8 |
9 |
10 | module.exports = yeoman.Base.extend({
11 | CHOICE_AIRBNB: 'airbnb',
12 | CHOICE_ES6_RECOMMENDED: 'es6-recommended',
13 | CHOICE_NO_ESLINT: 'none',
14 |
15 | prompting() {
16 | const done = this.async();
17 |
18 | const prompts = [{
19 | type: 'list',
20 | name: 'lintProfile',
21 | choices: [{
22 | value: this.CHOICE_AIRBNB,
23 | name: `${chalk.bold('eslint-config-airbnb')} [strict enforcement of ES2015]`,
24 | }, {
25 | value: this.CHOICE_ES6_RECOMMENDED,
26 | name: `${chalk.bold('es6-recommended')} [encourages ES2015 syntax, but more forgiving]`,
27 | }, {
28 | value: this.CHOICE_NO_ESLINT,
29 | name: `${chalk.bold('No ESLint')} [skips ESLint installation altogether]`,
30 | }],
31 | message: 'Would you like to add an ESLint configuration?',
32 | store: true,
33 | default: 0,
34 | }];
35 |
36 | this.prompt(prompts, (answers) => {
37 | this.lintProfile = answers.lintProfile;
38 | done();
39 | });
40 | },
41 |
42 | writing() {
43 | if (this.lintProfile === this.CHOICE_NO_ESLINT) return;
44 |
45 | const esLintConfig = {
46 | root: true,
47 | parser: 'babel-eslint',
48 | rules: {
49 | 'no-console': 0,
50 | },
51 | env: {
52 | browser: true,
53 | },
54 | };
55 |
56 | switch (this.lintProfile) {
57 | case (this.CHOICE_AIRBNB):
58 | Object.assign(esLintConfig, {
59 | extends: 'airbnb',
60 | });
61 | break;
62 | case (this.CHOICE_ES6_RECOMMENDED):
63 | Object.assign(esLintConfig, {
64 | extends: 'eslint:recommended',
65 | plugins: ['es6-recommended'],
66 | });
67 | break;
68 | default:
69 | }
70 |
71 | this.fs.writeJSON('./src/.eslintrc.json', esLintConfig);
72 | },
73 |
74 | install() {
75 | if (this.lintProfile === this.CHOICE_NO_ESLINT) return;
76 |
77 | const npmDeps = [
78 | 'eslint@4',
79 | 'babel-eslint',
80 | ];
81 |
82 | switch (this.lintProfile) {
83 | case this.CHOICE_AIRBNB:
84 | npmDeps.push(
85 | 'eslint-plugin-import@^2.6',
86 | 'eslint-plugin-react@^7.1',
87 | 'eslint-plugin-jsx-a11y@^5.1',
88 | 'eslint-config-airbnb@^15.0');
89 | break;
90 | case this.CHOICE_ES6_RECOMMENDED:
91 | npmDeps.push('eslint-plugin-es6-recommended');
92 | break;
93 | default:
94 | }
95 |
96 | this.npmInstall(npmDeps, { 'save-dev': true });
97 | },
98 | });
99 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # generator-dmninteractives [![NPM version][npm-image]][npm-url]
2 |
3 | A [Yeoman](http://yeoman.io) generator for DMN-flavored "interactive" pages with easy publishing.
4 |
5 | Translation: A simple app that helps speed up developing a custom "interactive" page using our DMN house template.
6 |
7 | ### What it does:
8 |
9 | - Scaffolds your project's development directory, shortcutting setup time.
10 | - Compiles and bundles SCSS and JS files.
11 | - Populates meta tags from a JSON file.
12 | - Creates responsive image sets optimized for mobile devices.
13 | - Publishes your project to an Amazon S3 bucket.
14 |
15 | **See the [wiki](https://github.com/DallasMorningNews/generator-dmninteractives/wiki) for complete instructions on using the app.**
16 |
17 | ## Requirements
18 |
19 | - Node - `brew install node`
20 | - Git - `brew install git`
21 | - _Recommended:_ `git-secrets` - `brew install git-secrets`
22 |
23 | ## Installation
24 |
25 | Install global dependencies, including Yeoman and the generator.
26 |
27 | ```bash
28 | $ npm install -g gulp-cli yo
29 | $ npm install -g --production generator-dmninteractives
30 | ```
31 |
32 | _(The `--production` flag is optional, but prevents your global Node modules folder from getting confused by the dev tooling for the generator)_
33 |
34 | ## Usage
35 |
36 | #### Starting a new project
37 |
38 | Create a clean directory for your project in your terminal.
39 |
40 | ```bash
41 | $ mkdir your-app-directory
42 | $ cd your-app-directory
43 | ```
44 |
45 | Run the generator in your new project directory.
46 |
47 | ```bash
48 | $ yo dmninteractives
49 | ```
50 |
51 | The generator will set up your working directory, install dependencies, copy template files and scripts, start a local webserver and open your browser.
52 |
53 | Be sure to fill out the `meta.json` file to correctly complete metatags in the template.
54 |
55 | #### Developing your project
56 |
57 | The generator uses [gulp](http://gulpjs.com/), a node-based task runner, to watch your directories for changes as you code, render templates, prepare static files and start a local webserver to preview your project in the browser.
58 |
59 | To work on your project, launch gulp in your app's root directory:
60 |
61 | ```bash
62 | $ gulp
63 | ```
64 |
65 | Your project is separated into two main directories:
66 | - `src`
67 | - `dist`
68 |
69 | The `src` directory is your working directory. You'll write all your code and place all necessary static assets in this directory.
70 |
71 | The `dist` directory includes transpiled SCSS, minified JavaScript and responsive images. Gulp serves a live preview of your page from this folder.
72 |
73 | #### Publishing your project
74 |
75 | Execute one of the gulp publish commands to publish to either the test or production directory of the bucket:
76 | - `gulp publish`
77 | - `gulp publish-test`
78 |
79 | [npm-image]: https://badge.fury.io/js/generator-dmninteractives.svg
80 | [npm-url]: https://npmjs.org/package/generator-dmninteractives
81 |
--------------------------------------------------------------------------------
/generators/graphic-module/templates/src/js/chart.js:
--------------------------------------------------------------------------------
1 | import * as d3 from "d3";
2 |
3 |
4 | // This is the chart function that will be exported
5 | export default () => ({
6 |
7 | // Develop the reusable function for you chart in this init function.
8 | // cf. https://bost.ocks.org/mike/chart/
9 | init: function() {
10 |
11 | // Default chart properties
12 | var stroke = '#eee';
13 |
14 | // Inner chart function
15 | function chart(selection){
16 | selection.each(function(data){
17 |
18 | var bbox = this.getBoundingClientRect();
19 | var width = bbox.width < bbox.height ? bbox.width : bbox.height;
20 | var height = width;
21 | var t = d3.transition()
22 | .duration(750);
23 |
24 |
25 | // Check if we've already appended our SVG.
26 | var g = d3.select(this).select('svg').select('g').size() === 0 ?
27 | d3.select(this).append("svg")
28 | .attr("width", width)
29 | .attr("height", height)
30 | .style("display", "block")
31 | .style("margin", "auto")
32 | .append("g") :
33 | d3.select(this).select('svg').select('g');
34 |
35 | var circles = g.selectAll("circle")
36 | .data(data, function(d, i){ return i; });
37 |
38 | circles.transition(t)
39 | .attr("fill", "#0f516e");
40 |
41 | circles.enter().append('circle') // Enter
42 | .attr("fill","#FF7216")
43 | .attr("cy", "60")
44 | .attr("stroke", stroke)
45 | .attr('cx',function(d, i){
46 | function add(a,b){return a + b;}
47 | return data.slice(0, i).reduce(add, 0) + d/2;
48 | })
49 | .merge(circles)
50 | .transition(t)
51 | .attr('cx',function(d, i){
52 | function add(a,b){return a + b;}
53 | return data.slice(0, i).reduce(add, 0) + d/2;
54 | })
55 | .attr("r", function(d){
56 | return d/2;
57 | });
58 |
59 | });
60 | }
61 |
62 | // Getter-setters
63 | chart.stroke = function(_){
64 | if (!arguments.length) return stroke;
65 | stroke = _;
66 | return chart;
67 | };
68 |
69 | return chart;
70 | },
71 |
72 |
73 | // This function actually draws the chart using the
74 | // reusable init function.
75 | draw: function(){
76 | var chart = this.init()
77 | .stroke(this._stroke);
78 |
79 | d3.select(this._selection)
80 | .datum(this._data)
81 | .call(chart);
82 | },
83 |
84 | // Call this function to initially create the chart.
85 | create: function(selection, data, stroke){
86 | this._selection = selection;
87 | this._data = data;
88 | this._stroke = stroke;
89 |
90 | this.draw();
91 | },
92 |
93 | // This updates the data and elements.
94 | update: function(data){
95 | this._data = data;
96 |
97 | this.draw();
98 | }
99 | });
100 |
--------------------------------------------------------------------------------
/generators/page/templates/gulp/tasks/aws.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 |
3 | 'use strict';
4 |
5 | /* eslint-enable strict */
6 |
7 | const awspublish = require('gulp-awspublish');
8 | const awspublishRouter = require("gulp-awspublish-router");
9 | const cloudfront = require('gulp-cloudfront-invalidate-aws-publish');
10 | const confirm = require('gulp-confirm');
11 | const deline = require('deline');
12 | const gulp = require('gulp');
13 | const gutil = require('gulp-util');
14 | const rename = require('gulp-rename');
15 | const S = require('string');
16 |
17 |
18 | const awsJson = require('./../../aws.json');
19 | const meta = require('./../../meta.json');
20 |
21 |
22 | const appName = S(meta.name).slugify().s;
23 | const publisher = awspublish.create(awsJson);
24 | const year = meta.publishYear;
25 |
26 |
27 | const awsDirectory = `${year}/${appName}/`;
28 |
29 | const cfSettings = {
30 | distribution: 'E3QK9W6AW5LAVH',
31 | accessKeyId: awsJson.accessKeyId,
32 | secretAccessKey: awsJson.secretAccessKey,
33 | wait: false,
34 | indexRootPath: true,
35 | };
36 |
37 | const oneDayInMS = 60 * 60 * 24;
38 |
39 | const routes = {
40 | routes: {
41 | // Cache video and audio for 1 week on user's computer, one month in CloudFront
42 | '^.*\\.(aif|iff|m3u|m4a|mid|mp3|mpa|ra|wav|wma|3g2|3gp|asf|asx|avi|flv|mov|mp4|mpg|rm|swf|vob|wmv)': {
43 | cacheTime: oneDayInMS * 7,
44 | sharedCacheTime: oneDayInMS * 30,
45 | },
46 | // Cache images 2 days on user's computer, one month in CloudFront
47 | '^.*\\.(jpg|jpeg|svg|bmp|png|tiff|gif)': {
48 | cacheTime: oneDayInMS * 2,
49 | sharedCacheTime: oneDayInMS * 30,
50 | },
51 | // Cache HTML/CSS/JS 5 minutes on user's computer, one month in CloudFront
52 | '^.*\\.(html|js|css)': {
53 | cacheTime: 60 * 5,
54 | sharedCacheTime: oneDayInMS,
55 | },
56 | // Catch everything else in an empty route, or we'll get errors
57 | '.*': {},
58 | },
59 | };
60 |
61 |
62 | module.exports = () =>
63 | gulp.src('./dist/**/*')
64 | .pipe(confirm({
65 | question: deline`You're about to publish this project to AWS
66 | under the directory '${awsDirectory}'.
67 | In the process, we'll also wipe out any
68 | uploads to the test directory.
69 | Are you sure you want to do this?`,
70 | input: '_key:y',
71 | }))
72 | .pipe(rename((filePath) => {
73 | // eslint-disable-next-line no-param-reassign
74 | filePath.dirname = awsDirectory + filePath.dirname.replace('.\\', '');
75 | }))
76 | .pipe(awspublishRouter(routes))
77 | .pipe(publisher.publish({}, { force: false }))
78 | .pipe(cloudfront(cfSettings))
79 | .pipe(publisher.cache())
80 | .pipe(awspublish.reporter())
81 | .on(
82 | 'end',
83 | gutil.log.bind(
84 | gutil,
85 | // eslint-disable-next-line comma-dangle
86 | `Published at 'http://interactives.dallasnews.com/${awsDirectory}'.`
87 | ) // eslint-disable-line comma-dangle
88 | );
89 |
--------------------------------------------------------------------------------
/generators/common/templates/gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/osx,windows,linux,node,sass,git,video,audio
3 |
4 | ### Audio ###
5 | *.aif
6 | *.iff
7 | *.m3u
8 | *.m4a
9 | *.mid
10 | *.mp3
11 | *.mpa
12 | *.ra
13 | *.wav
14 | *.wma
15 |
16 | ### Git ###
17 | *.orig
18 |
19 | ### Linux ###
20 | *~
21 |
22 | # temporary files which can be created if a process still has a handle open of a deleted file
23 | .fuse_hidden*
24 |
25 | # KDE directory preferences
26 | .directory
27 |
28 | # Linux trash folder which might appear on any partition or disk
29 | .Trash-*
30 |
31 | # .nfs files are created when an open file is removed but is still being accessed
32 | .nfs*
33 |
34 | ### Node ###
35 | # Logs
36 | logs
37 | *.log
38 | npm-debug.log*
39 | yarn-debug.log*
40 | yarn-error.log*
41 |
42 | # Runtime data
43 | pids
44 | *.pid
45 | *.seed
46 | *.pid.lock
47 |
48 | # Directory for instrumented libs generated by jscoverage/JSCover
49 | lib-cov
50 |
51 | # Coverage directory used by tools like istanbul
52 | coverage
53 |
54 | # nyc test coverage
55 | .nyc_output
56 |
57 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
58 | .grunt
59 |
60 | # Bower dependency directory (https://bower.io/)
61 | bower_components
62 |
63 | # node-waf configuration
64 | .lock-wscript
65 |
66 | # Compiled binary addons (http://nodejs.org/api/addons.html)
67 | build/Release
68 |
69 | # Dependency directories
70 | node_modules/
71 | jspm_packages/
72 |
73 | # Typescript v1 declaration files
74 | typings/
75 |
76 | # Optional npm cache directory
77 | .npm
78 |
79 | # Optional eslint cache
80 | .eslintcache
81 |
82 | # Optional REPL history
83 | .node_repl_history
84 |
85 | # Output of 'npm pack'
86 | *.tgz
87 |
88 | # Yarn Integrity file
89 | .yarn-integrity
90 |
91 | # dotenv environment variables file
92 | .env
93 |
94 |
95 | ### OSX ###
96 | *.DS_Store
97 | .AppleDouble
98 | .LSOverride
99 |
100 | # Icon must end with two \r
101 | Icon
102 |
103 | # Thumbnails
104 | ._*
105 |
106 | # Files that might appear in the root of a volume
107 | .DocumentRevisions-V100
108 | .fseventsd
109 | .Spotlight-V100
110 | .TemporaryItems
111 | .Trashes
112 | .VolumeIcon.icns
113 | .com.apple.timemachine.donotpresent
114 |
115 | # Directories potentially created on remote AFP share
116 | .AppleDB
117 | .AppleDesktop
118 | Network Trash Folder
119 | Temporary Items
120 | .apdisk
121 |
122 | ### Sass ###
123 | .sass-cache/
124 | *.css.map
125 |
126 | ### Video ###
127 | *.3g2
128 | *.3gp
129 | *.asf
130 | *.asx
131 | *.avi
132 | *.flv
133 | *.mov
134 | *.mp4
135 | *.mpg
136 | *.rm
137 | *.swf
138 | *.vob
139 | *.wmv
140 |
141 | ### Windows ###
142 | # Windows thumbnail cache files
143 | Thumbs.db
144 | ehthumbs.db
145 | ehthumbs_vista.db
146 |
147 | # Folder config file
148 | Desktop.ini
149 |
150 | # Recycle Bin used on file shares
151 | $RECYCLE.BIN/
152 |
153 | # Windows Installer files
154 | *.cab
155 | *.msi
156 | *.msm
157 | *.msp
158 |
159 | # Windows shortcuts
160 | *.lnk
161 |
162 | # End of https://www.gitignore.io/api/osx,windows,linux,node,sass,git,video,audio
163 |
164 | ### DMN-specific items
165 |
166 | # Secrets
167 | .env
168 | aws.json
169 |
170 | # gulp-awspublish cache
171 | .awspublish*
172 |
173 | # Vendored JavaScript
174 | src/vendor/*
175 |
176 | # Transpiled assets
177 | dist/*
178 |
179 | # Optimized images in src/
180 | src/images/opt
181 |
182 | # Original media assets
183 | src/assets/*
184 |
185 | # ZIP files
186 | *.zip
187 |
188 | # .gitkeeps (used by some subgenerators)
189 | !src/assets/.gitkeep
190 |
--------------------------------------------------------------------------------
/generators/page/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "verboseName": "Interactive page",
3 | "description": "TK",
4 | "priority": 1,
5 | "filesFromRepos": {
6 | "DallasMorningNews/interactives_starterkit": [
7 | {
8 | "source": "templates/base.html",
9 | "dest": { "path": "./src/templates/" }
10 | },
11 | {
12 | "source": "templates/index.html",
13 | "dest": { "path": "./src/templates/" }
14 | },
15 | {
16 | "source": "templates/adblock1.html",
17 | "dest": { "path": "./src/templates/" }
18 | },
19 | {
20 | "source": "templates/adblock2.html",
21 | "dest": { "path": "./src/templates/" }
22 | },
23 | {
24 | "source": "templates/adblock3.html",
25 | "dest": { "path": "./src/templates/" }
26 | },
27 | {
28 | "source": "css/theme.scss",
29 | "dest": { "path": "./src/scss/", "name": "_base.scss" }
30 | },
31 | {
32 | "source": "css/_mixins.scss",
33 | "dest": { "path": "./src/scss/" }
34 | },
35 | {
36 | "source": "css/_variables.scss",
37 | "dest": { "path": "./src/scss/" }
38 | },
39 | {
40 | "source": "css/header.scss",
41 | "dest": { "path": "./src/scss/", "name": "_header.scss" }
42 | },
43 | {
44 | "source": "css/footer.scss",
45 | "dest": { "path": "./src/scss/", "name": "_footer.scss" }
46 | },
47 | {
48 | "source": "css/components/_breakouts.scss",
49 | "dest": { "path": "./src/scss/components/", "name": "_breakouts.scss" }
50 | },
51 | {
52 | "source": "css/components/_charts.scss",
53 | "dest": { "path": "./src/scss/components/", "name": "_charts.scss" }
54 | },
55 | {
56 | "source": "css/components/_pullquotes.scss",
57 | "dest": { "path": "./src/scss/components/", "name": "_pullquotes.scss" }
58 | },
59 | {
60 | "source": "css/components/_reporter-bios.scss",
61 | "dest": { "path": "./src/scss/components/", "name": "_reporter-bios.scss" }
62 | },
63 | {
64 | "source": "css/components/_side-blocks.scss",
65 | "dest": { "path": "./src/scss/components/", "name": "_side-blocks.scss" }
66 | },
67 | {
68 | "source": "css/components/_slideshows.scss",
69 | "dest": { "path": "./src/scss/components/", "name": "_slideshows.scss" }
70 | },
71 | {
72 | "source": "css/components/_subscribe.scss",
73 | "dest": { "path": "./src/scss/components/", "name": "_subscribe.scss" }
74 | },
75 | {
76 | "source": "css/components/_videos.scss",
77 | "dest": { "path": "./src/scss/components/", "name": "_videos.scss" }
78 | },
79 | {
80 | "source": "js/furniture.js",
81 | "dest": { "path": "./src/js/", "name": "furniture.js" }
82 | },
83 | {
84 | "source": "js/customES6.js",
85 | "dest": { "path": "./src/js/", "name": "scripts.js" }
86 | },
87 | {
88 | "source": "js/slideshow.js",
89 | "dest": { "path": "./src/js/", "name": "slideshow.js" }
90 | },
91 | {
92 | "source": "style-guide/style-guide.md",
93 | "dest": { "path": "./src/style-guide/", "name": "style-guide.md" }
94 | }
95 | ]
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/generators/graphic-module/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const camelize = require('underscore.string/camelize');
4 |
5 | var yeoman = require('yeoman-generator'),
6 | mkdirp = require('mkdirp');
7 |
8 |
9 | module.exports = yeoman.Base.extend({
10 | initializing: function() {
11 | this.composeWith('dmninteractives:linters');
12 | this.composeWith('dmninteractives:common');
13 | this.composeWith('dmninteractives:gitsecrets');
14 | },
15 |
16 | prompting: function () {
17 | var done = this.async();
18 |
19 | this.log('Starting up a GRAPHIC MODULE...');
20 |
21 | var prompts = [{
22 | name:'appName',
23 | message: 'What\'s your npm project name, e.g., "dmn-chart-scatterplot"?'
24 | },{
25 | name:'objName',
26 | message: 'What\'s the name of the chart class users will call, e.g., "TexasChoropleth"?'
27 | }];
28 |
29 | this.prompt(prompts, function (props) {
30 |
31 | var features = props.features;
32 |
33 | function hasFeature(feat) {
34 | return features && features.indexOf(feat) !== -1;
35 | }
36 |
37 | this.objName = camelize(props.objName);
38 | this.appName = props.appName;
39 | done();
40 | }.bind(this));
41 | },
42 |
43 | writing: {
44 | appFiles: function () {
45 | this.fs.copyTpl(
46 | this.templatePath('package.json'),
47 | this.destinationPath('./package.json'),
48 | { appName: this.appName }
49 | );
50 | this.fs.copyTpl(
51 | this.templatePath('README'),
52 | this.destinationPath('./README.md'),
53 | {
54 | appName: this.appName,
55 | objName: this.objName
56 | }
57 | );
58 | this.fs.copy(
59 | this.templatePath('gulpfile.js'),
60 | this.destinationPath('./gulpfile.js')
61 | );
62 | this.fs.copy(
63 | this.templatePath('preview.png'),
64 | this.destinationPath('./preview.png')
65 | );
66 | },
67 |
68 | srcFiles: function () {
69 | this.fs.copy(
70 | this.templatePath('src/js/chart.js'),
71 | this.destinationPath('./src/js/chart.js')
72 | );
73 | this.fs.copyTpl(
74 | this.templatePath('src/js/global-chart.js'),
75 | this.destinationPath('./src/js/global-chart.js'),
76 | { objName: this.objName }
77 | );
78 | this.fs.copyTpl(
79 | this.templatePath('src/scss/_variables.scss'),
80 | this.destinationPath('./src/scss/_variables.scss'),
81 | { objName: this.objName }
82 | );
83 | this.fs.copyTpl(
84 | this.templatePath('src/scss/_chart-styles.scss'),
85 | this.destinationPath('./src/scss/_chart-styles.scss'),
86 | { objName: this.objName }
87 | );
88 | this.fs.copyTpl(
89 | this.templatePath('src/scss/styles.scss'),
90 | this.destinationPath('./src/scss/styles.scss'),
91 | { objName: this.objName }
92 | );
93 | },
94 |
95 | gulpFiles: function () {
96 | this.fs.copy(
97 | this.templatePath('gulp/index.js'),
98 | this.destinationPath('./gulp/index.js')
99 | );
100 | this.fs.copy(
101 | this.templatePath('gulp/tasks/browserify.js'),
102 | this.destinationPath('./gulp/tasks/browserify.js')
103 | );
104 | this.fs.copy(
105 | this.templatePath('gulp/tasks/sass.js'),
106 | this.destinationPath('./gulp/tasks/sass.js')
107 | );
108 | this.fs.copy(
109 | this.templatePath('gulp/tasks/server.js'),
110 | this.destinationPath('./gulp/tasks/server.js')
111 | );
112 | },
113 |
114 | distFiles: function() {
115 | this.fs.copyTpl(
116 | this.templatePath('dist/index.html'),
117 | this.destinationPath('./dist/index.html'),
118 | { objName: this.objName }
119 | );
120 | this.fs.copy(
121 | this.templatePath('dist/data/create.json'),
122 | this.destinationPath('./dist/data/create.json')
123 | );
124 | this.fs.copy(
125 | this.templatePath('dist/data/update.json'),
126 | this.destinationPath('./dist/data/update.json')
127 | );
128 | mkdirp('./dist/css');
129 | mkdirp('./dist/js');
130 | }
131 | },
132 |
133 | install: function () {
134 | this.installDependencies({
135 | callback: function() {
136 | this.emit('dependenciesInstalled');
137 | }.bind(this)
138 | });
139 |
140 | this.on('dependenciesInstalled', function() {
141 | this.spawnCommand('gulp');
142 | });
143 |
144 | },
145 |
146 | });
147 |
--------------------------------------------------------------------------------
/generators/embeddable-graphic/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const chalk = require('chalk');
4 | const slugify = require('underscore.string/slugify');
5 | const yeoman = require('yeoman-generator');
6 |
7 | const STARTERKIT = 'https://raw.githubusercontent.com/DallasMorningNews/' +
8 | 'interactives_starterkit/master/';
9 |
10 |
11 | module.exports = yeoman.Base.extend({
12 | initializing() {
13 | this.composeWith('dmninteractives:linters');
14 | this.composeWith('dmninteractives:common');
15 | this.composeWith('dmninteractives:nvm');
16 | this.composeWith('dmninteractives:gitsecrets');
17 | },
18 |
19 | prompting() {
20 | const done = this.async();
21 |
22 | this.log(
23 | 'Starting up an "Embeddable Graphic". See the generated README.md file for usage info.');
24 |
25 | const prompts = [{
26 | type: 'input',
27 | name: 'slug',
28 | message: 'What\'s your graphics\'s slug?',
29 | default: process.cwd().split(path.sep).pop(),
30 | filter: answer => slugify(answer),
31 | }, {
32 | type: 'checkbox',
33 | message: 'Which fonts would you like to include?',
34 | name: 'fonts',
35 | choices: [{
36 | name: 'PT Serif',
37 | value: 'https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700,700italic',
38 | }, {
39 | name: 'Montserrat',
40 | value: 'https://fonts.googleapis.com/css?family=Montserrat:400,700',
41 | checked: true,
42 | }, {
43 | name: 'FontAwesome',
44 | value: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
45 | }],
46 | }, {
47 | type: 'input',
48 | name: 'year',
49 | message: 'What year will this graphic publish?',
50 | default: () => (new Date()).getFullYear(),
51 | }, {
52 | type: 'input',
53 | name: 'awsAccessKey',
54 | message: 'What\'s your AWS access key?',
55 | store: true,
56 | }, {
57 | type: 'input',
58 | name: 'awsSecretKey',
59 | message: 'What\'s your AWS secret key?',
60 | store: true,
61 | }];
62 |
63 | this.prompt(prompts, (answers) => {
64 | this.prefs = answers;
65 | this.prefs.projectName = `embed_${answers.slug}`;
66 |
67 | done();
68 | });
69 | },
70 |
71 | writing: {
72 | app() {
73 | this.fs.copyTpl(
74 | this.templatePath('package.json'),
75 | this.destinationPath('./package.json'),
76 | { appName: this.prefs.projectName });
77 |
78 | this.fs.copy(
79 | this.templatePath('gulpfile.js'),
80 | this.destinationPath('./gulpfile.js'));
81 |
82 | this.fs.copy(
83 | this.templatePath('gulp/**/*.js'),
84 | this.destinationPath('./gulp/'));
85 | },
86 |
87 | projectfiles() {
88 | const done = this.async();
89 |
90 | // SCSS
91 | this.fetch(`${STARTERKIT}css/_mixins.scss`, './src/sass/', () => {
92 | this.fetch(`${STARTERKIT}css/_variables.scss`, './src/sass/', done);
93 | });
94 |
95 | this.fs.copy(
96 | this.templatePath('src/sass/**.scss'),
97 | this.destinationPath('./src/sass/'));
98 |
99 | // JavaScript
100 | this.fs.copy(
101 | this.templatePath('src/js/scripts.js'),
102 | this.destinationPath('./src/js/scripts.js'));
103 |
104 | // HTML
105 | this.fs.copyTpl(
106 | this.templatePath('src/index.html'),
107 | this.destinationPath('./src/index.html'),
108 | { fonts: this.prefs.fonts });
109 | this.fs.copyTpl(
110 | this.templatePath('src/embed.html'),
111 | this.destinationPath('./src/embed.html'),
112 | { slug: this.prefs.slug, fonts: this.prefs.fonts });
113 | },
114 |
115 | aws() {
116 | const awsJson = {
117 | accessKeyId: this.prefs.awsAccessKey,
118 | secretAccessKey: this.prefs.awsSecretKey,
119 | params: {
120 | Bucket: 'interactives.dallasnews.com',
121 | },
122 | };
123 |
124 | this.fs.writeJSON('aws.json', awsJson);
125 | },
126 |
127 | meta() {
128 | const metaJson = {
129 | name: this.prefs.slug,
130 | publishYear: this.prefs.year,
131 | };
132 | this.fs.writeJSON('meta.json', metaJson);
133 | },
134 |
135 | git() {
136 | this.fs.copyTpl(
137 | this.templatePath('README.md'),
138 | this.destinationPath('./README.md'),
139 | { slug: this.prefs.slug, year: this.prefs.year });
140 | },
141 | },
142 |
143 | install() {
144 | this.installDependencies({ bower: false });
145 | },
146 |
147 | end() {
148 | const done = this.async();
149 | const buildProcess = this.spawnCommand('gulp', ['build']);
150 |
151 | buildProcess.on('close', () => {
152 | this.log(`\n${chalk.bold('Done!')}`);
153 | this.log(` See the generated ${chalk.yellow('README.md')} for usage info.`);
154 | this.log(` Run ${chalk.magenta('gulp')} to start developing.\n`);
155 |
156 | done();
157 | });
158 | },
159 |
160 | });
161 |
--------------------------------------------------------------------------------
/generators/page/index.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const camelize = require('underscore.string/camelize');
3 | const chalk = require('chalk');
4 | const fs = require('fs');
5 | const github = require('octonode');
6 | const googleURL = require('google-url-helper');
7 | const mkdirp = require('mkdirp');
8 | const path = require('path');
9 | const slugify = require('underscore.string/slugify');
10 | const validURL = require('valid-url');
11 | const yeoman = require('yeoman-generator');
12 |
13 |
14 | const githubClient = github.client();
15 |
16 |
17 | module.exports = yeoman.Base.extend({
18 | initializing() {
19 | this.composeWith('dmninteractives:linters');
20 | this.composeWith('dmninteractives:common');
21 | this.composeWith('dmninteractives:nvm');
22 | this.composeWith('dmninteractives:gitsecrets');
23 | },
24 |
25 | prompting() {
26 | const done = this.async();
27 |
28 | this.log('Starting up an INTERACTIVE PAGE with BROWSERIFY...');
29 |
30 | this.baseConfig = this.options.baseConfig;
31 |
32 | const prompts = [
33 | {
34 | name: 'directoryName',
35 | message: 'What\'s your directory name?',
36 | },
37 | {
38 | name: 'awsAccessKey',
39 | message: 'What\'s your AWS access key?',
40 | },
41 | {
42 | name: 'awsSecretKey',
43 | message: 'What\'s your AWS secret key?',
44 | },
45 | {
46 | name: 'hotCopyID',
47 | message: '[Optional] Enter the ID or URL of this page\'s Google Doc copy.',
48 | },
49 | ];
50 |
51 | this.prompt(prompts, (props) => {
52 | this.directoryName = slugify(props.directoryName);
53 | this.appName = camelize(props.directoryName);
54 | this.awsAccessKey = props.awsAccessKey;
55 | this.awsSecretKey = props.awsSecretKey;
56 |
57 | if (props.hotCopyID === '') {
58 | this.hotCopyID = null;
59 | } else {
60 | this.hotCopyID = (
61 | validURL.isUri(props.hotCopyID)
62 | ) ? (
63 | googleURL.parseId(props.hotCopyID)
64 | ) : (
65 | props.hotCopyID
66 | );
67 | }
68 |
69 | done();
70 | });
71 | },
72 |
73 | writing: {
74 | app() {
75 | this.fs.copyTpl(
76 | this.templatePath('package.json'),
77 | this.destinationPath('./package.json'),
78 | { appName: this.appName } // eslint-disable-line comma-dangle
79 | );
80 | },
81 |
82 | gulpfiles() {
83 | this.fs.copy(
84 | this.templatePath('gulpfile.js'),
85 | this.destinationPath('./gulpfile.js') // eslint-disable-line comma-dangle
86 | );
87 |
88 | this.fs.copy(
89 | this.templatePath('./gulp/**/*'),
90 | this.destinationPath('./gulp/') // eslint-disable-line comma-dangle
91 | );
92 | },
93 |
94 | projectfiles() {
95 | // ---------------------------------------
96 | // Fetch remote template files from github
97 | // hosted in interactives_starterkit
98 | if (!_.isUndefined(this.baseConfig.filesFromRepos)) {
99 | _.each(this.baseConfig.filesFromRepos, (repoFiles, repoName) => {
100 | const repo = githubClient.repo(repoName);
101 |
102 | _.each(repoFiles, (file) => {
103 | const destFilePath = (
104 | _.has(file.dest, 'name')
105 | ) ? (
106 | path.join(file.dest.path, file.dest.name)
107 | ) : (
108 | path.join(file.dest.path, path.basename(file.source))
109 | );
110 |
111 | mkdirp(path.dirname(destFilePath));
112 |
113 | repo.contents(file.source, (error, contents) => {
114 | const fileContents = new Buffer(contents.content, 'base64');
115 |
116 | fs.writeFileSync(destFilePath, fileContents);
117 | });
118 | });
119 | });
120 | }
121 |
122 | // ---------------------------
123 | // Copy rest of template files
124 |
125 | this.fs.copy(
126 | this.templatePath('styles.scss'),
127 | // eslint-disable-next-line comma-dangle
128 | this.destinationPath('./src/scss/styles.scss')
129 | );
130 |
131 | this.fs.copy(
132 | this.templatePath('data.json'),
133 | // eslint-disable-next-line comma-dangle
134 | this.destinationPath('./src/data/data.json')
135 | );
136 |
137 | this.fs.copy(
138 | this.templatePath('img.html'),
139 | // eslint-disable-next-line comma-dangle
140 | this.destinationPath('./src/templates/partials/img.html')
141 | );
142 |
143 | this.fs.copy(
144 | this.templatePath('defaultImage.jpg'),
145 | // eslint-disable-next-line comma-dangle
146 | this.destinationPath('./src/images/_defaultImage.jpg')
147 | );
148 |
149 | this.fs.copy(
150 | this.templatePath('buttonLeft.svg'),
151 | // eslint-disable-next-line comma-dangle
152 | this.destinationPath('./src/images/buttonLeft.svg')
153 | );
154 |
155 | this.fs.copy(
156 | this.templatePath('buttonRight.svg'),
157 | // eslint-disable-next-line comma-dangle
158 | this.destinationPath('./src/images/buttonRight.svg')
159 | );
160 |
161 | this.fs.copyTpl(
162 | this.templatePath('README.md'),
163 | this.destinationPath('./README.md'),
164 | // eslint-disable-next-line comma-dangle
165 | { slug: this.directoryName, year: (new Date()).getFullYear() }
166 | );
167 |
168 | // -------------------------
169 | // Create output directories
170 | mkdirp('./dist');
171 | },
172 |
173 | aws() {
174 | const awsJson = {
175 | accessKeyId: this.awsAccessKey,
176 | secretAccessKey: this.awsSecretKey,
177 | params: {
178 | Bucket: 'interactives.dallasnews.com',
179 | },
180 | };
181 |
182 | this.fs.writeJSON('aws.json', awsJson);
183 | },
184 |
185 | meta() {
186 | const timestamp = new Date();
187 | const rawMonth = (timestamp.getMonth() + 1).toString();
188 | const rawDate = timestamp.getDate().toString();
189 |
190 | const month = rawMonth.length === 1 ? `0${rawMonth}` : rawMonth;
191 | const date = rawDate.length === 1 ? `0${rawDate}` : rawDate;
192 |
193 | const defaultKeywords = [
194 | 'interactives', 'dallas', 'dallas news', 'dfw news', 'dallas newspaper',
195 | 'dallas morning news', 'dallas morning news newspaper',
196 | ]; // Archie-able
197 | const metaJson = {
198 | id: (Math.floor(Math.random() * 100000000000) + 1).toString(),
199 | name: this.directoryName,
200 | pageTitle: '