<commit_before>// enable debug for gulp
process.env.DEBUG = process.env.DEBUG || 'freecc:*';

require('babel-core/register');
var Rx = require('rx'),
  gulp = require('gulp'),
  path = require('path'),

  // utils
  plumber = require('gulp-plumber'),
  notify = require('gulp-notify'),
  gutil = require('gulp-util'),
  reduce = require('gulp-reduce-file'),
  sortKeys = require('sort-keys'),
  debug = require('debug')('freecc:gulp'),
  yargs = require('yargs'),
  concat = require('gulp-concat'),
  uglify = require('gulp-uglify'),
  merge = require('merge-stream'),
  babel = require('gulp-babel'),
  sourcemaps = require('gulp-sourcemaps'),

  // react app
  webpack = require('webpack-stream'),
  webpackConfig = require('./webpack.config.js'),
  webpackConfigNode = require('./webpack.config.node.js'),

  // server process
  nodemon = require('gulp-nodemon'),
  sync = require('browser-sync'),

  // css
  less = require('gulp-less'),

  // rev
  rev = require('gulp-rev'),
  revReplace = require('gulp-rev-replace'),
  revDel = require('rev-del'),

  // lint
  jsonlint = require('gulp-jsonlint'),
  eslint = require('gulp-eslint'),

  // unit-tests
  tape = require('gulp-tape'),
  tapSpec = require('tap-spec');

Rx.config.longStackSupport = true;

var __DEV__ = !yargs.argv.p;
var reloadDelay = 1000;
var reload = sync.reload;
var paths = {
  server: './server/server.js',
  serverIgnore: [
    'gulpfile.js',
    'public/',
    '!public/js/bundle*',
    'node_modules/',
    'client/',
    'seed',
    'server/manifests/*.json',
    'server/rev-manifest.json'
  ],

  publicJs: './public/js',
  css: 'public/css',

  loopback: {
    client: './client/loopbackClient',
    root: path.join(__dirname, 'client/'),
    clientName: 'lbApp'
  },

  client: {
    src: './client',
    dest: 'public/js'
  },

  vendorChallenges: [
    'public/bower_components/jshint/dist/jshint.js',
    'public/bower_components/chai/chai.js',
    'public/bower_components/CodeMirror/lib/codemirror.js',
    'public/bower_components/CodeMirror/addon/edit/closebrackets.js',
    'public/bower_components/CodeMirror/addon/edit/matchbrackets.js',
    'public/bower_components/CodeMirror/addon/lint/lint.js',
    'public/bower_components/CodeMirror/addon/lint/javascript-lint.js',
    'public/bower_components/CodeMirror/mode/javascript/javascript.js',
    'public/bower_components/CodeMirror/mode/xml/xml.js',
    'public/bower_components/CodeMirror/mode/css/css.js',
    'public/bower_components/CodeMirror/mode/htmlmixed/htmlmixed.js',
    'node_modules/emmet-codemirror/dist/emmet.js',
    'public/js/lib/loop-protect/loop-protect.js'
  ],

  vendorMain: [
    'public/bower_components/jquery/dist/jquery.min.js',
    'public/bower_components/bootstrap/dist/js/bootstrap.min.js',
    'public/bower_components/d3/d3.min.js',
    'public/bower_components/moment/min/moment.min.js',
    'public/bower_components/lightbox2/dist/js/lightbox.min.js',
    'public/bower_components/rxjs/dist/rx.all.min.js'
  ],

  js: [
    'client/main.js',
    'client/iFrameScripts.js',
    'client/plugin.js'
  ],

  commonFramework: [
    'init',
    'bindings',
    'add-test-to-string',
    'code-storage',
    'code-uri',
    'add-loop-protect',
    'get-iframe',
    'update-preview',
    'create-editor',
    'detect-unsafe-code-stream',
    'display-test-results',
    'execute-challenge-stream',
    'output-display',
    'phone-scroll-lock',
    'report-issue',
    'run-tests-stream',
    'show-completion',
    'step-challenge',
    'end'
  ],

  less: './client/less/main.less',
  lessFiles: './client/less/*.less',

  manifest: 'server/manifests/',

  node: {
    src: './client',
    dest: 'common/app'
  },

  syncWatch: [
    'public/**/*.*'
  ],

  challenges: [
    'seed/challenges/*/*.json'
  ]
};

var webpackOptions = {
  devtool: 'inline-source-map'
};

function formatCommonFrameworkPaths() {
  return this.map(function(script) {
    return 'client/commonFramework/' + script + '.js';
  });
}

function errorHandler() {
  var args = Array.prototype.slice.call(arguments);

  // Send error to notification center with gulp-notify
  notify.onError({
    title: 'Compile Error',
    message: '<%= error %>'
  }).apply(this, args);

  // Keep gulp from hanging on this task
  this.emit('end');
}

function delRev(dest, manifestName) {
  // in production do not delete old revisions
  if (!__DEV__) {
    return gutil.noop();
  }
  return revDel({
    oldManifest: path.join(paths.manifest, manifestName),
    dest: dest
  });
}

gulp.task('serve', ['build-manifest'], function(cb) {
  var called = false;
  nodemon({
    script: paths.server,
    ext: '.jsx .js .json',
    ignore: paths.serverIgnore,
    exec: path.join(__dirname, 'node_modules/.bin/babel-node'),
    env: {
      'NODE_ENV': 'development',
      'DEBUG': process.env.DEBUG || 'freecc:*'
    }
  })
    .on('start', function() {
      if (!called) {
        called = true;
        setTimeout(function() {
          cb();
        }, reloadDelay);
      }
    })
    .on('restart', function(files) {
      if (files) {
        debug('Files that changes: ', files);
      }
      setTimeout(function() {
        debug('Restarting browsers');
        reload();
      }, reloadDelay);
    });
});

var syncDepenedents = [
  'serve',
  'js',
  'less',
  'dependents',
  'pack-watch',
  'build-manifest'
];

gulp.task('sync', syncDepenedents, function() {
  sync.init(null, {
    proxy: 'http://localhost:3000',
    logLeval: 'debug',
    files: paths.syncWatch,
    port: 3001,
    open: false,
    reloadDelay: reloadDelay
  });
});

gulp.task('lint-js', function() {
  return gulp.src([
    'common/**/*.js',
    'common/**/*.jsx',
    'client/**/*.js',
    'client/**/*.jsx',
    'server/**/*.js',
    'config/**/*.js'
  ])
    .pipe(eslint())
    .pipe(eslint.format());
});

gulp.task('lint-json', function() {
  return gulp.src(paths.challenges)
    .pipe(jsonlint())
    .pipe(jsonlint.reporter());
});

gulp.task('test-challenges', ['lint-json']);

gulp.task('pack-client', function() {
  if (!__DEV__) { console.log('\n\nbundling production\n\n'); }

  var manifestName = 'react-manifest.json';
  var dest = webpackConfig.output.path;

  return gulp.src(webpackConfig.entry)
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(webpack(Object.assign(
      {},
      webpackConfig,
      webpackOptions
    )))
    .pipe(__DEV__ ? gutil.noop() : uglify())
    .pipe(gulp.dest(dest))
    .pipe(rev())
    // copy files to public
    .pipe(gulp.dest(dest))
    // create manifest
    .pipe(rev.manifest(manifestName))
    // delete old rev
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

var defaultStatsOptions = {
  colors: gutil.colors.supportsColor,
  hash: false,
  timings: false,
  chunks: false,
  chunkModules: false,
  modules: false,
  children: true,
  version: true,
  cached: false,
  cachedAssets: false,
  reasons: false,
  source: false,
  errorDetails: false
};

var webpackCalled = false;
gulp.task('pack-watch', function(cb) {
  if (webpackCalled) {
    console.log('webpack watching already runnning');
    return cb();
  }
  gulp.src(webpackConfig.entry)
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(webpack(Object.assign(
      {},
      webpackConfig,
      webpackOptions,
      { watch: true }
    ), null, function(notUsed, stats) {
      if (stats) {
        gutil.log(stats.toString(defaultStatsOptions));
      }

      if (!webpackCalled) {
        debug('webpack init completed');
        webpackCalled = true;
        cb();
      }

    }))
    .pipe(gulp.dest(webpackConfig.output.path));
});

gulp.task('pack-watch-manifest', ['pack-watch'], function() {
  var manifestName = 'react-manifest.json';
  var dest = webpackConfig.output.path;
  return gulp.src(dest + '/bundle.js')
    .pipe(rev())
    // copy files to public
    .pipe(gulp.dest(dest))
    // create manifest
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

gulp.task('pack-node', function() {
  return gulp.src(webpackConfigNode.entry)
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(webpack(webpackConfigNode))
    .pipe(gulp.dest(webpackConfigNode.output.path));
});

gulp.task('pack', ['pack-client', 'pack-node']);

gulp.task('less', function() {
  var manifestName = 'css-manifest.json';
  var dest = paths.css;
  return gulp.src(paths.less)
    .pipe(plumber({ errorHandler: errorHandler }))
    // compile
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest(dest))
    // add revision
    .pipe(rev())
    // copy files to public
    .pipe(gulp.dest(dest))
    // create and merge manifest
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

function getFilesGlob(files) {
  if (!__DEV__) {
    return files;
  }
  return files.map(function(file) {
    return file
      .replace('.min.', '.')
      // moment breaks the pattern
      .replace('/min/', '/');
  });
}

gulp.task('js', function() {
  var manifestName = 'js-manifest.json';
  var dest = paths.publicJs;

  var jsFiles = merge(

    gulp.src(getFilesGlob(paths.vendorMain))
      .pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
      .pipe(concat('vendor-main.js'))
      .pipe(
        __DEV__ ?
          sourcemaps.write({ sourceRoot: '/vendor' }) :
          gutil.noop()
      ),

    gulp.src(paths.vendorChallenges)
      .pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
      .pipe(__DEV__ ? gutil.noop() : uglify())
      .pipe(concat('vendor-challenges.js'))
      .pipe(
        __DEV__ ?
          sourcemaps.write({ sourceRoot: '/vendor' }) :
          gutil.noop()
      ),

    gulp.src(paths.js)
      .pipe(plumber({ errorHandler: errorHandler }))
      .pipe(babel())
      .pipe(__DEV__ ? gutil.noop() : uglify())
  );

  return jsFiles
    .pipe(gulp.dest(dest))
    // create registry file
    .pipe(rev())
    // copy revisioned assets to dest
    .pipe(gulp.dest(dest))
    // create manifest file
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    // copy manifest file to dest
    .pipe(gulp.dest(paths.manifest));
});

// commonFramework depend on iFrameScripts
// and faux.js
gulp.task('dependents', ['js'], function() {
  var manifestName = 'dependents-manifest.json';
  var dest = paths.publicJs;

  var manifest = gulp.src(
    path.join(__dirname, paths.manifest, 'js-manifest.json')
  );

  return gulp.src(formatCommonFrameworkPaths.call(paths.commonFramework))
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(babel())
    .pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
    .pipe(concat('commonFramework.js'))
    .pipe(
      __DEV__ ?
        sourcemaps.write({ sourceRoot: '/commonFramework' }) :
        gutil.noop()
    )
    .pipe(__DEV__ ? gutil.noop() : uglify())
    .pipe(revReplace({ manifest: manifest }))
    .pipe(gulp.dest(dest))
    .pipe(rev())
    .pipe(gulp.dest(dest))
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

function collector(file, memo) {
  return Object.assign({}, JSON.parse(file.contents), memo);
}

function done(manifest) {
  return sortKeys(manifest);
}

function buildManifest() {
  return gulp.src(paths.manifest + '*.json')
    .pipe(reduce('rev-manifest.json', collector, done, {}))
    .pipe(gulp.dest('server/'));
}

var buildDependents = ['less', 'js', 'dependents', 'pack-watch-manifest'];

gulp.task('build-manifest', buildDependents, function() {
  return buildManifest();
});

gulp.task('build-manifest-watch', function() {
  return buildManifest();
});

gulp.task('build', [
  'less',
  'js',
  'dependents',
  'pack-client',
  'build-manifest'
]);

var watchDependents = [
  'less',
  'js',
  'dependents',
  'serve',
  'sync',
  'pack-watch',
  'pack-watch-manifest',
  'build-manifest'
];

gulp.task('reload', function() {
  notify({ message: 'test changed' });
  reload();
});

gulp.task('watch', watchDependents, function() {
  gulp.watch(paths.lessFiles, ['less']);
  gulp.watch(paths.js, ['js']);
  gulp.watch(paths.challenges, ['test-challenges', 'reload']);
  gulp.watch(paths.js, ['js', 'dependents']);
  gulp.watch(
    formatCommonFrameworkPaths.call(paths.commonFramework),
    ['dependents']
  );
  gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']);
  gulp.watch(webpackConfig.output.path + '/bundle.js', ['pack-watch-manifest']);
});

gulp.task('default', [
  'less',
  'serve',
  'pack-watch',
  'pack-watch-manifest',
  'build-manifest-watch',
  'watch',
  'sync'
]);

gulp.task('test', function() {
  return gulp.src('test/**/*.js')
    .pipe(tape({
      reporter: tapSpec()
    }));
});
<commit_msg>Fix production build never completing<commit_after>// enable debug for gulp
process.env.DEBUG = process.env.DEBUG || 'freecc:*';

require('babel-core/register');
var Rx = require('rx'),
  gulp = require('gulp'),
  path = require('path'),

  // utils
  plumber = require('gulp-plumber'),
  notify = require('gulp-notify'),
  gutil = require('gulp-util'),
  reduce = require('gulp-reduce-file'),
  sortKeys = require('sort-keys'),
  debug = require('debug')('freecc:gulp'),
  yargs = require('yargs'),
  concat = require('gulp-concat'),
  uglify = require('gulp-uglify'),
  merge = require('merge-stream'),
  babel = require('gulp-babel'),
  sourcemaps = require('gulp-sourcemaps'),

  // react app
  webpack = require('webpack-stream'),
  webpackConfig = require('./webpack.config.js'),
  webpackConfigNode = require('./webpack.config.node.js'),

  // server process
  nodemon = require('gulp-nodemon'),
  sync = require('browser-sync'),

  // css
  less = require('gulp-less'),

  // rev
  rev = require('gulp-rev'),
  revReplace = require('gulp-rev-replace'),
  revDel = require('rev-del'),

  // lint
  jsonlint = require('gulp-jsonlint'),
  eslint = require('gulp-eslint'),

  // unit-tests
  tape = require('gulp-tape'),
  tapSpec = require('tap-spec');

Rx.config.longStackSupport = true;

var __DEV__ = !yargs.argv.p;
var reloadDelay = 1000;
var reload = sync.reload;
var paths = {
  server: './server/server.js',
  serverIgnore: [
    'gulpfile.js',
    'public/',
    '!public/js/bundle*',
    'node_modules/',
    'client/',
    'seed',
    'server/manifests/*.json',
    'server/rev-manifest.json'
  ],

  publicJs: './public/js',
  css: 'public/css',

  loopback: {
    client: './client/loopbackClient',
    root: path.join(__dirname, 'client/'),
    clientName: 'lbApp'
  },

  client: {
    src: './client',
    dest: 'public/js'
  },

  vendorChallenges: [
    'public/bower_components/jshint/dist/jshint.js',
    'public/bower_components/chai/chai.js',
    'public/bower_components/CodeMirror/lib/codemirror.js',
    'public/bower_components/CodeMirror/addon/edit/closebrackets.js',
    'public/bower_components/CodeMirror/addon/edit/matchbrackets.js',
    'public/bower_components/CodeMirror/addon/lint/lint.js',
    'public/bower_components/CodeMirror/addon/lint/javascript-lint.js',
    'public/bower_components/CodeMirror/mode/javascript/javascript.js',
    'public/bower_components/CodeMirror/mode/xml/xml.js',
    'public/bower_components/CodeMirror/mode/css/css.js',
    'public/bower_components/CodeMirror/mode/htmlmixed/htmlmixed.js',
    'node_modules/emmet-codemirror/dist/emmet.js',
    'public/js/lib/loop-protect/loop-protect.js'
  ],

  vendorMain: [
    'public/bower_components/jquery/dist/jquery.min.js',
    'public/bower_components/bootstrap/dist/js/bootstrap.min.js',
    'public/bower_components/d3/d3.min.js',
    'public/bower_components/moment/min/moment.min.js',
    'public/bower_components/lightbox2/dist/js/lightbox.min.js',
    'public/bower_components/rxjs/dist/rx.all.min.js'
  ],

  js: [
    'client/main.js',
    'client/iFrameScripts.js',
    'client/plugin.js'
  ],

  commonFramework: [
    'init',
    'bindings',
    'add-test-to-string',
    'code-storage',
    'code-uri',
    'add-loop-protect',
    'get-iframe',
    'update-preview',
    'create-editor',
    'detect-unsafe-code-stream',
    'display-test-results',
    'execute-challenge-stream',
    'output-display',
    'phone-scroll-lock',
    'report-issue',
    'run-tests-stream',
    'show-completion',
    'step-challenge',
    'end'
  ],

  less: './client/less/main.less',
  lessFiles: './client/less/*.less',

  manifest: 'server/manifests/',

  node: {
    src: './client',
    dest: 'common/app'
  },

  syncWatch: [
    'public/**/*.*'
  ],

  challenges: [
    'seed/challenges/*/*.json'
  ]
};

var webpackOptions = {
  devtool: 'inline-source-map'
};

function formatCommonFrameworkPaths() {
  return this.map(function(script) {
    return 'client/commonFramework/' + script + '.js';
  });
}

function errorHandler() {
  var args = Array.prototype.slice.call(arguments);

  // Send error to notification center with gulp-notify
  notify.onError({
    title: 'Compile Error',
    message: '<%= error %>'
  }).apply(this, args);

  // Keep gulp from hanging on this task
  this.emit('end');
}

function delRev(dest, manifestName) {
  // in production do not delete old revisions
  if (!__DEV__) {
    return gutil.noop();
  }
  return revDel({
    oldManifest: path.join(paths.manifest, manifestName),
    dest: dest
  });
}

gulp.task('serve', ['build-manifest'], function(cb) {
  var called = false;
  nodemon({
    script: paths.server,
    ext: '.jsx .js .json',
    ignore: paths.serverIgnore,
    exec: path.join(__dirname, 'node_modules/.bin/babel-node'),
    env: {
      'NODE_ENV': 'development',
      'DEBUG': process.env.DEBUG || 'freecc:*'
    }
  })
    .on('start', function() {
      if (!called) {
        called = true;
        setTimeout(function() {
          cb();
        }, reloadDelay);
      }
    })
    .on('restart', function(files) {
      if (files) {
        debug('Files that changes: ', files);
      }
      setTimeout(function() {
        debug('Restarting browsers');
        reload();
      }, reloadDelay);
    });
});

var syncDepenedents = [
  'serve',
  'js',
  'less',
  'dependents',
  'pack-watch',
  'build-manifest'
];

gulp.task('sync', syncDepenedents, function() {
  sync.init(null, {
    proxy: 'http://localhost:3000',
    logLeval: 'debug',
    files: paths.syncWatch,
    port: 3001,
    open: false,
    reloadDelay: reloadDelay
  });
});

gulp.task('lint-js', function() {
  return gulp.src([
    'common/**/*.js',
    'common/**/*.jsx',
    'client/**/*.js',
    'client/**/*.jsx',
    'server/**/*.js',
    'config/**/*.js'
  ])
    .pipe(eslint())
    .pipe(eslint.format());
});

gulp.task('lint-json', function() {
  return gulp.src(paths.challenges)
    .pipe(jsonlint())
    .pipe(jsonlint.reporter());
});

gulp.task('test-challenges', ['lint-json']);

gulp.task('pack-client', function() {
  if (!__DEV__) { console.log('\n\nbundling production\n\n'); }

  var manifestName = 'react-manifest.json';
  var dest = webpackConfig.output.path;

  return gulp.src(webpackConfig.entry)
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(webpack(Object.assign(
      {},
      webpackConfig,
      webpackOptions
    )))
    .pipe(__DEV__ ? gutil.noop() : uglify())
    .pipe(gulp.dest(dest))
    .pipe(rev())
    // copy files to public
    .pipe(gulp.dest(dest))
    // create manifest
    .pipe(rev.manifest(manifestName))
    // delete old rev
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

var defaultStatsOptions = {
  colors: gutil.colors.supportsColor,
  hash: false,
  timings: false,
  chunks: false,
  chunkModules: false,
  modules: false,
  children: true,
  version: true,
  cached: false,
  cachedAssets: false,
  reasons: false,
  source: false,
  errorDetails: false
};

var webpackCalled = false;
gulp.task('pack-watch', function(cb) {
  if (webpackCalled) {
    console.log('webpack watching already runnning');
    return cb();
  }
  gulp.src(webpackConfig.entry)
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(webpack(Object.assign(
      {},
      webpackConfig,
      webpackOptions,
      { watch: true }
    ), null, function(notUsed, stats) {
      if (stats) {
        gutil.log(stats.toString(defaultStatsOptions));
      }

      if (!webpackCalled) {
        debug('webpack init completed');
        webpackCalled = true;
        cb();
      }

    }))
    .pipe(gulp.dest(webpackConfig.output.path));
});

gulp.task('pack-watch-manifest', ['pack-watch'], function() {
  var manifestName = 'react-manifest.json';
  var dest = webpackConfig.output.path;
  return gulp.src(dest + '/bundle.js')
    .pipe(rev())
    // copy files to public
    .pipe(gulp.dest(dest))
    // create manifest
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

gulp.task('pack-node', function() {
  return gulp.src(webpackConfigNode.entry)
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(webpack(webpackConfigNode))
    .pipe(gulp.dest(webpackConfigNode.output.path));
});

gulp.task('pack', ['pack-client', 'pack-node']);

gulp.task('less', function() {
  var manifestName = 'css-manifest.json';
  var dest = paths.css;
  return gulp.src(paths.less)
    .pipe(plumber({ errorHandler: errorHandler }))
    // compile
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest(dest))
    // add revision
    .pipe(rev())
    // copy files to public
    .pipe(gulp.dest(dest))
    // create and merge manifest
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

function getFilesGlob(files) {
  if (!__DEV__) {
    return files;
  }
  return files.map(function(file) {
    return file
      .replace('.min.', '.')
      // moment breaks the pattern
      .replace('/min/', '/');
  });
}

gulp.task('js', function() {
  var manifestName = 'js-manifest.json';
  var dest = paths.publicJs;

  var jsFiles = merge(

    gulp.src(getFilesGlob(paths.vendorMain))
      .pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
      .pipe(concat('vendor-main.js'))
      .pipe(
        __DEV__ ?
          sourcemaps.write({ sourceRoot: '/vendor' }) :
          gutil.noop()
      ),

    gulp.src(paths.vendorChallenges)
      .pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
      .pipe(__DEV__ ? gutil.noop() : uglify())
      .pipe(concat('vendor-challenges.js'))
      .pipe(
        __DEV__ ?
          sourcemaps.write({ sourceRoot: '/vendor' }) :
          gutil.noop()
      ),

    gulp.src(paths.js)
      .pipe(plumber({ errorHandler: errorHandler }))
      .pipe(babel())
      .pipe(__DEV__ ? gutil.noop() : uglify())
  );

  return jsFiles
    .pipe(gulp.dest(dest))
    // create registry file
    .pipe(rev())
    // copy revisioned assets to dest
    .pipe(gulp.dest(dest))
    // create manifest file
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    // copy manifest file to dest
    .pipe(gulp.dest(paths.manifest));
});

// commonFramework depend on iFrameScripts
// and faux.js
gulp.task('dependents', ['js'], function() {
  var manifestName = 'dependents-manifest.json';
  var dest = paths.publicJs;

  var manifest = gulp.src(
    path.join(__dirname, paths.manifest, 'js-manifest.json')
  );

  return gulp.src(formatCommonFrameworkPaths.call(paths.commonFramework))
    .pipe(plumber({ errorHandler: errorHandler }))
    .pipe(babel())
    .pipe(__DEV__ ? sourcemaps.init() : gutil.noop())
    .pipe(concat('commonFramework.js'))
    .pipe(
      __DEV__ ?
        sourcemaps.write({ sourceRoot: '/commonFramework' }) :
        gutil.noop()
    )
    .pipe(__DEV__ ? gutil.noop() : uglify())
    .pipe(revReplace({ manifest: manifest }))
    .pipe(gulp.dest(dest))
    .pipe(rev())
    .pipe(gulp.dest(dest))
    .pipe(rev.manifest(manifestName))
    .pipe(delRev(
      dest,
      manifestName
    ))
    .pipe(gulp.dest(paths.manifest));
});

function collector(file, memo) {
  return Object.assign({}, JSON.parse(file.contents), memo);
}

function done(manifest) {
  return sortKeys(manifest);
}

function buildManifest() {
  return gulp.src(paths.manifest + '*.json')
    .pipe(reduce('rev-manifest.json', collector, done, {}))
    .pipe(gulp.dest('server/'));
}

var buildDependents = ['less', 'js', 'dependents'];

if (__DEV__) {
  buildDependents.push('pack-watch-manifest');
}

gulp.task('build-manifest', buildDependents, function() {
  return buildManifest();
});

gulp.task('build-manifest-watch', function() {
  return buildManifest();
});

gulp.task('build', [
  'less',
  'js',
  'dependents',
  'pack-client',
  'build-manifest'
]);

var watchDependents = [
  'less',
  'js',
  'dependents',
  'serve',
  'sync',
  'pack-watch',
  'pack-watch-manifest',
  'build-manifest'
];

gulp.task('reload', function() {
  notify({ message: 'test changed' });
  reload();
});

gulp.task('watch', watchDependents, function() {
  gulp.watch(paths.lessFiles, ['less']);
  gulp.watch(paths.js, ['js']);
  gulp.watch(paths.challenges, ['test-challenges', 'reload']);
  gulp.watch(paths.js, ['js', 'dependents']);
  gulp.watch(
    formatCommonFrameworkPaths.call(paths.commonFramework),
    ['dependents']
  );
  gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']);
  gulp.watch(webpackConfig.output.path + '/bundle.js', ['pack-watch-manifest']);
});

gulp.task('default', [
  'less',
  'serve',
  'pack-watch',
  'pack-watch-manifest',
  'build-manifest-watch',
  'watch',
  'sync'
]);

gulp.task('test', function() {
  return gulp.src('test/**/*.js')
    .pipe(tape({
      reporter: tapSpec()
    }));
});
<|endoftext|>


<commit_before>const {Slice, Fragment, DOMParser, DOMSerializer} = require("prosemirror-model")

function serializeForClipboard(view, slice) {
  let context = [], {content, openStart, openEnd} = slice
  while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.firstChild.childCount == 1) {
    openStart--
    openEnd--
    let node = content.firstChild
    context.push(node.type.name, node.type.hasRequiredAttrs() ? node.attrs : null)
    content = node.content
  }

  let serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema)
  let wrap = document.createElement("div")
  wrap.appendChild(serializer.serializeFragment(content))
  let child = wrap.firstChild.nodeType == 1 && wrap.firstChild
  if (child) {
    let singleNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1 && !slice.content.firstChild.isText
    child.setAttribute("data-pm-context", singleNode ? "none" : JSON.stringify(context))
  }
  return wrap
}
exports.serializeForClipboard = serializeForClipboard

// : (EditorView, string, string, ?bool, ResolvedPos) → ?Slice
// Read a slice of content from the clipboard (or drop data).
function parseFromClipboard(view, text, html, plainText, $context) {
  let dom, inCode = $context.parent.type.spec.code
  if (!html && !text) return null
  if ((plainText || inCode || !html) && text) {
    view.someProp("transformPastedText", f => text = f(text))
    if (inCode) return new Slice(Fragment.from(view.state.schema.text(text)), 0, 0)
    dom = document.createElement("div")
    text.trim().split(/(?:\r\n?|\n)+/).forEach(block => {
      dom.appendChild(document.createElement("p")).textContent = block
    })
  } else {
    view.someProp("transformPastedHTML", f => html = f(html))
    dom = readHTML(html)
  }

  let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema)
  let slice = parser.parseSlice(dom, {preserveWhitespace: true, context: $context})
  slice = closeIsolatingStart(slice)
  let contextNode = dom.querySelector("[data-pm-context]"), context = contextNode && contextNode.getAttribute("data-pm-context")
  if (context == "none")
    slice = new Slice(slice.content, 0, 0)
  else if (context)
    slice = addContext(slice, context)
  else // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
    slice = normalizeSiblings(slice, $context)
  view.someProp("transformPasted", f => { slice = f(slice) })
  return slice
}
exports.parseFromClipboard = parseFromClipboard

// Takes a slice parsed with parseSlice, which means there hasn't been
// any content-expression checking done on the top nodes, tries to
// find a parent node in the current context that might fit the nodes,
// and if successful, rebuilds the slice so that it fits into that parent.
//
// This addresses the problem that Transform.replace expects a
// coherent slice, and will fail to place a set of siblings that don't
// fit anywhere in the schema.
function normalizeSiblings(slice, $context) {
  if (slice.content.childCount < 2) return slice
  for (let d = $context.depth; d >= 0; d--) {
    let parent = $context.node(d)
    let match = parent.contentMatchAt($context.index(d))
    let lastWrap, result = []
    slice.content.forEach(node => {
      if (!result) return
      let wrap = match.findWrappingFor(node), inLast
      if (!wrap) return result = null
      if (inLast = result.length && lastWrap.length && addToSibling(wrap, lastWrap, node, result[result.length - 1], 0)) {
        result[result.length - 1] = inLast
      } else {
        if (result.length) result[result.length - 1] = closeRight(result[result.length - 1], lastWrap.length)
        let wrapped = withWrappers(node, wrap)
        result.push(wrapped)
        match = match.matchType(wrapped.type, wrapped.attrs)
        lastWrap = wrap
      }
    })
    if (result) return Slice.maxOpen(Fragment.from(result))
  }
  return slice
}

function withWrappers(node, wrap, from = 0) {
  for (let i = wrap.length - 1; i >= from; i--)
    node = wrap[i].type.create(wrap[i].attrs, Fragment.from(node))
  return node
}

// Used to group adjacent nodes wrapped in similar parents by
// normalizeSiblings into the same parent node
function addToSibling(wrap, lastWrap, node, sibling, depth) {
  if (depth < wrap.length && depth < lastWrap.length && wrap[depth].type == lastWrap[depth].type) {
    let inner = addToSibling(wrap, lastWrap, node, sibling.lastChild, depth + 1)
    if (inner) return sibling.copy(sibling.content.replaceChild(sibling.childCount - 1, inner))
    let match = sibling.contentMatchAt(sibling.childCount)
    if (depth == wrap.length - 1 ? match.matchNode(node) : match.matchType(wrap[depth + 1].type, wrap[depth + 1].attrs))
      return sibling.copy(sibling.content.append(Fragment.from(withWrappers(node, wrap, depth + 1))))
  }
}

function closeRight(node, depth) {
  if (depth == 0) return node
  let fragment = node.content.replaceChild(node.childCount - 1, closeRight(node.lastChild, depth - 1))
  let fill = node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true)
  return node.copy(fragment.append(fill))
}

// Trick from jQuery -- some elements must be wrapped in other
// elements for innerHTML to work. I.e. if you do `div.innerHTML =
// "..	"` the table cells are ignored.
const wrapMap = {thead: "table", colgroup: "table", col: "table colgroup",
                 tr: "table tbody", td: "table tbody tr", th: "table tbody tr"}
let detachedDoc = null
function readHTML(html) {
  let metas = /(\s*<meta [^>]*>)*/.exec(html)
  if (metas) html = html.slice(metas[0].length)
  let doc = detachedDoc || (detachedDoc = document.implementation.createHTMLDocument("title"))
  let elt = doc.createElement("div")
  let firstTag = /(?:<meta [^>]*>)*<([a-z][^>\s]+)/i.exec(html), wrap, depth = 0
  if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()]) {
    let nodes = wrap.split(" ")
    html = nodes.map(n => "<" + n + ">").join("") + html + nodes.map(n => "</" + n + ">").reverse().join("")
    depth = nodes.length
  }
  elt.innerHTML = html
  for (let i = 0; i < depth; i++) elt = elt.firstChild
  return elt
}

function addContext(slice, context) {
  if (!slice.size) return slice
  let schema = slice.content.firstChild.type.schema, array
  try { array = JSON.parse(context) }
  catch(e) { return slice }
  let {content, openStart, openEnd} = slice
  for (let i = array.length - 2; i >= 0; i -= 2) {
    let type = schema.nodes[array[i]]
    if (!type || type.hasRequiredAttrs()) break
    content = Fragment.from(type.create(array[i + 1], content))
    openStart++; openEnd++
  }
  return new Slice(content, openStart, openEnd)
}

function closeIsolatingStart(slice) {
  let closeTo = 0, frag = slice.content
  for (let i = 1; i <= slice.openStart; i++) {
    let node = frag.firstChild
    if (node.type.spec.isolating) { closeTo = i; break }
    frag = node.content
  }

  if (closeTo == 0) return slice
  return new Slice(closeFragment(slice.content, closeTo, slice.openEnd), slice.openStart - closeTo, slice.openEnd)
}

function closeFragment(frag, n, openEnd) {
  if (n == 0) return frag
  let node = frag.firstChild
  let content = closeFragment(node.content, n - 1, openEnd - 1)
  let fill = node.contentMatchAt(0).fillBefore(node.content, openEnd <= 0)
  return frag.replaceChild(0, node.copy(fill.append(content)))
}
<commit_msg>Wrap copied table elements in tables to avoid browser normalization<commit_after>const {Slice, Fragment, DOMParser, DOMSerializer} = require("prosemirror-model")

function serializeForClipboard(view, slice) {
  let context = [], {content, openStart, openEnd} = slice
  while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.firstChild.childCount == 1) {
    openStart--
    openEnd--
    let node = content.firstChild
    context.push(node.type.name, node.type.hasRequiredAttrs() ? node.attrs : null)
    content = node.content
  }

  let serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema)
  let wrap = document.createElement("div")
  wrap.appendChild(serializer.serializeFragment(content))

  let firstChild = wrap.firstChild, needsWrap
  while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
    for (let i = 0; i < needsWrap.length; i++) {
      let wrapper = document.createElement(needsWrap[i])
      while (wrap.firstChild) wrapper.appendChild(wrap.firstChild)
      wrap.appendChild(wrapper)
    }
    firstChild = wrap.firstChild
  }

  if (firstChild && firstChild.nodeType == 1) {
    let singleNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1 && !slice.content.firstChild.isText
    firstChild.setAttribute("data-pm-context", singleNode ? "none" : JSON.stringify(context))
  }
  return wrap
}
exports.serializeForClipboard = serializeForClipboard

// : (EditorView, string, string, ?bool, ResolvedPos) → ?Slice
// Read a slice of content from the clipboard (or drop data).
function parseFromClipboard(view, text, html, plainText, $context) {
  let dom, inCode = $context.parent.type.spec.code
  if (!html && !text) return null
  if ((plainText || inCode || !html) && text) {
    view.someProp("transformPastedText", f => text = f(text))
    if (inCode) return new Slice(Fragment.from(view.state.schema.text(text)), 0, 0)
    dom = document.createElement("div")
    text.trim().split(/(?:\r\n?|\n)+/).forEach(block => {
      dom.appendChild(document.createElement("p")).textContent = block
    })
  } else {
    view.someProp("transformPastedHTML", f => html = f(html))
    dom = readHTML(html)
  }

  let parser = view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema)
  let slice = parser.parseSlice(dom, {preserveWhitespace: true, context: $context})
  slice = closeIsolatingStart(slice)
  let contextNode = dom.querySelector("[data-pm-context]"), context = contextNode && contextNode.getAttribute("data-pm-context")
  if (context == "none")
    slice = new Slice(slice.content, 0, 0)
  else if (context)
    slice = addContext(slice, context)
  else // HTML wasn't created by ProseMirror. Make sure top-level siblings are coherent
    slice = normalizeSiblings(slice, $context)
  view.someProp("transformPasted", f => { slice = f(slice) })
  return slice
}
exports.parseFromClipboard = parseFromClipboard

// Takes a slice parsed with parseSlice, which means there hasn't been
// any content-expression checking done on the top nodes, tries to
// find a parent node in the current context that might fit the nodes,
// and if successful, rebuilds the slice so that it fits into that parent.
//
// This addresses the problem that Transform.replace expects a
// coherent slice, and will fail to place a set of siblings that don't
// fit anywhere in the schema.
function normalizeSiblings(slice, $context) {
  if (slice.content.childCount < 2) return slice
  for (let d = $context.depth; d >= 0; d--) {
    let parent = $context.node(d)
    let match = parent.contentMatchAt($context.index(d))
    let lastWrap, result = []
    slice.content.forEach(node => {
      if (!result) return
      let wrap = match.findWrappingFor(node), inLast
      if (!wrap) return result = null
      if (inLast = result.length && lastWrap.length && addToSibling(wrap, lastWrap, node, result[result.length - 1], 0)) {
        result[result.length - 1] = inLast
      } else {
        if (result.length) result[result.length - 1] = closeRight(result[result.length - 1], lastWrap.length)
        let wrapped = withWrappers(node, wrap)
        result.push(wrapped)
        match = match.matchType(wrapped.type, wrapped.attrs)
        lastWrap = wrap
      }
    })
    if (result) return Slice.maxOpen(Fragment.from(result))
  }
  return slice
}

function withWrappers(node, wrap, from = 0) {
  for (let i = wrap.length - 1; i >= from; i--)
    node = wrap[i].type.create(wrap[i].attrs, Fragment.from(node))
  return node
}

// Used to group adjacent nodes wrapped in similar parents by
// normalizeSiblings into the same parent node
function addToSibling(wrap, lastWrap, node, sibling, depth) {
  if (depth < wrap.length && depth < lastWrap.length && wrap[depth].type == lastWrap[depth].type) {
    let inner = addToSibling(wrap, lastWrap, node, sibling.lastChild, depth + 1)
    if (inner) return sibling.copy(sibling.content.replaceChild(sibling.childCount - 1, inner))
    let match = sibling.contentMatchAt(sibling.childCount)
    if (depth == wrap.length - 1 ? match.matchNode(node) : match.matchType(wrap[depth + 1].type, wrap[depth + 1].attrs))
      return sibling.copy(sibling.content.append(Fragment.from(withWrappers(node, wrap, depth + 1))))
  }
}

function closeRight(node, depth) {
  if (depth == 0) return node
  let fragment = node.content.replaceChild(node.childCount - 1, closeRight(node.lastChild, depth - 1))
  let fill = node.contentMatchAt(node.childCount).fillBefore(Fragment.empty, true)
  return node.copy(fragment.append(fill))
}

// Trick from jQuery -- some elements must be wrapped in other
// elements for innerHTML to work. I.e. if you do `div.innerHTML =
// "..	"` the table cells are ignored.
const wrapMap = {thead: ["table"], colgroup: ["table"], col: ["table", "colgroup"],
                 tr: ["table", "tbody"], td: ["table", "tbody", "tr"], th: ["table", "tbody", "tr"]}
let detachedDoc = null
function readHTML(html) {
  let metas = /(\s*<meta [^>]*>)*/.exec(html)
  if (metas) html = html.slice(metas[0].length)
  let doc = detachedDoc || (detachedDoc = document.implementation.createHTMLDocument("title"))
  let elt = doc.createElement("div")
  let firstTag = /(?:<meta [^>]*>)*<([a-z][^>\s]+)/i.exec(html), wrap, depth = 0
  if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()]) {
    html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("")
    depth = wrap.length
  }
  elt.innerHTML = html
  for (let i = 0; i < depth; i++) elt = elt.firstChild
  return elt
}

function addContext(slice, context) {
  if (!slice.size) return slice
  let schema = slice.content.firstChild.type.schema, array
  try { array = JSON.parse(context) }
  catch(e) { return slice }
  let {content, openStart, openEnd} = slice
  for (let i = array.length - 2; i >= 0; i -= 2) {
    let type = schema.nodes[array[i]]
    if (!type || type.hasRequiredAttrs()) break
    content = Fragment.from(type.create(array[i + 1], content))
    openStart++; openEnd++
  }
  return new Slice(content, openStart, openEnd)
}

function closeIsolatingStart(slice) {
  let closeTo = 0, frag = slice.content
  for (let i = 1; i <= slice.openStart; i++) {
    let node = frag.firstChild
    if (node.type.spec.isolating) { closeTo = i; break }
    frag = node.content
  }

  if (closeTo == 0) return slice
  return new Slice(closeFragment(slice.content, closeTo, slice.openEnd), slice.openStart - closeTo, slice.openEnd)
}

function closeFragment(frag, n, openEnd) {
  if (n == 0) return frag
  let node = frag.firstChild
  let content = closeFragment(node.content, n - 1, openEnd - 1)
  let fill = node.contentMatchAt(0).fillBefore(node.content, openEnd <= 0)
  return frag.replaceChild(0, node.copy(fill.append(content)))
}
<|endoftext|>


<commit_before>/* eslint-disable no-unused-vars */
var utils = {
  strWrap: (str) => {
    if (str.includes('\n') || (str.includes('"') && str.includes("'"))) return '`';
    return !str.includes("'") ? "'" : '"';
  },

  escapeRegExp: (str, q) => str.replace(new RegExp(`[\\\\${q}\\n]`, 'g'), '\\$&').replace(/-/g, '\\x2d'),

  _unescape: (str) => {
    for (let i = 32; i < 128; i++) {
      str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i));
    }
    str = str.replace(/\\x09/g, '\t');
    return str;
  },

  strMerge: (str) => str.replace(/'\s*\+\s*'|"\s*\+\s*"/g, ''),

  methodChain: (str) => str.replace(/(?<!\()\[("|')((?!\d)[a-z_\d$]*)("|')\]/gi, '.$2 '),

  calc: (str) =>
    str.replace(/(?<!('|"))([+\-*/]*(?<!\w)0x[a-f\d]+[+\-*/]*)+(?!('|"))/gi, (m) => {
      const notChain = m.match(/^([+\-*/]+)((?<!\w)0x[a-f\d]+)|((?<!\w)0x[a-f\d]+)([+\-*/]+)$/i);
      if (!notChain) return eval(m);
      if (notChain[2]) return notChain[1] + eval(notChain[2]);
      return eval(notChain[3]) + notChain[4];
    }),
};
<commit_msg>Fix calc<commit_after>/* eslint-disable no-unused-vars */
var utils = {
  strWrap: (str) => {
    if (str.includes('\n') || (str.includes('"') && str.includes("'"))) return '`';
    return !str.includes("'") ? "'" : '"';
  },

  escapeRegExp: (str, q) => str.replace(new RegExp(`[\\\\${q}\\n]`, 'g'), '\\$&').replace(/-/g, '\\x2d'),

  _unescape: (str) => {
    for (let i = 32; i < 128; i++) {
      str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i));
    }
    str = str.replace(/\\x09/g, '\t');
    return str;
  },

  strMerge: (str) => str.replace(/'\s*\+\s*'|"\s*\+\s*"/g, ''),

  methodChain: (str) => str.replace(/(?<!\()\[("|')((?!\d)[a-z_\d$]*)("|')\]/gi, '.$2 '),

  calc: (str) =>
    str.replace(/(?<!('|"))([+\-*/]*(?<!\w)0x[a-f\d]+[+\-*/]*)+(?!('|"))/gi, (m) => {
      const notChain = m.match(/^(([+\-*/]+)((?<!\w)0x[a-f\d]+)|((?<!\w)0x[a-f\d]+)([+\-*/]+))$/i);
      if (!notChain) return eval(m);
      if (notChain[2]) return notChain[1] + eval(notChain[2]);
      return eval(notChain[3]) + notChain[4];
    }),
};
<|endoftext|>


<commit_before>import { alias, readOnly, gt } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import Controller from '@ember/controller';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import ArrayProxy from '@ember/array/proxy';
// eslint-disable-next-line ember/no-observers
import { computed, observer } from '@ember/object';
import moment from 'moment';
import ajax from 'ember-fetch/ajax';

const NUM_VERSIONS = 5;

const PromiseArray = ArrayProxy.extend(PromiseProxyMixin);

export default Controller.extend({
  session: service(),

  isDownloading: false,

  downloadsContext: computed('requestedVersion', 'model', 'crate', function() {
    return this.requestedVersion ? this.model : this.crate;
  }),
  downloads: alias('downloadsContext.version_downloads'),
  extraDownloads: alias('downloads.content.meta.extra_downloads'),

  fetchingFollowing: true,
  following: false,
  currentVersion: alias('model'),
  crateTomlText: computed('crate.name', 'currentVersion.num', function() {
    return `${this.get('crate.name')} = "${this.get('currentVersion.num')}"`;
  }),
  requestedVersion: null,
  keywords: alias('crate.keywords'),
  categories: alias('crate.categories'),
  badges: alias('crate.badges'),
  isOwner: computed('crate.owner_user', 'session.currentUser.id', function() {
    return this.get('crate.owner_user').findBy('id', this.get('session.currentUser.id'));
  }),
  notYankedOrIsOwner: computed('model', 'crate.owner_user', 'session.currentUser.id', function() {
    return !this.get('model').yanked || this.get('crate.owner_user').findBy('id', this.get('session.currentUser.id'));
  }),

  sortedVersions: readOnly('crate.versions'),

  smallSortedVersions: computed('sortedVersions', function() {
    return this.sortedVersions.slice(0, NUM_VERSIONS);
  }),

  hasMoreVersions: gt('sortedVersions.length', NUM_VERSIONS),

  displayedAuthors: computed('currentVersion.authors.[]', function() {
    return PromiseArray.create({
      promise: this.get('currentVersion.authors').then(authors => {
        let ret = authors.slice();
        let others = authors.get('meta');
        for (let i = 0; i < others.names.length; i++) {
          ret.push({ name: others.names[i] });
        }
        return ret;
      }),
    });
  }),

  anyKeywords: gt('keywords.length', 0),
  anyCategories: gt('categories.length', 0),

  currentDependencies: computed('currentVersion.dependencies', function() {
    let deps = this.get('currentVersion.dependencies');

    if (deps === null) {
      return [];
    }

    return PromiseArray.create({
      promise: deps.then(deps => deps.filterBy('kind', 'normal').uniqBy('crate_id')),
    });
  }),

  currentBuildDependencies: computed('currentVersion.dependencies', function() {
    let deps = this.get('currentVersion.dependencies');

    if (deps === null) {
      return [];
    }

    return PromiseArray.create({
      promise: deps.then(deps => deps.filterBy('kind', 'build').uniqBy('crate_id')),
    });
  }),

  currentDevDependencies: computed('currentVersion.dependencies', function() {
    let deps = this.get('currentVersion.dependencies');
    if (deps === null) {
      return [];
    }
    return PromiseArray.create({
      promise: deps.then(deps => deps.filterBy('kind', 'dev').uniqBy('crate_id')),
    });
  }),

  downloadData: computed('downloads', 'extraDownloads', 'requestedVersion', function() {
    let downloads = this.downloads;
    if (!downloads) {
      return;
    }

    let extra = this.extraDownloads || [];

    let dates = {};
    let versions = [];
    for (let i = 0; i < 90; i++) {
      let now = moment().subtract(i, 'days');
      dates[now.format('MMM D')] = { date: now, cnt: {} };
    }

    downloads.forEach(d => {
      let version_id = d.get('version.id');
      let key = moment(d.get('date'))
        .utc()
        .format('MMM D');
      if (dates[key]) {
        let prev = dates[key].cnt[version_id] || 0;
        dates[key].cnt[version_id] = prev + d.get('downloads');
      }
    });

    extra.forEach(d => {
      let key = moment(d.date)
        .utc()
        .format('MMM D');
      if (dates[key]) {
        let prev = dates[key].cnt[null] || 0;
        dates[key].cnt[null] = prev + d.downloads;
      }
    });
    if (this.requestedVersion) {
      versions.push(this.model.getProperties('id', 'num'));
    } else {
      this.smallSortedVersions.forEach(version => {
        versions.push(version.getProperties('id', 'num'));
      });
    }
    if (extra.length > 0) {
      versions.push({
        id: null,
        num: 'Other',
      });
    }

    let headers = ['Date'];
    versions.sort(b => b.num).reverse();
    for (let i = 0; i < versions.length; i++) {
      headers.push(versions[i].num);
    }
    let data = [headers];
    for (let date in dates) {
      let row = [dates[date].date.toDate()];
      for (let i = 0; i < versions.length; i++) {
        row.push(dates[date].cnt[versions[i].id] || 0);
      }
      data.push(row);
    }

    return data;
  }),

  requestFollowState() {
    this.set('fetchingFollowing', true);
    ajax(`/api/v1/crates/${this.crate.get('name')}/following`)
      .then(d => this.set('following', d.following))
      .finally(() => this.set('fetchingFollowing', false));
  },

  actions: {
    toggleFollow() {
      this.set('fetchingFollowing', true);

      let crate = this.crate;
      let op = this.toggleProperty('following') ? crate.follow() : crate.unfollow();

      return op.finally(() => this.set('fetchingFollowing', false));
    },
  },

  // eslint-disable-next-line ember/no-observers
  report: observer('crate.readme', function() {
    if (typeof document === 'undefined') {
      return;
    }
    setTimeout(() => {
      let e = document.createEvent('CustomEvent');
      e.initCustomEvent('hashchange', true, true);
      window.dispatchEvent(e);
    });
  }),
});
<commit_msg>crate.version: Convert `requestFollowState()` to async/await<commit_after>import { alias, readOnly, gt } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import Controller from '@ember/controller';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import ArrayProxy from '@ember/array/proxy';
// eslint-disable-next-line ember/no-observers
import { computed, observer } from '@ember/object';
import moment from 'moment';
import ajax from 'ember-fetch/ajax';

const NUM_VERSIONS = 5;

const PromiseArray = ArrayProxy.extend(PromiseProxyMixin);

export default Controller.extend({
  session: service(),

  isDownloading: false,

  downloadsContext: computed('requestedVersion', 'model', 'crate', function() {
    return this.requestedVersion ? this.model : this.crate;
  }),
  downloads: alias('downloadsContext.version_downloads'),
  extraDownloads: alias('downloads.content.meta.extra_downloads'),

  fetchingFollowing: true,
  following: false,
  currentVersion: alias('model'),
  crateTomlText: computed('crate.name', 'currentVersion.num', function() {
    return `${this.get('crate.name')} = "${this.get('currentVersion.num')}"`;
  }),
  requestedVersion: null,
  keywords: alias('crate.keywords'),
  categories: alias('crate.categories'),
  badges: alias('crate.badges'),
  isOwner: computed('crate.owner_user', 'session.currentUser.id', function() {
    return this.get('crate.owner_user').findBy('id', this.get('session.currentUser.id'));
  }),
  notYankedOrIsOwner: computed('model', 'crate.owner_user', 'session.currentUser.id', function() {
    return !this.get('model').yanked || this.get('crate.owner_user').findBy('id', this.get('session.currentUser.id'));
  }),

  sortedVersions: readOnly('crate.versions'),

  smallSortedVersions: computed('sortedVersions', function() {
    return this.sortedVersions.slice(0, NUM_VERSIONS);
  }),

  hasMoreVersions: gt('sortedVersions.length', NUM_VERSIONS),

  displayedAuthors: computed('currentVersion.authors.[]', function() {
    return PromiseArray.create({
      promise: this.get('currentVersion.authors').then(authors => {
        let ret = authors.slice();
        let others = authors.get('meta');
        for (let i = 0; i < others.names.length; i++) {
          ret.push({ name: others.names[i] });
        }
        return ret;
      }),
    });
  }),

  anyKeywords: gt('keywords.length', 0),
  anyCategories: gt('categories.length', 0),

  currentDependencies: computed('currentVersion.dependencies', function() {
    let deps = this.get('currentVersion.dependencies');

    if (deps === null) {
      return [];
    }

    return PromiseArray.create({
      promise: deps.then(deps => deps.filterBy('kind', 'normal').uniqBy('crate_id')),
    });
  }),

  currentBuildDependencies: computed('currentVersion.dependencies', function() {
    let deps = this.get('currentVersion.dependencies');

    if (deps === null) {
      return [];
    }

    return PromiseArray.create({
      promise: deps.then(deps => deps.filterBy('kind', 'build').uniqBy('crate_id')),
    });
  }),

  currentDevDependencies: computed('currentVersion.dependencies', function() {
    let deps = this.get('currentVersion.dependencies');
    if (deps === null) {
      return [];
    }
    return PromiseArray.create({
      promise: deps.then(deps => deps.filterBy('kind', 'dev').uniqBy('crate_id')),
    });
  }),

  downloadData: computed('downloads', 'extraDownloads', 'requestedVersion', function() {
    let downloads = this.downloads;
    if (!downloads) {
      return;
    }

    let extra = this.extraDownloads || [];

    let dates = {};
    let versions = [];
    for (let i = 0; i < 90; i++) {
      let now = moment().subtract(i, 'days');
      dates[now.format('MMM D')] = { date: now, cnt: {} };
    }

    downloads.forEach(d => {
      let version_id = d.get('version.id');
      let key = moment(d.get('date'))
        .utc()
        .format('MMM D');
      if (dates[key]) {
        let prev = dates[key].cnt[version_id] || 0;
        dates[key].cnt[version_id] = prev + d.get('downloads');
      }
    });

    extra.forEach(d => {
      let key = moment(d.date)
        .utc()
        .format('MMM D');
      if (dates[key]) {
        let prev = dates[key].cnt[null] || 0;
        dates[key].cnt[null] = prev + d.downloads;
      }
    });
    if (this.requestedVersion) {
      versions.push(this.model.getProperties('id', 'num'));
    } else {
      this.smallSortedVersions.forEach(version => {
        versions.push(version.getProperties('id', 'num'));
      });
    }
    if (extra.length > 0) {
      versions.push({
        id: null,
        num: 'Other',
      });
    }

    let headers = ['Date'];
    versions.sort(b => b.num).reverse();
    for (let i = 0; i < versions.length; i++) {
      headers.push(versions[i].num);
    }
    let data = [headers];
    for (let date in dates) {
      let row = [dates[date].date.toDate()];
      for (let i = 0; i < versions.length; i++) {
        row.push(dates[date].cnt[versions[i].id] || 0);
      }
      data.push(row);
    }

    return data;
  }),

  async requestFollowState() {
    this.set('fetchingFollowing', true);
    try {
      let d = await ajax(`/api/v1/crates/${this.crate.get('name')}/following`);
      this.set('following', d.following);
    } finally {
      this.set('fetchingFollowing', false);
    }
  },

  actions: {
    toggleFollow() {
      this.set('fetchingFollowing', true);

      let crate = this.crate;
      let op = this.toggleProperty('following') ? crate.follow() : crate.unfollow();

      return op.finally(() => this.set('fetchingFollowing', false));
    },
  },

  // eslint-disable-next-line ember/no-observers
  report: observer('crate.readme', function() {
    if (typeof document === 'undefined') {
      return;
    }
    setTimeout(() => {
      let e = document.createEvent('CustomEvent');
      e.initCustomEvent('hashchange', true, true);
      window.dispatchEvent(e);
    });
  }),
});
<|endoftext|>


<commit_before>"use strict";

/*
 Build file for Jenkins Design Language theme.
 */

const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const babel = require('gulp-babel');
const concat = require('gulp-concat');
const less = require('gulp-less');
const clean = require('gulp-clean');
const runSequence = require('run-sequence');
const rename = require('gulp-rename');
const copy = require('gulp-copy');
const svgmin = require('gulp-svgmin');
const mocha = require('gulp-mocha');
const babelCompiler = require('babel-core/register');

// Options, src/dest folders, etc

const config = {
    react: {
        sources: "src/**/*.{js,jsx}",
        dest: "dist"
    },
    less: {
        sources: "less/theme.less",
        watch: "less/**/*.less", // Watch includes as well as main
        dest: "dist/assets/css"
    },
    svgmin: {
        sources: "icons/**/*",
        dest: "icons/"
    },
    copy: {
        icons: {
            sources: "icons/**/*",
            dest: "dist/assets/"
        },
        octicons: {
            sources: "node_modules/octicons/octicons/octicons.{eot,woff,ttf,svg,css}",
            dest: "dist/assets/"
        },
        normalize: {
            sources: "node_modules/normalize.css/normalize.css",
            dest: "dist/assets/css/"
        },
        fontsCSS: {
            sources: "fonts/*.css",
            dest: "dist/assets/css/"
        },
        fonts: {
            sources: "fonts/*.woff",
            dest: "dist/assets/fonts/"
        },
        licenses_octicons: {
            sources: "node_modules/octicons/LICENSE.txt",
            dest: "licenses/"
        },
        licenses_ofl: {
            sources: "fonts/OFL.txt",
            dest: "licenses/"
        }
    },
    clean: ["dist", "licenses"],
    test: {
        sources: ["test/**/*-spec.js"]
    }
};

// Watch

gulp.task("watch", ["default"], () => {
   gulp.watch(config.react.sources, ["compile-react"]);
   gulp.watch(config.less.watch, ["less"]);
});

// Watch styles only, when you're using Storybook
gulp.task("watch-styles", ["default"], () => {
   gulp.watch(config.less.watch, ["less"]);
});

// Default to clean and build

gulp.task("default", () =>
    runSequence("clean", "test", "build"));

// Clean

gulp.task("clean", () =>
    gulp.src(config.clean, {read: false})
        .pipe(clean()));

// Build all

gulp.task("build", ["compile-react", "less", "copy"]);

// Compile react sources

gulp.task("compile-react", () =>
    gulp.src(config.react.sources)
        .pipe(sourcemaps.init())
        .pipe(babel(config.react.babel))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest(config.react.dest)));

gulp.task("test", () => {
    gulp.src(config.test.sources)
        .pipe(mocha({
            compilers: { js: babelCompiler }
        }));
});

// Build the CSS

gulp.task("less", () =>
    gulp.src(config.less.sources)
        .pipe(sourcemaps.init())
        .pipe(less())
        .pipe(rename("jenkins-design-language.css"))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest(config.less.dest)));

// Optimize SVG
gulp.task("svgmin", () =>
    gulp.src(config.svgmin.sources)
        .pipe(svgmin())
        .pipe(gulp.dest(config.svgmin.dest)));

// Copy things

gulp.task("copy", ["copy-icons", "copy-octicons", "copy-normalize", "copy-fontsCSS", "copy-fonts",
    "copy-licenses-octicons", "copy-licenses-ofl"]);

gulp.task("copy-icons", () =>
    gulp.src(config.copy.icons.sources)
        .pipe(copy(config.copy.icons.dest)));

gulp.task("copy-octicons", () =>
    gulp.src(config.copy.octicons.sources)
        .pipe(copy(config.copy.octicons.dest, {prefix: 2})));

gulp.task("copy-normalize", () =>
    gulp.src(config.copy.normalize.sources)
        .pipe(copy(config.copy.normalize.dest, {prefix: 2})));

gulp.task("copy-fontsCSS", () =>
    gulp.src(config.copy.fontsCSS.sources)
        .pipe(copy(config.copy.fontsCSS.dest, {prefix: 1})));

gulp.task("copy-fonts", () =>
    gulp.src(config.copy.fonts.sources)
        .pipe(copy(config.copy.fonts.dest, {prefix: 1})));

gulp.task("copy-licenses-octicons", () =>
    gulp.src(config.copy.licenses_octicons.sources)
        .pipe(copy(config.copy.licenses_octicons.dest, {prefix: 1})));

gulp.task("copy-licenses-ofl", () =>
    gulp.src(config.copy.licenses_ofl.sources)
        .pipe(copy(config.copy.licenses_ofl.dest, {prefix: 1})));
<commit_msg>[UX-476] tweak layout<commit_after>"use strict";

/*
 Build file for Jenkins Design Language theme.
 */

const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const babel = require('gulp-babel');
const concat = require('gulp-concat');
const less = require('gulp-less');
const clean = require('gulp-clean');
const runSequence = require('run-sequence');
const rename = require('gulp-rename');
const copy = require('gulp-copy');
const svgmin = require('gulp-svgmin');
const mocha = require('gulp-mocha');
const babelCompiler = require('babel-core/register');

// Options, src/dest folders, etc

const config = {
    react: {
        sources: "src/**/*.{js,jsx}",
        dest: "dist"
    },
    less: {
        sources: "less/theme.less",
        watch: "less/**/*.less", // Watch includes as well as main
        dest: "dist/assets/css"
    },
    svgmin: {
        sources: "icons/**/*",
        dest: "icons/"
    },
    copy: {
        icons: {
            sources: "icons/**/*",
            dest: "dist/assets/"
        },
        octicons: {
            sources: "node_modules/octicons/octicons/octicons.{eot,woff,ttf,svg,css}",
            dest: "dist/assets/"
        },
        normalize: {
            sources: "node_modules/normalize.css/normalize.css",
            dest: "dist/assets/css/"
        },
        fontsCSS: {
            sources: "fonts/*.css",
            dest: "dist/assets/css/"
        },
        fonts: {
            sources: "fonts/*.woff",
            dest: "dist/assets/fonts/"
        },
        licenses_octicons: {
            sources: "node_modules/octicons/LICENSE.txt",
            dest: "licenses/"
        },
        licenses_ofl: {
            sources: "fonts/OFL.txt",
            dest: "licenses/"
        }
    },
    clean: ["dist", "licenses"],
    test: {
        sources: ["test/**/*-spec.js"]
    }
};

// Watch

gulp.task("watch", ["default"], () => {
   gulp.watch(config.react.sources, ["compile-react"]);
   gulp.watch(config.less.watch, ["less"]);
});

// Watch styles only, when you're using Storybook
gulp.task("watch-styles", ["default"], () => {
   gulp.watch(config.less.watch, ["less"]);
});

// Default to clean and build

gulp.task("default", () =>
    runSequence("clean", "test", "build"));

// Clean

gulp.task("clean", () =>
    gulp.src(config.clean, {read: false})
        .pipe(clean()));

// Testing

gulp.task("test", () => (
    gulp.src(config.test.sources)
        .pipe(mocha({
            compilers: { js: babelCompiler }
        }))
));

// Build all

gulp.task("build", ["compile-react", "less", "copy"]);

// Compile react sources

gulp.task("compile-react", () =>
    gulp.src(config.react.sources)
        .pipe(sourcemaps.init())
        .pipe(babel(config.react.babel))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest(config.react.dest)));

// Build the CSS

gulp.task("less", () =>
    gulp.src(config.less.sources)
        .pipe(sourcemaps.init())
        .pipe(less())
        .pipe(rename("jenkins-design-language.css"))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest(config.less.dest)));

// Optimize SVG
gulp.task("svgmin", () =>
    gulp.src(config.svgmin.sources)
        .pipe(svgmin())
        .pipe(gulp.dest(config.svgmin.dest)));

// Copy things

gulp.task("copy", ["copy-icons", "copy-octicons", "copy-normalize", "copy-fontsCSS", "copy-fonts",
    "copy-licenses-octicons", "copy-licenses-ofl"]);

gulp.task("copy-icons", () =>
    gulp.src(config.copy.icons.sources)
        .pipe(copy(config.copy.icons.dest)));

gulp.task("copy-octicons", () =>
    gulp.src(config.copy.octicons.sources)
        .pipe(copy(config.copy.octicons.dest, {prefix: 2})));

gulp.task("copy-normalize", () =>
    gulp.src(config.copy.normalize.sources)
        .pipe(copy(config.copy.normalize.dest, {prefix: 2})));

gulp.task("copy-fontsCSS", () =>
    gulp.src(config.copy.fontsCSS.sources)
        .pipe(copy(config.copy.fontsCSS.dest, {prefix: 1})));

gulp.task("copy-fonts", () =>
    gulp.src(config.copy.fonts.sources)
        .pipe(copy(config.copy.fonts.dest, {prefix: 1})));

gulp.task("copy-licenses-octicons", () =>
    gulp.src(config.copy.licenses_octicons.sources)
        .pipe(copy(config.copy.licenses_octicons.dest, {prefix: 1})));

gulp.task("copy-licenses-ofl", () =>
    gulp.src(config.copy.licenses_ofl.sources)
        .pipe(copy(config.copy.licenses_ofl.dest, {prefix: 1})));
<|endoftext|>


<commit_before>/**
 *  nodeca.io
 *
 *  This module provides realtime communication methods for nodeca/nlib based
 *  applications.
 **/

/*global window, $, _, Faye, nodeca*/

//= depend_on nodeca
//= require faye-browser

(function () {
  'use strict';

  var // registered events
      events = {},
      // underlying bayeux client
      bayeux = null,
      connected = false,
      // api3 related (used by apiTree() send/receive calls) properies
      api3 = {
        req_channel: '/x/api3-req/' + window.REALTIME_ID,
        res_channel: '/x/api3-res/' + window.REALTIME_ID,
        callbacks:    {},
        last_msg_id:  0
      };

  // exported IO object
  var io = nodeca.io = {};

  //
  // Errors
  //

  io.ENOCONN    = 'IO_ENOCONN';
  io.ETIMEOUT   = 'IO_ETIMEOUT';
  io.EWRONGVER  = 'IO_EWRONGVER';

  // error constructor
  function ioerr(code, message) {
    var err = new Error(message);
    err.code = code;
    return err;
  }

  //
  // Events
  //

  // executes all handlers registered for given `event`
  function emit(event, args) {
    _.each(events[event] || [], function (handler) {
      handler.apply(null, args);
    });
  }

  /**
   *  nodeca.io.on(event, handler) -> Void
   *  - event (String)
   *  - handler (Function)
   *
   *  Registers `handler` for an `event`.
   *
   *
   *  ##### Known events
   *
   *  - `api3:version-mismatch(versions)`
   **/
  io.on = function on(event, handler) {
    if (!events[event]) {
      events[event] = [];
    }

    events[event].push(handler);
  };

  /**
   *  nodeca.io.off(event[, handler]) -> Void
   *  - event (String)
   *  - handler (Function)
   *
   *  Unsubscribes `handler` (or all handlers) from specified `event`.
   *
   *
   *  ##### See also
   *
   *  - [nodeca.io.on]
   **/
  io.off = function off(event, handler) {
    events[event] = (!handler) ? [] : _.without(events[event], handler);
  };

  //
  // Main API
  //

  function bayeux_call(name, args) {
    var result = bayeux[name].apply(bayeux, args);

    // provide jQuery.Defered style methods
    result.done = _.bind(function (fn) { this.callback(fn); return this; }, result);
    result.fail = _.bind(function (fn) { this.errback(fn); return this; }, result);

    return result;
  }

  /**
   *  nodeca.io.subscribe(channel, handler) -> Object
   **/
  io.subscribe = function subscribe(channel, handler) {
    return bayeux_call('subscribe', [channel, handler]);
  };

  /**
   *  nodeca.io.unsubscribe(channel[, handler]) -> Object
   **/
  io.unsubscribe = function unsubscribe(channel, handler) {
    return bayeux_call('unsubscribe', [channel, handler]);
  };

  /**
   *  nodeca.io.publish(channel, message) -> Object
   **/
  io.publish = function publish(channel, message) {
    return bayeux_call('publish', [channel, message]);
  };

  /**
   *  nodeca.io.apiTree(name, params[, options][, callback]) -> Void
   **/
  io.apiTree = function apiTree(name, params, options, callback) {
    var timeout, id = api3.last_msg_id++, data = {id: id};

    // Scenario: rpc(name, params, callback);
    if (_.isFunction(options)) {
      callback = options;
      options = {};
    }

    // fill in defaults
    options   = _.extend({timeout: 30}, options);
    callback  = callback || $.noop;

    // check if there active connection
    if (!connected) {
      clearTimeout(timeout); // make sure no postponed timeout error will happen
      callback(ioerr(io.ENOCONN, 'No active realtime connection.'));
      return;
    }

    // fill in message
    data.msg = {
      version:  nodeca.runtime.version,
      method:   name,
      params:   params
    };

    // store callback for the response
    api3.callbacks[id] = function (msg) {
      clearTimeout(timeout); // stop timeout counter
      timeout = null; // mark timeout as "removed"

      if (msg.version !== nodeca.runtime.version) {
        // emit version mismatch error
        emit('api3:version-mismatch', {
          client: nodeca.runtime.version,
          server: msg.version
        });
        callback(ioerr(io.EWRONGVER, 'Client version does not match server.'));
        return;
      }

      // run actual callback
      callback(msg.err, msg.result);
    };

    // simple error handler
    function handle_error(err) {
      delete api3.callbacks[id];
      callback(err);
    }

    // send request
    bayeux_call('publish', [api3.req_channel, data])
      .fail(handle_error)
      .done(function () {
        // schedule timeout error
        timeout = setTimeout(function () {
          handle_error(ioerr(io.ETIMEOUT, 'Timeout ' + name + ' execution.'));
        }, (options.timeout || 30) * 1000);
      });
  };

  //
  // Initialization API
  //

  /**
   *  nodeca.io.auth(callback) -> Void
   **/
  io.auth = function (callback) {
    // Not implemented yet
    callback(null);
  };

  /**
   *  nodeca.io.init() -> Void
   **/
  io.init = function () {
    bayeux = new Faye.Client('/faye');

    bayeux.bind('transport:up', function () { connected = true; });
    bayeux.bind('transport:down', function () { connected = false; });

    bayeux.subscribe(api3.res_channel, function (data) {
      var callback = api3.callbacks[data.id];

      if (!callback) {
        // unknown response id
        return;
      }

      delete api3.callbacks[data.id];
      callback(data.msg);
    });
  };

  if ('development' === nodeca.runtime.env) {
    // export some internals for debugging
    window.fontello_bayeux = bayeux;
  }
}());
<commit_msg>Update nodeca-io<commit_after>/**
 *  nodeca.io
 *
 *  This module provides realtime communication methods for nodeca/nlib based
 *  applications.
 **/

/*global window, $, _, Faye, nodeca*/

//= depend_on nodeca
//= require faye-browser

(function () {
  'use strict';

  var // registered events
      events = {},
      // underlying bayeux client
      bayeux = null,
      connected = false,
      // api3 related (used by apiTree() send/receive calls) properies
      api3 = {
        req_channel: '/x/api3-req/' + window.REALTIME_ID,
        res_channel: '/x/api3-res/' + window.REALTIME_ID,
        callbacks:    {},
        last_msg_id:  0
      };

  // exported IO object
  var io = nodeca.io = {};

  //
  // Errors
  //

  io.ENOCONN    = 'IO_ENOCONN';
  io.ETIMEOUT   = 'IO_ETIMEOUT';
  io.EWRONGVER  = 'IO_EWRONGVER';

  // error constructor
  function ioerr(code, message) {
    var err = new Error(message);
    err.code = code;
    return err;
  }

  //
  // Events
  //

  // executes all handlers registered for given `event`
  function emit(event, args) {
    _.each(events[event] || [], function (handler) {
      handler.apply(null, args);
    });
  }

  /**
   *  nodeca.io.on(event, handler) -> Void
   *  - event (String)
   *  - handler (Function)
   *
   *  Registers `handler` for an `event`.
   *
   *
   *  ##### Known events
   *
   *  - `api3:version-mismatch(versions)`
   **/
  io.on = function on(event, handler) {
    if (!events[event]) {
      events[event] = [];
    }

    events[event].push(handler);
  };

  /**
   *  nodeca.io.off(event[, handler]) -> Void
   *  - event (String)
   *  - handler (Function)
   *
   *  Unsubscribes `handler` (or all handlers) from specified `event`.
   *
   *
   *  ##### See also
   *
   *  - [nodeca.io.on]
   **/
  io.off = function off(event, handler) {
    events[event] = (!handler) ? [] : _.without(events[event], handler);
  };

  //
  // Main API
  //

  function bayeux_call(name, args) {
    var result = bayeux[name].apply(bayeux, args);

    //
    // provide jQuery.Defered style methods
    //

    // FAYE DOCUMENTATION:
    //
    //  Bear in mind that ‘success’ here just means the server received and
    //  routed the message successfully, not that it has been received by all
    //  other clients.
    result.done = _.bind(function (fn) { this.callback(fn); return this; }, result);

    // FAYE DOCUMENTATION:
    //
    //  An error means the server found a problem processing the message.
    //  Network errors are not covered by this.
    result.fail = _.bind(function (fn) { this.errback(fn); return this; }, result);

    return result;
  }

  /**
   *  nodeca.io.subscribe(channel, handler) -> Object
   **/
  io.subscribe = function subscribe(channel, handler) {
    return bayeux_call('subscribe', [channel, handler]);
  };

  /**
   *  nodeca.io.unsubscribe(channel[, handler]) -> Object
   **/
  io.unsubscribe = function unsubscribe(channel, handler) {
    return bayeux_call('unsubscribe', [channel, handler]);
  };

  /**
   *  nodeca.io.publish(channel, message) -> Object
   **/
  io.publish = function publish(channel, message) {
    return bayeux_call('publish', [channel, message]);
  };

  /**
   *  nodeca.io.apiTree(name, params[, options][, callback]) -> Void
   **/
  io.apiTree = function apiTree(name, params, options, callback) {
    var timeout, id = api3.last_msg_id++, data = {id: id};

    // Scenario: rpc(name, params, callback);
    if (_.isFunction(options)) {
      callback = options;
      options = {};
    }

    // fill in defaults
    options   = options || {};
    callback  = callback || $.noop;

    //
    // ERROR HANDLING
    //
    // - when there's no connection (or client is `connecting`), we execute
    //   callback with `ENOCONN` error imediately
    // - when connection lost during waiting for server to send a message into
    //   response channel, we execute calback with `ECONNGONE` error
    // - when server didn't received published request message within 30 seconds
    //   we execute callback with `ETIMEOUT` error
    //

    // check if there active connection
    if (!connected) {
      callback(ioerr(io.ENOCONN, 'Cannot connect to server (RT).'));
      return;
    }

    // fill in message
    data.msg = {
      version:  nodeca.runtime.version,
      method:   name,
      params:   params
    };

    // stop timer
    function stop_timer() {
      clearTimeout(timeout); // stop timeout counter
      timeout = null; // mark timeout as "removed"
    }

    // simple error handler
    function handle_error(err) {
      stop_timer();
      delete api3.callbacks[id];
      callback(err);
    }

    // handle transport down during request error
    function handle_transport_down() {
      handle_error(ioerr(io.ECONNGONE, 'Server gone. (RT)'));
    }

    bayeux.bind('transport:down', handle_transport_down);

    // store callback for the response
    api3.callbacks[id] = function (msg) {
      stop_timer();

      bayeux.unbind('transport:down', handle_transport_down);

      if (msg.version !== nodeca.runtime.version) {
        // emit version mismatch error
        emit('api3:version-mismatch', {
          client: nodeca.runtime.version,
          server: msg.version
        });
        callback(ioerr(io.EWRONGVER, 'Client version does not match server.'));
        return;
      }

      // run actual callback
      callback(msg.err, msg.result);
    };

    // wait for successfull message delivery 30 seconds
    timeout = setTimeout(function () {
      handle_error(ioerr(io.ETIMEOUT, 'Timeout ' + name + ' execution.'));
    }, 30000);

    // send request
    bayeux_call('publish', [api3.req_channel, data])
      // see bayeux_call info for details on fail/done
      .fail(handle_error)
      .done(stop_timer);
  };

  //
  // Initialization API
  //

  /**
   *  nodeca.io.auth(callback) -> Void
   **/
  io.auth = function (callback) {
    // Not implemented yet
    callback(null);
  };

  /**
   *  nodeca.io.init() -> Void
   **/
  io.init = function () {
    bayeux = new Faye.Client('/faye');

    bayeux.bind('transport:up', function () { connected = true; });
    bayeux.bind('transport:down', function () { connected = false; });

    bayeux.subscribe(api3.res_channel, function (data) {
      var callback = api3.callbacks[data.id];

      if (!callback) {
        // unknown response id
        return;
      }

      delete api3.callbacks[data.id];
      callback(data.msg);
    });
  };

  if ('development' === nodeca.runtime.env) {
    // export some internals for debugging
    window.fontello_bayeux = bayeux;
  }
}());
<|endoftext|>


<commit_before>'use strict';

const Queries = require('../../helpers/queries');
const D3 = require('d3');

exports.showPage = {
    handler: function(request, reply) {
        return reply.view('monitor/usage', null, { layout: 'monitor' });
    }
};

exports.metric = {
    handler: function(request, reply) {
        var sequelize = request.server.plugins.sequelize.db.sequelize;

        var queryString = Queries.usage_nav();

        // API CODE
        sequelize.query(queryString, {
            type: sequelize.QueryTypes.SELECT
        }).then(function(rows) {

            var data = D3.nest()
                .key(function(d) {
                    return d.metric;
                })
                .rollup(function(v) {
                    return {
                        'value': v[0].metric,
                        'label': v[0].metric_label,
                        'comparisons': v.filter(function(d) {
                                return d.type === 'comparison'; })
                            .map(function(d) {
                                return {
                                    'value': d.filter_comparison,
                                    'label': d.filter_comparison_label,
                                };
                            }),
                        'filters': D3.nest()
                            .key(function(d) {
                                return d.filter_comparison; })
                            .rollup(function(w) {
                                return {
                                    'filter': w[0].filter_comparison,
                                    'filter_label': w[0].filter_comparison_label,
                                    'options': w.map(function(d) {
                                        return {
                                            'value': d.option,
                                            'label': d.option_label
                                        };
                                    })
                                };
                            })
                            .entries(v.filter(function(d) {
                                return d.type === 'filter'; }))
                            .map(function(d) {
                                return d.values;
                            })
                    };
                })
                .entries(rows)
                .map(function(d) {
                    return d.values;
                });

            reply(data);

        });

    }
};
<commit_msg>add method to return chart data<commit_after>'use strict';

const Queries = require('../../helpers/queries');
const D3 = require('d3');

exports.showPage = {
    handler: function(request, reply) {
        return reply.view('monitor/usage', null, { layout: 'monitor' });
    }
};

exports.metric = {
    handler: function(request, reply) {
        var sequelize = request.server.plugins.sequelize.db.sequelize;

        var queryString = Queries.usage_nav();

        // API CODE
        sequelize.query(queryString, {
            type: sequelize.QueryTypes.SELECT
        }).then(function(rows) {

            var data = D3.nest()
                .key(function(d) {
                    return d.metric;
                })
                .rollup(function(v) {
                    return {
                        'value': v[0].metric,
                        'label': v[0].metric_label,
                        'comparisons': v.filter(function(d) {
                                return d.type === 'comparison'; })
                            .map(function(d) {
                                return {
                                    'value': d.filter_comparison,
                                    'label': d.filter_comparison_label,
                                };
                            }),
                        'filters': D3.nest()
                            .key(function(d) {
                                return d.filter_comparison; })
                            .rollup(function(w) {
                                return {
                                    'filter': w[0].filter_comparison,
                                    'filter_label': w[0].filter_comparison_label,
                                    'options': w.map(function(d) {
                                        return {
                                            'value': d.option,
                                            'label': d.option_label
                                        };
                                    })
                                };
                            })
                            .entries(v.filter(function(d) {
                                return d.type === 'filter'; }))
                            .map(function(d) {
                                return d.values;
                            })
                    };
                })
                .entries(rows)
                .map(function(d) {
                    return d.values;
                });

            reply(data);

        });

    }
};

exports.chartData = {
    handler: function(request, reply) {
        var sequelize = request.server.plugins.sequelize.db.sequelize;

        // users_1session_date
        var queryString = 'SELECT SUM(IF(session_count>0,1,0)) as session_flag, COUNT(DISTINCT user_id) as count, date'+compVars+' FROM ga_sessions '+whereClause+'GROUP BY date'+compVars+';'

        // API CODE
        sequelize.query(queryString, {
            type: sequelize.QueryTypes.SELECT
        }).then(function(rows) {

        });

    }
};
<|endoftext|>


<commit_before>const { describe, it } = require('../')

describe('slow example', () => {
  it('passes slowly', done => {
    setTimeout(() => done(), 200)
  })
})
<commit_msg>test: add mediumly slow code<commit_after>const { describe, it } = require('../')

describe('slow example', () => {
  it('passes slowly', done => {
    setTimeout(() => done(), 200)
  })
  it('passes medium slowly', done => {
    setTimeout(() => done(), 60)
  })
})
<|endoftext|>


<commit_before>var passport = require('passport');
require('./oauth.js')(passport);

module.exports = {
  delete: {
    auth: function(req, res, context) {
      passport.authenticate('bearer', {
          session: false
      })(req, res, function() { // this is the function called after auth
          if(req.user){
            context.continue();
          } else {
            context.stop();
          }
      });
    }
  },
  write: {
    auth: function(req, res, context) {
      passport.authenticate('bearer', {
          session: false
      })(req, res, function() { // this is the function called after auth
          if(req.user){
            context.continue();
          } else {
            context.stop();
          }
      });
    }
  }
};

// SCOPE VS context.criteria();
<commit_msg>fix milestone - issue #16<commit_after>var passport = require('passport');
require('./oauth.js')(passport);

module.exports = {
  delete: {
    auth: function(req, res, context) {
      passport.authenticate('bearer', {
          session: false
      })(req, res, function() { // this is the function called after auth
          if(req.user){
            context.continue();
          } else {
            context.stop();
          }
      });
    }
  },
  create: {
    auth: function(req, res, context) {
      passport.authenticate('bearer', {
          session: false
      })(req, res, function() { // this is the function called after auth
          if(req.user){
            context.continue();
          } else {
            context.stop();
          }
      });
    }
  }
};
// SCOPE VS context.criteria();
<|endoftext|>


<commit_before>'use strict';
var path = require('path');
var gulp = require('gulp');
var eslint = require('gulp-eslint');
var excludeGitignore = require('gulp-exclude-gitignore');
var mocha = require('gulp-mocha');
var istanbul = require('gulp-istanbul');
var nsp = require('gulp-nsp');
var plumber = require('gulp-plumber');
var coveralls = require('gulp-coveralls');

gulp.task('static', function () {
  return gulp.src('**/*.js')
    .pipe(excludeGitignore())
    .pipe(eslint())
    .pipe(eslint.format())
    .pipe(eslint.failAfterError());
});

gulp.task('nsp', function (cb) {
  nsp({package: path.resolve('package.json')}, cb);
});

gulp.task('pre-test', function () {
  return gulp.src('generators/**/*.js')
    .pipe(istanbul({
      includeUntested: true
    }))
    .pipe(istanbul.hookRequire());
});

gulp.task('test', ['pre-test'], function (cb) {
  var mochaErr;

  gulp.src('test/**/*.js')
    .pipe(plumber())
    .pipe(mocha({reporter: 'spec'}))
    .on('error', function (err) {
      mochaErr = err;
    })
    .pipe(istanbul.writeReports())
    .on('end', function () {
      cb(mochaErr);
    });
});

gulp.task('coveralls', ['test'], function () {
  if (!process.env.CI) {
    return;
  }

  return gulp.src(path.join(__dirname, 'coverage/lcov.info'))
    .pipe(coveralls());
});

gulp.task('prepublish', ['nsp']);
gulp.task('default', ['static', 'test', 'coveralls']);
<commit_msg>need to disable nsp because of cordova security vulnerabilities<commit_after>'use strict';
var path = require('path');
var gulp = require('gulp');
var eslint = require('gulp-eslint');
var excludeGitignore = require('gulp-exclude-gitignore');
var mocha = require('gulp-mocha');
var istanbul = require('gulp-istanbul');
var nsp = require('gulp-nsp');
var plumber = require('gulp-plumber');
var coveralls = require('gulp-coveralls');

gulp.task('static', function () {
  return gulp.src('**/*.js')
    .pipe(excludeGitignore())
    .pipe(eslint())
    .pipe(eslint.format())
    .pipe(eslint.failAfterError());
});

gulp.task('nsp', function (cb) {
  nsp({package: path.resolve('package.json')}, cb);
});

gulp.task('pre-test', function () {
  return gulp.src('generators/**/*.js')
    .pipe(istanbul({
      includeUntested: true
    }))
    .pipe(istanbul.hookRequire());
});

gulp.task('test', ['pre-test'], function (cb) {
  var mochaErr;

  gulp.src('test/**/*.js')
    .pipe(plumber())
    .pipe(mocha({reporter: 'spec'}))
    .on('error', function (err) {
      mochaErr = err;
    })
    .pipe(istanbul.writeReports())
    .on('end', function () {
      cb(mochaErr);
    });
});

gulp.task('coveralls', ['test'], function () {
  if (!process.env.CI) {
    return;
  }

  return gulp.src(path.join(__dirname, 'coverage/lcov.info'))
    .pipe(coveralls());
});

gulp.task('prepublish');
gulp.task('default', ['static', 'test', 'coveralls']);
<|endoftext|>