<commit_before>(function(undefined) {

  'use strict';

  /**
   * Requires gulp
   */
  var gulp = require('gulp');

  /**
   * Requires gulp dependencies
   */
  var concat     = require('gulp-concat');
  var connect    = require('gulp-connect');
  var jade       = require('gulp-jade');
  var jshint     = require('gulp-jshint');
  var livereload = require('gulp-livereload');
  var plumber    = require('gulp-plumber');
  var sourcemaps = require('gulp-sourcemaps');
  var stylus     = require('gulp-stylus');
  var uglify     = require('gulp-uglify');
  var watch      = require('gulp-watch');

  /** Stylus dependencies */
  var jeet    = require('jeet');
  var kouto   = require('kouto-swiss');
  var rupture = require('rupture');

  /** Jshint dependencies */
  var stylish = require('jshint-stylish');

  /**
   * Define paths, sources and destinations
   */
   var stylusSrc   = ['front/stylesheets/paper.styl'];
   var stylusFiles = ['front/stylesheets/**/*.styl'];
   var stylusDest  = 'public/assets/stylesheets';

   var scriptsSrc   = ['front/javascripts/**/*.js'];
   var scriptsFiles = scriptsSrc;
   var scriptsDest  = 'public/assets/javascripts';

   var jadeSrc   = ['front/**/*.jade'];
   var jadeFiles = jadeSrc;
   var jadeDest  = 'public';

  /**
   * Server task
   *
   * Creates a static server using connect with livereload support
   */
  gulp.task('server', function() {
    connect.server({
      port: 3000,
      root: 'public',
      livereload: true
    });
  });

  /**
   * Jade task
   *
   * Concats javascripts files and minifies them
   */
  gulp.task('jade', function() {
    gulp.src(jadeSrc)
      .pipe(plumber())
      .pipe(jade({pretty: true}))
      .pipe(gulp.dest(jadeDest))
      .pipe(livereload());
  });

  /**
   * Jade task
   *
   * Concats javascripts files and minifies them
   */
  gulp.task('jshint', function() {
    gulp.src(scriptsFiles)
      .pipe(plumber())
      .pipe(watch(scriptsFiles))
      .pipe(jshint())
      .pipe(jshint.reporter(stylish))
  });

  /**
   * Scripts task
   *
   * Concats javascripts files and minifies them
   */
  gulp.task('scripts', function() {
    gulp.src(scriptsSrc)
      .pipe(plumber())
      .pipe(sourcemaps.init())
      .pipe(concat('paper.js'))
      .pipe(uglify())
      .pipe(sourcemaps.write())
      .pipe(gulp.dest(scriptsDest))
      .pipe(livereload());
  });

  /**
   * Stylus task
   *
   * Compiles stylus files into css files and creates sourcemaps
   */
  gulp.task('stylus', function() {
    var stylusOptions = {
      compress: true,
      use: [jeet(), kouto(), rupture()]
    };

    gulp.src(stylusSrc)
      .pipe(plumber())
      .pipe(sourcemaps.init())
      .pipe(stylus(stylusOptions))
      .pipe(sourcemaps.write())
      .pipe(gulp.dest(stylusDest))
      .pipe(livereload());
  });

  /**
   * Watch task
   *
   * Watches for changes and perform gulp tasks accordingly
   */
  gulp.task('watch', ['jade', 'server', 'scripts', 'stylus'], function() {
    livereload.listen();

    gulp.watch(stylusFiles, ['stylus']);
    gulp.watch(scriptsFiles, ['scripts']);
    gulp.watch(jadeFiles, ['jade']);
  });

})();
<commit_msg>Update gulpfile.js<commit_after>(function(undefined) {

  'use strict';

  /**
   * Requires gulp
   */
  var gulp = require('gulp');

  /**
   * Requires gulp dependencies
   */
  var concat     = require('gulp-concat');
  var connect    = require('gulp-connect');
  var jade       = require('gulp-jade');
  var jshint     = require('gulp-jshint');
  var livereload = require('gulp-livereload');
  var plumber    = require('gulp-plumber');
  var sourcemaps = require('gulp-sourcemaps');
  var stylus     = require('gulp-stylus');
  var uglify     = require('gulp-uglify');
  var watch      = require('gulp-watch');

  /** Stylus dependencies */
  var jeet    = require('jeet');
  var kouto   = require('kouto-swiss');
  var rupture = require('rupture');

  /** Jshint dependencies */
  var stylish = require('jshint-stylish');

  /**
   * Define paths, sources and destinations
   */
  var stylusSrc   = ['front/stylesheets/paper.styl'];
  var stylusFiles = ['front/stylesheets/**/*.styl'];
  var stylusDest  = 'public/assets/stylesheets';

  var scriptsSrc   = ['front/javascripts/**/*.js'];
  var scriptsFiles = scriptsSrc;
  var scriptsDest  = 'public/assets/javascripts';

  var jadeSrc   = ['front/**/*.jade'];
  var jadeFiles = jadeSrc;
  var jadeDest  = 'public';

  /**
   * Server task
   *
   * Creates a static server using connect with livereload support
   */
  gulp.task('server', function() {
    connect.server({
      port: 3000,
      root: 'public',
      livereload: true
    });
  });

  /**
   * Jade task
   *
   * Concats javascripts files and minifies them
   */
  gulp.task('jade', function() {
    gulp.src(jadeSrc)
      .pipe(plumber())
      .pipe(jade({pretty: true}))
      .pipe(gulp.dest(jadeDest))
      .pipe(livereload());
  });

  /**
   * Jade task
   *
   * Concats javascripts files and minifies them
   */
  gulp.task('jshint', function() {
    gulp.src(scriptsFiles)
      .pipe(plumber())
      .pipe(watch(scriptsFiles))
      .pipe(jshint())
      .pipe(jshint.reporter(stylish))
  });

  /**
   * Scripts task
   *
   * Concats javascripts files and minifies them
   */
  gulp.task('scripts', function() {
    gulp.src(scriptsSrc)
      .pipe(plumber())
      .pipe(sourcemaps.init())
      .pipe(concat('paper.js'))
      .pipe(uglify())
      .pipe(sourcemaps.write())
      .pipe(gulp.dest(scriptsDest))
      .pipe(livereload());
  });

  /**
   * Stylus task
   *
   * Compiles stylus files into css files and creates sourcemaps
   */
  gulp.task('stylus', function() {
    var stylusOptions = {
      compress: true,
      use: [jeet(), kouto(), rupture()]
    };

    gulp.src(stylusSrc)
      .pipe(plumber())
      .pipe(sourcemaps.init())
      .pipe(stylus(stylusOptions))
      .pipe(sourcemaps.write())
      .pipe(gulp.dest(stylusDest))
      .pipe(livereload());
  });

  /**
   * Watch task
   *
   * Watches for changes and perform gulp tasks accordingly
   */
  gulp.task('watch', ['jade', 'server', 'scripts', 'stylus'], function() {
    livereload.listen();

    gulp.watch(stylusFiles, ['stylus']);
    gulp.watch(scriptsFiles, ['scripts']);
    gulp.watch(jadeFiles, ['jade']);
  });

})();
<|endoftext|>


<commit_before>/*!
 * gulp
 * Dependencies
 * - You need node installed if you don't already have it - https://nodejs.org/en/
 * - Open Terminal
 * - Run command 'npm install --global gulp'
 *
 * Project usage
 * - Navigate to the root of your project in terminal with 'cd <path to your project>'
 * - Run the command 'npm install' which installs all of the gulp plugins to the project
 * - Run the command 'gulp' if you want to manually clean up and compile all CSS, JS, and images
 * - Run the command 'gulp watch'
 */

var

	// Config
	config = {
		url: 'front-end-baseplate.dev.com',
		tinypngKey: process.env.TINYPNG_KEY,
		autoprefixer: ['last 2 versions', 'IE 9', 'Safari 8']
	},

	// Dependencies
	browserSync = require('browser-sync'),
	gulp = require('gulp'),
	del = require('del'),
	path = require('path'),
	summary = require('engage-eslint-summary'),
	plugins = require('gulp-load-plugins')(),

	// Paths
	base = {
		src: 'src',
		public: 'public'
	},
	assets = base.public + '/assets',
	paths = {
		styles: {
			src: base.src + '/scss/**/*.scss',
			dest: assets + '/css'
		},
		scripts: {
			dir: base.src + '/js',
			src: base.src + '/js/**/*.js',
			dest: assets + '/js'
		},
		images: {
			dir: base.src + '/img',
			src: base.src + '/img/**/*',
			dest: assets + '/img',
			icon: base.src + '/img/meta/favicon-180.png'
		},
		svgIcons: {
			src: base.src + '/img/svg-icons/**/*',
			dest: assets + '/img/svg-icons'
		},
		static: {
			src: base.src + '/static/**/*',
			dest: assets + '/static'
		}
	};

// Clean

gulp.task('clean', function(cb) {
	return del(assets, cb);
});

// Styles

gulp.task('styles', function() {
	return gulp
		.src(paths.styles.src)
		.pipe(plugins.sourcemaps.init())
		.pipe(
			plugins.sass({ includePaths: ['node_modules'] })
				.on('error', plugins.notify.onError({
					title: 'Sass Error',
					subtitle: [
						'<%= error.relativePath %>',
						'<%= error.line %>'
					].join(':'),
					message: '<%= error.messageOriginal %>',
					open: 'file://<%= error.file %>',
					onLast: true,
					icon: paths.images.icon
				}))
		)
		.pipe(plugins.cleanCss({ restructuring: false }))
		.pipe(plugins.autoprefixer({
			browsers: config.autoprefixer,
			cascade: false
		}))
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.styles.dest));
});

// JS

gulp.task('scripts.lint', function() {
	return gulp
		.src(paths.scripts.dir + '/site/**/*.js')
		.pipe(plugins.eslint())
		.pipe(plugins.eslint.format(summary))
		.pipe(
			plugins.eslint.failOnError()
				.on('error', plugins.notify.onError({
					title: 'JavaScript Error',
					subtitle: [
						'<%= options.relative(options.cwd, error.fileName) %>',
						'<%= error.lineNumber %>'
					].join(':'),
					message: '<%= error.message %>',
					open: 'file://<%= error.fileName %>',
					templateOptions: {
						relative: path.relative,
						cwd: process.cwd()
					},
					icon: paths.images.icon
				}))
		)
		.pipe(plugins.eslint.failAfterError());
});

gulp.task('scripts.lint.full', function() {
	return gulp
		.src(paths.scripts.dir + '/site/**/*.js')
		.pipe(plugins.eslint())
		.pipe(plugins.eslint.format())
		.pipe(plugins.eslint.failAfterError());
});

gulp.task('scripts.site', function() {
	return gulp
		.src([
			paths.scripts.dir + '/site/core.js',
			paths.scripts.dir + '/site/modules/*.js',
			paths.scripts.dir + '/site/pages/*.js'
		])
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('site.js'))
		.pipe(plugins.changed(paths.scripts.dest))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.scripts.dest));
});

gulp.task('scripts.plugins', function() {
	return gulp
		.src([
			paths.scripts.dir + '/plugins/*.js',
			paths.scripts.dir + '/plugins/jquery/*.js'
		])
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('plugins.js'))
		.pipe(plugins.changed(paths.scripts.dest))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.scripts.dest));
});

gulp.task('scripts.libs', function() {
	return gulp
		.src(paths.scripts.dir + '/libs/*.js')
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('libs.js'))
		.pipe(plugins.changed(paths.scripts.dest))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.scripts.dest));
});

gulp.task('scripts', ['scripts.lint'], function() {
	gulp.start('scripts.site', 'scripts.plugins', 'scripts.libs');
});

// Images

gulp.task('images', function() {
	var
		optimised = plugins.filter('**/*.{jpg,png}', { restore: true }),
		svgs = plugins.filter('**/*.svg', { restore: true });

	return gulp
		.src(paths.images.src)
		.pipe(optimised)
		.pipe(plugins.tinypngCompress({
			key: config.tinypngKey,
			sigFile: paths.images.dir + '/.tinypng',
			summarise: true
		}))
		.pipe(optimised.restore)
		.pipe(svgs)
		.pipe(plugins.svgmin())
		.pipe(svgs.restore)
		.pipe(gulp.dest(paths.images.dest));
});

// SVG icon sprite

gulp.task('svg-icon-sprite', function() {
	return gulp
		.src(paths.svgIcons.src)
		.pipe(plugins.svgSprite({
			mode: {
				symbol: {
					dest: '',
					sprite: 'sprite.svg'
				}
			},
			svg: {
				xmlDeclaration: false,
				doctypeDeclaration: false
			}
		}))
		.pipe(gulp.dest(paths.svgIcons.dest));
});

// Static

gulp.task('static', function() {
	return gulp
		.src(paths.static.src)
		.pipe(gulp.dest(paths.static.dest));
});

// Watch

gulp.task('watch', function() {
	browserSync.init({
		ghostMode: { scroll: false },
		notify: false,
		open: false,
		proxy: config.url,
		port: 5757,
		files: [
			paths.styles.dest + '/**/*.css',
			paths.scripts.dest + '/**/*.js',
			paths.images.dest,
			base.public + '/**/*.{html,php}',
			'app/views/**/*.php',
			'resources/views/**/*.php'
		]
	});

	gulp.watch(paths.styles.src, ['styles']);
	gulp.watch(paths.scripts.src, ['scripts']);
	gulp.watch(paths.images.src, ['images']);
	gulp.watch(paths.svgIcons.src, ['svg-icon-sprite']);
	gulp.watch(paths.static.src, ['static']);
});

gulp.task('default', [], function() {
	gulp.start('styles', 'scripts', 'images', 'svg-icon-sprite', 'static');
});
<commit_msg>Moves tinyPng sigfile to public directory.<commit_after>/*!
 * gulp
 * Dependencies
 * - You need node installed if you don't already have it - https://nodejs.org/en/
 * - Open Terminal
 * - Run command 'npm install --global gulp'
 *
 * Project usage
 * - Navigate to the root of your project in terminal with 'cd <path to your project>'
 * - Run the command 'npm install' which installs all of the gulp plugins to the project
 * - Run the command 'gulp' if you want to manually clean up and compile all CSS, JS, and images
 * - Run the command 'gulp watch'
 */

var

	// Config
	config = {
		url: 'front-end-baseplate.dev.com',
		tinypngKey: process.env.TINYPNG_KEY,
		autoprefixer: ['last 2 versions', 'IE 9', 'Safari 8']
	},

	// Dependencies
	browserSync = require('browser-sync'),
	gulp = require('gulp'),
	del = require('del'),
	path = require('path'),
	summary = require('engage-eslint-summary'),
	plugins = require('gulp-load-plugins')(),

	// Paths
	base = {
		src: 'src',
		public: 'public'
	},
	assets = base.public + '/assets',
	paths = {
		styles: {
			src: base.src + '/scss/**/*.scss',
			dest: assets + '/css'
		},
		scripts: {
			dir: base.src + '/js',
			src: base.src + '/js/**/*.js',
			dest: assets + '/js'
		},
		images: {
			dir: base.src + '/img',
			src: base.src + '/img/**/*',
			dest: assets + '/img',
			icon: base.src + '/img/meta/favicon-180.png'
		},
		svgIcons: {
			src: base.src + '/img/svg-icons/**/*',
			dest: assets + '/img/svg-icons'
		},
		static: {
			src: base.src + '/static/**/*',
			dest: assets + '/static'
		}
	};

// Clean

gulp.task('clean', function(cb) {
	return del(assets, cb);
});

// Styles

gulp.task('styles', function() {
	return gulp
		.src(paths.styles.src)
		.pipe(plugins.sourcemaps.init())
		.pipe(
			plugins.sass({ includePaths: ['node_modules'] })
				.on('error', plugins.notify.onError({
					title: 'Sass Error',
					subtitle: [
						'<%= error.relativePath %>',
						'<%= error.line %>'
					].join(':'),
					message: '<%= error.messageOriginal %>',
					open: 'file://<%= error.file %>',
					onLast: true,
					icon: paths.images.icon
				}))
		)
		.pipe(plugins.cleanCss({ restructuring: false }))
		.pipe(plugins.autoprefixer({
			browsers: config.autoprefixer,
			cascade: false
		}))
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.styles.dest));
});

// JS

gulp.task('scripts.lint', function() {
	return gulp
		.src(paths.scripts.dir + '/site/**/*.js')
		.pipe(plugins.eslint())
		.pipe(plugins.eslint.format(summary))
		.pipe(
			plugins.eslint.failOnError()
				.on('error', plugins.notify.onError({
					title: 'JavaScript Error',
					subtitle: [
						'<%= options.relative(options.cwd, error.fileName) %>',
						'<%= error.lineNumber %>'
					].join(':'),
					message: '<%= error.message %>',
					open: 'file://<%= error.fileName %>',
					templateOptions: {
						relative: path.relative,
						cwd: process.cwd()
					},
					icon: paths.images.icon
				}))
		)
		.pipe(plugins.eslint.failAfterError());
});

gulp.task('scripts.lint.full', function() {
	return gulp
		.src(paths.scripts.dir + '/site/**/*.js')
		.pipe(plugins.eslint())
		.pipe(plugins.eslint.format())
		.pipe(plugins.eslint.failAfterError());
});

gulp.task('scripts.site', function() {
	return gulp
		.src([
			paths.scripts.dir + '/site/core.js',
			paths.scripts.dir + '/site/modules/*.js',
			paths.scripts.dir + '/site/pages/*.js'
		])
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('site.js'))
		.pipe(plugins.changed(paths.scripts.dest))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.scripts.dest));
});

gulp.task('scripts.plugins', function() {
	return gulp
		.src([
			paths.scripts.dir + '/plugins/*.js',
			paths.scripts.dir + '/plugins/jquery/*.js'
		])
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('plugins.js'))
		.pipe(plugins.changed(paths.scripts.dest))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.scripts.dest));
});

gulp.task('scripts.libs', function() {
	return gulp
		.src(paths.scripts.dir + '/libs/*.js')
		.pipe(plugins.sourcemaps.init())
		.pipe(plugins.concat('libs.js'))
		.pipe(plugins.changed(paths.scripts.dest))
		.pipe(plugins.uglify())
		.pipe(plugins.sourcemaps.write('.'))
		.pipe(gulp.dest(paths.scripts.dest));
});

gulp.task('scripts', ['scripts.lint'], function() {
	gulp.start('scripts.site', 'scripts.plugins', 'scripts.libs');
});

// Images

gulp.task('images', function() {
	var
		optimised = plugins.filter('**/*.{jpg,png}', { restore: true }),
		svgs = plugins.filter('**/*.svg', { restore: true });

	return gulp
		.src(paths.images.src)
		.pipe(optimised)
		.pipe(plugins.tinypngCompress({
			key: config.tinypngKey,
			sigFile: paths.images.dest + '/.tinypng',
			summarise: true
		}))
		.pipe(optimised.restore)
		.pipe(svgs)
		.pipe(plugins.svgmin())
		.pipe(svgs.restore)
		.pipe(gulp.dest(paths.images.dest));
});

// SVG icon sprite

gulp.task('svg-icon-sprite', function() {
	return gulp
		.src(paths.svgIcons.src)
		.pipe(plugins.svgSprite({
			mode: {
				symbol: {
					dest: '',
					sprite: 'sprite.svg'
				}
			},
			svg: {
				xmlDeclaration: false,
				doctypeDeclaration: false
			}
		}))
		.pipe(gulp.dest(paths.svgIcons.dest));
});

// Static

gulp.task('static', function() {
	return gulp
		.src(paths.static.src)
		.pipe(gulp.dest(paths.static.dest));
});

// Watch

gulp.task('watch', function() {
	browserSync.init({
		ghostMode: { scroll: false },
		notify: false,
		open: false,
		proxy: config.url,
		port: 5757,
		files: [
			paths.styles.dest + '/**/*.css',
			paths.scripts.dest + '/**/*.js',
			paths.images.dest,
			base.public + '/**/*.{html,php}',
			'app/views/**/*.php',
			'resources/views/**/*.php'
		]
	});

	gulp.watch(paths.styles.src, ['styles']);
	gulp.watch(paths.scripts.src, ['scripts']);
	gulp.watch(paths.images.src, ['images']);
	gulp.watch(paths.svgIcons.src, ['svg-icon-sprite']);
	gulp.watch(paths.static.src, ['static']);
});

gulp.task('default', [], function() {
	gulp.start('styles', 'scripts', 'images', 'svg-icon-sprite', 'static');
});
<|endoftext|>


<commit_before>// NPM packages
var browserify = require('browserify');
var gjslint = require('gulp-gjslint');
var gulp = require('gulp');
var gutil = require('gulp-util');
var source = require('vinyl-source-stream');
var htmlProcessor = require('gulp-htmlprocessor');
var sass = require('gulp-sass');

// Node modules
var exec = require('child_process').exec;
var fs = require('fs');

// These are the primary tasks
gulp.task('test-gas', ['deploy-gas'], openGAS);
gulp.task('test-web', ['build-web'], openWeb);

// General
gulp.task('lint-all', closureLint);
gulp.task('fix-all', closureFix);
gulp.task('browserify', browserifyBundle);
gulp.task('compile-sass', compileSASS);

// Web specific
gulp.task('build-web', ['browserify', 'compile-sass'], buildWeb);

// GAS specific
gulp.task('deploy-gas', ['swap-tags'], deployGAS);
gulp.task('swap-tags', ['build-gas'], replaceTags);
gulp.task('build-gas', ['browserify'], buildGAS);

/**
 * Bundles up client.js (and all required functionality) and places it in a build directory.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function browserifyBundle() {
  return browserify('./src/client/js/client.js')
      .bundle()
      .on('error', function(e) {
        gutil.log(e);
      })
      .pipe(source('bundle.js'))
      .pipe(gulp.dest('./build/common'));
}

/**
 * Builds the project for GAS.
 * GAS doesn't allow seperate CSS or JS, so both need to be injected into the main HTML.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function buildGAS() {

  gulp.src('./src/client/html/**', {
    base: './src/client'
  })
      .pipe(gulp.dest('./build/gas'));

  // GAS
  return gulp.src('./src/GAS/*')
      .pipe(gulp.dest('./build/gas/GAS'));
}

/**
 * Replaces all script tags and css links.
 * Note: This is done relative to this gulpfile.
 * All swap tags are relative to this gulpfile.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function replaceTags() {
  return gulp.src('./build/gas/html/*.html')
      .pipe(htmlProcessor({
        includeBase: './'
      }))
      .pipe(gulp.dest('./build/gas/html/'));
}

/**
 * Builds the project for the web.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function buildWeb() {
  gulp.src('./build/common/bundle.js')
      .pipe(gulp.dest('./build/web/client/js'));

  gulp.src('./src/client/css/*')
      .pipe(gulp.dest('./build/web/client/css'));

  gulp.src('./src/client/html/*')
      .pipe(gulp.dest('./build/web/client/html'));

  gulp.src('./src/client/images/*')
      .pipe(gulp.dest('./build/web/client/images'));

  gulp.src('./build/common/css/*')
      .pipe(gulp.dest('./build/web/client/css'));

  return gulp.src('./src/GAS/*')
      .pipe(gulp.dest('./build/web/GAS'));
}

/**
 * Deploys the GAS code up to the project.
 * Calls browserifyBundle, then buildGAS.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function deployGAS(cb) {
  return exec('gapps push', function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Opens up the project in GAS in chrome.
 * Calls browserifyBundle, then buildGAS, then deployGAS.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function openGAS(cb) {
  // Open the project in chrome
  var key = JSON.parse(fs.readFileSync('gapps.config.json', 'utf8')).fileId;

  var chrome = 'start chrome https://script.google.com/a/edmonton.ca/d/' + key + '/edit';
  return exec(chrome, function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Opens up the project in chrome.
 * Calls browserifyBundle, then buildWeb.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function openWeb(cb) {
  var chrome = 'start chrome ./build/web/client/html/ListSetupSidebar.html';
  return exec(chrome, function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Runs Google's own Closure Linter on the JS destined for the web
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function closureLint() {
  // flags: https://github.com/jmendiara/node-closure-linter-wrapper#flags
  var lintOptions = {
    flags: ['--max_line_length 120', '--strict']
  };

  // Output all failures to the console, and \then fail.
  return gulp.src(['./src/**/*.js'])
      .pipe(gjslint(lintOptions))
      .pipe(gjslint.reporter('console'));
}

/**
 * Attempts to automatically fix many of the errors that gjslint checks for.
 * Runs a shell command.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function closureFix(cb) {

  var fixJS = 'fixjsstyle --strict --max_line_length 120 -r ./src';

  return exec(fixJS, function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Compiles SASS (?) ¯\_(ツ)_/¯
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function compileSASS() {
  return gulp.src('./src/client/sass/*.scss')
      .pipe(sass().on('error', function(error) {
        var message = new gutil.PluginError('sass', error.messageFormatted).toString();
        process.stderr.write(message + '\n');
        process.exit(1);
      }))
      .pipe(gulp.dest('./build/common/css'));
}
<commit_msg>compile sass before deploying to GAS<commit_after>// NPM packages
var browserify = require('browserify');
var gjslint = require('gulp-gjslint');
var gulp = require('gulp');
var gutil = require('gulp-util');
var source = require('vinyl-source-stream');
var htmlProcessor = require('gulp-htmlprocessor');
var sass = require('gulp-sass');

// Node modules
var exec = require('child_process').exec;
var fs = require('fs');

// These are the primary tasks
gulp.task('test-gas', ['deploy-gas'], openGAS);
gulp.task('test-web', ['build-web'], openWeb);

// General
gulp.task('lint-all', closureLint);
gulp.task('fix-all', closureFix);
gulp.task('browserify', browserifyBundle);
gulp.task('compile-sass', compileSASS);

// Web specific
gulp.task('build-web', ['browserify', 'compile-sass'], buildWeb);

// GAS specific
gulp.task('deploy-gas', ['swap-tags'], deployGAS);
gulp.task('swap-tags', ['build-gas'], replaceTags);
gulp.task('build-gas', ['browserify', 'compile-sass'], buildGAS);

/**
 * Bundles up client.js (and all required functionality) and places it in a build directory.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function browserifyBundle() {
  return browserify('./src/client/js/client.js')
      .bundle()
      .on('error', function(e) {
        gutil.log(e);
      })
      .pipe(source('bundle.js'))
      .pipe(gulp.dest('./build/common'));
}

/**
 * Builds the project for GAS.
 * GAS doesn't allow seperate CSS or JS, so both need to be injected into the main HTML.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function buildGAS() {

  gulp.src('./src/client/html/**', {
    base: './src/client'
  })
      .pipe(gulp.dest('./build/gas'));

  // GAS
  return gulp.src('./src/GAS/*')
      .pipe(gulp.dest('./build/gas/GAS'));
}

/**
 * Replaces all script tags and css links.
 * Note: This is done relative to this gulpfile.
 * All swap tags are relative to this gulpfile.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function replaceTags() {
  return gulp.src('./build/gas/html/*.html')
      .pipe(htmlProcessor({
        includeBase: './'
      }))
      .pipe(gulp.dest('./build/gas/html/'));
}

/**
 * Builds the project for the web.
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function buildWeb() {
  gulp.src('./build/common/bundle.js')
      .pipe(gulp.dest('./build/web/client/js'));

  gulp.src('./src/client/css/*')
      .pipe(gulp.dest('./build/web/client/css'));

  gulp.src('./src/client/html/*')
      .pipe(gulp.dest('./build/web/client/html'));

  gulp.src('./src/client/images/*')
      .pipe(gulp.dest('./build/web/client/images'));

  gulp.src('./build/common/css/*')
      .pipe(gulp.dest('./build/web/client/css'));

  return gulp.src('./src/GAS/*')
      .pipe(gulp.dest('./build/web/GAS'));
}

/**
 * Deploys the GAS code up to the project.
 * Calls browserifyBundle, then buildGAS.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function deployGAS(cb) {
  return exec('gapps push', function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Opens up the project in GAS in chrome.
 * Calls browserifyBundle, then buildGAS, then deployGAS.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function openGAS(cb) {
  // Open the project in chrome
  var key = JSON.parse(fs.readFileSync('gapps.config.json', 'utf8')).fileId;

  var chrome = 'start chrome https://script.google.com/a/edmonton.ca/d/' + key + '/edit';
  return exec(chrome, function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Opens up the project in chrome.
 * Calls browserifyBundle, then buildWeb.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function openWeb(cb) {
  var chrome = 'start chrome ./build/web/client/html/ListSetupSidebar.html';
  return exec(chrome, function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Runs Google's own Closure Linter on the JS destined for the web
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function closureLint() {
  // flags: https://github.com/jmendiara/node-closure-linter-wrapper#flags
  var lintOptions = {
    flags: ['--max_line_length 120', '--strict']
  };

  // Output all failures to the console, and \then fail.
  return gulp.src(['./src/**/*.js'])
      .pipe(gjslint(lintOptions))
      .pipe(gjslint.reporter('console'));
}

/**
 * Attempts to automatically fix many of the errors that gjslint checks for.
 * Runs a shell command.
 *
 * @param  {callback} cb - a callback so the engine knows when it'll be done
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function closureFix(cb) {

  var fixJS = 'fixjsstyle --strict --max_line_length 120 -r ./src';

  return exec(fixJS, function(err, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
}

/**
 * Compiles SASS (?) ¯\_(ツ)_/¯
 *
 * @return {stream} the stream as the completion hint to the gulp engine
 */
function compileSASS() {
  return gulp.src('./src/client/sass/*.scss')
      .pipe(sass().on('error', function(error) {
        var message = new gutil.PluginError('sass', error.messageFormatted).toString();
        process.stderr.write(message + '\n');
        process.exit(1);
      }))
      .pipe(gulp.dest('./build/common/css'));
}
<|endoftext|>


<commit_before>var babelify = require('babelify'),
	browserify = require('browserify'),
	buffer = require('vinyl-buffer'),
	concat = require('gulp-concat'),
	debug = require('./config').DEBUG,
	deps = require('./deps'),
	gulp = require('gulp'),
	gulpif = require('gulp-if'),
	gutil = require('gulp-util'),
	less = require('gulp-less'),
	minifyCSS = require('gulp-minify-css'),
	rename = require('gulp-rename'),
	rev = require('gulp-rev'),
	source = require('vinyl-source-stream'),
	sourcemaps = require('gulp-sourcemaps'),
	uglify = require('gulp-uglify');

function gulper(name, files, dest) {
	gulp.task(name, function() {
		return gulp.src(files)
			.pipe(concat(name))
			.pipe(gulpif(!debug, uglify()))
			.pipe(rev())
			.pipe(rename({suffix: '.' + (debug ? 'debug' : 'min') + '.js'}))
			.pipe(gulp.dest(dest))
			.pipe(rev.manifest(name + '.json'))
			.pipe(gulp.dest('./state'));
	});
}

gulp.task('css', function() {
	return gulp.src('./less/*.less')
		.pipe(less({paths: ['./less/mixins']}))
		.pipe(minifyCSS({rebase: false}))
		.pipe(rev())
		.pipe(gulp.dest('./www/css'))
		.pipe(rev.manifest('css.json'))
		.pipe(gulp.dest('./state'));
});

gulp.task('client', function() {
	var b = browserify(require.resolve('./client/main.js'), {
		entry: true,
		// Needed for sourcemaps
		debug: true,
		// Make available outside the bundle with require(). Needed for mod.js.
		require: [
			'jquery',
			'underscore',
			'backbone'
		]
	})
		// Transpile to ES5
		.transform(babelify.configure({
			blacklist: [
				'es6.constants',
				'flow',
				'react',
				'reactCompat',
				'regenerator'
			]
		}))
		// Exclude these requires on the client
		.exclude('../config')
		.exclude('../lang/')
		.exclude('../server/state');

	return b.bundle()
		// Transform into vinyl stream
		.pipe(source('client.js'))
		.pipe(buffer())
		.pipe(sourcemaps.init({loadMaps: true}))
		.pipe(gulpif(!debug, uglify()))
		.on('error', gutil.log)
		.pipe(sourcemaps.write('./'))
		.pipe(gulp.dest('./www/js'));
});

(function() {
	gulper('mod', deps.mod, './state');
})();
<commit_msg>Limit amount of Babel transformers in favour of native implementations<commit_after>var babelify = require('babelify'),
	browserify = require('browserify'),
	buffer = require('vinyl-buffer'),
	concat = require('gulp-concat'),
	debug = require('./config').DEBUG,
	deps = require('./deps'),
	gulp = require('gulp'),
	gulpif = require('gulp-if'),
	gutil = require('gulp-util'),
	less = require('gulp-less'),
	minifyCSS = require('gulp-minify-css'),
	rename = require('gulp-rename'),
	rev = require('gulp-rev'),
	source = require('vinyl-source-stream'),
	sourcemaps = require('gulp-sourcemaps'),
	uglify = require('gulp-uglify');

function gulper(name, files, dest) {
	gulp.task(name, function() {
		return gulp.src(files)
			.pipe(concat(name))
			.pipe(gulpif(!debug, uglify()))
			.pipe(rev())
			.pipe(rename({suffix: '.' + (debug ? 'debug' : 'min') + '.js'}))
			.pipe(gulp.dest(dest))
			.pipe(rev.manifest(name + '.json'))
			.pipe(gulp.dest('./state'));
	});
}

gulp.task('css', function() {
	return gulp.src('./less/*.less')
		.pipe(less({paths: ['./less/mixins']}))
		.pipe(minifyCSS({rebase: false}))
		.pipe(rev())
		.pipe(gulp.dest('./www/css'))
		.pipe(rev.manifest('css.json'))
		.pipe(gulp.dest('./state'));
});

gulp.task('client', function() {
	var b = browserify(require.resolve('./client/main.js'), {
		entry: true,
		// Needed for sourcemaps
		debug: true,
		// Make available outside the bundle with require(). Needed for mod.js.
		require: [
			'jquery',
			'underscore',
			'backbone'
		]
	})
		/*
		Trasnpile to ES5. We use already implemented native functionality, where
		possible. These are mainly to prevent the minifier from throwing errors
		and some syntactic sugar, that is not yet implemented. Nobody cares about
		your ancient retarded browser.
		 */
		.transform(babelify.configure({
			whitelist: [
				'es6.forOf',
				'es6.arrowFunctions',
				'es6.parameters.default',
				'es6.parameters.rest',
				'es6.spread',
				'es6.properties.computed',
				'es6.properties.shorthand',
				'es6.spec.templateLiterals',
				'es6.templateLiterals',
				'strict'
			]
		}))
		// Exclude these requires on the client
		.exclude('../config')
		.exclude('../lang/')
		.exclude('../server/state');

	return b.bundle()
		// Transform into vinyl stream
		.pipe(source('client.js'))
		.pipe(buffer())
		.pipe(sourcemaps.init({loadMaps: true}))
		.pipe(gulpif(!debug, uglify()))
		.on('error', gutil.log)
		.pipe(sourcemaps.write('./'))
		.pipe(gulp.dest('./www/js'));
});

(function() {
	gulper('mod', deps.mod, './state');
})();
<|endoftext|>


<commit_before>var gulp = require('gulp'),
    sass = require('gulp-sass'),
    minifyCSS = require('gulp-minify-css'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    uglify = require('gulp-uglify'),
    karma = require('gulp-karma'),
    testFiles = ['spec/test-main.js', 'spec/tests.js'];

gulp.task('sass', function() {
    'use strict';
    gulp.src('styles/styles.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(minifyCSS({
            keepBreaks: true
        }))
        .pipe(rename('styles.min.css'))
        .pipe(gulp.dest('styles'));
});

gulp.task('js', function() {
    'use strict';
    gulp.src([
            'js/utils/utils.js',
            'js/app.js',
            'js/echoContent.js',
            'js/opmlContent.js',
            'js/services.js',
            'js/topBar.js',
            'js/main.js'
        ])
        .pipe(concat('sphere.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('script/'));

    gulp.src(testFiles)
        .pipe(karma({
            configFile: 'karma.conf.js',
            action: 'run'
        }))
        .on('error', function(err) {
            throw err;
        });
});

gulp.task('watch', function() {
    'use strict';
    gulp.watch([
        'styles/styles.scss'
    ], function() {
        gulp.start('sass');
    });

    gulp.watch([
        'js/utils/utils.js',
        'js/app.js',
        'js/echoContent.js',
        'js/opmlContent.js',
        'js/services.js',
        'js/topBar.js',
        'js/main.js'
    ], function() {
        gulp.start('js');
    });
});
<commit_msg>Include test changes watcher<commit_after>var gulp = require('gulp'),
    sass = require('gulp-sass'),
    minifyCSS = require('gulp-minify-css'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    uglify = require('gulp-uglify'),
    karma = require('gulp-karma'),
    testFiles = ['spec/test-main.js', 'spec/tests.js'];

gulp.task('sass', function() {
    'use strict';
    gulp.src('styles/styles.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(minifyCSS({
            keepBreaks: true
        }))
        .pipe(rename('styles.min.css'))
        .pipe(gulp.dest('styles'));
});

gulp.task('js', function() {
    'use strict';
    gulp.src([
            'js/utils/utils.js',
            'js/app.js',
            'js/echoContent.js',
            'js/opmlContent.js',
            'js/services.js',
            'js/topBar.js',
            'js/main.js'
        ])
        .pipe(concat('sphere.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('script/'));

    gulp.src(testFiles)
        .pipe(karma({
            configFile: 'karma.conf.js',
            action: 'run'
        }))
        .on('error', function(err) {
            throw err;
        });
});

gulp.task('watch', function() {
    'use strict';
    gulp.watch([
        'styles/styles.scss'
    ], function() {
        gulp.start('sass');
    });

    gulp.watch([
        'js/utils/utils.js',
        'js/app.js',
        'js/echoContent.js',
        'js/opmlContent.js',
        'js/services.js',
        'js/topBar.js',
        'js/main.js',
        'tests/appSpec.js'
    ], function() {
        gulp.start('js');
    });
});
<|endoftext|>


<commit_before>if (typeof define !== 'function') { var define = require('amdefine')(module); }
if (typeof expect !== 'function') { var expect = require('expect.js'); }

define([
  'app/logicalOperators'
], function(answers) {
  describe("logial operators", function(){
    it("you should be able to work with logical or", function() {
      expect(answers.and(false, false)).not.to.be.ok();
      expect(answers.and(true, false)).not.to.be.ok();
      expect(answers.and(true, true)).to.be.ok();
    });

    it("you should be able to work with logical and", function() {
      expect(answers.or(true, false)).to.be.ok();
      expect(answers.or(true, true)).to.be.ok();
      expect(answers.or(false, false)).not.to.be.ok();
    });
  });
});<commit_msg>Fix typo in logical operators test<commit_after>if (typeof define !== 'function') { var define = require('amdefine')(module); }
if (typeof expect !== 'function') { var expect = require('expect.js'); }

define([
  'app/logicalOperators'
], function(answers) {
  describe("logical operators", function(){
    it("you should be able to work with logical or", function() {
      expect(answers.and(false, false)).not.to.be.ok();
      expect(answers.and(true, false)).not.to.be.ok();
      expect(answers.and(true, true)).to.be.ok();
    });

    it("you should be able to work with logical and", function() {
      expect(answers.or(true, false)).to.be.ok();
      expect(answers.or(true, true)).to.be.ok();
      expect(answers.or(false, false)).not.to.be.ok();
    });
  });
});<|endoftext|>


<commit_before>var gulp = require('gulp');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
var hoganCompiler = require('gulp-hogan-precompile');
var declare = require('gulp-declare');
var uglify = require('gulp-uglify');

var coffeeFiles = [
    'src/Bus.coffee',
    'src/DOM.coffee',
    'src/Board.coffee',
    'src/Generator.coffee',
    'src/RuleMatcher.coffee',
    'src/Thumbnails.coffee',
    'src/Tabs.coffee',
    'src/TopRowEditor.coffee',
    'src/WolfCage.coffee'
];

gulp.task('compile-coffee', function() {
    gulp.src(coffeeFiles)
        .pipe(sourcemaps.init())
        .pipe(coffee({bare:true}))
        .pipe(sourcemaps.write())
        .pipe(concat('wolfcage.js'))
        .pipe(gulp.dest('dist/'));
});

gulp.task('compile-templates', function() {
  gulp.src('templates/**/*.mustache')
      .pipe(hoganCompiler())
      .pipe(declare({
        namespace: 'templates',
        noRedeclare: true
      }))
      .pipe(concat('wolfcage.templates.js'))
      .pipe(gulp.dest('./dist'));
});

gulp.task('watch-coffee', function() {
    gulp.start('compile-coffee');
    gulp.watch('src/*.coffee', ['compile-coffee']);
});

gulp.task('minify-js', function(){
    gulp.src(['dist/wolfcage.templates.js', 'dist/wolfcage.js'])
        .pipe(concat('wolfcage.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./dist'));
});<commit_msg>Added banner to the uglify gulp command.<commit_after>var gulp = require('gulp');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
var hoganCompiler = require('gulp-hogan-precompile');
var declare = require('gulp-declare');
var uglify = require('gulp-uglify');
var header = require('gulp-header');

var coffeeFiles = [
    'src/Bus.coffee',
    'src/DOM.coffee',
    'src/Board.coffee',
    'src/Generator.coffee',
    'src/RuleMatcher.coffee',
    'src/Thumbnails.coffee',
    'src/Tabs.coffee',
    'src/TopRowEditor.coffee',
    'src/WolfCage.coffee'
];

gulp.task('compile-coffee', function() {
    gulp.src(coffeeFiles)
        .pipe(sourcemaps.init())
        .pipe(coffee({bare:true}))
        .pipe(sourcemaps.write())
        .pipe(concat('wolfcage.js'))
        .pipe(gulp.dest('dist/'));
});

gulp.task('compile-templates', function() {
  gulp.src('templates/**/*.mustache')
      .pipe(hoganCompiler())
      .pipe(declare({
        namespace: 'templates',
        noRedeclare: true
      }))
      .pipe(concat('wolfcage.templates.js'))
      .pipe(gulp.dest('./dist'));
});

gulp.task('watch-coffee', function() {
    gulp.start('compile-coffee');
    gulp.watch('src/*.coffee', ['compile-coffee']);
});

var pkg = require('./package.json');
var banner = ['/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' * @author <%= pkg.author %>',
  ' * @version v<%= pkg.version %>',
  ' * @link <%= pkg.homepage %>',
  ' * @license <%= pkg.license %>',
  ' */',
  ''].join('\n');

gulp.task('uglify-js', function(){
    gulp.src(['dist/wolfcage.templates.js', 'dist/wolfcage.js'])
        .pipe(concat('wolfcage.min.js'))
        .pipe(uglify())
        .pipe(header(banner, { pkg : pkg } ))
        .pipe(gulp.dest('./dist'));
});<|endoftext|>


<commit_before>// ===================================================
// Settin'
// ===================================================

var gulp            = require('gulp'),
    gulpLoadPlugins = require('gulp-load-plugins'),
    $               = gulpLoadPlugins({
                        rename: {
                          'gulp-sourcemaps'  : 'sourcemaps',
                          'gulp-minify-css'  : 'mincss',
                          'gulp-minify-html' : 'minhtml',
                          'gulp-gh-pages'    : 'ghPages',
                          'gulp-foreach'     : 'foreach',
                          'gulp-mocha'       : 'mocha',
                          'gulp-if'          : 'if'
                        }
                      }),
    assemble        = require('assemble'),
    del             = require('del'),
    merge           = require('merge-stream'),
    basename        = require('path').basename,
    extname         = require('path').extname;

$.exec   = require('child_process').exec;
$.fs     = require('fs');

// ===================================================
// Configin'
// ===================================================

var env_flag = false;

var asset_dir = {
  site: 'site',
  templates : 'templates',
  data: 'data',
  dist: 'dist',
  js: 'js',
  css: 'css',
  sass: 'src'
};

var path = {
  site: asset_dir.site,
  data: asset_dir.data,
  templates: asset_dir.site + '/' + asset_dir.templates,
  dist: asset_dir.dist,
  js: asset_dir.site + '/' + asset_dir.js,
  css: asset_dir.site + '/' + asset_dir.css,
  sass: asset_dir.site + '/' + asset_dir.css + '/' + asset_dir.sass
};

var glob = {
  html: path.site + '/*.html',
  css: path.css + '/*.css',
  sass: path.sass + '/**/*.scss',
  js: path.js + '/src/**/*.js',
  jslibs : path.js + '/lib/**/*.js',
  layouts: path.templates + '/layouts/*.{md,hbs}',
  pages: path.templates + '/pages/**/*.{md,hbs}',
  includes: path.templates + '/includes/**/*.{md,hbs}',
  data: path.data + '/**/*.{json,yaml}',
  rootData: ['site.yaml', 'package.json']
};

// ===================================================
// Developin'
// ===================================================

gulp.task('serve', ['assemble'], function() {
  $.connect.server({
    root: [path.site],
    port: 5000,
    livereload: true,
    middleware: function(connect) {
      return [
        connect().use(connect.query())
      ];
    }
  });

  $.exec('open http://localhost:5000');
});

// ===================================================
// Previewin'
// ===================================================

gulp.task('preview', function() {
  $.connect.server({
    root: [path.dist],
    port: 5001
  });

  $.exec('open http://localhost:5001');
});

// ===================================================
// Testin'
// ===================================================

gulp.task('mocha', function () {
  return gulp.src('test/*.js', {read: false})
    .pipe($.mocha({ reporter: 'nyan' }));
});

// ===================================================
// Stylin'
// ===================================================

gulp.task('sass', function() {
  var stream = gulp.src(glob.sass)
    .pipe($.if(env_flag === false, $.sourcemaps.init()))
    .pipe($.sass({
      outputStyle: $.if(env_flag === false, 'expanded', 'compressed')
    }))
    .pipe($.if(env_flag === false,
      $.sourcemaps.write({
        debug: true,
        includeContent: false,
        sourceRoot: path.css
      })
    ))
    .pipe($.autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    .pipe(gulp.dest(path.css))
    .pipe($.connect.reload());

  return stream;
});

// ===================================================
// Templatin'
// ===================================================

// Pull #3. Also see https://github.com/assemble/assemble/issues/715
// create a `categories` object to keep categories in (e.g. 'clients')
// categories: {
//  clients: {
//    "polyon": { ... }
//  }
// };
assemble.set('categories', {});

/**
 Populate categories with pages that specify the categories they belong to.
 When the onLoad middleware runs for a single file, it looks at the file's front-matter (file.data) to see if it contains a categories property. This property can be a string or an array of strings. If it exists, then the middleware updates the categories object for each category in the array. In the case of polyon.hbs, there is only 1 category called client, so the categories object becomes:

categories: {
 clients: {
   "polyon": { ... }
 }
};
 */

assemble.onLoad(/\.hbs/, function(file, next) {
  // if the file doesn't have a data object or
  // doesn't contain `categories` in it's
  // front-matter, move on.
  if (!file.data || !file.data.categories) {
    return next();
  }

  // use the default `renameKey` function to store
  // pages on the `categories` object
  var renameKey = assemble.option('renameKey');

  // get the categories object
  var categories = assemble.get('categories');

  // figure out which categories this file belongs to
  var cats = file.data.categories;
  cats = Array.isArray(cats) ? cats : [cats];

  // add this file's data (file object) to each of
  // it's catogories
  cats.forEach(function(cat) {
    categories[cat] = categories[cat] || [];
    categories[cat][renameKey(file.path)] = file;
  });

  // done
  next();
});

/**
 * Handlebars helper to iterate over an object of pages for a specific category
 *
 * ```
 * {{#category "clients"}}
 *   <li>{{data.summary}}</li>
 * {{/category}}
 * ```
 */

assemble.helper('category', function(category, options) {
  var pages = this.app.get('categories.' + category);
  if (!pages) {
    return '';
  }
  return Object.keys(pages).map(function(page) {
    // this renders the block between `{{#category}}` and `{{category}}` passing the
    // entire page object as the context.
    // If you only want to use the page's front-matter, then change this to something like
    // return options.fn(pages[page].data);
    return options.fn(pages[page]).toLowerCase();
  }).join('\n');
});

/**
 * Load data onto assemble cache.
 * This loads data from `glob.data` and `glob.rootData`.
 * When loading `glob.rootData`, use a custom namespace function
 * to return `pkg` for `package.json`.
 *
 * After all data is loaded, process the data to resolve templates
 * in values.
 * @doowb PR: https://github.com/grayghostvisuals/grayghostvisuals/pull/5
 */

function loadData() {
  assemble.data(glob.data);
  assemble.data(assemble.plasma(glob.rootData, {namespace: function (fp) {
    var name = basename(fp, extname(fp));
    if (name === 'package') return 'pkg';
    return name;
  }}));
  assemble.data(assemble.process(assemble.data()));
}

// Placing assemble setups inside the task allows
// live reloading/monitoring for files changes.
gulp.task('assemble', function() {
  assemble.option('production', env_flag);
  assemble.option('layout', 'default');
  assemble.layouts(glob.layouts);
  assemble.partials(glob.includes);
  loadData();

  var stream = assemble.src(glob.pages)
    .pipe($.extname())
    .pipe(assemble.dest(path.site))
    .pipe($.connect.reload());

  return stream;
});

// ===================================================
// Optimizin'
// ===================================================

gulp.task('svgstore', function() {
  return gulp
    .src(path.site + '/img/icons/linear/*.svg')
    .pipe($.svgmin({
      plugins: [{
        removeDoctype: true
      }]
    }))
    .pipe($.svgstore())
    .pipe($.cheerio(function($) {
      $('svg').attr('style', 'display:none');
    }))
    .pipe(gulp.dest(path.templates + '/includes/atoms/svg-sprite.svg'));
});

// ===================================================
// Buildin'
// ===================================================

/*
 * foreach is because usemin 0.3.11 won't manipulate
 * multiple files as an array.
 */

gulp.task('usemin', ['assemble', 'sass'], function() {
  return gulp.src(glob.html)
    .pipe($.foreach(function(stream, file) {
      return stream
        .pipe($.usemin({
          assetsDir: path.site,
          css: [ $.rev() ],
          html: [$.minhtml({
            empty: true,
            quotes: true,
            comment: true
          })],
          js: [ $.uglify(), $.rev() ]
        }))
        .pipe(gulp.dest(path.dist));
    }));
});

// ===================================================
// Duplicatin'
// ===================================================

gulp.task('copy', ['usemin'], function() {
  return merge(
    gulp.src([path.site + '/{img,bower_components,js/lib}/**/*'])
        .pipe(gulp.dest(path.dist)),

    gulp.src([
        path.site + '/*.{ico,png,txt}',
        path.site + '/CNAME'
      ]).pipe(gulp.dest(path.dist))
  );
});

// ===================================================
// Releasin'
// ===================================================

gulp.task('deploy', function() {
  return gulp.src([path.dist + '/**/*'])
             .pipe($.ghPages(
                $.if(env_flag === false,
                { branch: 'staging' },
                { branch: 'gh-pages'})
             ));
});

// ===================================================
// Cleanin'
// ===================================================

gulp.task('clean', function(cb) {
  del([
    'dist',
    glob.css,
    glob.html
  ], cb);
});

// ===================================================
// Monitorin'
// ===================================================

gulp.task('watch', function() {
  gulp.watch([
    glob.sass
  ], ['sass']);

  gulp.watch([
    glob.includes,
    glob.pages,
    glob.layouts
  ], ['assemble']);
});

// ===================================================
// Taskin'
// ===================================================

gulp.task('build', [ 'copy','usemin' ]);
gulp.task('default', [ 'sass','assemble','serve','watch' ]);<commit_msg>releasing prep<commit_after>// ===================================================
// Settin'
// ===================================================

var gulp            = require('gulp'),
    gulpLoadPlugins = require('gulp-load-plugins'),
    $               = gulpLoadPlugins({
                        rename: {
                          'gulp-sourcemaps'  : 'sourcemaps',
                          'gulp-minify-css'  : 'mincss',
                          'gulp-minify-html' : 'minhtml',
                          'gulp-gh-pages'    : 'ghPages',
                          'gulp-foreach'     : 'foreach',
                          'gulp-mocha'       : 'mocha',
                          'gulp-if'          : 'if'
                        }
                      }),
    assemble        = require('assemble'),
    del             = require('del'),
    merge           = require('merge-stream'),
    basename        = require('path').basename,
    extname         = require('path').extname;

$.exec   = require('child_process').exec;
$.fs     = require('fs');

// ===================================================
// Configin'
// ===================================================

var env_flag = false;

var asset_dir = {
  site: 'site',
  templates : 'templates',
  data: 'data',
  dist: 'dist',
  js: 'js',
  css: 'css',
  sass: 'src'
};

var path = {
  site: asset_dir.site,
  data: asset_dir.data,
  templates: asset_dir.site + '/' + asset_dir.templates,
  dist: asset_dir.dist,
  js: asset_dir.site + '/' + asset_dir.js,
  css: asset_dir.site + '/' + asset_dir.css,
  sass: asset_dir.site + '/' + asset_dir.css + '/' + asset_dir.sass
};

var glob = {
  html: path.site + '/*.html',
  css: path.css + '/*.css',
  sass: path.sass + '/**/*.scss',
  js: path.js + '/src/**/*.js',
  jslibs : path.js + '/lib/**/*.js',
  layouts: path.templates + '/layouts/*.{md,hbs}',
  pages: path.templates + '/pages/**/*.{md,hbs}',
  includes: path.templates + '/includes/**/*.{md,hbs}',
  data: path.data + '/**/*.{json,yaml}',
  rootData: ['site.yaml', 'package.json']
};

// ===================================================
// Developin'
// ===================================================

gulp.task('serve', ['assemble'], function() {
  $.connect.server({
    root: [path.site],
    port: 5000,
    livereload: true,
    middleware: function(connect) {
      return [
        connect().use(connect.query())
      ];
    }
  });

  $.exec('open http://localhost:5000');
});

// ===================================================
// Previewin'
// ===================================================

gulp.task('preview', function() {
  $.connect.server({
    root: [path.dist],
    port: 5001
  });

  $.exec('open http://localhost:5001');
});

// ===================================================
// Testin'
// ===================================================

gulp.task('mocha', function () {
  return gulp.src('test/*.js', {read: false})
    .pipe($.mocha({ reporter: 'nyan' }));
});

// ===================================================
// Stylin'
// ===================================================

gulp.task('sass', function() {
  var stream = gulp.src(glob.sass)
    .pipe($.if(env_flag === false, $.sourcemaps.init()))
    .pipe($.sass({
      outputStyle: $.if(env_flag === false, 'expanded', 'compressed')
    }))
    .pipe($.if(env_flag === false,
      $.sourcemaps.write({
        debug: true,
        includeContent: false,
        sourceRoot: path.css
      })
    ))
    .pipe($.autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    .pipe(gulp.dest(path.css))
    .pipe($.connect.reload());

  return stream;
});

// ===================================================
// Templatin'
// ===================================================

// Pull #3. Also see https://github.com/assemble/assemble/issues/715
// create a `categories` object to keep categories in (e.g. 'clients')
// categories: {
//  clients: {
//    "polyon": { ... }
//  }
// };
assemble.set('categories', {});

/**
 Populate categories with pages that specify the categories they belong to.
 When the onLoad middleware runs for a single file, it looks at the file's front-matter (file.data) to see if it contains a categories property. This property can be a string or an array of strings. If it exists, then the middleware updates the categories object for each category in the array. In the case of polyon.hbs, there is only 1 category called client, so the categories object becomes:

categories: {
 clients: {
   "polyon": { ... }
 }
};
 */

assemble.onLoad(/\.hbs/, function(file, next) {
  // if the file doesn't have a data object or
  // doesn't contain `categories` in it's
  // front-matter, move on.
  if (!file.data || !file.data.categories) {
    return next();
  }

  // use the default `renameKey` function to store
  // pages on the `categories` object
  var renameKey = assemble.option('renameKey');

  // get the categories object
  var categories = assemble.get('categories');

  // figure out which categories this file belongs to
  var cats = file.data.categories;
  cats = Array.isArray(cats) ? cats : [cats];

  // add this file's data (file object) to each of
  // it's catogories
  cats.forEach(function(cat) {
    categories[cat] = categories[cat] || [];
    categories[cat][renameKey(file.path)] = file;
  });

  // done
  next();
});

/**
 * Handlebars helper to iterate over an object of pages for a specific category
 *
 * ```
 * {{#category "clients"}}
 *   <li>{{data.summary}}</li>
 * {{/category}}
 * ```
 */

assemble.helper('category', function(category, options) {
  var pages = this.app.get('categories.' + category);
  if (!pages) {
    return '';
  }
  return Object.keys(pages).map(function(page) {
    // this renders the block between `{{#category}}` and `{{category}}` passing the
    // entire page object as the context.
    // If you only want to use the page's front-matter, then change this to something like
    // return options.fn(pages[page].data);
    return options.fn(pages[page]).toLowerCase();
  }).join('\n');
});

/**
 * Load data onto assemble cache.
 * This loads data from `glob.data` and `glob.rootData`.
 * When loading `glob.rootData`, use a custom namespace function
 * to return `pkg` for `package.json`.
 *
 * After all data is loaded, process the data to resolve templates
 * in values.
 * @doowb PR: https://github.com/grayghostvisuals/grayghostvisuals/pull/5
 */

function loadData() {
  assemble.data(glob.data);
  assemble.data(assemble.plasma(glob.rootData, {namespace: function (fp) {
    var name = basename(fp, extname(fp));
    if (name === 'package') return 'pkg';
    return name;
  }}));
  assemble.data(assemble.process(assemble.data()));
}

// Placing assemble setups inside the task allows
// live reloading/monitoring for files changes.
gulp.task('assemble', function() {
  assemble.option('production', env_flag);
  assemble.option('layout', 'default');
  assemble.layouts(glob.layouts);
  assemble.partials(glob.includes);
  loadData();

  var stream = assemble.src(glob.pages)
    .pipe($.extname())
    .pipe(assemble.dest(path.site))
    .pipe($.connect.reload());

  return stream;
});

// ===================================================
// Optimizin'
// ===================================================

gulp.task('svgstore', function() {
  return gulp
    .src(path.site + '/img/icons/linear/*.svg')
    .pipe($.svgmin({
      plugins: [{
        removeDoctype: true
      }]
    }))
    .pipe($.svgstore())
    .pipe($.cheerio(function($) {
      $('svg').attr('style', 'display:none');
    }))
    .pipe(gulp.dest(path.templates + '/includes/atoms/svg-sprite.svg'));
});

// ===================================================
// Buildin'
// ===================================================

/*
 * foreach is because usemin 0.3.11 won't manipulate
 * multiple files as an array.
 */

gulp.task('usemin', ['assemble', 'sass'], function() {
  return gulp.src(glob.html)
    .pipe($.foreach(function(stream, file) {
      return stream
        .pipe($.usemin({
          assetsDir: path.site,
          css: [ $.rev() ],
          html: [$.minhtml({
            empty: true,
            quotes: true,
            comment: true
          })],
          js: [ $.uglify(), $.rev() ]
        }))
        .pipe(gulp.dest(path.dist));
    }));
});

// ===================================================
// Duplicatin'
// ===================================================

gulp.task('copy', ['usemin'], function() {
  return merge(
    gulp.src([path.site + '/{articles/img,bower_components,js/lib}/**/*'])
        .pipe(gulp.dest(path.dist)),

    gulp.src([
        path.site + '/*.{ico,png,txt}',
        path.site + '/CNAME'
      ]).pipe(gulp.dest(path.dist))
  );
});

// ===================================================
// Releasin'
// ===================================================

gulp.task('deploy', function() {
  return gulp.src([path.dist + '/**/*'])
             .pipe($.ghPages(
                $.if(env_flag === false,
                { branch: 'staging' },
                { branch: 'gh-pages'})
             ));
});

// ===================================================
// Cleanin'
// ===================================================

gulp.task('clean', function(cb) {
  del([
    'dist',
    glob.css,
    glob.html
  ], cb);
});

// ===================================================
// Monitorin'
// ===================================================

gulp.task('watch', function() {
  gulp.watch([
    glob.sass
  ], ['sass']);

  gulp.watch([
    glob.includes,
    glob.pages,
    glob.layouts
  ], ['assemble']);
});

// ===================================================
// Taskin'
// ===================================================

gulp.task('build', [ 'copy','usemin' ]);
gulp.task('default', [ 'sass','assemble','serve','watch' ]);<|endoftext|>


<commit_before>const {app, BrowserWindow, Menu} = require('electron')
const path = require('path')

let frame = null

function init() {
  frame = new BrowserWindow({
    title: 'TODU',
    width: 900,
    height: 700,
    minWidth: 450,
    frame: false,
  })
  frame.loadURL(`file://${__dirname}/src/index.html`)
  frame.on('closed', () => {
    frame = null
  })
  frame.setMenu(createMenu())
  frame.setMenuBarVisibility(false)
  // this handler must be binded in main process
  frame.webContents.session.on('will-download', (e, item, webContents) => {
    item.setSavePath(path.resolve(app.getPath('downloads'), item.getFilename()))
  })
}

function createMenu() {
  return Menu.buildFromTemplate([
    {
      label: 'Toggle Developer Tools',
      accelerator: 'CommandOrControl+Shift+I',
      click() {
        frame && frame.webContents.openDevTools()
      }
    }
  ])
}

const second = app.makeSingleInstance((argv, directory) => {
  if (frame) {
    if (frame.isMinimized()) {
      frame.restore()
    }
    frame.focus()
  }
  return true
})

if (second) {
  app.quit()
}

app.on('ready', init)

app.on('activate', () => {
  if (frame === null) {
    init()
  }
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
<commit_msg>Support system printer<commit_after>const {app, BrowserWindow, Menu} = require('electron')
const path = require('path')

let frame = null

function init() {
  frame = new BrowserWindow({
    title: 'TODU',
    width: 900,
    height: 700,
    minWidth: 450,
    frame: false,
  })
  frame.loadURL(`file://${__dirname}/src/index.html`)
  frame.on('closed', () => {
    frame = null
  })
  frame.setMenu(createMenu())
  frame.setMenuBarVisibility(false)
  // this handler must be binded in main process
  frame.webContents.session.on('will-download', (e, item, webContents) => {
    item.setSavePath(path.resolve(app.getPath('downloads'), item.getFilename()))
  })
}

function createMenu() {
  return Menu.buildFromTemplate([
    {
      label: 'Print',
      accelerator: 'CommandOrControl+P',
      click() {
        frame && frame.webContents.print({printBackground: true})
      }
    },
    {
      label: 'Toggle Developer Tools',
      accelerator: 'CommandOrControl+Shift+I',
      click() {
        frame && frame.webContents.openDevTools()
      }
    }
  ])
}

const second = app.makeSingleInstance((argv, directory) => {
  if (frame) {
    if (frame.isMinimized()) {
      frame.restore()
    }
    frame.focus()
  }
  return true
})

if (second) {
  app.quit()
}

app.on('ready', init)

app.on('activate', () => {
  if (frame === null) {
    init()
  }
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
<|endoftext|>


<commit_before>const CI = process.env.CI === 'true';

const gulp = require('gulp');
const mocha = require('gulp-mocha');
const watch = require('gulp-watch');
const batch = require('gulp-batch');
const semistandard = require('gulp-semistandard');
const runSequence = require('run-sequence');

const sources = ['main.js', './lib/**/*.js'];
const tests = ['./test/*.test.js'];

gulp.task('test', function (cb) {
  runSequence(['tests', 'semistandard'], cb);
});

gulp.task('tests', () =>
  gulp.src(tests, { read: false })
    .pipe(mocha({
      require: ['./test/setup.js'],
      reporter: CI ? 'spec' : 'nyan'
    }))
);

gulp.task('semistandard', () =>
  gulp.src(sources.concat(tests))
    .pipe(semistandard())
    .pipe(semistandard.reporter('default', {
      breakOnError: true,
      quiet: true
    }))
);

gulp.task('watch', function () {
  watch(sources.concat(tests), batch(function (events, done) {
    gulp.start('test', done);
  }));
});
<commit_msg>do gulp watch the recommended way<commit_after>const CI = process.env.CI === 'true';

const gulp = require('gulp');
const mocha = require('gulp-mocha');
const watch = require('gulp-watch');
const batch = require('gulp-batch');
const semistandard = require('gulp-semistandard');
const runSequence = require('run-sequence');

const sources = ['main.js', './lib/**/*.js'];
const tests = ['./test/*.test.js'];

gulp.task('test', function (cb) {
  runSequence(['tests', 'semistandard'], cb);
});

gulp.task('tests', () =>
  gulp.src(tests, { read: false })
    .pipe(mocha({
      require: ['./test/setup.js'],
      reporter: CI ? 'spec' : 'nyan'
    }))
);

gulp.task('semistandard', () =>
  gulp.src(sources.concat(tests))
    .pipe(semistandard())
    .pipe(semistandard.reporter('default', {
      breakOnError: true,
      quiet: true
    }))
);

gulp.task('watch', function() {
  gulp.watch(sources.concat(tests), ['tests', 'semistandard']);
});
<|endoftext|>


<commit_before>// UI code.
"use strict";

function s(selector, opt_silent) {
  var el = document.querySelector(selector);
  if (!el && !opt_silent) {
    throw new Error("Could not find " + selector);
  }
  return el;
}

var textarea = s('textarea');
var errlog = s('#errlog');

function toArray(nodeList) {
  return Array.prototype.slice.call(nodeList);
}

function showError(err) {
  console.error(err, err.stack);
  if (!errlog.textContent) {
    errlog.textContent = 'Error: ';
  }
  errlog.textContent += err + '\n';
}

///////////////
// Resizable //
///////////////

(function() {
  var resizeEl = s('#noteList');

  function handleMove(e) {
    var w = e.clientX - resizeEl.offsetLeft - 4;
    resizeEl.style.width = w + 'px';
  }

  s('#dragbar')
      .addEventListener('mousedown', function(e) {
        document.body.classList.add('resizing');
        e.preventDefault();
        document.addEventListener('mousemove', handleMove);
      });

  document.addEventListener('mouseup', function(e) {
    document.body.classList.remove('resizing');
    document.removeEventListener('mousemove', handleMove);
  });
})();

///////////
// Dirty //
///////////

var dirtMonitor = (function() {
  var isDirty = false;
  var titlebar = s('.titlebar');
  var setDirty = function() {
    if (!isDirty) {
      isDirty = true;
      titlebar.classList.add('dirty');
    }
  };
  var setClean = function() {
    if (isDirty) {
      isDirty = false;
      titlebar.classList.remove('dirty');
    }
  };
  return {
    isDirty: function() { return isDirty; },
    setDirty: setDirty,
    setClean: setClean
  };
})();

////////////
// Search //
////////////

var highlight = (function() {
  var div = document.createElement('div');

  function safeHtml(text) {
    div.textContent = text;
    return div.innerHTML;
  }

  function escapeRegExp(str) {
    return str.replace(/[.^$*+?()[{\\|\]-]/g, '\\$&');
  }

  // Don't hightlight 1 and 2 char words.
  function keepTerm(term) { return term.length > 2; }

  function highlight(terms, text) {
    text = safeHtml(text);
    if (terms && terms.length) {
      var regexStr = terms.filter(keepTerm).map(escapeRegExp).join('|');
      var regex = new RegExp(regexStr, 'gi');
      text = text.replace(regex, '<q>$&</q>');
    }
    return text;
  }
  return highlight;
})();

var search = (function() {

  var index;
  var selected = null;
  var searchEl = s('#search');

  function createIndex(notes) {
    index = lunr(function() {
      this.field('title', {boost: 10});
      this.field('text');
      this.ref('id');
    });
    notes.forEach(function(n) { index.add(n); });
  }

  // Store any edits in the textarea.
  function storeEdits() {
    if (typeof selected === "number") {
      var old = model.getNote(selected);
      if (old.text === textarea.value) {
        return;
      }
      var updatedNote = DB.parseNote(textarea.value, selected);
      model.setNote(selected, updatedNote);
      // Update the listed item.
      displayNotes();
    }
  }

  function resetSelected() {
    searchEl.value = '';
    searchEl.focus();
    onInput();
  }

  function setSelected(i) {
    if (typeof selected === 'number') {
      storeEdits();
      var old = s('.selected', true);
      if (old) {
        old.classList.remove('selected');
      }
    }
    selected = i;
    if (typeof selected === 'number') {
      var li = s('li[data-id="' + i + '"]');
      li.classList.add('selected');
      li.scrollIntoViewIfNeeded();
      // Set textarea to current note.
      var focus = false;
      updateTextArea(model.getNote(selected), focus);
    } else {
      textarea.value = '';
    }
  }

  var noteListEl = s('#noteList');

  // TODO(wdm) modify summary to show highlighted terms?
  function htmlDisplayNote(terms, note) {
    if (typeof note.id !== 'number') {
      throw new Error("note without id", note);
    }
    var ID = note.id;
    var TITLE = highlight(terms, note.title);
    var SUMMARY = highlight(terms, note.summary);
    var html = '
' + TITLE + '' + SUMMARY +
               '
';
    return html;
  }

  // highlight terms.
  function displayMatches(terms, results) {
    var html;
    var visibleNotes = {};

    if (!results || !results.length) {
      // Display all notes.
      html = model.getNotes().map(function(note) {
        visibleNotes[note.id] = true;
        return htmlDisplayNote(terms, note);
      });
    } else {
      // Filter by term and order by score.
      html = results.map(function(r) {
        var id = parseInt(r.ref, 10);
        var note = model.getNote(id);
        visibleNotes[id] = true;
        return htmlDisplayNote(terms, note);
      });
    }
    noteList.innerHTML = html.join('\n');
    if (visibleNotes[selected]) {
      setSelected(selected);
    }
  }

  function onInput() {
    setSelected(null);
    displayNotes();
  }

  // Display notes that match the current search terms (or all).
  // TOOD(wdm) Do something with score?
  function displayNotes() {
    var q = searchEl.value;
    var results;
    var terms;

    if (q.length) {
      results = index.search(q);
      terms = q.split(/\s+/);
    }
    displayMatches(terms, results);
  }

  function moveSelection(isDown) {
    var toSelect;
    var old = s('.selected', true);
    if (!old) {
      toSelect = noteListEl.firstChild;
    } else {
      toSelect = isDown ? old.nextElementSibling : old.previousElementSibling;
    }
    if (toSelect) {
      var id = parseInt(toSelect.dataset.id, 10);
      setSelected(id);
    }
  }

  function moveSelectionUp() { moveSelection(false); }

  function moveSelectionDown() { moveSelection(true); }

  function click(e) {
    var t = e.target;
    // Find the enclosing
.
    for (var i = 0; t.nodeName !== 'LI' && i < 3; i++) {
      t = t.parentNode;
    }
    if (t.nodeName !== 'LI') {
      console.error('Could not find
', e.target, t);
      return;
    }
    var id = parseInt(t.dataset.id, 10);
    setSelected(id);
  }

  function selectOrCreateNote() {
    if (typeof selected !== 'number') {
      // One newline is required to mark end of title. The next newline is
      // optional but looks nicer.
      var rawText = searchEl.value + '\n\n';
      textarea.value = rawText;  // HACK: required because otherwise storeEdits
                                 // will wipe out our new note.
      var note = model.newNote(rawText);
      index.add(note);
      displayNotes();  // HACK: need to add listitem to ul.
      setSelected(note.id);
    }
    var focus = true;
    updateTextArea(model.getNote(selected), focus);
  }

  function init(notes) {
    createIndex(notes);
    displayMatches();
    return notes;
  }

  return {
    onInput: onInput,
    selectOrCreateNote: selectOrCreateNote,
    moveSelectionUp: moveSelectionUp,
    moveSelectionDown: moveSelectionDown,
    resetSelected: resetSelected,
    click: click,
    storeEdits: storeEdits,
    init: init
  };
})();

// TODO(wdm) Does this actually buy us anything over a global 'notes'?
var model = (function() {

  // In memory note objects.
  var notes = [];

  return {
    init: function init(notes_) {
      notes = notes_;
      return notes;
    },
    newNote: function(rawText) {
      var id = notes.length;
      var note = DB.parseNote(rawText, id);
      notes.push(note);
      return note;
    },
    setNote: function setNote(i, note) {
      notes[i] = note;
      saveNoteLocal(note);  // TODO(wdm) .then() ?
    },
    getNote: function getNote(i) { return notes[i]; },
    getNotes: function getNotes() { return notes; }
  };

})();

////////
// DB //
////////

var DB = (function() {

  // TODO(wdm) Check that SEPERATOR does not appear in notes.
  var SEPERATOR = '\n[//]: # (NVC: Do not edit this line)\n\n';

  function parse(rawText) {
    var rawNotes = rawText.split(SEPERATOR);
    var notes = rawNotes.map(parseNote);
    return notes;
  }

  function parseNote(rawNote, i) {
    if (typeof i !== 'number') {
      throw new Error('tried to create note without index');
    }
    var firstLine = rawNote.indexOf('\n');
    return {
      id: i,
      title: rawNote.substring(0, firstLine),
      summary: rawNote.substring(firstLine + 1, 150),
      text: rawNote
    };
  }

  // TODO(wdm) Better name!
  function notesAsBlob() {
    search.storeEdits();  // Grabs any changes in textarea.
    var texts = model.getNotes().map(function(note) { return note.text; });
    var rawText = texts.join(SEPERATOR);
    return new Blob([rawText], { 'type' : 'text/plain' });
  }
  return {
    parse: parse,
    parseNote: parseNote,
    notesAsBlob: notesAsBlob,
  };
})();

//////////////
// TextArea //
//////////////

function updateTextArea(note, focus) {
  var text = note.text;
  textarea.value = text;
  if (focus) {
    textarea.focus();
    // Move to first line after title.
    var pos = note.title.length + 1;
    if (text[pos] === '\n') {
      pos++;
    }
    textarea.setSelectionRange(pos, pos);
    textarea.scrollTop = 0;
  }
}

function setCaret(elem, pos) {
  var range = elem.createTextRange();
  range.collapse(true);
  range.moveEnd('character', pos);
  range.moveStart('character', pos);
  range.select();
}

///////////////
/// File UI ///
///////////////

var fileEntry;

function maybeChooseFile() {
  if (fileEntry) {
    return Promise.resolve(fileEntry);
  }
  return onChooseFile();
}

function onChooseFile() {
  return chooseBackupFile().then(showFilename).catch(showError);
}

function showFilename(fileEntry_) {
  fileEntry = fileEntry_;
  var name = '[no backup file]';
  if (fileEntry) {
    name = fileEntry.name;
  }
  s('#filename').textContent = name;
  return fileEntry;
}

function doSave() {
  if (!dirtMonitor.isDirty) {
    console.log('save skipped: not dirty');
    return Promise.resolve();
  }
  var blob = DB.notesAsBlob();
  return maybeChooseFile()
      .then(function(fileEntry) { return writeFileEntry(fileEntry, blob); })
      .then(dirtMonitor.setClean)
      .catch(showError);
}

function doHide() {
  chrome.app.window.current().hide();
}

function doClose() {
  doSave().then(window.close);
}

//////////////////
// Key Handling //
//////////////////

var keyHandler = (function() {

  var global_handlers = {
    27: search.resetSelected,  // Escape.
    'Alt-1': doHide,
    'Ctrl-W': doClose,
    'Ctrl-S': doSave,
  };

  var almost_global_handlers = {
    13: search.selectOrCreateNote,  // Enter.
    40: search.moveSelectionDown,   // Cursor down.
    38: search.moveSelectionUp,     // Cursor up.
  };

  return function keyHandler(e) {
    var k = e.keyCode;
    var pressed = (e.ctrlKey ? 'Ctrl-' : '') + (e.altKey ? 'Alt-' : '') +
                  (k < 48 ? k : String.fromCharCode(k));
    var handler = global_handlers[pressed];
    if (!handler && (e.target !== textarea)) {
      handler = almost_global_handlers[pressed];
    }
    if (handler) {
      e.preventDefault();
      handler();
    }
  }
})();

/* Debug: show whole store:
chrome.storage.local.get(null, function(s) { console.log('store', s) });
*/

function loadLocalNotes() {
  return new Promise(function(resolve, reject) {
    // Load everything in local storage.
    chrome.storage.local.get(null, function(items) {
      var notes = [];
      for (var key in items) {
        if (key.indexOf('NOTE.') === 0) {
          var id = parseInt(key.substr(5), 10);
          var rawText = items[key];
          var note = DB.parseNote(rawText, id);
          notes.push(note);
        } else {
          console.log('loadLocalNotes: ignoring', key);
        }
      }
      resolve(notes);
    });
  });
}

function saveNoteLocal(note) {
  return new Promise(function(resolve, reject) {
    var key = 'NOTE.' + note.id;
    var obj = {};
    obj[key] = note.text;

    chrome.storage.local.set(obj, function() { resolve(); });
  });
}

function init() {
  // TODO(wdm) Import
  // .then(loadFileEntry)
  //          .then(DB.parse)

  restoreBackupFile()
      .then(showFilename)
      .then(loadLocalNotes)
      .then(model.init)
      .then(search.init)
      .catch(showError);

  window.addEventListener('keydown', keyHandler);
  textarea.addEventListener('input', dirtMonitor.setDirty);
  s('#chooseFile').addEventListener('click', onChooseFile);
  s('#noteList').addEventListener('click', search.click);
  s('#search').addEventListener('input', search.onInput);
}

init();
<commit_msg>Fix assumption about storage order.<commit_after>// UI code.
"use strict";

function s(selector, opt_silent) {
  var el = document.querySelector(selector);
  if (!el && !opt_silent) {
    throw new Error("Could not find " + selector);
  }
  return el;
}

var textarea = s('textarea');
var errlog = s('#errlog');

function toArray(nodeList) {
  return Array.prototype.slice.call(nodeList);
}

function showError(err) {
  console.error(err, err.stack);
  if (!errlog.textContent) {
    errlog.textContent = 'Error: ';
  }
  errlog.textContent += err + '\n';
}

///////////////
// Resizable //
///////////////

(function() {
  var resizeEl = s('#noteList');

  function handleMove(e) {
    var w = e.clientX - resizeEl.offsetLeft - 4;
    resizeEl.style.width = w + 'px';
  }

  s('#dragbar')
      .addEventListener('mousedown', function(e) {
        document.body.classList.add('resizing');
        e.preventDefault();
        document.addEventListener('mousemove', handleMove);
      });

  document.addEventListener('mouseup', function(e) {
    document.body.classList.remove('resizing');
    document.removeEventListener('mousemove', handleMove);
  });
})();

///////////
// Dirty //
///////////

var dirtMonitor = (function() {
  var isDirty = false;
  var titlebar = s('.titlebar');
  var setDirty = function() {
    if (!isDirty) {
      isDirty = true;
      titlebar.classList.add('dirty');
    }
  };
  var setClean = function() {
    if (isDirty) {
      isDirty = false;
      titlebar.classList.remove('dirty');
    }
  };
  return {
    isDirty: function() { return isDirty; },
    setDirty: setDirty,
    setClean: setClean
  };
})();

////////////
// Search //
////////////

var highlight = (function() {
  var div = document.createElement('div');

  function safeHtml(text) {
    div.textContent = text;
    return div.innerHTML;
  }

  function escapeRegExp(str) {
    return str.replace(/[.^$*+?()[{\\|\]-]/g, '\\$&');
  }

  // Don't hightlight 1 and 2 char words.
  function keepTerm(term) { return term.length > 2; }

  function highlight(terms, text) {
    text = safeHtml(text);
    if (terms && terms.length) {
      var regexStr = terms.filter(keepTerm).map(escapeRegExp).join('|');
      var regex = new RegExp(regexStr, 'gi');
      text = text.replace(regex, '<q>$&</q>');
    }
    return text;
  }
  return highlight;
})();

var search = (function() {

  var index;
  var selected = null;
  var searchEl = s('#search');

  function createIndex(notes) {
    index = lunr(function() {
      this.field('title', {boost: 10});
      this.field('text');
      this.ref('id');
    });
    notes.forEach(function(n) { index.add(n); });
  }

  // Store any edits in the textarea.
  function storeEdits() {
    if (typeof selected === "number") {
      var old = model.getNote(selected);
      if (old.text === textarea.value) {
        return;
      }
      var updatedNote = DB.parseNote(textarea.value, selected);
      model.setNote(selected, updatedNote);
      // Update the listed item.
      displayNotes();
    }
  }

  function resetSelected() {
    searchEl.value = '';
    searchEl.focus();
    onInput();
  }

  function setSelected(i) {
    if (typeof selected === 'number') {
      storeEdits();
      var old = s('.selected', true);
      if (old) {
        old.classList.remove('selected');
      }
    }
    selected = i;
    if (typeof selected === 'number') {
      var li = s('li[data-id="' + i + '"]');
      li.classList.add('selected');
      li.scrollIntoViewIfNeeded();
      // Set textarea to current note.
      var focus = false;
      updateTextArea(model.getNote(selected), focus);
    } else {
      textarea.value = '';
    }
  }

  var noteListEl = s('#noteList');

  // TODO(wdm) modify summary to show highlighted terms?
  function htmlDisplayNote(terms, note) {
    if (typeof note.id !== 'number') {
      throw new Error("note without id", note);
    }
    var ID = note.id;
    var TITLE = highlight(terms, note.title);
    var SUMMARY = highlight(terms, note.summary);
    var html = '
' + TITLE + '' + SUMMARY +
               '
';
    return html;
  }

  // highlight terms.
  function displayMatches(terms, results) {
    var html;
    var visibleNotes = {};

    if (!results || !results.length) {
      // Display all notes.
      html = model.getNotes().map(function(note) {
        visibleNotes[note.id] = true;
        return htmlDisplayNote(terms, note);
      });
    } else {
      // Filter by term and order by score.
      html = results.map(function(r) {
        var id = parseInt(r.ref, 10);
        var note = model.getNote(id);
        visibleNotes[id] = true;
        return htmlDisplayNote(terms, note);
      });
    }
    noteList.innerHTML = html.join('\n');
    if (visibleNotes[selected]) {
      setSelected(selected);
    }
  }

  function onInput() {
    setSelected(null);
    displayNotes();
  }

  // Display notes that match the current search terms (or all).
  // TOOD(wdm) Do something with score?
  function displayNotes() {
    var q = searchEl.value;
    var results;
    var terms;

    if (q.length) {
      results = index.search(q);
      terms = q.split(/\s+/);
    }
    displayMatches(terms, results);
  }

  function moveSelection(isDown) {
    var toSelect;
    var old = s('.selected', true);
    if (!old) {
      toSelect = noteListEl.firstChild;
    } else {
      toSelect = isDown ? old.nextElementSibling : old.previousElementSibling;
    }
    if (toSelect) {
      var id = parseInt(toSelect.dataset.id, 10);
      setSelected(id);
    }
  }

  function moveSelectionUp() { moveSelection(false); }

  function moveSelectionDown() { moveSelection(true); }

  function click(e) {
    var t = e.target;
    // Find the enclosing
.
    for (var i = 0; t.nodeName !== 'LI' && i < 3; i++) {
      t = t.parentNode;
    }
    if (t.nodeName !== 'LI') {
      console.error('Could not find
', e.target, t);
      return;
    }
    var id = parseInt(t.dataset.id, 10);
    setSelected(id);
  }

  function selectOrCreateNote() {
    if (typeof selected !== 'number') {
      // One newline is required to mark end of title. The next newline is
      // optional but looks nicer.
      var rawText = searchEl.value + '\n\n';
      textarea.value = rawText;  // HACK: required because otherwise storeEdits
                                 // will wipe out our new note.
      var note = model.newNote(rawText);
      index.add(note);
      displayNotes();  // HACK: need to add listitem to ul.
      setSelected(note.id);
    }
    var focus = true;
    updateTextArea(model.getNote(selected), focus);
  }

  function init(notes) {
    createIndex(notes);
    displayMatches();
    return notes;
  }

  return {
    onInput: onInput,
    selectOrCreateNote: selectOrCreateNote,
    moveSelectionUp: moveSelectionUp,
    moveSelectionDown: moveSelectionDown,
    resetSelected: resetSelected,
    click: click,
    storeEdits: storeEdits,
    init: init
  };
})();

// TODO(wdm) Does this actually buy us anything over a global 'notes'?
var model = (function() {

  // In memory note objects.
  var notes = [];

  return {
    init: function init(notes_) {
      notes = notes_;
      return notes;
    },
    newNote: function(rawText) {
      var id = notes.length;
      var note = DB.parseNote(rawText, id);
      notes.push(note);
      return note;
    },
    setNote: function setNote(i, note) {
      notes[i] = note;
      saveNoteLocal(note);  // TODO(wdm) .then() ?
    },
    getNote: function getNote(i) {
      var note = notes[i];
      if (!note) {
        throw new Error('invalid note index', i);
      }
      return notes[i];
    },
    getNotes: function getNotes() { return notes; }
  };

})();

////////
// DB //
////////

var DB = (function() {

  // TODO(wdm) Check that SEPERATOR does not appear in notes.
  var SEPERATOR = '\n[//]: # (NVC: Do not edit this line)\n\n';

  function parse(rawText) {
    var rawNotes = rawText.split(SEPERATOR);
    var notes = rawNotes.map(parseNote);
    return notes;
  }

  function parseNote(rawNote, i) {
    if (typeof i !== 'number') {
      throw new Error('tried to create note without index');
    }
    var firstLine = rawNote.indexOf('\n');
    return {
      id: i,
      title: rawNote.substring(0, firstLine),
      summary: rawNote.substring(firstLine + 1, 150),
      text: rawNote
    };
  }

  // TODO(wdm) Better name!
  function notesAsBlob() {
    search.storeEdits();  // Grabs any changes in textarea.
    var texts = model.getNotes().map(function(note) { return note.text; });
    var rawText = texts.join(SEPERATOR);
    return new Blob([rawText], { 'type' : 'text/plain' });
  }
  return {
    parse: parse,
    parseNote: parseNote,
    notesAsBlob: notesAsBlob,
  };
})();

//////////////
// TextArea //
//////////////

function updateTextArea(note, focus) {
  if (!note) {
    throw new Error('invalid note', note);
  }
  var text = note.text;
  textarea.value = text;
  if (focus) {
    textarea.focus();
    // Move to first line after title.
    var pos = note.title.length + 1;
    if (text[pos] === '\n') {
      pos++;
    }
    textarea.setSelectionRange(pos, pos);
    textarea.scrollTop = 0;
  }
}

function setCaret(elem, pos) {
  var range = elem.createTextRange();
  range.collapse(true);
  range.moveEnd('character', pos);
  range.moveStart('character', pos);
  range.select();
}

///////////////
/// File UI ///
///////////////

var fileEntry;

function maybeChooseFile() {
  if (fileEntry) {
    return Promise.resolve(fileEntry);
  }
  return onChooseFile();
}

function onChooseFile() {
  return chooseBackupFile().then(showFilename).catch(showError);
}

function showFilename(fileEntry_) {
  fileEntry = fileEntry_;
  var name = '[no backup file]';
  if (fileEntry) {
    name = fileEntry.name;
  }
  s('#filename').textContent = name;
  return fileEntry;
}

function doSave() {
  if (!dirtMonitor.isDirty) {
    console.log('save skipped: not dirty');
    return Promise.resolve();
  }
  var blob = DB.notesAsBlob();
  return maybeChooseFile()
      .then(function(fileEntry) { return writeFileEntry(fileEntry, blob); })
      .then(dirtMonitor.setClean)
      .catch(showError);
}

function doHide() {
  chrome.app.window.current().hide();
}

function doClose() {
  doSave().then(window.close);
}

//////////////////
// Key Handling //
//////////////////

var keyHandler = (function() {

  var global_handlers = {
    27: search.resetSelected,  // Escape.
    'Alt-1': doHide,
    'Ctrl-W': doClose,
    'Ctrl-S': doSave,
  };

  var almost_global_handlers = {
    13: search.selectOrCreateNote,  // Enter.
    40: search.moveSelectionDown,   // Cursor down.
    38: search.moveSelectionUp,     // Cursor up.
  };

  return function keyHandler(e) {
    var k = e.keyCode;
    var pressed = (e.ctrlKey ? 'Ctrl-' : '') + (e.altKey ? 'Alt-' : '') +
                  (k < 48 ? k : String.fromCharCode(k));
    var handler = global_handlers[pressed];
    if (!handler && (e.target !== textarea)) {
      handler = almost_global_handlers[pressed];
    }
    if (handler) {
      e.preventDefault();
      handler();
    }
  }
})();

/* Debug: show whole store:
chrome.storage.local.get(null, function(s) { console.log('store', s) });
*/

function loadLocalNotes() {
  return new Promise(function(resolve, reject) {
    // Load everything in local storage.
    chrome.storage.local.get(null, function(items) {
      var notes = [];
      for (var key in items) {
        if (key.indexOf('NOTE.') === 0) {
          var id = parseInt(key.substr(5), 10);
          var rawText = items[key];
          var note = DB.parseNote(rawText, id);
          // Items may not be in order and some ids may be missing.
          // So we use fill a sparse array rather than using push().
          notes[id] = note;
        } else {
          console.log('loadLocalNotes: ignoring', key);
        }
      }
      resolve(notes);
    });
  });
}

function saveNoteLocal(note) {
  return new Promise(function(resolve, reject) {
    var key = 'NOTE.' + note.id;
    var obj = {};
    obj[key] = note.text;

    chrome.storage.local.set(obj, function() { resolve(); });
  });
}

function init() {
  // TODO(wdm) Import
  // .then(loadFileEntry)
  //          .then(DB.parse)

  restoreBackupFile()
      .then(showFilename)
      .then(loadLocalNotes)
      .then(model.init)
      .then(search.init)
      .catch(showError);

  window.addEventListener('keydown', keyHandler);
  textarea.addEventListener('input', dirtMonitor.setDirty);
  s('#chooseFile').addEventListener('click', onChooseFile);
  s('#noteList').addEventListener('click', search.click);
  s('#search').addEventListener('input', search.onInput);
}

init();
<|endoftext|>


<commit_before>var gulp = require('gulp'),
	postcss = require('gulp-postcss'),
	rename = require('gulp-rename'),
	jade = require('gulp-jade'),
	changelog = require('gulp-conventional-changelog');

	require('gulp-release-tasks')(gulp);

var pkg = require('./package.json'),
	slim_banner = [
		'*',
		'* Copyright (c) ' + new Date().getFullYear() + ' ' + pkg.author.name,
		'* ' + pkg.name + ' - ' + pkg.description,
		'* @version ' + pkg.version,
		'* @link ' + pkg.homepage,
		'* @license ' + pkg.license.type,
		'*'
		].join('\n');

gulp.task('changelog', function() {
    return gulp.src('CHANGELOG.md', {
        buffer: false
    })
    .pipe(changelog({
      preset: 'angular'
    }))
    .pipe(gulp.dest('./'));
});

gulp.task('jade', function() {
    return gulp.src('app/jade/*.jade')
    	.pipe(jade())
    	.pipe(gulp.dest('./dist/'));
});

gulp.task('pcss', function() {
    return gulp.src('./app/pcss/' + pkg.name + '.pcss')
    	.pipe(postcss([
            require('postcss-import')(),
            require('postcss-mixins')(),
            require('postcss-nested')(),
            require('postcss-for-variables')(),
            require('postcss-for')(),
            require('postcss-custom-properties')(),
            require('postcss-calc')({ precision: 2 }),
    		require('postcss-banner')({banner: slim_banner})
    	]))
    	.pipe(rename({ extname: '.css' }))
    	.pipe(gulp.dest('./dist/css/'));
});

gulp.task('default',['pcss', 'jade']);<commit_msg>remove jade<commit_after>var gulp = require('gulp'),
	postcss = require('gulp-postcss'),
	rename = require('gulp-rename'),
	changelog = require('gulp-conventional-changelog');

	require('gulp-release-tasks')(gulp);

var pkg = require('./package.json'),
	slim_banner = [
		'*',
		'* Copyright (c) ' + new Date().getFullYear() + ' ' + pkg.author.name,
		'* ' + pkg.name + ' - ' + pkg.description,
		'* @version ' + pkg.version,
		'* @link ' + pkg.homepage,
		'* @license ' + pkg.license.type,
		'*'
		].join('\n');

gulp.task('changelog', function() {
    return gulp.src('CHANGELOG.md', {
        buffer: false
    })
    .pipe(changelog({
      preset: 'angular'
    }))
    .pipe(gulp.dest('./'));
});

gulp.task('pcss', function() {
    return gulp.src('./app/pcss/' + pkg.name + '.pcss')
    	.pipe(postcss([
            require('postcss-import')(),
            require('postcss-mixins')(),
            require('postcss-nested')(),
            require('postcss-for-variables')(),
            require('postcss-for')(),
            require('postcss-custom-properties')(),
            require('postcss-calc')({ precision: 3 }),
    		require('postcss-banner')({banner: slim_banner})
    	]))
    	.pipe(rename({ extname: '.css' }))
    	.pipe(gulp.dest('./dist/css/'));
});

gulp.task('default',['pcss', 'jade']);<|endoftext|>