<commit_before>var gulp = require('gulp');
var rebound = require('gulp-rebound');
var merge = require('gulp-merge');
var es = require('event-stream');
var babel = require('gulp-babel');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var jshint = require('gulp-jshint');
var rename = require('gulp-rename');
var rjs = require('gulp-requirejs');
var connect = require('gulp-connect');
var sourcemaps = require('gulp-sourcemaps');
var del = require('del');
var qunit = require('node-qunit-phantomjs');
var docco = require("gulp-docco");
var git = require('gulp-git');
var pjson = require('./package.json');
var mkdirp = require('mkdirp');
var replace = require('gulp-replace');

var paths = {
    all: ['packages/**/*.js', 'wrap/*.js', 'shims/*.js', 'test/**/*.html'],
    propertyCompiler:   'packages/property-compiler/lib/**/*.js',
    reboundCompiler:    'packages/rebound-compiler/lib/**/*.js',
    reboundComponent:   'packages/rebound-component/lib/**/*.js',
    reboundData:        'packages/rebound-data/lib/**/*.js',
    reboundPrecompiler: 'packages/rebound-compiler/lib/**/*.js',
    reboundRouter:      'packages/rebound-router/lib/**/*.js',
    reboundRuntime:     'packages/runtime.js'
  };

gulp.task('clean', function(cb) {
  // You can use multiple globbing patterns as you would with `gulp.src`
  return del(['dist', 'test/demo/templates'], cb);
});

// JS hint task
gulp.task('jshint', function() {
  gulp.src('./packages/**/*.js')
  .pipe(jshint({
    curly: false,
    loopfunc: true,
    eqnull: true,
    browser: true,
    esnext: true,
    '-W058': true, // Allow prens-less constructors (new Object;)
    '-W093': true, // Allow returning assignment (return a = b;)
    '-W030': true, // Allow unused expressions. Good for conditional assignment.
    globals: {
      jQuery: true
    }
  }))
  .pipe(jshint.reporter('default'));
});

gulp.task('cjs', ['clean'], function() {
  return es.merge(
    gulp.src(paths.propertyCompiler).pipe(rename({prefix: "property-compiler/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundComponent).pipe(rename({prefix: "rebound-component/"})),
    gulp.src(paths.reboundData).pipe(rename({prefix: "rebound-data/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundRouter).pipe(rename({prefix: "rebound-router/"})),
    gulp.src(paths.reboundRuntime)
  )
  .pipe(babel({blacklist: ['es6.forOf','regenerator','es6.spread','es6.destructuring']}))
  .pipe(gulp.dest('dist/cjs'))
  .pipe(connect.reload());
});

gulp.task('docco', ['clean'], function() {
  return gulp.src([
    'packages/runtime.js',
    'packages/rebound-data/lib/rebound-data.js',
    'packages/rebound-data/lib/model.js',
    'packages/rebound-data/lib/collection.js',
    'packages/rebound-data/lib/computed-property.js',
    'packages/rebound-component/lib/component.js',
    'packages/rebound-component/lib/helpers.js',
    'packages/rebound-component/lib/hooks.js',
    'packages/rebound-component/lib/lazy-value.js',
    'packages/rebound-router/lib/rebound-router.js',
    'packages/rebound-component/lib/utils.js',
    'packages/property-compiler/lib/property-compiler.js',
    'packages/rebound-compiler/lib/rebound-compiler.js',
  ])
  .pipe(concat('rebound.js'))
  .pipe(docco())
  .pipe(gulp.dest('dist/docs'));
});

gulp.task('amd', ['clean'], function() {
  return es.merge(
    gulp.src(paths.propertyCompiler).pipe(rename({prefix: "property-compiler/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundComponent).pipe(rename({prefix: "rebound-component/"})),
    gulp.src(paths.reboundData).pipe(rename({prefix: "rebound-data/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundRouter).pipe(rename({prefix: "rebound-router/"})),
    gulp.src(paths.reboundRuntime)
  )
  .pipe(babel({
    modules: "amd",
    moduleIds: true,
    blacklist: ['es6.forOf','regenerator','es6.spread','es6.destructuring']
  }))
  .pipe(gulp.dest('dist/amd'))
  .pipe(concat('rebound.runtime.js'))
  .pipe(gulp.dest('dist'))
});

gulp.task('runtime', ['amd'], function() {
  return gulp.src([
    'shims/classList.js',
    'shims/matchesSelector.js',
    // 'bower_components/webcomponentsjs/webcomponents-lite.min.js',
    'node_modules/document-register-element/build/document-register-element.max.js',
    'bower_components/backbone/backbone.js',
    'bower_components/requirejs/require.js',
    'wrap/start.frag',
    'bower_components/almond/almond.js',
    'node_modules/htmlbars/dist/amd/htmlbars-util.amd.js',
    'node_modules/htmlbars/dist/amd/htmlbars-runtime.amd.js',
    'dist/rebound.runtime.js',
    'wrap/end.runtime.frag'
    ])
  .pipe(concat('rebound.runtime.js'))
  .pipe(gulp.dest('dist'))
  .pipe(uglify())
  .pipe(rename({basename: "rebound.runtime.min"}))
  .pipe(gulp.dest('dist'));
});

gulp.task('test-helpers', ['runtime'], function(){
  return gulp.src([
      "bower_components/underscore/underscore.js",
      "bower_components/jquery/dist/jquery.min.js",
      "bower_components/route-recognizer/dist/route-recognizer.js",
      "bower_components/FakeXMLHttpRequest/fake_xml_http_request.js",
      "bower_components/pretender/pretender.js",
      "dist/rebound.runtime.js",
      "packages/rebound-test/lib/test-helpers.js",
    ])
  .pipe(concat('rebound.test.js'))
  .pipe(gulp.dest('dist'));
});

gulp.task('compile-tests', function(){
  return gulp.src([
      "packages/*/test/*.js",
      "packages/tests.js"
    ])
  .pipe(babel({
    modules: "amd",
    moduleIds: true,
    blacklist: ['es6.forOf','regenerator','es6.spread','es6.destructuring']
  }))
  .pipe(concat('rebound.tests.js'))
  .pipe(gulp.dest('test'));
});

gulp.task('compile-demo', ['cjs', 'test-helpers', 'docco', 'runtime', 'compile-tests'],  function(){

  var demo = gulp.src(["test/demo/**/*.html", "!test/index.html", "!test/demo/index.html"])
  .pipe(rebound())
  .pipe(gulp.dest('test/demo/templates'));

  return demo;
});
gulp.task('compile-apps', ['cjs', 'test-helpers', 'docco', 'runtime', 'compile-tests'],  function(){

  var apps = gulp.src(["test/dummy-apps/**/*.html"])
  .pipe(rebound())
  .pipe(gulp.dest('test/dummy-apps'));

  return apps;
});

// Start the test server
gulp.task('connect', ['compile-demo', 'compile-apps'], function() {
  return connect.server({
    livereload: true,
    port: 8000
  });
});

// Rerun the tasks when a file changes
gulp.task('watch', ['connect'], function() {
  gulp.watch(paths.all, ['compile-demo', 'compile-apps']);
});

// The default task (called when you run `gulp` from cli)
gulp.task('default', [ 'watch' ]);

gulp.task('build', [ 'recompile-demo' ]);

gulp.task('test', function() {
  qunit('test/index.html', {
    verbose: true,
    timeout: 15
  }, function(code) {
    process.exit(code);
  })
});

/*******************************************************************************

Release Tasks:

gulp release is run on postpublish and automatically pushes the contents of /dist
to https://github.com/epicmiller/reboundjs-dist for consumption by bower.

*******************************************************************************/
gulp.task('cleanrelease', function(cb){
  return del(['tmp'], cb);
});

// Clone a remote repo
gulp.task('clone', ['cleanrelease'],  function(cb){
  console.log('Cloning reboundjs-dist to /tmp');
  mkdirp.sync('./tmp');
  git.clone('https://github.com/reboundjs/rebound-dist.git', {cwd: './tmp'}, cb);
});

gulp.task('release-copy', ['clone'], function(cb){
  return gulp.src('dist/**/*')
      .pipe(gulp.dest('tmp/reboundjs-dist'));
});

gulp.task('bump-version', ['release-copy'], function(cb){
  return gulp.src(['tmp/reboundjs-dist/bower.json'])
    .pipe(replace(/(.\s"version": ")[^"]*(")/g, '$1'+pjson.version+'$2'))
    .pipe(gulp.dest('tmp/reboundjs-dist'));
});

gulp.task('add', ['bump-version'], function(){
  console.log('Adding Rebound /dist to reboundjs-dist');
  process.chdir('tmp/reboundjs-dist');
  return gulp.src('./*')
      .pipe(git.add({args: '-A'}));
});

gulp.task('commit', ['add'], function(cb){
  console.log('Committing Rebound v' + pjson.version);
  return gulp.src('./*')
      .pipe(git.commit('Rebound version v'+pjson.version));
});

// Tag the repo with a version
gulp.task('tag', ['commit'], function(cb){
  console.log('Tagging Rebound as version ' + pjson.version);
  git.tag(''+pjson.version, "Rebound version v"+pjson.version, cb);
});

gulp.task('push', ['tag'], function(cb){
  console.log('Pushing Rebound v' + pjson.version);
  git.push('origin', 'master', {args: '--tags'}, cb);
});

gulp.task('release', ['push'], function(cb){
  git.status();
  console.log('Rebound v'+pjson.version+' successfully released!');
  return del(['tmp'], cb);
});
<commit_msg>Fixed gulp build task<commit_after>var gulp = require('gulp');
var rebound = require('gulp-rebound');
var merge = require('gulp-merge');
var es = require('event-stream');
var babel = require('gulp-babel');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var jshint = require('gulp-jshint');
var rename = require('gulp-rename');
var rjs = require('gulp-requirejs');
var connect = require('gulp-connect');
var sourcemaps = require('gulp-sourcemaps');
var del = require('del');
var qunit = require('node-qunit-phantomjs');
var docco = require("gulp-docco");
var git = require('gulp-git');
var pjson = require('./package.json');
var mkdirp = require('mkdirp');
var replace = require('gulp-replace');

var paths = {
    all: ['packages/**/*.js', 'wrap/*.js', 'shims/*.js', 'test/**/*.html'],
    propertyCompiler:   'packages/property-compiler/lib/**/*.js',
    reboundCompiler:    'packages/rebound-compiler/lib/**/*.js',
    reboundComponent:   'packages/rebound-component/lib/**/*.js',
    reboundData:        'packages/rebound-data/lib/**/*.js',
    reboundPrecompiler: 'packages/rebound-compiler/lib/**/*.js',
    reboundRouter:      'packages/rebound-router/lib/**/*.js',
    reboundRuntime:     'packages/runtime.js'
  };

gulp.task('clean', function(cb) {
  // You can use multiple globbing patterns as you would with `gulp.src`
  return del(['dist', 'test/demo/templates'], cb);
});

// JS hint task
gulp.task('jshint', function() {
  gulp.src('./packages/**/*.js')
  .pipe(jshint({
    curly: false,
    loopfunc: true,
    eqnull: true,
    browser: true,
    esnext: true,
    '-W058': true, // Allow prens-less constructors (new Object;)
    '-W093': true, // Allow returning assignment (return a = b;)
    '-W030': true, // Allow unused expressions. Good for conditional assignment.
    globals: {
      jQuery: true
    }
  }))
  .pipe(jshint.reporter('default'));
});

gulp.task('cjs', ['clean'], function() {
  return es.merge(
    gulp.src(paths.propertyCompiler).pipe(rename({prefix: "property-compiler/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundComponent).pipe(rename({prefix: "rebound-component/"})),
    gulp.src(paths.reboundData).pipe(rename({prefix: "rebound-data/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundRouter).pipe(rename({prefix: "rebound-router/"})),
    gulp.src(paths.reboundRuntime)
  )
  .pipe(babel({blacklist: ['es6.forOf','regenerator','es6.spread','es6.destructuring']}))
  .pipe(gulp.dest('dist/cjs'))
  .pipe(connect.reload());
});

gulp.task('docco', ['clean'], function() {
  return gulp.src([
    'packages/runtime.js',
    'packages/rebound-data/lib/rebound-data.js',
    'packages/rebound-data/lib/model.js',
    'packages/rebound-data/lib/collection.js',
    'packages/rebound-data/lib/computed-property.js',
    'packages/rebound-component/lib/component.js',
    'packages/rebound-component/lib/helpers.js',
    'packages/rebound-component/lib/hooks.js',
    'packages/rebound-component/lib/lazy-value.js',
    'packages/rebound-router/lib/rebound-router.js',
    'packages/rebound-component/lib/utils.js',
    'packages/property-compiler/lib/property-compiler.js',
    'packages/rebound-compiler/lib/rebound-compiler.js',
  ])
  .pipe(concat('rebound.js'))
  .pipe(docco())
  .pipe(gulp.dest('dist/docs'));
});

gulp.task('amd', ['clean'], function() {
  return es.merge(
    gulp.src(paths.propertyCompiler).pipe(rename({prefix: "property-compiler/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundComponent).pipe(rename({prefix: "rebound-component/"})),
    gulp.src(paths.reboundData).pipe(rename({prefix: "rebound-data/"})),
    gulp.src(paths.reboundCompiler).pipe(rename({prefix: "rebound-compiler/"})),
    gulp.src(paths.reboundRouter).pipe(rename({prefix: "rebound-router/"})),
    gulp.src(paths.reboundRuntime)
  )
  .pipe(babel({
    modules: "amd",
    moduleIds: true,
    blacklist: ['es6.forOf','regenerator','es6.spread','es6.destructuring']
  }))
  .pipe(gulp.dest('dist/amd'))
  .pipe(concat('rebound.runtime.js'))
  .pipe(gulp.dest('dist'))
});

gulp.task('runtime', ['amd'], function() {
  return gulp.src([
    'shims/classList.js',
    'shims/matchesSelector.js',
    // 'bower_components/webcomponentsjs/webcomponents-lite.min.js',
    'node_modules/document-register-element/build/document-register-element.max.js',
    'bower_components/backbone/backbone.js',
    'bower_components/requirejs/require.js',
    'wrap/start.frag',
    'bower_components/almond/almond.js',
    'node_modules/htmlbars/dist/amd/htmlbars-util.amd.js',
    'node_modules/htmlbars/dist/amd/htmlbars-runtime.amd.js',
    'dist/rebound.runtime.js',
    'wrap/end.runtime.frag'
    ])
  .pipe(concat('rebound.runtime.js'))
  .pipe(gulp.dest('dist'))
  .pipe(uglify())
  .pipe(rename({basename: "rebound.runtime.min"}))
  .pipe(gulp.dest('dist'));
});

gulp.task('test-helpers', ['runtime'], function(){
  return gulp.src([
      "bower_components/underscore/underscore.js",
      "bower_components/jquery/dist/jquery.min.js",
      "bower_components/route-recognizer/dist/route-recognizer.js",
      "bower_components/FakeXMLHttpRequest/fake_xml_http_request.js",
      "bower_components/pretender/pretender.js",
      "dist/rebound.runtime.js",
      "packages/rebound-test/lib/test-helpers.js",
    ])
  .pipe(concat('rebound.test.js'))
  .pipe(gulp.dest('dist'));
});

gulp.task('compile-tests', function(){
  return gulp.src([
      "packages/*/test/*.js",
      "packages/tests.js"
    ])
  .pipe(babel({
    modules: "amd",
    moduleIds: true,
    blacklist: ['es6.forOf','regenerator','es6.spread','es6.destructuring']
  }))
  .pipe(concat('rebound.tests.js'))
  .pipe(gulp.dest('test'));
});

gulp.task('compile-demo', ['cjs', 'test-helpers', 'docco', 'runtime', 'compile-tests'],  function(){

  var demo = gulp.src(["test/demo/**/*.html", "!test/index.html", "!test/demo/index.html"])
  .pipe(rebound())
  .pipe(gulp.dest('test/demo/templates'));

  return demo;
});
gulp.task('compile-apps', ['cjs', 'test-helpers', 'docco', 'runtime', 'compile-tests'],  function(){

  var apps = gulp.src(["test/dummy-apps/**/*.html"])
  .pipe(rebound())
  .pipe(gulp.dest('test/dummy-apps'));

  return apps;
});

// Start the test server
gulp.task('connect', ['compile-demo', 'compile-apps'], function() {
  return connect.server({
    livereload: true,
    port: 8000
  });
});

// Rerun the tasks when a file changes
gulp.task('watch', ['connect'], function() {
  gulp.watch(paths.all, ['compile-demo', 'compile-apps']);
});

// The default task (called when you run `gulp` from cli)
gulp.task('default', [ 'watch' ]);

gulp.task('build', [ 'compile-demo', 'compile-apps' ]);

gulp.task('test', function() {
  qunit('test/index.html', {
    verbose: true,
    timeout: 15
  }, function(code) {
    process.exit(code);
  })
});

/*******************************************************************************

Release Tasks:

gulp release is run on postpublish and automatically pushes the contents of /dist
to https://github.com/epicmiller/reboundjs-dist for consumption by bower.

*******************************************************************************/
gulp.task('cleanrelease', function(cb){
  return del(['tmp'], cb);
});

// Clone a remote repo
gulp.task('clone', ['cleanrelease'],  function(cb){
  console.log('Cloning reboundjs-dist to /tmp');
  mkdirp.sync('./tmp');
  git.clone('https://github.com/reboundjs/rebound-dist.git', {cwd: './tmp'}, cb);
});

gulp.task('release-copy', ['clone'], function(cb){
  return gulp.src('dist/**/*')
      .pipe(gulp.dest('tmp/reboundjs-dist'));
});

gulp.task('bump-version', ['release-copy'], function(cb){
  return gulp.src(['tmp/reboundjs-dist/bower.json'])
    .pipe(replace(/(.\s"version": ")[^"]*(")/g, '$1'+pjson.version+'$2'))
    .pipe(gulp.dest('tmp/reboundjs-dist'));
});

gulp.task('add', ['bump-version'], function(){
  console.log('Adding Rebound /dist to reboundjs-dist');
  process.chdir('tmp/reboundjs-dist');
  return gulp.src('./*')
      .pipe(git.add({args: '-A'}));
});

gulp.task('commit', ['add'], function(cb){
  console.log('Committing Rebound v' + pjson.version);
  return gulp.src('./*')
      .pipe(git.commit('Rebound version v'+pjson.version));
});

// Tag the repo with a version
gulp.task('tag', ['commit'], function(cb){
  console.log('Tagging Rebound as version ' + pjson.version);
  git.tag(''+pjson.version, "Rebound version v"+pjson.version, cb);
});

gulp.task('push', ['tag'], function(cb){
  console.log('Pushing Rebound v' + pjson.version);
  git.push('origin', 'master', {args: '--tags'}, cb);
});

gulp.task('release', ['push'], function(cb){
  git.status();
  console.log('Rebound v'+pjson.version+' successfully released!');
  return del(['tmp'], cb);
});
<|endoftext|>


<commit_before>/**
 * @file echo command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

exports.run = (Bastion, message, args) => {
  if (args.length < 1) {
    /**
     * The command was ran with invalid parameters.
     * @fires commandUsage
     */
    return Bastion.emit('commandUsage', message, this.help);
  }

  message.channel.send({
    embed: {
      color: Bastion.colors.DEFAULT,
      description: args.join(' ')
    }
  }).catch(e => {
    Bastion.log.error(e);
  });
};

exports.config = {
  aliases: [ 'say' ],
  enabled: true
};

exports.help = {
  name: 'echo',
  botPermission: '',
  userPermission: 'MANAGE_GUILD',
  usage: 'echo <text>',
  example: [ 'echo Hello, world!' ]
};
<commit_msg>Fixed implementation of perms restriction<commit_after>/**
 * @file echo command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

exports.run = (Bastion, message, args) => {
  if (!message.member.hasPermission(this.help.userPermission)) {
    /**
     * User has missing permissions.
     * @fires userMissingPermissions
     */
    return Bastion.emit('userMissingPermissions', this.help.userPermission);
  }

  if (args.length < 1) {
    /**
     * The command was ran with invalid parameters.
     * @fires commandUsage
     */
    return Bastion.emit('commandUsage', message, this.help);
  }

  message.channel.send({
    embed: {
      color: Bastion.colors.DEFAULT,
      description: args.join(' ')
    }
  }).catch(e => {
    Bastion.log.error(e);
  });
};

exports.config = {
  aliases: [ 'say' ],
  enabled: true
};

exports.help = {
  name: 'echo',
  botPermission: '',
  userPermission: 'MANAGE_GUILD',
  usage: 'echo <text>',
  example: [ 'echo Hello, world!' ]
};
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var gulp = require('gulp');
  var fs   = require('fs');
  var git  = require('git-rev');
  var rjs  = require('requirejs');

  var overtake = require('overtake');

  var rimraf = require('gulp-rimraf');
  var less   = require('gulp-less');
  var mincss = require('gulp-minify-css');
  var rename = require('gulp-rename');

  var nodemon = require('gulp-nodemon');

  var minified = { suffix: '.min' };

  /**
   * Clean existing build files
   */
  gulp.task('clean', function() {
    return gulp.src([
      './build.json',
      './public/css',
      './public/js',
      './frame/css/*.min.css'
    ], { read: false })
    .pipe(rimraf());
  });

  /**
   * Optimize scripts from AMD modules and build a single compressed JS file
   */
  gulp.task('js', function(done) {
    rjs.optimize({
      baseUrl: './public/src/js',
      mainConfigFile: './public/src/js/require.config.js',

      name: 'main',
      out: './public/js/debuggerio.min.js',

      pragmas: { bustCache: true },
      findNestedDependencies: true,
      preserveLicenseComments: true,
      useStrict: true,

      exclude: ['promise']
    }, function() { done(); }, function(err) { console.error(err); done(); });
  });

  /**
   * Build LESS files for each theme into a single compressed CSS file
   */
  gulp.task('less', function() {
    return gulp.src('./public/src/less/debuggerio.*.less')
      .pipe(less())
      .on('error', function(err) { console.error(err); })
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./public/css'));
  });

  /**
   * Compress static CSS files
   */
  gulp.task('css', function() {
    return gulp.src(['./frame/css/*.css', '!./**/*.min.css'])
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./frame/css'));
  });

  /**
   * Write a `build.json` file with build information
   */
  gulp.task('rev', function(done) {
    git.short(function(rev) {
      fs.writeFile('./build.json', JSON.stringify({
        date: (new Date()).toISOString(),
        rev: rev
      }, null, 2) + '\n', function() { done(); });
    });
  });

  /**
   * Watch for changes in JS and LESS files
   */
  gulp.task('watch', ['default'], function() {
    gulp.watch('./public/src/js/*.js', ['js']);
    gulp.watch('./public/src/less/*.less', ['less']);
  });

  /**
   * Keep watching and restart
   */
  gulp.task('dev', ['watch'], function(done) {
    nodemon({ script: 'debuggerio.js', ext: 'js json less' }).on('quit', done);
  });

  /**
   * Check for CDN updates
   */
  gulp.task('cdn', function(done) {
    var filenames = [];
    overtake.opts.update_msg = '  [%s] [%s] [%s] %s => %s';

    gulp.src([
      '!node_modules/**',
      '!./**/*.min.*',
      './**/*.js',
      './**/*.less',
      './**/*.html'
    ], { read: false })
    .on('data', function(file) {
      filenames.push(file.path);
    }).on('end', function() {
      overtake.check_files(filenames, true).done(function() {
        done();
      });
    });
  });

  /**
   * Default task
   */
  gulp.task('default', ['clean'], function() {
    return gulp.start('js', 'less', 'css', 'rev');
  });

})();
<commit_msg>Gulpfile cleanup<commit_after>(function() {
  'use strict';

  var fs       = require('fs');
  var git      = require('git-rev');
  var gulp     = require('gulp');
  var less     = require('gulp-less');
  var mincss   = require('gulp-minify-css');
  var nodemon  = require('gulp-nodemon');
  var overtake = require('overtake');
  var rename   = require('gulp-rename');
  var rimraf   = require('gulp-rimraf');
  var rjs      = require('requirejs');

  var minified = { suffix: '.min' };

  /**
   * Clean existing build files
   */
  gulp.task('clean', function() {
    return gulp.src([
      './build.json',
      './public/css',
      './public/js',
      './frame/css/*.min.css'
    ], { read: false })
    .pipe(rimraf());
  });

  /**
   * Optimize scripts from AMD modules and build a single compressed JS file
   */
  gulp.task('js', function(done) {
    rjs.optimize({
      baseUrl: './public/src/js',
      mainConfigFile: './public/src/js/require.config.js',

      name: 'main',
      out: './public/js/debuggerio.min.js',

      pragmas: { bustCache: true },
      findNestedDependencies: true,
      preserveLicenseComments: true,
      useStrict: true,

      exclude: ['promise']
    },
    function() { done(); },
    function(err) {
      console.error(err);
      done();
    });
  });

  /**
   * Build LESS files for each theme into a single compressed CSS file
   */
  gulp.task('less', function() {
    return gulp.src('./public/src/less/debuggerio.*.less')
      .pipe(less())
      .on('error', function(err) { console.error(err); })
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./public/css'));
  });

  /**
   * Compress static CSS files
   */
  gulp.task('css', function() {
    return gulp.src(['./frame/css/*.css', '!./**/*.min.css'])
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./frame/css'));
  });

  /**
   * Write a `build.json` file with build information
   */
  gulp.task('rev', function(done) {
    git.short(function(rev) {
      fs.writeFile('./build.json', JSON.stringify({
        date: (new Date()).toISOString(),
        rev: rev
      }, null, 2) + '\n', function() { done(); });
    });
  });

  /**
   * Watch for changes in JS and LESS files
   */
  gulp.task('watch', ['default'], function() {
    gulp.watch('./public/src/js/*.js', ['js']);
    gulp.watch('./public/src/less/*.less', ['less']);
  });

  /**
   * Keep watching and restart
   */
  gulp.task('dev', ['watch'], function(done) {
    nodemon({ script: 'debuggerio.js', ext: 'js json less' }).on('quit', done);
  });

  /**
   * Check for CDN updates
   */
  gulp.task('cdn', function(done) {
    var filenames = [];
    overtake.opts.update_msg = '  [%s] [%s] [%s] %s => %s';

    gulp.src([
      '!node_modules/**',
      '!./**/*.min.*',
      './**/*.js',
      './**/*.less',
      './**/*.html'
    ], { read: false })
    .on('data', function(file) {
      filenames.push(file.path);
    }).on('end', function() {
      overtake.check_files(filenames, true).done(function() {
        done();
      });
    });
  });

  /**
   * Default task
   */
  gulp.task('default', ['clean'], function() {
    return gulp.start('js', 'less', 'css', 'rev');
  });

})();
<|endoftext|>


<commit_before>var gulp = require('gulp');
var concat = require('gulp-concat');
var async = require('async');

gulp.task('clean', function (done) {
  var del = require('del');

  del(['dist','demo/build'], done);
});

gulp.task('build', function(done) {
  async.series({
    scripts: function (done) {
      //TODO: This should load modules first. Fix once files are renamed
      // gulp.src(['lib/scripts/**/module.js', 'src/**/*.js'])
      gulp.src(['lib/scripts/common.library.js', 'lib/scripts/**/*.js'])
        .pipe(concat('canon-directives.js'))
        .pipe(gulp.dest('dist'))
        .on('end', done);
    },
    templates: function (done) {
      gulp.src('lib/templates/*.html')
        .pipe(gulp.dest('dist/templates'))
        .on('end', done);
    }
  }, done);
});

gulp.task('build:demo', ['build'], function() {
  //Move demo files
  gulp.src('demo/*')
    .pipe(gulp.dest('demo/build'));

  //Move built components
  gulp.src('dist/**/*')
    .pipe(gulp.dest('demo/build'));

  //Move mock json data
  gulp.src('test/data/*.json')
    .pipe(gulp.dest('demo/build/data'));
})

gulp.task('server', ['build:demo'], function () {
  var webserver = require('gulp-webserver');

  gulp.watch(['lib/**/*', 'demo/!(build)/**/*.*', 'test/data/*.json'], ['build:demo']);

  return gulp.src('demo/build')
    .pipe(webserver({ livereload: true }));
});

gulp.task('default', ['build']);
<commit_msg>chore(build): update demo build to run async<commit_after>var gulp = require('gulp');
var concat = require('gulp-concat');
var async = require('async');

gulp.task('clean', function (done) {
  var del = require('del');

  del(['dist','demo/build'], done);
});

gulp.task('build', function(done) {
  async.parallel({
    scripts: function (done) {
      //TODO: This should load modules first. Fix once files are renamed
      // gulp.src(['lib/scripts/**/module.js', 'src/**/*.js'])
      gulp.src(['lib/scripts/common.library.js', 'lib/scripts/**/*.js'])
        .pipe(concat('canon-directives.js'))
        .pipe(gulp.dest('dist'))
        .on('end', done);
    },
    templates: function (done) {
      gulp.src('lib/templates/*.html')
        .pipe(gulp.dest('dist/templates'))
        .on('end', done);
    }
  }, done);
});

gulp.task('build:demo', ['build'], function(done) {
  async.parallel({
    demo: function (done) {
      gulp.src('demo/*')
        .pipe(gulp.dest('demo/build'))
        .on('end', done);
    },
    build: function (done) {
      gulp.src('dist/**/*')
        .pipe(gulp.dest('demo/build'))
        .on('end', done);
    },
    data: function (done) {
      gulp.src('test/data/*.json')
        .pipe(gulp.dest('demo/build/data'))
        .on('end', done);
    },
  }, done);
})

gulp.task('demo', ['build:demo'], function () {
  var webserver = require('gulp-webserver');

  gulp.watch(['lib/**/*', 'demo/!(build)/**/*.*', 'test/data/*.json'], ['build:demo']);

  return gulp.src('demo/build')
    .pipe(webserver({ livereload: true }));
});

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


<commit_before>'use strict';

const Promise = require('bluebird');
const nodemailer = require('nodemailer');

const options = {
    host: process.env.SMTP_HOST,
    post: process.env.SMTP_PORT,
    secure: false,
    auth: {
        user: process.env.SMTP_AUTH_USER,
        pass: process.env.SMTP_AUTH_PASSWORD
    }
};

const transporter = nodemailer.createTransport(options);

const compose = (email) => {
    return new Promise((resolve) => {
        transporter.sendMail(email, (ex, ack) => {
            if (ex) {
                console.log('Error sending email: %s', ex.message);
            } else {
                console.log('Email sent: %s', ack.response);
                resolve(ack.response);
            }
        });
    });
};

module.exports = exports = {
    compose: compose
};<commit_msg>Rejecting the promise when the mail fail<commit_after>'use strict';

const Promise = require('bluebird');
const nodemailer = require('nodemailer');

const options = {
    host: process.env.SMTP_HOST,
    post: process.env.SMTP_PORT,
    secure: false,
    auth: {
        user: process.env.SMTP_AUTH_USER,
        pass: process.env.SMTP_AUTH_PASSWORD
    }
};

const transporter = nodemailer.createTransport(options);

const compose = (email) => {
    return new Promise((resolve, reject) => {
        transporter.sendMail(email, (ex, ack) => {
            if (ex) {
                console.log('Error sending email: %s', ex.message);
                reject(ex);
            } else {
                console.log('Email sent: %s', ack.response);
                resolve(ack.response);
            }
        });
    });
};

module.exports = exports = {
    compose: compose
};<|endoftext|>


<commit_before>import _throttle from 'lodash-es/throttle';

import { geoBounds as d3_geoBounds, geoPath as d3_geoPath } from 'd3-geo';
import { text as d3_text } from 'd3-fetch';
import { event as d3_event, select as d3_select } from 'd3-selection';

import stringify from 'fast-json-stable-stringify';
import toGeoJSON from '@mapbox/togeojson';

import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
import { services } from '../services';
import { svgPath } from './helpers';
import { utilDetect } from '../util/detect';
import { utilArrayFlatten, utilArrayUnion, utilHashcode } from '../util';

var _initialized = false;
var _enabled = false;
var _geojson;

export function svgData(projection, context, dispatch) {
    var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
    var _showLabels = true;
    var detected = utilDetect();
    var layer = d3_select(null);
    var _vtService;
    var _fileList;
    var _template;
    var _src;

    function init() {
        if (_initialized) return;  // run once

        _geojson = {};
        _enabled = true;

        function over() {
            d3_event.stopPropagation();
            d3_event.preventDefault();
            d3_event.dataTransfer.dropEffect = 'copy';
        }

        context.container()
            .attr('dropzone', 'copy')
            .on('drop.svgData', function() {
                d3_event.stopPropagation();
                d3_event.preventDefault();
                if (!detected.filedrop) return;
                drawData.fileList(d3_event.dataTransfer.files);
            })
            .on('dragenter.svgData', over)
            .on('dragexit.svgData', over)
            .on('dragover.svgData', over);

        _initialized = true;
    }

    function getService() {
        if (services.vectorTile && !_vtService) {
            _vtService = services.vectorTile;
            _vtService.event.on('loadedData', throttledRedraw);
        } else if (!services.vectorTile && _vtService) {
            _vtService = null;
        }

        return _vtService;
    }

    function showLayer() {
        layerOn();

        layer
            .style('opacity', 0)
            .transition()
            .duration(250)
            .style('opacity', 1)
            .on('end', function () { dispatch.call('change'); });
    }

    function hideLayer() {
        throttledRedraw.cancel();

        layer
            .transition()
            .duration(250)
            .style('opacity', 0)
            .on('end', layerOff);
    }

    function layerOn() {
        layer.style('display', 'block');
    }

    function layerOff() {
        layer.selectAll('.viewfield-group').remove();
        layer.style('display', 'none');
    }

    // ensure that all geojson features in a collection have IDs
    function ensureIDs(gj) {
        if (!gj) return null;

        if (gj.type === 'FeatureCollection') {
            for (var i = 0; i < gj.features.length; i++) {
                ensureFeatureID(gj.features[i]);
            }
        } else {
            ensureFeatureID(gj);
        }
        return gj;
    }

    // ensure that each single Feature object has a unique ID
    function ensureFeatureID(feature) {
        if (!feature) return;
        feature.__featurehash__ = utilHashcode(stringify(feature));
        return feature;
    }

    // Prefer an array of Features instead of a FeatureCollection
    function getFeatures(gj) {
        if (!gj) return [];

        if (gj.type === 'FeatureCollection') {
            return gj.features;
        } else {
            return [gj];
        }
    }

    function featureKey(d) {
        return d.__featurehash__;
    }

    function isPolygon(d) {
        return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
    }

    function clipPathID(d) {
        return 'ideditor-data-' + d.__featurehash__ + '-clippath';
    }

    function featureClasses(d) {
        return [
            'data' + d.__featurehash__,
            d.geometry.type,
            isPolygon(d) ? 'area' : '',
            d.__layerID__ || ''
        ].filter(Boolean).join(' ');
    }

    function drawData(selection) {
        var vtService = getService();
        var getPath = svgPath(projection).geojson;
        var getAreaPath = svgPath(projection, null, true).geojson;
        var hasData = drawData.hasData();

        layer = selection.selectAll('.layer-mapdata')
            .data(_enabled && hasData ? [0] : []);

        layer.exit()
            .remove();

        layer = layer.enter()
            .append('g')
            .attr('class', 'layer-mapdata')
            .merge(layer);

        var surface = context.surface();
        if (!surface || surface.empty()) return;  // not ready to draw yet, starting up

        // Gather data
        var geoData, polygonData;
        if (_template && vtService) {   // fetch data from vector tile service
            var sourceID = _template;
            vtService.loadTiles(sourceID, _template, projection);
            geoData = vtService.data(sourceID, projection);
        } else {
            geoData = getFeatures(_geojson);
        }
        geoData = geoData.filter(getPath);
        polygonData = geoData.filter(isPolygon);

        // Draw clip paths for polygons
        var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
           .data(polygonData, featureKey);

        clipPaths.exit()
           .remove();

        var clipPathsEnter = clipPaths.enter()
           .append('clipPath')
           .attr('class', 'clipPath-data')
           .attr('id', clipPathID);

        clipPathsEnter
           .append('path');

        clipPaths.merge(clipPathsEnter)
           .selectAll('path')
           .attr('d', getAreaPath);

        // Draw fill, shadow, stroke layers
        var datagroups = layer
            .selectAll('g.datagroup')
            .data(['fill', 'shadow', 'stroke']);

        datagroups = datagroups.enter()
            .append('g')
            .attr('class', function(d) { return 'datagroup datagroup-' + d; })
            .merge(datagroups);

        // Draw paths
        var pathData = {
            fill: polygonData,
            shadow: geoData,
            stroke: geoData
        };

        var paths = datagroups
            .selectAll('path')
            .data(function(layer) { return pathData[layer]; }, featureKey);

        // exit
        paths.exit()
            .remove();

        // enter/update
        paths = paths.enter()
            .append('path')
            .attr('class', function(d) {
                var datagroup = this.parentNode.__data__;
                return 'pathdata ' + datagroup + ' ' + featureClasses(d);
            })
            .attr('clip-path', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
            })
            .merge(paths)
            .attr('d', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
            });

        // Draw labels
        layer
            .call(drawLabels, 'label-halo', geoData)
            .call(drawLabels, 'label', geoData);

        function drawLabels(selection, textClass, data) {
            var labelPath = d3_geoPath(projection);
            var labelData = data.filter(function(d) {
                return _showLabels && d.properties && (d.properties.desc || d.properties.name);
            });

            var labels = selection.selectAll('text.' + textClass)
                .data(labelData, featureKey);

            // exit
            labels.exit()
                .remove();

            // enter/update
            labels = labels.enter()
                .append('text')
                .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
                .merge(labels)
                .text(function(d) {
                    return d.properties.desc || d.properties.name;
                })
                .attr('x', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[0] + 11;
                })
                .attr('y', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[1];
                });
        }
    }

    function getExtension(fileName) {
        if (!fileName) return;

        var re = /\.(gpx|kml|(geo)?json)$/i;
        var match = fileName.toLowerCase().match(re);
        return match && match.length && match[0];
    }

    function xmlToDom(textdata) {
        return (new DOMParser()).parseFromString(textdata, 'text/xml');
    }

    drawData.setFile = function(extension, data) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        var gj;
        switch (extension) {
            case '.gpx':
                gj = toGeoJSON.gpx(xmlToDom(data));
                break;
            case '.kml':
                gj = toGeoJSON.kml(xmlToDom(data));
                break;
            case '.geojson':
            case '.json':
                gj = JSON.parse(data);
                break;
        }

        gj = gj || {};
        if (Object.keys(gj).length) {
            _geojson = ensureIDs(gj);
            _src = extension + ' data file';
            this.fitZoom();
        }

        dispatch.call('change');
        return this;
    };

    drawData.showLabels = function(val) {
        if (!arguments.length) return _showLabels;

        _showLabels = val;
        return this;
    };

    drawData.enabled = function(val) {
        if (!arguments.length) return _enabled;

        _enabled = val;
        if (_enabled) {
            showLayer();
        } else {
            hideLayer();
        }

        dispatch.call('change');
        return this;
    };

    drawData.hasData = function() {
        var gj = _geojson || {};
        return !!(_template || Object.keys(gj).length);
    };

    drawData.template = function(val, src) {
        if (!arguments.length) return _template;

        // test source against OSM imagery blacklists..
        var osm = context.connection();
        if (osm) {
            var blacklists = osm.imageryBlacklists();
            var fail = false;
            var tested = 0;
            var regex;

            for (var i = 0; i < blacklists.length; i++) {
                try {
                    regex = new RegExp(blacklists[i]);
                    fail = regex.test(val);
                    tested++;
                    if (fail) break;
                } catch (e) {
                    /* noop */
                }
            }

            // ensure at least one test was run.
            if (!tested) {
                regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
                fail = regex.test(val);
            }
        }

        _template = val;
        _fileList = null;
        _geojson = null;

        // strip off the querystring/hash from the template,
        // it often includes the access token
        _src = src || ('vectortile:' + val.split(/[?#]/)[0]);

        dispatch.call('change');
        return this;
    };

    drawData.geojson = function(gj, src) {
        if (!arguments.length) return _geojson;

        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        gj = gj || {};
        if (Object.keys(gj).length) {
            _geojson = ensureIDs(gj);
            _src = src || 'unknown.geojson';
        }

        dispatch.call('change');
        return this;
    };

    drawData.fileList = function(fileList) {
        if (!arguments.length) return _fileList;

        _template = null;
        _fileList = fileList;
        _geojson = null;
        _src = null;

        if (!fileList || !fileList.length) return this;
        var f = fileList[0];
        var extension = getExtension(f.name);
        var reader = new FileReader();
        reader.onload = (function() {
            return function(e) {
                drawData.setFile(extension, e.target.result);
            };
        })(f);

        reader.readAsText(f);

        return this;
    };

    drawData.url = function(url, defaultExtension) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        // strip off any querystring/hash from the url before checking extension
        var testUrl = url.split(/[?#]/)[0];
        var extension = getExtension(testUrl) || defaultExtension;
        if (extension) {
            _template = null;
            d3_text(url)
                .then(function(data) {
                    drawData.setFile(extension, data);
                })
                .catch(function() {
                    /* ignore */
                });

        } else {
            drawData.template(url);
        }

        return this;
    };

    drawData.getSrc = function() {
        return _src || '';
    };

    drawData.fitZoom = function() {
        var features = getFeatures(_geojson);
        if (!features.length) return;

        var map = context.map();
        var viewport = map.trimmedExtent().polygon();
        var coords = features.reduce(function(coords, feature) {
            var c = feature.geometry.coordinates;

            /* eslint-disable no-fallthrough */
            switch (feature.geometry.type) {
                case 'Point':
                    c = [c];
                case 'MultiPoint':
                case 'LineString':
                    break;

                case 'MultiPolygon':
                    c = utilArrayFlatten(c);
                case 'Polygon':
                case 'MultiLineString':
                    c = utilArrayFlatten(c);
                    break;
            }
            /* eslint-enable no-fallthrough */

            return utilArrayUnion(coords, c);
        }, []);

        if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
            var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
            map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
        }

        return this;
    };

    init();
    return drawData;
}
<commit_msg>Add additional check for malformed GeoJSON data<commit_after>import _throttle from 'lodash-es/throttle';

import { geoBounds as d3_geoBounds, geoPath as d3_geoPath } from 'd3-geo';
import { text as d3_text } from 'd3-fetch';
import { event as d3_event, select as d3_select } from 'd3-selection';

import stringify from 'fast-json-stable-stringify';
import toGeoJSON from '@mapbox/togeojson';

import { geoExtent, geoPolygonIntersectsPolygon } from '../geo';
import { services } from '../services';
import { svgPath } from './helpers';
import { utilDetect } from '../util/detect';
import { utilArrayFlatten, utilArrayUnion, utilHashcode } from '../util';

var _initialized = false;
var _enabled = false;
var _geojson;

export function svgData(projection, context, dispatch) {
    var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
    var _showLabels = true;
    var detected = utilDetect();
    var layer = d3_select(null);
    var _vtService;
    var _fileList;
    var _template;
    var _src;

    function init() {
        if (_initialized) return;  // run once

        _geojson = {};
        _enabled = true;

        function over() {
            d3_event.stopPropagation();
            d3_event.preventDefault();
            d3_event.dataTransfer.dropEffect = 'copy';
        }

        context.container()
            .attr('dropzone', 'copy')
            .on('drop.svgData', function() {
                d3_event.stopPropagation();
                d3_event.preventDefault();
                if (!detected.filedrop) return;
                drawData.fileList(d3_event.dataTransfer.files);
            })
            .on('dragenter.svgData', over)
            .on('dragexit.svgData', over)
            .on('dragover.svgData', over);

        _initialized = true;
    }

    function getService() {
        if (services.vectorTile && !_vtService) {
            _vtService = services.vectorTile;
            _vtService.event.on('loadedData', throttledRedraw);
        } else if (!services.vectorTile && _vtService) {
            _vtService = null;
        }

        return _vtService;
    }

    function showLayer() {
        layerOn();

        layer
            .style('opacity', 0)
            .transition()
            .duration(250)
            .style('opacity', 1)
            .on('end', function () { dispatch.call('change'); });
    }

    function hideLayer() {
        throttledRedraw.cancel();

        layer
            .transition()
            .duration(250)
            .style('opacity', 0)
            .on('end', layerOff);
    }

    function layerOn() {
        layer.style('display', 'block');
    }

    function layerOff() {
        layer.selectAll('.viewfield-group').remove();
        layer.style('display', 'none');
    }

    // ensure that all geojson features in a collection have IDs
    function ensureIDs(gj) {
        if (!gj) return null;

        if (gj.type === 'FeatureCollection') {
            for (var i = 0; i < gj.features.length; i++) {
                ensureFeatureID(gj.features[i]);
            }
        } else {
            ensureFeatureID(gj);
        }
        return gj;
    }

    // ensure that each single Feature object has a unique ID
    function ensureFeatureID(feature) {
        if (!feature) return;
        feature.__featurehash__ = utilHashcode(stringify(feature));
        return feature;
    }

    // Prefer an array of Features instead of a FeatureCollection
    function getFeatures(gj) {
        if (!gj) return [];

        if (gj.type === 'FeatureCollection') {
            return gj.features;
        } else {
            return [gj];
        }
    }

    function featureKey(d) {
        return d.__featurehash__;
    }

    function isPolygon(d) {
        return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
    }

    function clipPathID(d) {
        return 'ideditor-data-' + d.__featurehash__ + '-clippath';
    }

    function featureClasses(d) {
        return [
            'data' + d.__featurehash__,
            d.geometry.type,
            isPolygon(d) ? 'area' : '',
            d.__layerID__ || ''
        ].filter(Boolean).join(' ');
    }

    function drawData(selection) {
        var vtService = getService();
        var getPath = svgPath(projection).geojson;
        var getAreaPath = svgPath(projection, null, true).geojson;
        var hasData = drawData.hasData();

        layer = selection.selectAll('.layer-mapdata')
            .data(_enabled && hasData ? [0] : []);

        layer.exit()
            .remove();

        layer = layer.enter()
            .append('g')
            .attr('class', 'layer-mapdata')
            .merge(layer);

        var surface = context.surface();
        if (!surface || surface.empty()) return;  // not ready to draw yet, starting up

        // Gather data
        var geoData, polygonData;
        if (_template && vtService) {   // fetch data from vector tile service
            var sourceID = _template;
            vtService.loadTiles(sourceID, _template, projection);
            geoData = vtService.data(sourceID, projection);
        } else {
            geoData = getFeatures(_geojson);
        }
        geoData = geoData.filter(getPath);
        polygonData = geoData.filter(isPolygon);

        // Draw clip paths for polygons
        var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
           .data(polygonData, featureKey);

        clipPaths.exit()
           .remove();

        var clipPathsEnter = clipPaths.enter()
           .append('clipPath')
           .attr('class', 'clipPath-data')
           .attr('id', clipPathID);

        clipPathsEnter
           .append('path');

        clipPaths.merge(clipPathsEnter)
           .selectAll('path')
           .attr('d', getAreaPath);

        // Draw fill, shadow, stroke layers
        var datagroups = layer
            .selectAll('g.datagroup')
            .data(['fill', 'shadow', 'stroke']);

        datagroups = datagroups.enter()
            .append('g')
            .attr('class', function(d) { return 'datagroup datagroup-' + d; })
            .merge(datagroups);

        // Draw paths
        var pathData = {
            fill: polygonData,
            shadow: geoData,
            stroke: geoData
        };

        var paths = datagroups
            .selectAll('path')
            .data(function(layer) { return pathData[layer]; }, featureKey);

        // exit
        paths.exit()
            .remove();

        // enter/update
        paths = paths.enter()
            .append('path')
            .attr('class', function(d) {
                var datagroup = this.parentNode.__data__;
                return 'pathdata ' + datagroup + ' ' + featureClasses(d);
            })
            .attr('clip-path', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
            })
            .merge(paths)
            .attr('d', function(d) {
                var datagroup = this.parentNode.__data__;
                return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
            });

        // Draw labels
        layer
            .call(drawLabels, 'label-halo', geoData)
            .call(drawLabels, 'label', geoData);

        function drawLabels(selection, textClass, data) {
            var labelPath = d3_geoPath(projection);
            var labelData = data.filter(function(d) {
                return _showLabels && d.properties && (d.properties.desc || d.properties.name);
            });

            var labels = selection.selectAll('text.' + textClass)
                .data(labelData, featureKey);

            // exit
            labels.exit()
                .remove();

            // enter/update
            labels = labels.enter()
                .append('text')
                .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
                .merge(labels)
                .text(function(d) {
                    return d.properties.desc || d.properties.name;
                })
                .attr('x', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[0] + 11;
                })
                .attr('y', function(d) {
                    var centroid = labelPath.centroid(d);
                    return centroid[1];
                });
        }
    }

    function getExtension(fileName) {
        if (!fileName) return;

        var re = /\.(gpx|kml|(geo)?json)$/i;
        var match = fileName.toLowerCase().match(re);
        return match && match.length && match[0];
    }

    function xmlToDom(textdata) {
        return (new DOMParser()).parseFromString(textdata, 'text/xml');
    }

    drawData.setFile = function(extension, data) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        var gj;
        switch (extension) {
            case '.gpx':
                gj = toGeoJSON.gpx(xmlToDom(data));
                break;
            case '.kml':
                gj = toGeoJSON.kml(xmlToDom(data));
                break;
            case '.geojson':
            case '.json':
                gj = JSON.parse(data);
                break;
        }

        gj = gj || {};
        if (Object.keys(gj).length) {
            _geojson = ensureIDs(gj);
            _src = extension + ' data file';
            this.fitZoom();
        }

        dispatch.call('change');
        return this;
    };

    drawData.showLabels = function(val) {
        if (!arguments.length) return _showLabels;

        _showLabels = val;
        return this;
    };

    drawData.enabled = function(val) {
        if (!arguments.length) return _enabled;

        _enabled = val;
        if (_enabled) {
            showLayer();
        } else {
            hideLayer();
        }

        dispatch.call('change');
        return this;
    };

    drawData.hasData = function() {
        var gj = _geojson || {};
        return !!(_template || Object.keys(gj).length);
    };

    drawData.template = function(val, src) {
        if (!arguments.length) return _template;

        // test source against OSM imagery blacklists..
        var osm = context.connection();
        if (osm) {
            var blacklists = osm.imageryBlacklists();
            var fail = false;
            var tested = 0;
            var regex;

            for (var i = 0; i < blacklists.length; i++) {
                try {
                    regex = new RegExp(blacklists[i]);
                    fail = regex.test(val);
                    tested++;
                    if (fail) break;
                } catch (e) {
                    /* noop */
                }
            }

            // ensure at least one test was run.
            if (!tested) {
                regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
                fail = regex.test(val);
            }
        }

        _template = val;
        _fileList = null;
        _geojson = null;

        // strip off the querystring/hash from the template,
        // it often includes the access token
        _src = src || ('vectortile:' + val.split(/[?#]/)[0]);

        dispatch.call('change');
        return this;
    };

    drawData.geojson = function(gj, src) {
        if (!arguments.length) return _geojson;

        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        gj = gj || {};
        if (Object.keys(gj).length) {
            _geojson = ensureIDs(gj);
            _src = src || 'unknown.geojson';
        }

        dispatch.call('change');
        return this;
    };

    drawData.fileList = function(fileList) {
        if (!arguments.length) return _fileList;

        _template = null;
        _fileList = fileList;
        _geojson = null;
        _src = null;

        if (!fileList || !fileList.length) return this;
        var f = fileList[0];
        var extension = getExtension(f.name);
        var reader = new FileReader();
        reader.onload = (function() {
            return function(e) {
                drawData.setFile(extension, e.target.result);
            };
        })(f);

        reader.readAsText(f);

        return this;
    };

    drawData.url = function(url, defaultExtension) {
        _template = null;
        _fileList = null;
        _geojson = null;
        _src = null;

        // strip off any querystring/hash from the url before checking extension
        var testUrl = url.split(/[?#]/)[0];
        var extension = getExtension(testUrl) || defaultExtension;
        if (extension) {
            _template = null;
            d3_text(url)
                .then(function(data) {
                    drawData.setFile(extension, data);
                })
                .catch(function() {
                    /* ignore */
                });

        } else {
            drawData.template(url);
        }

        return this;
    };

    drawData.getSrc = function() {
        return _src || '';
    };

    drawData.fitZoom = function() {
        var features = getFeatures(_geojson);
        if (!features.length) return;

        var map = context.map();
        var viewport = map.trimmedExtent().polygon();
        var coords = features.reduce(function(coords, feature) {
            var geom = feature.geometry;
            if (!geom) return coords;

            var c = geom.coordinates;

            /* eslint-disable no-fallthrough */
            switch (geom.type) {
                case 'Point':
                    c = [c];
                case 'MultiPoint':
                case 'LineString':
                    break;

                case 'MultiPolygon':
                    c = utilArrayFlatten(c);
                case 'Polygon':
                case 'MultiLineString':
                    c = utilArrayFlatten(c);
                    break;
            }
            /* eslint-enable no-fallthrough */

            return utilArrayUnion(coords, c);
        }, []);

        if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
            var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
            map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
        }

        return this;
    };

    init();
    return drawData;
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
//            (c) 2011 panacoda GmbH. All rights reserved.
// Creator:   Dominik
// Date:      02.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

/**
 * A constant value for hyperlink of type email.
 *
 * @type String
 */
M.HYPERLINK_EMAIL = 'mail';

/**
 * A constant value for hyperlink of type website.
 *
 * @type String
 */
M.HYPERLINK_WEBSITE = 'website';

/**
 * A constant value for hyperlink of type phone number.
 *
 * @type String
 */
M.HYPERLINK_PHONE = 'phone';

/**
 * @class
 *
 * The is the prototype of any label view. It basically renders a simple plain
 * text can be styled using several properties of M.LabelView or providing one
 * ore more css classes.
 *
 * @extends M.View
 */
M.LabelView = M.View.extend(
/** @scope M.LabelView.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.LabelView',

    /**
     * Determines whether a new line '\n' within the label's value should be transformed
     * into a line break '
' before it is rendered. Default: YES.
     *
     * @type Boolean
     */
    newLineToBreak: YES,

    /**
     * Determines whether a tabulator '\t' within the label's value should be transformed
     * into four spaces ' ' before it is rendered. Default: YES.
     *
     * @type Boolean
     */
    tabToSpaces: YES,

    /**
     * This property can be used to specify a certain hyperlink type for this label. It only
     * works in combination with the hyperlinkTarget property.
     *
     * @type String
     */
    hyperlinkType: null,

    /**
     * This property can be used to specify a hyperlink target for this label. It only
     * works in combination with the hyperlinkType property.
     *
     * @type String
     */
    hyperlinkTarget: null,

    /**
     * This property specifies the recommended events for this type of view.
     *
     * @type Array
     */
    recommendedEvents: ['tap'],

    isMovable: NO,

    extraStyle: null,

    moveRulesAvailable: NO,

    /**
     * Renders a label view as a div tag with corresponding data-role attribute and inner
     * text defined by value.
     *
     * @private
     * @returns {String} The image view's styling as html representation.
     */
    render: function() {
        var that = this;
        this.computeValue();
        if(typeof(this.movable) === 'object') {
            if (this.movable.time && this.movable.offset){
                this.isMovable = YES;
                this.html = '
';
                this.extraStyle = this.createExtraStyle();
                this.makeMovable(this.movable.offset, this.movable.time);
                window.setTimeout(function(){
                    that.html += '
';
                }, 0);
            }else {
                M.Logger.log('both properties "time" and "offset" are needed', M.WARN);
            }
        }
        this.html += '
';

        if(this.hyperlinkTarget && this.hyperlinkType) {
            switch (this.hyperlinkType) {
                case M.HYPERLINK_EMAIL:
                    this.html += '';
                    break;
                case M.HYPERLINK_WEBSITE:
                    this.html += '';
                    break;
                case M.HYPERLINK_PHONE:
                    this.html += '';
                    break;
            }
        }

        this.html += this.newLineToBreak ? this.nl2br(this.tabToSpaces ? this.tab2space(this.value) : this.value) : (this.tabToSpaces ? this.tab2space(this.value) : this.value);

        if(this.hyperlinkTarget && this.hyperlinkType) {
            this.html += '';
        }

        this.html += '
';

        return this.html;
    },

    /**
     * Updates the value of the label with DOM access by jQuery.
     *
     * @private
     */
    renderUpdate: function() {
        var that = this, self$ = $('#' + this.id);
        this.computeValue();
        self$.html(this.newLineToBreak ? this.nl2br(this.value) : this.value);
        if(this.isMovable){
            var parent$ = $('.outer-' + this.id);
            this.addMoveClasses(self$, parent$);
            var diff = this.getDiff(self$, parent$);
            if(diff > 0) {
                if(this.moveRulesAvailable) {
                    this.deleteMoveRules();
                }
                window.setTimeout(function(){
                    that.insertMoveRules(that.getBrowserKeyframeRule(), diff, that.movable.offset, that.movable.time);
                }, 0);
            }else {
                this.removeMoveClasses(self$, parent$);
                if(this.moveRulesAvailable) {
                    this.deleteMoveRules();
                }
            }
        }
    },

    /**
     *
     * Appends an extra style tag to the head
     *
     * @private
     * @returns {HTMLElement} The style element as CSSOM
     */
    createExtraStyle: function(){
        var animationStyle = document.createElement('style'), styles;
        animationStyle.type = "text/css";
        document.getElementsByTagName('head').item(0).appendChild(animationStyle);
        styles = document.styleSheets.length;
        animationStyle = document.styleSheets[styles-1];
        return animationStyle;
    },

    /**
     *
     * Calculates the width-difference of self$ minus parent$
     *
     * @private
     * @param {Object} self$
     * @param {Object} parent$
     * @returns {number} difference self-parent
     */
    getDiff: function(self$, parent$){
        var diff = self$.width() - parent$.width();
        return diff;
    },

    /**
     * Returns the CSSRule for the specific browser.
     *
     * @private
     * @returns {string} the name of the browser for css3-animation
     */
    getBrowserKeyframeRule: function(){
        if(CSSRule.WEBKIT_KEYFRAME_RULE) {
            return "-webkit-";
        }else if(CSSRule.MOZ_KEYRAME_RULE) {
            return "-moz-";
        }else if(CSSRule.O_KEYFRAME_RULE) {
            return "-o-";
        }else {
            return "";
        }
    },

    /**
     * Adds special classes responsible for making the label move.
     *
     * @private
     * @param {Object} self$ The jQuery-Object of this label
     * @param {Object} parent$ The jQuery-Object of the surrounding div-container of the label
     */
    addMoveClasses: function(self$, parent$) {
        self$.addClass('tmp-movable-inner');
        self$.addClass('inner-' + this.id);
        parent$.addClass('tmp-movable-outer');
    },

    /**
     * Removes special classes responsible for making the label move.
     *
     * @private
     * @param {Object} self$ The jQuery-Object of this label
     * @param {Object} parent$ The jQuery-Object of the surrounding div-container of the label
     */
    removeMoveClasses: function(self$, parent$) {
        self$.removeClass('tmp-movable-inner');
        self$.removeClass('inner-' + this.id);
        parent$.removeClass('tmp-movable-outer');
    },

    /**
     * Inserts Animation-Rules to the CSSOM in the document-head.
     *
     * @private
     * @param {String} The String for the specific browser
     * @param diff The difference self-parent
     * @param offset The offset value of the passed movable-object
     * @param sec The time value of the passed movable-object
     */
    insertMoveRules: function(browsertype, diff, offset, sec){
        this.extraStyle.insertRule('.inner-' + this.id + ' {'+
            browsertype+'animation-name: move-' + this.id + ';'+
            browsertype+'animation-duration: ' + sec + 's;'+
            browsertype+'animation-iteration-count: infinite;'+
            browsertype+'animation-timing-function: linear;'+
            '}', 0);
        this.extraStyle.insertRule('@' + browsertype + 'keyframes move-' + this.id + '{ 0%,100% { left: ' + offset + 'px;} 50% { left:' + (-diff - offset) + 'px;}}', 1);
        this.moveRulesAvailable = YES;
    },

    /**
     * Deletes the extra CSS3 animation-rules from the CSSOM in the document-head.
     *
     * @private
     *
     */
    deleteMoveRules: function(){
        var l = this.extraStyle.cssRules.length;
        while(l > 0){
            this.extraStyle.removeRule(l-1);
            l = this.extraStyle.cssRules.length;
        }
        this.moveRulesAvailable = NO;
    },

    /**
     * "Initial" method to make the label movable. Gets called in render if a movable-object is passed to the View.
     *
     * @private
     * @param offset The offset value of the passed movable-object
     * @param sec The time value of the passed movable-object
     */
    makeMovable: function(offset, sec){
        var that = this, browsertype = "", parent$, self$, diff;
        browsertype = this.getBrowserKeyframeRule();
        window.setTimeout(function(){
            self$ = $('#' + that.id);
            parent$ = $('.outer-' + that.id);
            that.addMoveClasses(self$, parent$);
            diff = that.getDiff(self$, parent$);
            if(diff > 0){
                that.insertMoveRules(browsertype, diff, offset, sec);
            }else{
                that.removeMoveClasses(self$, parent$);
            }
        }, 0);
    },

    /**
     * Applies some style-attributes to the label.
     *
     * @private
     * @returns {String} The label's styling as html representation.
     */
    style: function() {
        var html = '';
        if(this.isInline) {
            html += ' style="display:inline;"';
        }
        if(this.cssClass) {
            html += ' class="' + this.cssClass + '"';
        }
        return html;
    },

    /**
     * This method sets the label's value and initiates its re-rendering.
     *
     * @param {String} value The value to be applied to the label view.
     */
    setValue: function(value) {
        this.value = value;
        this.renderUpdate();
    }

});<commit_msg>refactor movable code pt.1/2<commit_after>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
//            (c) 2011 panacoda GmbH. All rights reserved.
// Creator:   Dominik
// Date:      02.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

/**
 * A constant value for hyperlink of type email.
 *
 * @type String
 */
M.HYPERLINK_EMAIL = 'mail';

/**
 * A constant value for hyperlink of type website.
 *
 * @type String
 */
M.HYPERLINK_WEBSITE = 'website';

/**
 * A constant value for hyperlink of type phone number.
 *
 * @type String
 */
M.HYPERLINK_PHONE = 'phone';

/**
 * @class
 *
 * The is the prototype of any label view. It basically renders a simple plain
 * text can be styled using several properties of M.LabelView or providing one
 * ore more css classes.
 *
 * @extends M.View
 */
M.LabelView = M.View.extend(
/** @scope M.LabelView.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.LabelView',

    /**
     * Determines whether a new line '\n' within the label's value should be transformed
     * into a line break '
' before it is rendered. Default: YES.
     *
     * @type Boolean
     */
    newLineToBreak: YES,

    /**
     * Determines whether a tabulator '\t' within the label's value should be transformed
     * into four spaces ' ' before it is rendered. Default: YES.
     *
     * @type Boolean
     */
    tabToSpaces: YES,

    /**
     * This property can be used to specify a certain hyperlink type for this label. It only
     * works in combination with the hyperlinkTarget property.
     *
     * @type String
     */
    hyperlinkType: null,

    /**
     * This property can be used to specify a hyperlink target for this label. It only
     * works in combination with the hyperlinkType property.
     *
     * @type String
     */
    hyperlinkTarget: null,

    /**
     * This property specifies the recommended events for this type of view.
     *
     * @type Array
     */
    recommendedEvents: ['tap'],

    extraStyle: null,

    moveRulesAvailable: NO,

    /**
     * jQuery object of the DOM representativ of this view
     *
     * @type {Object}
     */
    $this: null,

    /**
     * Renders a label view as a div tag with corresponding data-role attribute and inner
     * text defined by value.
     *
     * @private
     * @returns {String} The image view's styling as html representation.
     */
    render: function() {
        this.computeValue();
        if(_.isObject(this.movable)) {
            if (this.movable.time && this.movable.offset){
                this.html = '
';

                this._initMovable();

            }else {
                M.Logger.log('Both properties, "time" and "offset", are needed.', M.WARN);
            }
        }
        this.html += '
';

        if(this.hyperlinkTarget && this.hyperlinkType) {
            switch (this.hyperlinkType) {
                case M.HYPERLINK_EMAIL:
                    this.html += '';
                    break;
                case M.HYPERLINK_WEBSITE:
                    this.html += '';
                    break;
                case M.HYPERLINK_PHONE:
                    this.html += '';
                    break;
            }
        }

        this.html += this.newLineToBreak ? this.nl2br(this.tabToSpaces ? this.tab2space(this.value) : this.value) : (this.tabToSpaces ? this.tab2space(this.value) : this.value);

        if(this.hyperlinkTarget && this.hyperlinkType) {
            this.html += '';
        }

        this.html += '
';

        /* If movable is set, an outer div box was defined before and we need to close it here */
        if(_.isObject(this.movable) && this.movable.time && this.movable.offset) {
            this.html += '
';
        }

        return this.html;
    },

    /**
     * Updates the value of the label with DOM access by jQuery.
     *
     * @private
     */
    renderUpdate: function() {
        var that = this;

        this.$this = this.$this || $('#' +  this.id);

        this.computeValue();
        this.$this.html(this.newLineToBreak ? this.nl2br(this.value) : this.value);
        if(this.movable && this.movable.time && this.movable.offset) {

            setTimeout(function() {
                that._makeMovable();
            }, 0);
        }
    },

    /**
     * "Initial" method to make the label movable. Gets called in render if a movable-object is passed to the View.
     *
     * @private
     */
    _makeMovable: function() {
        this.$this = this.$this || $('#' + this.id);

        var that = this;

        var $parent = this.$this.parent();

        this.addMoveClasses(this.$this, $parent);

        var diff = this._checkIfMovingNecessary(this.$this, $parent);

        if(diff) {

            if(that.moveRulesAvailable) {
                that.deleteMoveRules();
            }

            window.setTimeout(function() {
                that.insertMoveRules(that.getBrowserKeyframeRule(), diff, that.movable.offset, that.movable.time);
            }, 0);

        } else {
            that.removeMoveClasses(that.$this, $parent);

            if(that.moveRulesAvailable) {
                that.deleteMoveRules();
            }
        }
    },

    _initMovable: function() {
        var that = this;
        window.setTimeout(function() {

            if(that._checkIfMovingNecessary()) {
                that.extraStyle = that.createExtraStyle();
                that._makeMovable();
            }

        }, 0);
    },

    _checkIfMovingNecessary: function() {
        this.$this = this.$this || $('#' + this.id);

        var diff = this.getDiff(this.$this, this.$this.parent());

        if(diff > 0) {
            return diff;
        }

        return NO;
    },

    /**
     *
     * Appends an extra style tag to the head
     *
     * @private
     * @returns {HTMLElement} The style element as CSSOM
     */
    createExtraStyle: function(){
        var animationStyle = document.createElement('style'), styles;
        animationStyle.type = "text/css";
        document.getElementsByTagName('head').item(0).appendChild(animationStyle);
        styles = document.styleSheets.length;
        animationStyle = document.styleSheets[styles-1];
        return animationStyle;
    },

    /**
     *
     * Calculates the width-difference of the inner div (the one containing the value) and
     * its outer box.
     *
     * Difference + offset results in the "moving value", the offset that the label is animated.
     *
     * @private
     * @param {Object} $self
     * @param {Object} $parent
     * @returns {number} difference self-parent
     */
    getDiff: function($self, $parent) {
        var diff = $self.outerWidth() - $parent.width();
        return diff;
    },

    /**
     * Returns the CSSRule for the specific browser.
     *
     * @private
     * @returns {string} the name of the browser for css3-animation
     */
    getBrowserKeyframeRule: function(){
        if(CSSRule.WEBKIT_KEYFRAME_RULE) {
            return "-webkit-";
        }else if(CSSRule.MOZ_KEYRAME_RULE) {
            return "-moz-";
        }else if(CSSRule.O_KEYFRAME_RULE) {
            return "-o-";
        }else {
            return "";
        }
    },

    /**
     * Adds special classes responsible for making the label move.
     *
     * @private
     * @param {Object} $self The jQuery-Object of this label
     * @param {Object} $parent The jQuery-Object of the surrounding div-container of the label
     */
    addMoveClasses: function($self, $parent) {
        $self.addClass('tmp-movable-inner inner-' + this.id);
        $parent.addClass('tmp-movable-outer');
    },

    /**
     * Removes special classes responsible for making the label move.
     *
     * @private
     * @param {Object} $self The jQuery-Object of this label
     * @param {Object} $parent The jQuery-Object of the surrounding div-container of the label
     */
    removeMoveClasses: function($self, $parent) {
        $self.removeClass('tmp-movable-inner');
        $self.removeClass('inner-' + this.id);
        $parent.removeClass('tmp-movable-outer');
    },

    /**
     * Inserts Animation-Rules to the CSSOM in the document-head.
     *
     * @private
     * @param {String} The String for the specific browser
     * @param diff The difference self-parent
     * @param offset The offset value of the passed movable-object
     * @param sec The time value of the passed movable-object
     */
    insertMoveRules: function(browsertype, diff, offset, sec){
        this.extraStyle.insertRule('.inner-' + this.id + ' {'+
            browsertype+'animation-name: move-' + this.id + ';'+
            browsertype+'animation-duration: ' + sec + 's;'+
            browsertype+'animation-iteration-count: infinite;'+
            browsertype+'animation-timing-function: linear;'+
            '}', 0);
        this.extraStyle.insertRule('@' + browsertype + 'keyframes move-' + this.id + '{ 0%,100% { left: ' + offset + 'px;} 50% { left:' + (-diff - offset) + 'px;}}', 1);
        this.moveRulesAvailable = YES;
    },

    /**
     * Deletes the extra CSS3 animation-rules from the CSSOM in the document-head.
     *
     * @private
     *
     */
    deleteMoveRules: function(){
        var l = this.extraStyle.cssRules.length;
        while(l > 0){
            this.extraStyle.removeRule(l-1);
            l = this.extraStyle.cssRules.length;
        }
        this.moveRulesAvailable = NO;
    },

    /**
     * Applies some style-attributes to the label.
     *
     * @private
     * @returns {String} The label's styling as html representation.
     */
    style: function() {
        var html = '';
        if(this.isInline) {
            html += ' style="display:inline;"';
        }
        if(this.cssClass) {
            var tmp_movable_inner_class = this.movable ? 'tmp-movable-inner inner-' + this.id : '';
            html += ' class="' + tmp_movable_inner_class + ' ' + this.cssClass + '"';
        } else {
            html += ' class="tmp-movable-inner inner-' + this.id + '"';
        }
        return html;
    },

    /**
     * This method sets the label's value and initiates its re-rendering.
     *
     * @param {String} value The value to be applied to the label view.
     */
    setValue: function(value) {
        this.value = value;
        this.renderUpdate();
    }

});<|endoftext|>


<commit_before>odoo.define('mail.systray.ActivityMenu', function (require) {
"use strict";

var core = require('web.core');
var session = require('web.session');
var SystrayMenu = require('web.SystrayMenu');
var Widget = require('web.Widget');
var QWeb = core.qweb;

/**
 * Menu item appended in the systray part of the navbar, redirects to the next
 * activities of all app
 */
var ActivityMenu = Widget.extend({
    name: 'activity_menu',
    template:'mail.systray.ActivityMenu',
    events: {
        'click .o_mail_activity_action': '_onActivityActionClick',
        'click .o_mail_preview': '_onActivityFilterClick',
        'show.bs.dropdown': '_onActivityMenuShow',
    },
    willStart: function () {
        return $.when(this.call('mail_service', 'isReady'));
    },
    start: function () {
        this._$activitiesPreview = this.$('.o_mail_systray_dropdown_items');
        this.call('mail_service', 'getMailBus').on('activity_updated', this, this._updateCounter);
        this._updateCounter();
        this._updateActivityPreview();
        return this._super();
    },
    //--------------------------------------------------
    // Private
    //--------------------------------------------------
    /**
     * Make RPC and get current user's activity details
     * @private
     */
    _getActivityData: function () {
        var self = this;

        return self._rpc({
            model: 'res.users',
            method: 'systray_get_activities',
            args: [],
            kwargs: {context: session.user_context},
        }).then(function (data) {
            self._activities = data;
            self.activityCounter = _.reduce(data, function (total_count, p_data) { return total_count + p_data.total_count; }, 0);
            self.$('.o_notification_counter').text(self.activityCounter);
            self.$el.toggleClass('o_no_notification', !self.activityCounter);
        });
    },
    /**
     * Get particular model view to redirect on click of activity scheduled on that model.
     * @private
     * @param {string} model
     */
    _getActivityModelViewID: function (model) {
        return this._rpc({
            model: model,
            method: 'get_activity_view_id'
        });
    },
    /**
     * Update(render) activity system tray view on activity updation.
     * @private
     */
    _updateActivityPreview: function () {
        var self = this;
        self._getActivityData().then(function (){
            self._$activitiesPreview.html(QWeb.render('mail.systray.ActivityMenu.Previews', {
                activities : self._activities
            }));
        });
    },
    /**
     * update counter based on activity status(created or Done)
     * @private
     * @param {Object} [data] key, value to decide activity created or deleted
     * @param {String} [data.type] notification type
     * @param {Boolean} [data.activity_deleted] when activity deleted
     * @param {Boolean} [data.activity_created] when activity created
     */
    _updateCounter: function (data) {
        if (data) {
            if (data.activity_created) {
                this.activityCounter ++;
            }
            if (data.activity_deleted && this.activityCounter > 0) {
                this.activityCounter --;
            }
            this.$('.o_notification_counter').text(this.activityCounter);
            this.$el.toggleClass('o_no_notification', !this.activityCounter);
        }
    },

    //------------------------------------------------------------
    // Handlers
    //------------------------------------------------------------

    /**
     * Redirect to specific action given its xml id
     * @private
     * @param {MouseEvent} ev
     */
    _onActivityActionClick: function (ev) {
        ev.stopPropagation();
        var actionXmlid = $(ev.currentTarget).data('action_xmlid');
        this.do_action(actionXmlid);
    },

    /**
     * Redirect to particular model view
     * @private
     * @param {MouseEvent} event
     */
    _onActivityFilterClick: function (event) {
        // fetch the data from the button otherwise fetch the ones from the parent (.o_mail_preview).
        var data = _.extend({}, $(event.currentTarget).data(), $(event.target).data());
        var context = {};
        if (data.filter === 'my') {
            context['search_default_activities_overdue'] = 1;
            context['search_default_activities_today'] = 1;
        } else {
            context['search_default_activities_' + data.filter] = 1;
        }
        this.do_action({
            type: 'ir.actions.act_window',
            name: data.model_name,
            res_model:  data.res_model,
            views: [[false, 'kanban'], [false, 'form']],
            search_view_id: [false],
            domain: [['activity_user_id', '=', session.uid]],
            context:context,
        });
    },
    /**
     * @private
     */
    _onActivityMenuShow: function () {
         this._updateActivityPreview();
    },
});

SystrayMenu.Items.push(ActivityMenu);

return ActivityMenu;

});
<commit_msg>[FIX] mail: keep activity notification counter on today's meetings<commit_after>odoo.define('mail.systray.ActivityMenu', function (require) {
"use strict";

var core = require('web.core');
var session = require('web.session');
var SystrayMenu = require('web.SystrayMenu');
var Widget = require('web.Widget');
var QWeb = core.qweb;

/**
 * Menu item appended in the systray part of the navbar, redirects to the next
 * activities of all app
 */
var ActivityMenu = Widget.extend({
    name: 'activity_menu',
    template:'mail.systray.ActivityMenu',
    events: {
        'click .o_mail_activity_action': '_onActivityActionClick',
        'click .o_mail_preview': '_onActivityFilterClick',
        'show.bs.dropdown': '_onActivityMenuShow',
    },
    willStart: function () {
        return $.when(this.call('mail_service', 'isReady'));
    },
    start: function () {
        this._$activitiesPreview = this.$('.o_mail_systray_dropdown_items');
        this.call('mail_service', 'getMailBus').on('activity_updated', this, this._updateCounter);
        this._updateCounter();
        this._updateActivityPreview();
        return this._super();
    },
    //--------------------------------------------------
    // Private
    //--------------------------------------------------
    /**
     * Make RPC and get current user's activity details
     * @private
     */
    _getActivityData: function () {
        var self = this;

        return self._rpc({
            model: 'res.users',
            method: 'systray_get_activities',
            args: [],
            kwargs: {context: session.user_context},
        }).then(function (data) {
            self._activities = data;
            self.activityCounter = _.reduce(data, function (total_count, p_data) { return total_count + p_data.total_count || 0; }, 0);
            self.$('.o_notification_counter').text(self.activityCounter);
            self.$el.toggleClass('o_no_notification', !self.activityCounter);
        });
    },
    /**
     * Get particular model view to redirect on click of activity scheduled on that model.
     * @private
     * @param {string} model
     */
    _getActivityModelViewID: function (model) {
        return this._rpc({
            model: model,
            method: 'get_activity_view_id'
        });
    },
    /**
     * Update(render) activity system tray view on activity updation.
     * @private
     */
    _updateActivityPreview: function () {
        var self = this;
        self._getActivityData().then(function (){
            self._$activitiesPreview.html(QWeb.render('mail.systray.ActivityMenu.Previews', {
                activities : self._activities
            }));
        });
    },
    /**
     * update counter based on activity status(created or Done)
     * @private
     * @param {Object} [data] key, value to decide activity created or deleted
     * @param {String} [data.type] notification type
     * @param {Boolean} [data.activity_deleted] when activity deleted
     * @param {Boolean} [data.activity_created] when activity created
     */
    _updateCounter: function (data) {
        if (data) {
            if (data.activity_created) {
                this.activityCounter ++;
            }
            if (data.activity_deleted && this.activityCounter > 0) {
                this.activityCounter --;
            }
            this.$('.o_notification_counter').text(this.activityCounter);
            this.$el.toggleClass('o_no_notification', !this.activityCounter);
        }
    },

    //------------------------------------------------------------
    // Handlers
    //------------------------------------------------------------

    /**
     * Redirect to specific action given its xml id
     * @private
     * @param {MouseEvent} ev
     */
    _onActivityActionClick: function (ev) {
        ev.stopPropagation();
        var actionXmlid = $(ev.currentTarget).data('action_xmlid');
        this.do_action(actionXmlid);
    },

    /**
     * Redirect to particular model view
     * @private
     * @param {MouseEvent} event
     */
    _onActivityFilterClick: function (event) {
        // fetch the data from the button otherwise fetch the ones from the parent (.o_mail_preview).
        var data = _.extend({}, $(event.currentTarget).data(), $(event.target).data());
        var context = {};
        if (data.filter === 'my') {
            context['search_default_activities_overdue'] = 1;
            context['search_default_activities_today'] = 1;
        } else {
            context['search_default_activities_' + data.filter] = 1;
        }
        this.do_action({
            type: 'ir.actions.act_window',
            name: data.model_name,
            res_model:  data.res_model,
            views: [[false, 'kanban'], [false, 'form']],
            search_view_id: [false],
            domain: [['activity_user_id', '=', session.uid]],
            context:context,
        });
    },
    /**
     * @private
     */
    _onActivityMenuShow: function () {
         this._updateActivityPreview();
    },
});

SystrayMenu.Items.push(ActivityMenu);

return ActivityMenu;

});
<|endoftext|>


<commit_before>odoo.define('website_sale.tour', function (require) {
'use strict';

var Tour = require('web.Tour');

Tour.register({
    id:   'shop_buy_product',
    name: "Try to buy products",
    path: '/shop',
    mode: 'test',
    steps: [
        {
            title:  "search ipod",
            element: 'form:has(input[name="search"]) a.a-submit',
            onload: function() {
                $('input[name="search"]').val("ipod");
            }
        },
        {
            title:     "select ipod",
            element:   '.oe_product_cart a:contains("iPod")',
        },
        {
            title:     "select ipod 32GB",
            waitFor:   '#product_detail',
            element:   'label:contains(32 GB) input',
        },
        {
            title:     "click on add to cart",
            waitFor:   'label:contains(32 GB) input:propChecked',
            element:   '#product_detail form[action^="/shop/cart/update"] .btn',
        },
        {
            title:     "add suggested",
            waitNot:   '#cart_products:contains("[A8767] Apple In-Ear Headphones")',
            element:   '.oe_cart a:contains("Add to Cart")',
        },
        {
            title:     "add one more iPod",
            waitFor:   '.my_cart_quantity:contains(2)',
            element:   '#cart_products tr:contains("32 GB") a.js_add_cart_json:eq(1)',
        },
        {
            title:     "remove Headphones",
            waitFor:   '#cart_products tr:contains("32 GB") input.js_quantity:propValue(2)',
            element:   '#cart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first',
        },
        {
            title:     "set one iPod",
            waitNot:   '#cart_products tr:contains("Apple In-Ear Headphones")',
            element:   '#cart_products input.js_quantity',
            sampleText: '1',
        },
        {
            title:     "go to checkout",
            waitFor:   '#cart_products input.js_quantity:propValue(1)',
            element:   'a[href="/shop/checkout"]',
        },
        {
            title:     "test with input error",
            element:   'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
            onload: function (tour) {
                $("input[name='phone']").val("");
            },
        },
        {
            title:     "test without input error",
            waitFor:   'form[action="/shop/confirm_order"] .has-error',
            element:   'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
            onload: function (tour) {
                if ($("input[name='name']").val() === "")
                    $("input[name='name']").val("website_sale-test-shoptest");
                if ($("input[name='email']").val() === "")
                    $("input[name='email']").val("website_sale_test_shoptest@websitesaletest.odoo.com");
                $("input[name='phone']").val("123");
                $("input[name='street2']").val("123");
                $("input[name='city']").val("123");
                $("input[name='zip']").val("123");
                $("select[name='country_id']").val("21");
            },
        },
        {
            title:     "select payment",
            element:   '#payment_method label:has(img[title="Wire Transfer"]) input',
        },
        {
            title:     "Pay Now",
            waitFor:   '#payment_method label:has(input:checked):has(img[title="Wire Transfer"])',
            element:   '.oe_sale_acquirer_button .btn[type="submit"]:visible',
        },
        {
            title:     "finish",
            waitFor:   '.oe_website_sale:contains("Thank you for your order")',
        }
    ]
});

});
<commit_msg>[HACK] test: modify website_sale_tour_buy.js (#2)<commit_after>odoo.define('website_sale.tour', function (require) {
'use strict';

var Tour = require('web.Tour');

Tour.register({
    id:   'shop_buy_product',
    name: "Try to buy products",
    path: '/shop',
    mode: 'test',
    steps: [
        {
            title:  "search ipod",
            element: 'form:has(input[name="search"]) a.a-submit',
            onload: function() {
                $('input[name="search"]').val("ipod");
            }
        },
        {
            title:     "select ipod",
            element:   '.oe_product_cart a:contains("iPod")',
        },
        {
            title:     "select ipod 32GB",
            waitFor:   '#product_detail',
            element:   'label:contains(32 GB) input',
        },
        {
            title:     "click on add to cart",
            waitFor:   'label:contains(32 GB) input:propChecked',
            element:   '#product_detail form[action^="/shop/cart/update"] .btn',
        },
        {
            title:     "add suggested",
            waitNot:   '#cart_products:contains("[A8767] Apple In-Ear Headphones")',
            element:   '.oe_cart a:contains("Add to Cart")',
        },
        {
            title:     "add one more iPod",
            waitFor:   '.my_cart_quantity:contains(2)',
            element:   '#cart_products tr:contains("32 GB") a.js_add_cart_json:eq(1)',
        },
        {
            title:     "remove Headphones",
            waitFor:   '#cart_products tr:contains("32 GB") input.js_quantity:propValue(2)',
            element:   '#cart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first',
        },
        {
            title:     "set one iPod",
            waitNot:   '#cart_products tr:contains("Apple In-Ear Headphones")',
            element:   '#cart_products input.js_quantity',
            sampleText: '1',
        },
        {
            title:     "go to checkout",
            waitFor:   '#cart_products input.js_quantity:propValue(1)',
            element:   'a[href="/shop/checkout"]',
        },
        {
            title:     "test with input error",
            element:   'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
            onload: function (tour) {
                $("input[name='phone']").val("");
            },
        },
        {
            title:     "test without input error",
            waitFor:   'form[action="/shop/confirm_order"] .has-error',
            element:   'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
            onload: function (tour) {
                // SLC - added for microcom_student
                if ($("input[name='firstname']").val() === "")
                    $("input[name='firstname']").val("website_sale-test-shoptest");
                if ($("input[name='lastname']").val() === "")
                    $("input[name='lastname']").val("website_sale-test-shoptest");
                // SLC - end
                if ($("input[name='name']").val() === "")
                    $("input[name='name']").val("website_sale-test-shoptest");
                if ($("input[name='email']").val() === "")
                    $("input[name='email']").val("website_sale_test_shoptest@websitesaletest.odoo.com");
                $("input[name='phone']").val("123");
                $("input[name='street2']").val("123");
                $("input[name='city']").val("123");
                $("input[name='zip']").val("123");
                $("select[name='country_id']").val("21");
            },
        },
        {
            title:     "select payment",
            element:   '#payment_method label:has(img[title="Wire Transfer"]) input',
        },
        {
            title:     "Pay Now",
            waitFor:   '#payment_method label:has(input:checked):has(img[title="Wire Transfer"])',
            element:   '.oe_sale_acquirer_button .btn[type="submit"]:visible',
        },
        {
            title:     "finish",
            waitFor:   '.oe_website_sale:contains("Thank you for your order")',
        }
    ]
});

});
<|endoftext|>