<commit_before>'use strict';

// Change this to `true` to post to the web
// when you run this script locally.
var canPostFromLocal = false;

// Load libraries.
var _ = require('underscore');
var wordfilter = require('wordfilter');
var SC = require('node-soundcloud');
var Masto = require('mastodon');
var Twit = require('twit');

// Are we on production? Check if an important environment variable exists.
function isProduction () {
  return (
    typeof(process.env.MASTODON_ACCESS_TOKEN) !== 'undefined' &&
    typeof(process.env.TWITTER_CONSUMER_KEY) !== 'undefined'
  );
}

// Use environment variables if we're on production, config files if we're local.
if (isProduction()) {
  var yandexKey = process.env.YANDEX_KEY;

  var twitter = new Twit({
    consumer_key: process.env.TWITTER_CONSUMER_KEY,
    consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
    access_token: process.env.TWITTER_ACCESS_TOKEN,
    access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
  });

  var mastodon = new Masto({
    access_token: process.env.MASTODON_ACCESS_TOKEN,
    api_url: 'https://botsin.space/api/v1/'
  });

  SC.init({
    id:     process.env.SOUNDCLOUD_CLIENT_ID,
    secret: process.env.SOUNDCLOUD_SECRET
  });
}
else {
  var yandexConfig = require('./config/yandex-config.js'),
      yandexKey    = yandexConfig.key;

  var mastodon = new Masto(require('./config/mastodon-config.js'));
  var twitter  = new Twit(require('./config/twitter-config.js'));

  SC.init(require('./config/soundcloud-config.js'));
}

// Load Yandex Translation API library.
var translate = require('yandex-translate')(yandexKey);

// Execute once upon initialization.
createAndPost();

// The main process. Get a useable comment and post it or try again.
function createAndPost () {

  getComment()
    .then(function (results) {
      post(results);
    })
    .catch(function (error) {
      console.log('ERROR:', error);
      createAndPost();
    });

}

// Get a random comment and see if it's usable.
function getComment () {

  return new Promise (function (resolve, reject) {

    console.log('\n---\n');

    // Pick a random comment. The API doesn't provide for this, but SoundCloud
    // comment IDs are sequential! There are a lot of missing comments (deleted
    // spam, etc.), but this has about a 40-50% success rate at finding an
    // actual comment, which is pretty good!
    var randomCommentID = String(_.random(100000000, 500000000));

    // Query the SoundCloud API and filter the results.
    SC.get('/comments/' + randomCommentID, function(error, comment) {

      // console.log('\n\nFULL API RESPONSE:\n\n', comment)

      console.log('LOOKING FOR A COMMENT AT ID #' + randomCommentID + '…');

      if (typeof(error) !== 'undefined') {
        reject('Comment does not exist at this ID anymore.');
      }
      else {
        var comment = comment.body.trim();

        console.log('FOUND A COMMENT:', comment);

        console.log('ANALYZING: Checking if too short…');

        if (comment.length < 1) {
          reject('Comment is too short.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if too long…');

        if (comment.length > 141) {
          reject('Comment is too long');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking for bad words…');

        if (wordfilter.blacklisted(comment)) {
          reject('Comment is a reply, contains a bad word, or looks like spam.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if English…');

        translate.detect(comment, function (error, result) {
          if (result.lang === 'en') {
            console.log('\tOK!');

            console.log('SUCCESS: All checks passed! Comment is useable!');

            resolve(comment);
          }
          else if (typeof(error) !== 'undefined') {
            reject(error);
          }
          else {
            reject('Comment is not in English.')
          }
        })

      }

    });
  });
}

function post (thePostToPost) {
  if (typeof(thePostToPost) !== 'undefined') {
    console.log('NOW ATTEMPTING TO POST:', thePostToPost);

    // Twitter.
    if (isProduction() || canPostFromLocal) {
      twitter.post('statuses/update', { status: thePostToPost }, function (error) {
        if (error) {
          console.log('ERROR POSTING:', error);
        }
        else {
          console.log('SUCCESSFULLY POSTED TO TWITTER!');
        }
      });

      // Mastodon.
      mastodon.post('statuses', { status: thePostToPost }, function (error) {
        if (error) {
          console.log('ERROR POSTING:', error);
        }
        else {
          console.log('SUCCESSFULLY POSTED TO MASTODON!');
        }
      });
    }
  }
  else {
    console.log('ERROR: No comment was fetched!');
  }
}

// Post on a regular schedule. 6 times a day means every 4 hours. Note that
// because Heroku cycles dynos once per day, the bot's schedule will not be
// regular: https://devcenter.heroku.com/articles/how-heroku-works#dyno-manager
if (isProduction()) {
  var millisecondsInDay = 1000 * 60 * 60 * 24;
  var timesToPostPerDay = 6;
  var postInterval = millisecondsInDay / timesToPostPerDay;

  setInterval(function () {
    try {
      createAndPost();
    }
    catch (error) {
      console.log('POSTING UNSUCCESSFUL!', error);
    }
  }, postInterval);
}

///

// Additional filters.
wordfilter.addWords([

  // To get a better set of comments, I'm filtering out a lot of things.
  // The lists below are used in addition to the default set:
  // https://github.com/dariusk/wordfilter/blob/master/lib/badwords.json

  // Promotion, spam, link pollution.
  'follow',
  'listen to my',
  'check out',
  'check my',
  'subscribe',
  'channel',
  'my cloud',
  'add me',
  'profile',
  'premiere',
  'promo',
  'app',
  'repost',
  'posted',
  'full support',
  'fully support',

  'soundcloud',
  'facebook',
  'twitter',
  'youtube',
  'instagram',
  'blog',

  'free',
  'download',

  // Traditional spam.
  'sex',
  'cam',
  'dollars',
  'money',
  'fans',
  'plays',
  'get play',
  'get fan',

  // URLs.
  'http',
  'www',
  '.co',
  '.net',

  // Replies, SoundCloud links, tags, etc.
  'user-',
  '\@',
  '\n',
  '_',
  '#',

  // A few curses and bad words.
  'fuck',
  'rape',
  'raping',
  'gay',
  'nig'

]);
<commit_msg>More bad words.<commit_after>'use strict';

// Change this to `true` to post to the web
// when you run this script locally.
var canPostFromLocal = false;

// Load libraries.
var _ = require('underscore');
var wordfilter = require('wordfilter');
var SC = require('node-soundcloud');
var Masto = require('mastodon');
var Twit = require('twit');

// Are we on production? Check if an important environment variable exists.
function isProduction () {
  return (
    typeof(process.env.MASTODON_ACCESS_TOKEN) !== 'undefined' &&
    typeof(process.env.TWITTER_CONSUMER_KEY) !== 'undefined'
  );
}

// Use environment variables if we're on production, config files if we're local.
if (isProduction()) {
  var yandexKey = process.env.YANDEX_KEY;

  var twitter = new Twit({
    consumer_key: process.env.TWITTER_CONSUMER_KEY,
    consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
    access_token: process.env.TWITTER_ACCESS_TOKEN,
    access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
  });

  var mastodon = new Masto({
    access_token: process.env.MASTODON_ACCESS_TOKEN,
    api_url: 'https://botsin.space/api/v1/'
  });

  SC.init({
    id:     process.env.SOUNDCLOUD_CLIENT_ID,
    secret: process.env.SOUNDCLOUD_SECRET
  });
}
else {
  var yandexConfig = require('./config/yandex-config.js'),
      yandexKey    = yandexConfig.key;

  var mastodon = new Masto(require('./config/mastodon-config.js'));
  var twitter  = new Twit(require('./config/twitter-config.js'));

  SC.init(require('./config/soundcloud-config.js'));
}

// Load Yandex Translation API library.
var translate = require('yandex-translate')(yandexKey);

// Execute once upon initialization.
createAndPost();

// The main process. Get a useable comment and post it or try again.
function createAndPost () {

  getComment()
    .then(function (results) {
      post(results);
    })
    .catch(function (error) {
      console.log('ERROR:', error);
      createAndPost();
    });

}

// Get a random comment and see if it's usable.
function getComment () {

  return new Promise (function (resolve, reject) {

    console.log('\n---\n');

    // Pick a random comment. The API doesn't provide for this, but SoundCloud
    // comment IDs are sequential! There are a lot of missing comments (deleted
    // spam, etc.), but this has about a 40-50% success rate at finding an
    // actual comment, which is pretty good!
    var randomCommentID = String(_.random(100000000, 500000000));

    // Query the SoundCloud API and filter the results.
    SC.get('/comments/' + randomCommentID, function(error, comment) {

      // console.log('\n\nFULL API RESPONSE:\n\n', comment)

      console.log('LOOKING FOR A COMMENT AT ID #' + randomCommentID + '…');

      if (typeof(error) !== 'undefined') {
        reject('Comment does not exist at this ID anymore.');
      }
      else {
        var comment = comment.body.trim();

        console.log('FOUND A COMMENT:', comment);

        console.log('ANALYZING: Checking if too short…');

        if (comment.length < 1) {
          reject('Comment is too short.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if too long…');

        if (comment.length > 141) {
          reject('Comment is too long');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking for bad words…');

        if (wordfilter.blacklisted(comment)) {
          reject('Comment is a reply, contains a bad word, or looks like spam.');
          return;
        }

        console.log('\tOK!');

        console.log('ANALYZING: Checking if English…');

        translate.detect(comment, function (error, result) {
          if (result.lang === 'en') {
            console.log('\tOK!');

            console.log('SUCCESS: All checks passed! Comment is useable!');

            resolve(comment);
          }
          else if (typeof(error) !== 'undefined') {
            reject(error);
          }
          else {
            reject('Comment is not in English.')
          }
        })

      }

    });
  });
}

function post (thePostToPost) {
  if (typeof(thePostToPost) !== 'undefined') {
    console.log('NOW ATTEMPTING TO POST:', thePostToPost);

    // Twitter.
    if (isProduction() || canPostFromLocal) {
      twitter.post('statuses/update', { status: thePostToPost }, function (error) {
        if (error) {
          console.log('ERROR POSTING:', error);
        }
        else {
          console.log('SUCCESSFULLY POSTED TO TWITTER!');
        }
      });

      // Mastodon.
      mastodon.post('statuses', { status: thePostToPost }, function (error) {
        if (error) {
          console.log('ERROR POSTING:', error);
        }
        else {
          console.log('SUCCESSFULLY POSTED TO MASTODON!');
        }
      });
    }
  }
  else {
    console.log('ERROR: No comment was fetched!');
  }
}

// Post on a regular schedule. 6 times a day means every 4 hours. Note that
// because Heroku cycles dynos once per day, the bot's schedule will not be
// regular: https://devcenter.heroku.com/articles/how-heroku-works#dyno-manager
if (isProduction()) {
  var millisecondsInDay = 1000 * 60 * 60 * 24;
  var timesToPostPerDay = 6;
  var postInterval = millisecondsInDay / timesToPostPerDay;

  setInterval(function () {
    try {
      createAndPost();
    }
    catch (error) {
      console.log('POSTING UNSUCCESSFUL!', error);
    }
  }, postInterval);
}

///

// Additional filters.
wordfilter.addWords([

  // To get a better set of comments, I'm filtering out a lot of things.
  // The lists below are used in addition to the default set:
  // https://github.com/dariusk/wordfilter/blob/master/lib/badwords.json

  // Promotion, spam, link pollution.
  'follow',
  'listen to my',
  'check out',
  'check my',
  'subscribe',
  'channel',
  'my cloud',
  'add me',
  'profile',
  'premiere',
  'promo',
  'app',
  'repost',
  'posted',
  'full support',
  'fully support',

  'soundcloud',
  'facebook',
  'twitter',
  'youtube',
  'instagram',
  'blog',

  'free',
  'download',

  // Traditional spam.
  'sex',
  'cam',
  'dollars',
  'money',
  'fans',
  'plays',
  'get play',
  'get fan',

  // URLs.
  'http',
  'www',
  '.co',
  '.net',

  // Replies, SoundCloud links, tags, etc.
  'user-',
  '\@',
  '\n',
  '_',
  '#',

  // A few curses and bad words.
  'fuck',
  'rape',
  'raping',
  'gay',
  'nig',
  'igg',
  'icc'

]);
<|endoftext|>


<commit_before>"use strict";

var AnimationTimer = function(terrain) {
  var FRAMES_PER_SECONDS = 60;
  var MILLISECONDS_PER_TICK = 1000.0 / FRAMES_PER_SECONDS;

  var masterStartTime;
  var previousTickStartTime;
  var totalTicks = 0;

  var animator = new Animator(terrain);

  var tick = function(timestamp) {
    var tickStartTime = performance.now();

    if (!masterStartTime) {
      masterStartTime = tickStartTime;
    }

    var ticksToProcess = 1;
    var expectedFramesSoFar = Math.floor((tickStartTime - masterStartTime) / MILLISECONDS_PER_TICK);
    if (expectedFramesSoFar > totalTicks) {
      ticksToProcess += (expectedFramesSoFar - totalTicks);
    }

    for (var i = 0; i < ticksToProcess; i++) {
      animator.tick();
      totalTicks += 1;
    }

    renderer.render(scene, camera);

    previousTickStartTime = tickStartTime;

    requestAnimFrame(tick);
  };

  var animationTimer = {};

  animationTimer.start = function() {
    requestAnimFrame(tick);
  };

  return animationTimer;
};

var Animator = function(terrain) {
  var animator = {};

  var xPositionGenerator = new MotionGenerator();
  var zPositionGenerator = new LinearMotionGenerator(2, CityConfig.HALF_SCENE_DEPTH);

  animator.tick = function() {
    camera.position.x += xPositionGenerator.nextValue();
    camera.position.z += -zPositionGenerator.nextValue();

    var mapX = Coordinates.sceneXToMapX(camera.position.x);
    var mapZ = Coordinates.sceneZToMapZ(camera.position.z);

    var y = terrain.heightAtCoordinates(mapX, mapZ);
    camera.position.y = y + 0.5;
  };

  return animator;
};

var MotionGenerator = function() {
  var motionGenerator = {};

  motionGenerator.nextValue = function() {
    return 0.0;
  };

  motionGenerator.isComplete = function() {
    return false;
  };

  motionGenerator.onComplete = function() { };

  return motionGenerator;
};

var LinearMotionGenerator = function(delta, target) {
  var accumulator = 0.0;
  var isComplete = false;

  var linearMotionGenerator = new MotionGenerator();

  linearMotionGenerator.nextValue = function() {
    if (isComplete) {
      return 0.0;
    }

    accumulator += delta;

    if (accumulator <= target) {
      return delta;
    }
    else {
      isComplete = true;
      return (delta - (accumulator - target));
    }
  };

  linearMotionGenerator.isComplete = function() {
    return isComplete;
  };

  linearMotionGenerator.onComplete = function() { };

  return linearMotionGenerator;
};

var AnimationManager = function() {
  var TARGET_FRAME_WINDOW = 1000 / 60;   // 60 frames per second
  var FOWARD_MOTION_DELTA = 0.2;

  var animationManager = {};
  var animators = [];
  var previousFrameTimestamp;

  var deltaX;
  var deltaZ;
  var targetX;
  var targetGridX;
  var targetZ;
  var targetGridZ;
  var targetAngle = 0;
  var deltaAngle;

  var init = function() {
    var START_X = 0;
    var START_Y = 40;
    var START_Z = CityConfig.HALF_SCENE_DEPTH;

    camera.position.x = START_X;
    camera.position.y = START_Y;
    camera.position.z = START_Z;

    targetX = 0;
    targetZ = 0;
    deltaX = 0.0;
    deltaZ = -FOWARD_MOTION_DELTA;

    var framesUntilCityEdge = Math.abs(((CityConfig.HALF_TERRAIN_ROWS - CityConfig.HALF_BLOCK_ROWS) * CityConfig.BLOCK_AND_STREET_DEPTH) / deltaZ);
    var swoopDescentDelta = (START_Y - 0.5) / framesUntilCityEdge;

    var ramp = new rampAnimation(framesUntilCityEdge, -swoopDescentDelta, 0.5, 1000000);
    var forward = new forwardAnimation(targetX, deltaX, targetZ, deltaZ);
    animators = [ramp, forward];
  };

  var determineNextTargetPoint = function() {
    if (targetGridX == undefined || targetGridZ == undefined) {
      targetGridX = 0;
      targetGridZ = -1;
    }
    else {
      var oldTargetGridX = targetGridX;
      var oldTargetGridZ = targetGridZ;

      while (oldTargetGridX == targetGridX && oldTargetGridZ == targetGridZ) {
        if (deltaX == 0) {
          targetGridX = Math.floor(Math.random() * CityConfig.BLOCK_ROWS) - (CityConfig.BLOCK_ROWS / 2);
        }
        else if (deltaZ == 0) {
          targetGridZ = Math.floor(Math.random() * CityConfig.BLOCK_COLUMNS) - (CityConfig.BLOCK_COLUMNS / 2);
        }
      }
    }

    targetX = (targetGridX * CityConfig.BLOCK_WIDTH) + (targetGridX * CityConfig.STREET_WIDTH);
    targetZ = (targetGridZ * CityConfig.BLOCK_WIDTH) + (targetGridZ * CityConfig.STREET_WIDTH);

    deltaX = (camera.position.x == targetX) ? 0 : FOWARD_MOTION_DELTA;
    deltaZ = (camera.position.z == targetZ) ? 0 : FOWARD_MOTION_DELTA;
    deltaX *= (camera.position.x > targetX) ? -1 : 1;
    deltaZ *= (camera.position.z > targetZ) ? -1 : 1;
  };

  var determineRotationAngle = function() {
    var RIGHT_ANGLE = Math.PI / 2;
    var ROTATION_DELTA = 0.03;

    var oldTargetAngle = targetAngle;
    if (deltaX != 0 && oldTargetAngle == 0) {  // NORTH
      targetAngle = (deltaX < 0) ? targetAngle + RIGHT_ANGLE : targetAngle - RIGHT_ANGLE;
    }
    else if (deltaZ != 0 && (oldTargetAngle == RIGHT_ANGLE || oldTargetAngle == ((-3 * Math.PI) / 2))) {  // EAST
      targetAngle = (deltaZ < 0) ? targetAngle - RIGHT_ANGLE : targetAngle + RIGHT_ANGLE;
    }
    else if (deltaX != 0 && (oldTargetAngle == Math.PI || oldTargetAngle == Math.PI * -1)) {  // SOUTH
      targetAngle = (deltaX < 0) ? targetAngle - RIGHT_ANGLE : targetAngle + RIGHT_ANGLE;
    }
    else if (deltaZ != 0 && (oldTargetAngle == Math.PI / -2 || oldTargetAngle == (Math.PI * 3) / 2)) {  // WEST
      targetAngle = (deltaZ < 0) ? targetAngle + RIGHT_ANGLE : targetAngle - RIGHT_ANGLE;
    }

    deltaAngle = ROTATION_DELTA;
    deltaAngle *= (targetAngle > oldTargetAngle) ? 1 : -1;
  };

  animationManager.animate = function() {
    var currentTimestamp = new Date().getTime();
    var frameCount;

    if (animators.length === 0) {
      init();
    }

    if (previousFrameTimestamp === undefined) {
      frameCount = 1;
    }
    else {
      frameCount = Math.floor((currentTimestamp - previousFrameTimestamp) / TARGET_FRAME_WINDOW);
      if (frameCount < 1) {
        frameCount = 1;
      }
    }
    previousFrameTimestamp = currentTimestamp;

    var newAnimators = [];
    animators.forEach(function (animator) {
      animator.animate(frameCount);

      if (animator.finished === true) {
        if (animator instanceof rampAnimation) {
          newAnimators.push(new hoverAnimation());
        }
        else if (animator instanceof forwardAnimation) {
          determineNextTargetPoint();
          determineRotationAngle();
          newAnimators.push(new rotationAnimation(targetAngle, deltaAngle));
        }
        else if (animator instanceof rotationAnimation) {
          newAnimators.push(new forwardAnimation(targetX, deltaX, targetZ, deltaZ));
        }
        else if (animator instanceof hoverAnimation) {
          newAnimators.push(new hoverAnimation());
        }
      }
      else {
        newAnimators.push(animator);
      }
    });
    animators = newAnimators;

    var mapX = Coordinates.sceneXToMapX(camera.position.x);
    var mapZ = Coordinates.sceneZToMapZ(camera.position.z);

    var y = terrain.heightAtCoordinates(mapX, mapZ);
    camera.position.y = Math.max(camera.position.y, y + 0.5);

    renderer.render(scene, camera);
    requestAnimFrame(animationManager.animate);
  };

  return animationManager;
};

function forwardAnimation(targetX, deltaX, targetZ, deltaZ) {
  this.targetX = targetX;
  this.deltaX = deltaX;
  this.targetZ = targetZ;
  this.deltaZ = deltaZ;
  this.finished = false;
}

forwardAnimation.prototype.animate = function(frameCount) {
  camera.position.x += this.deltaX * frameCount;
  camera.position.z += this.deltaZ * frameCount;

  if ((this.deltaX < 0 && camera.position.x < this.targetX) || (this.deltaX > 0 && camera.position.x > this.targetX) ||
      (this.deltaZ < 0 && camera.position.z < this.targetZ) || (this.deltaZ > 0 && camera.position.z > this.targetZ)) {
    camera.position.x = this.targetX;
    camera.position.z = this.targetZ;

    this.finished = true;
  }
}

function rotationAnimation(targetAngle, deltaAngle) {
  this.targetAngle = targetAngle;
  this.deltaAngle = deltaAngle;
  this.finished = false;
}

rotationAnimation.prototype.animate = function(frameCount) {
  camera.rotation.y += this.deltaAngle * frameCount;

  if ((this.deltaAngle < 0 && camera.rotation.y <= this.targetAngle) || (this.deltaAngle > 0 && camera.rotation.y >= this.targetAngle)) {
    if (this.targetAngle >= Math.PI * 2 || this.targetAngle <= Math.PI * -2) {
      this.targetAngle = 0;
    }

    camera.rotation.y = this.targetAngle;
    this.finished = true;
  }
}

function rampAnimation(frameDistance, deltaY, minHeight, maxHeight) {
  this.ticks = 0;
  this.frameDistance = frameDistance;
  this.deltaY = deltaY;
  this.minHeight = minHeight;
  this.maxHeight = maxHeight;
  this.finished = false;
}

rampAnimation.prototype.animate = function(frameCount) {
  if (camera.position.y >= this.minHeight && camera.position.y <= this.maxHeight) {
    camera.position.y += this.deltaY * frameCount;
  }
  if (camera.position.y < this.minHeight) {
    camera.position.y = this.minHeight;
  }
  if (camera.position.y > this.maxHeight) {
    camera.position.y = this.maxHeight;
  }

  this.ticks += frameCount;
  if (this.ticks > this.frameDistance) {
    this.finished = true;
  }
}

function hoverAnimation() {
  var frameDistance = (Math.random() * 300) + 300;
  var deltaY = (camera.position.y > 0.5) ? -0.05 : 0.05;

  this.rampAnimation = new rampAnimation(frameDistance, deltaY, 0.5, 15);
  this.finished = false;
}

hoverAnimation.prototype.animate = function(frameCount) {
  this.rampAnimation.animate(frameCount);
  this.finished = this.rampAnimation.finished;
}
<commit_msg>Fixing initial swoop to touch down at correct height<commit_after>"use strict";

var AnimationTimer = function(terrain) {
  var FRAMES_PER_SECONDS = 60;
  var MILLISECONDS_PER_TICK = 1000.0 / FRAMES_PER_SECONDS;

  var masterStartTime;
  var previousTickStartTime;
  var totalTicks = 0;

  var animator = new Animator(terrain);

  var tick = function(timestamp) {
    var tickStartTime = performance.now();

    if (!masterStartTime) {
      masterStartTime = tickStartTime;
    }

    var ticksToProcess = 1;
    var expectedFramesSoFar = Math.floor((tickStartTime - masterStartTime) / MILLISECONDS_PER_TICK);
    if (expectedFramesSoFar > totalTicks) {
      ticksToProcess += (expectedFramesSoFar - totalTicks);
    }

    for (var i = 0; i < ticksToProcess; i++) {
      animator.tick();
      totalTicks += 1;
    }

    renderer.render(scene, camera);

    previousTickStartTime = tickStartTime;

    requestAnimFrame(tick);
  };

  var animationTimer = {};

  animationTimer.start = function() {
    requestAnimFrame(tick);
  };

  return animationTimer;
};

var Animator = function(terrain) {
  var animator = {};

  var xPositionGenerator = new MotionGenerator();
  var zPositionGenerator = new LinearMotionGenerator(2, CityConfig.HALF_SCENE_DEPTH);

  animator.tick = function() {
    camera.position.x += xPositionGenerator.nextValue();
    camera.position.z += -zPositionGenerator.nextValue();

    var mapX = Coordinates.sceneXToMapX(camera.position.x);
    var mapZ = Coordinates.sceneZToMapZ(camera.position.z);

    var y = terrain.heightAtCoordinates(mapX, mapZ);
    camera.position.y = y + 0.5;
  };

  return animator;
};

var MotionGenerator = function() {
  var motionGenerator = {};

  motionGenerator.nextValue = function() {
    return 0.0;
  };

  motionGenerator.isComplete = function() {
    return false;
  };

  motionGenerator.onComplete = function() { };

  return motionGenerator;
};

var LinearMotionGenerator = function(delta, target) {
  var accumulator = 0.0;
  var isComplete = false;

  var linearMotionGenerator = new MotionGenerator();

  linearMotionGenerator.nextValue = function() {
    if (isComplete) {
      return 0.0;
    }

    accumulator += delta;

    if (accumulator <= target) {
      return delta;
    }
    else {
      isComplete = true;
      return (delta - (accumulator - target));
    }
  };

  linearMotionGenerator.isComplete = function() {
    return isComplete;
  };

  linearMotionGenerator.onComplete = function() { };

  return linearMotionGenerator;
};

var AnimationManager = function() {
  var TARGET_FRAME_WINDOW = 1000 / 60;   // 60 frames per second
  var FOWARD_MOTION_DELTA = 0.2;

  var animationManager = {};
  var animators = [];
  var previousFrameTimestamp;

  var deltaX;
  var deltaZ;
  var targetX;
  var targetGridX;
  var targetZ;
  var targetGridZ;
  var targetAngle = 0;
  var deltaAngle;

  var init = function() {
    var START_X = 0;
    var START_Y = 40;
    var START_Z = CityConfig.HALF_SCENE_DEPTH;

    camera.position.x = START_X;
    camera.position.y = START_Y;
    camera.position.z = START_Z;

    targetX = 0;
    targetZ = 0;
    deltaX = 0.0;
    deltaZ = -FOWARD_MOTION_DELTA;

    var distanceToCityEdge = Math.abs(START_Z - ((CityConfig.BLOCK_ROWS * CityConfig.BLOCK_AND_STREET_DEPTH) / 2));
    var framesUntilCityEdge = Math.abs(distanceToCityEdge / deltaZ);
    var terrainHeightAtTouchdown = terrain.heightAtCoordinates(0.0, CityConfig.HALF_BLOCK_ROWS) + 0.5;
    var swoopDescentDelta = (START_Y - terrainHeightAtTouchdown) / framesUntilCityEdge;

    var ramp = new rampAnimation(framesUntilCityEdge, -swoopDescentDelta, terrainHeightAtTouchdown + 0.5, 1000000);
    var forward = new forwardAnimation(targetX, deltaX, targetZ, deltaZ);
    animators = [ramp, forward];
  };

  var determineNextTargetPoint = function() {
    if (targetGridX == undefined || targetGridZ == undefined) {
      targetGridX = 0;
      targetGridZ = -1;
    }
    else {
      var oldTargetGridX = targetGridX;
      var oldTargetGridZ = targetGridZ;

      while (oldTargetGridX == targetGridX && oldTargetGridZ == targetGridZ) {
        if (deltaX == 0) {
          targetGridX = Math.floor(Math.random() * CityConfig.BLOCK_ROWS) - (CityConfig.BLOCK_ROWS / 2);
        }
        else if (deltaZ == 0) {
          targetGridZ = Math.floor(Math.random() * CityConfig.BLOCK_COLUMNS) - (CityConfig.BLOCK_COLUMNS / 2);
        }
      }
    }

    targetX = (targetGridX * CityConfig.BLOCK_WIDTH) + (targetGridX * CityConfig.STREET_WIDTH);
    targetZ = (targetGridZ * CityConfig.BLOCK_WIDTH) + (targetGridZ * CityConfig.STREET_WIDTH);

    deltaX = (camera.position.x == targetX) ? 0 : FOWARD_MOTION_DELTA;
    deltaZ = (camera.position.z == targetZ) ? 0 : FOWARD_MOTION_DELTA;
    deltaX *= (camera.position.x > targetX) ? -1 : 1;
    deltaZ *= (camera.position.z > targetZ) ? -1 : 1;
  };

  var determineRotationAngle = function() {
    var RIGHT_ANGLE = Math.PI / 2;
    var ROTATION_DELTA = 0.03;

    var oldTargetAngle = targetAngle;
    if (deltaX != 0 && oldTargetAngle == 0) {  // NORTH
      targetAngle = (deltaX < 0) ? targetAngle + RIGHT_ANGLE : targetAngle - RIGHT_ANGLE;
    }
    else if (deltaZ != 0 && (oldTargetAngle == RIGHT_ANGLE || oldTargetAngle == ((-3 * Math.PI) / 2))) {  // EAST
      targetAngle = (deltaZ < 0) ? targetAngle - RIGHT_ANGLE : targetAngle + RIGHT_ANGLE;
    }
    else if (deltaX != 0 && (oldTargetAngle == Math.PI || oldTargetAngle == Math.PI * -1)) {  // SOUTH
      targetAngle = (deltaX < 0) ? targetAngle - RIGHT_ANGLE : targetAngle + RIGHT_ANGLE;
    }
    else if (deltaZ != 0 && (oldTargetAngle == Math.PI / -2 || oldTargetAngle == (Math.PI * 3) / 2)) {  // WEST
      targetAngle = (deltaZ < 0) ? targetAngle + RIGHT_ANGLE : targetAngle - RIGHT_ANGLE;
    }

    deltaAngle = ROTATION_DELTA;
    deltaAngle *= (targetAngle > oldTargetAngle) ? 1 : -1;
  };

  animationManager.animate = function() {
    var currentTimestamp = new Date().getTime();
    var frameCount;

    if (animators.length === 0) {
      init();
    }

    if (previousFrameTimestamp === undefined) {
      frameCount = 1;
    }
    else {
      frameCount = Math.floor((currentTimestamp - previousFrameTimestamp) / TARGET_FRAME_WINDOW);
      if (frameCount < 1) {
        frameCount = 1;
      }
    }
    previousFrameTimestamp = currentTimestamp;

    var newAnimators = [];
    animators.forEach(function (animator) {
      animator.animate(frameCount);

      if (animator.finished === true) {
        if (animator instanceof rampAnimation) {
          newAnimators.push(new hoverAnimation());
        }
        else if (animator instanceof forwardAnimation) {
          determineNextTargetPoint();
          determineRotationAngle();
          newAnimators.push(new rotationAnimation(targetAngle, deltaAngle));
        }
        else if (animator instanceof rotationAnimation) {
          newAnimators.push(new forwardAnimation(targetX, deltaX, targetZ, deltaZ));
        }
        else if (animator instanceof hoverAnimation) {
          newAnimators.push(new hoverAnimation());
        }
      }
      else {
        newAnimators.push(animator);
      }
    });
    animators = newAnimators;

    var mapX = Coordinates.sceneXToMapX(camera.position.x);
    var mapZ = Coordinates.sceneZToMapZ(camera.position.z);

    var y = terrain.heightAtCoordinates(mapX, mapZ);
    camera.position.y = Math.max(camera.position.y, y + 0.5);

    renderer.render(scene, camera);
    requestAnimFrame(animationManager.animate);
  };

  return animationManager;
};

function forwardAnimation(targetX, deltaX, targetZ, deltaZ) {
  this.targetX = targetX;
  this.deltaX = deltaX;
  this.targetZ = targetZ;
  this.deltaZ = deltaZ;
  this.finished = false;
}

forwardAnimation.prototype.animate = function(frameCount) {
  camera.position.x += this.deltaX * frameCount;
  camera.position.z += this.deltaZ * frameCount;

  if ((this.deltaX < 0 && camera.position.x < this.targetX) || (this.deltaX > 0 && camera.position.x > this.targetX) ||
      (this.deltaZ < 0 && camera.position.z < this.targetZ) || (this.deltaZ > 0 && camera.position.z > this.targetZ)) {
    camera.position.x = this.targetX;
    camera.position.z = this.targetZ;

    this.finished = true;
  }
}

function rotationAnimation(targetAngle, deltaAngle) {
  this.targetAngle = targetAngle;
  this.deltaAngle = deltaAngle;
  this.finished = false;
}

rotationAnimation.prototype.animate = function(frameCount) {
  camera.rotation.y += this.deltaAngle * frameCount;

  if ((this.deltaAngle < 0 && camera.rotation.y <= this.targetAngle) || (this.deltaAngle > 0 && camera.rotation.y >= this.targetAngle)) {
    if (this.targetAngle >= Math.PI * 2 || this.targetAngle <= Math.PI * -2) {
      this.targetAngle = 0;
    }

    camera.rotation.y = this.targetAngle;
    this.finished = true;
  }
}

function rampAnimation(frameDistance, deltaY, minHeight, maxHeight) {
  this.ticks = 0;
  this.frameDistance = frameDistance;
  this.deltaY = deltaY;
  this.minHeight = minHeight;
  this.maxHeight = maxHeight;
  this.finished = false;
}

rampAnimation.prototype.animate = function(frameCount) {
  if (camera.position.y >= this.minHeight && camera.position.y <= this.maxHeight) {
    camera.position.y += this.deltaY * frameCount;
  }
  if (camera.position.y < this.minHeight) {
    camera.position.y = this.minHeight;
  }
  if (camera.position.y > this.maxHeight) {
    camera.position.y = this.maxHeight;
  }

  this.ticks += frameCount;
  if (this.ticks > this.frameDistance) {
    this.finished = true;
  }
}

function hoverAnimation() {
  var frameDistance = (Math.random() * 300) + 300;
  var deltaY = (camera.position.y > 0.5) ? -0.05 : 0.05;

  this.rampAnimation = new rampAnimation(frameDistance, deltaY, 0.5, 15);
  this.finished = false;
}

hoverAnimation.prototype.animate = function(frameCount) {
  this.rampAnimation.animate(frameCount);
  this.finished = this.rampAnimation.finished;
}
<|endoftext|>


<commit_before>(function(){

	function __loadScriptFile(fileName){
		var node = document.createElement("script");
		node.type = "text/javascript";
		node.src = fileName + "?nocache="+(+new Date());
		document.body.appendChild(node);
	}

	// Currently we are using embed.toJson() and need the "vodafone-apps-manager2.2" version
	// for those devices that have no JSON implementation natively.
	if (!window["JSON"]){
		__loadScriptFile("js/embedjs-vodafone-apps-manager2.2.js");
	} else {
		__loadScriptFile("js/embedjs-android.js");
	}
})();
<commit_msg>Fixed how embed is loaded.<commit_after>(function(){

	function __loadScriptFile(fileName){
		var node = document.createElement("script");
		node.type = "text/javascript";
		node.src = fileName + "?nocache="+(+new Date());
		document.body.appendChild(node);
	}

	// Currently we are using embed.toJson() and need the "vodafone-apps-manager2.2" version
	// for those devices that have no JSON implementation natively.
	if (!window["JSON"]){
		__loadScriptFile("embedjs-vodafone-apps-manager2.2.js");
	} else {
		__loadScriptFile("embedjs-android.js");
	}
})();
<|endoftext|>


<commit_before>const DEBUG = false;

var gLogger = {
  messages: [],
  log: function(aMessage) {
    this.messages.push(aMessage);
  },
  finalize: function() {
    if (!DEBUG)
      return;
    Cc['@mozilla.org/embedcomp/prompt-service;1']
      .getService(Ci.nsIPromptService)
      .alert(null, kID, this.messages.join('\n'));;
  }
}

const kCID  = Components.ID('{cd2892e0-87a8-11e2-9e96-0800200c9a66}');
const kID   = '@clear-code.com/force-addon-status/startup;1';
const kNAME = 'ForceAddonStatusStartupService';

const Cc = Components.classes;
const Ci = Components.interfaces;

const ObserverService = Cc['@mozilla.org/observer-service;1']
                         .getService(Ci.nsIObserverService);

Components.utils.import('resource://gre/modules/AddonManager.jsm');

Components.utils.import('resource://force-addon-status-modules/lib/prefs.js');
Components.utils.import('resource://force-addon-status-modules/lib/jsdeferred.js');

const BASE = 'extensions.force-addon-status@clear-code.com.';

function ForceAddonStatusStartupService() {
}
ForceAddonStatusStartupService.prototype = {
  classID          : kCID,
  contractID       : kID,
  classDescription : kNAME,

  observe : function(aSubject, aTopic, aData)
  {
    switch (aTopic)
    {
      case 'profile-after-change':
        ObserverService.addObserver(this, 'final-ui-startup', false);
        return;

      case 'final-ui-startup':
        ObserverService.removeObserver(this, 'final-ui-startup');
        var self = this;
        this.checkStatus()
          .next(function() {
            self.registerListener();
          });
        return;
    }
  },

  checkStatus : function()
  {
    if (this.checking)
      return;
    this.checking = true;

    var self = this;
    var changedCount = { value : 0 };
    return this.checkExtensionsStatus(changedCount)
      .next(function() {
        return self.checkPluginsStatus();
      })
      .next(function() {
        gLogger.log(changedCount.value + ' changed addon(s)');
        if (changedCount.value > 0) {
          self.restart();
        }
      })
      .error(function(error) {
        Components.utils.reportError(error);
      })
      .next(function() {
        self.checking = false;
      });
  },

  checkExtensionsStatus : function(aChangedCount)
  {
    gLogger.log('ForceAddonStatusStartupService::checkExtensionsStatus');
    var deferredTasks = [];

    var prefix = BASE + 'addons.';
    var keys = prefs.getDescendant(prefix);
    aChangedCount.value = aChangedCount.value || 0;
    keys.forEach(function(aKey) {
      if (!aKey)
        return;

      var id = aKey.replace(prefix, '');
      gLogger.log('  id ' + id);
      var newStatus;
      if (/\.status$/.test(id)) {
        newStatus = prefs.getPref(aKey);
        id = id.replace(/\.status$/, '');
        gLogger.log('  => ' + id);
      } else { // backward compatibility
        newStatus = prefs.getPref(aKey);
        if (newStatus)
          newStatus = 'enabled';
        else
          newStatus = 'disabled';
      }

      newStatus = String(newStatus).toLowerCase();

      gLogger.log('  newStatus ' + newStatus);

      var deferred = new Deferred();
      AddonManager.getAddonByID(id, function(aAddon) {
        if (!aAddon) {
          gLogger.log('  => not installed.');
          return deferred.call();
        }

        var shouldBeActive = newStatus.indexOf('enabled') > -1 || newStatus.indexOf('disabled') < 0;
        if (newStatus.indexOf('disabled') > -1)
          shouldBeActive = false;
        gLogger.log('  shouldBeActive ' + shouldBeActive);
        if (aAddon.isActive != shouldBeActive) {
          aAddon.userDisabled = !shouldBeActive;
          gLogger.log(aAddon.userDisabled ? ' => deactivated' : ' => activated');
          aChangedCount.value++;
        }

        var shouldUninstall = newStatus.indexOf('uninstall') > -1;
        gLogger.log('  shouldUninstall ' + shouldUninstall);
        var shouldGlobal = newStatus.indexOf('global') > -1;
        gLogger.log('  shouldGlobal ' + shouldGlobal);
        var isGlobal = aAddon.scope != AddonManager.SCOPE_PROFILE;
        gLogger.log('  isGlobal ' + isGlobal);
        if (shouldUninstall || shouldGlobal != isGlobal) {
          aAddon.uninstall();
          gLogger.log(' => uninstalled');
          aChangedCount.value++;
        }

        deferred.call();
      });
      deferredTasks.push(deferred);
    });

    if (deferredTasks.length > 0)
      return Deferred.parallel(deferredTasks);
    else
      return Deferred;
  },

  checkPluginsStatus : function()
  {
    var controlledPlugins = prefs.getChildren(BASE + 'plugins.');
    if (controlledPlugins.length == 0)
      return Deferred;

    var allPatterns = [];
    controlledPlugins = controlledPlugins.map(function(aEntryBaseKey) {
      var pattern = prefs.getPref(aEntryBaseKey + '.pattern');
      if (!pattern) return null;
      allPatterns.push(pattern);
      return {
        pattern :        new RegExp(pattern),
        shouldBeActive : prefs.getPref(aEntryBaseKey + '.status'),
        blocklisted :    prefs.getPref(aEntryBaseKey + '.blocklisted')
      };
    });
    controlledPlugins = controlledPlugins.filter(function(aControl) {
      return aControl;
    });

    allPatterns = new RegExp('(' + allPatterns.join('|') + ')');

    var PluginHost = Cc['@mozilla.org/plugin/host;1']
                      .getService(Ci.nsIPluginHost);
    var plugins = PluginHost.getPluginTags();
    plugins.forEach(function(aPluginTag) {
      if (!allPatterns.test(aPluginTag.name))
        return;

      controlledPlugins.some(function(aControl) {
        if (!aControl.pattern.test(aPluginTag.name))
          return false;
        if (aControl.shouldBeActive !== null &&
            aPluginTag.disabled !== !aControl.shouldBeActive) {
          aPluginTag.disabled = !aControl.shouldBeActive;
        }
        if (aControl.blocklisted !== null &&
            aPluginTag.blocklisted !== aControl.blocklisted) {
          aPluginTag.blocklisted = !!aControl.blocklisted;
        }
        return true;
      });
    });

    return Deferred;
  },

  registerListener : function()
  {
    AddonManager.addAddonListener(this);
  },
  deferredCheckStatus : function() {
    var self = this;
    return Deferred.next(function() {
      return self.checkStatus();
    });
  },
  onEnabling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onEnabled : function(aAddon) {
  },
  onDisabling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onDisabled : function(aAddon) {
  },
  onInstalling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onInstalled : function(aAddon) {
  },
  onUninstalling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onUninstalled : function(aAddon) {
  },
  onOperationCancelled : function(aAddon) {
    this.deferredCheckStatus();
  },
  onPropertyChanged : function(aAddon, aProperties) {
    this.deferredCheckStatus();
  },

  restart : function()
  {
    gLogger.log('try to restart');
    gLogger.log('  [restart (' + Ci.nsIAppStartup.eRestart + '), ' +
                   'force quit (' + Ci.nsIAppStartup.eForceQuit + ')]');
    gLogger.finalize;
    Cc['@mozilla.org/toolkit/app-startup;1']
      .getService(Ci.nsIAppStartup)
      .quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eForceQuit);
  },

  QueryInterface : function(aIID)
  {
    if(!aIID.equals(Ci.nsIObserver) &&
       !aIID.equals(Ci.nsISupports)) {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
    return this;
  }
};

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ForceAddonStatusStartupService]);
<commit_msg>Output log message as a dialog<commit_after>const DEBUG = false;

var gLogger = {
  messages: [],
  log: function(aMessage) {
    this.messages.push(aMessage);
  },
  finalize: function() {
    if (!DEBUG)
      return;
    Cc['@mozilla.org/embedcomp/prompt-service;1']
      .getService(Ci.nsIPromptService)
      .alert(null, kID, this.messages.join('\n'));;
  }
}

const kCID  = Components.ID('{cd2892e0-87a8-11e2-9e96-0800200c9a66}');
const kID   = '@clear-code.com/force-addon-status/startup;1';
const kNAME = 'ForceAddonStatusStartupService';

const Cc = Components.classes;
const Ci = Components.interfaces;

const ObserverService = Cc['@mozilla.org/observer-service;1']
                         .getService(Ci.nsIObserverService);

Components.utils.import('resource://gre/modules/AddonManager.jsm');

Components.utils.import('resource://force-addon-status-modules/lib/prefs.js');
Components.utils.import('resource://force-addon-status-modules/lib/jsdeferred.js');

const BASE = 'extensions.force-addon-status@clear-code.com.';

function ForceAddonStatusStartupService() {
}
ForceAddonStatusStartupService.prototype = {
  classID          : kCID,
  contractID       : kID,
  classDescription : kNAME,

  observe : function(aSubject, aTopic, aData)
  {
    switch (aTopic)
    {
      case 'profile-after-change':
        ObserverService.addObserver(this, 'final-ui-startup', false);
        return;

      case 'final-ui-startup':
        ObserverService.removeObserver(this, 'final-ui-startup');
        var self = this;
        this.checkStatus()
          .next(function() {
            self.registerListener();
          });
        return;
    }
  },

  checkStatus : function()
  {
    if (this.checking)
      return;
    this.checking = true;

    var self = this;
    var changedCount = { value : 0 };
    return this.checkExtensionsStatus(changedCount)
      .next(function() {
        return self.checkPluginsStatus();
      })
      .next(function() {
        gLogger.log(changedCount.value + ' changed addon(s)');
        if (changedCount.value > 0) {
          self.restart();
        }
      })
      .error(function(error) {
        Components.utils.reportError(error);
      })
      .next(function() {
        self.checking = false;
      });
  },

  checkExtensionsStatus : function(aChangedCount)
  {
    gLogger.log('ForceAddonStatusStartupService::checkExtensionsStatus');
    var deferredTasks = [];

    var prefix = BASE + 'addons.';
    var keys = prefs.getDescendant(prefix);
    aChangedCount.value = aChangedCount.value || 0;
    keys.forEach(function(aKey) {
      if (!aKey)
        return;

      var id = aKey.replace(prefix, '');
      gLogger.log('  id ' + id);
      var newStatus;
      if (/\.status$/.test(id)) {
        newStatus = prefs.getPref(aKey);
        id = id.replace(/\.status$/, '');
        gLogger.log('  => ' + id);
      } else { // backward compatibility
        newStatus = prefs.getPref(aKey);
        if (newStatus)
          newStatus = 'enabled';
        else
          newStatus = 'disabled';
      }

      newStatus = String(newStatus).toLowerCase();

      gLogger.log('  newStatus ' + newStatus);

      var deferred = new Deferred();
      AddonManager.getAddonByID(id, function(aAddon) {
        if (!aAddon) {
          gLogger.log('  => not installed.');
          return deferred.call();
        }

        var shouldBeActive = newStatus.indexOf('enabled') > -1 || newStatus.indexOf('disabled') < 0;
        if (newStatus.indexOf('disabled') > -1)
          shouldBeActive = false;
        gLogger.log('  shouldBeActive ' + shouldBeActive);
        if (aAddon.isActive != shouldBeActive) {
          aAddon.userDisabled = !shouldBeActive;
          gLogger.log(aAddon.userDisabled ? ' => deactivated' : ' => activated');
          aChangedCount.value++;
        }

        var shouldUninstall = newStatus.indexOf('uninstall') > -1;
        gLogger.log('  shouldUninstall ' + shouldUninstall);
        var shouldGlobal = newStatus.indexOf('global') > -1;
        gLogger.log('  shouldGlobal ' + shouldGlobal);
        var isGlobal = aAddon.scope != AddonManager.SCOPE_PROFILE;
        gLogger.log('  isGlobal ' + isGlobal);
        if (shouldUninstall || shouldGlobal != isGlobal) {
          aAddon.uninstall();
          gLogger.log(' => uninstalled');
          aChangedCount.value++;
        }

        deferred.call();
      });
      deferredTasks.push(deferred);
    });

    if (deferredTasks.length > 0)
      return Deferred.parallel(deferredTasks);
    else
      return Deferred;
  },

  checkPluginsStatus : function()
  {
    var controlledPlugins = prefs.getChildren(BASE + 'plugins.');
    if (controlledPlugins.length == 0)
      return Deferred;

    var allPatterns = [];
    controlledPlugins = controlledPlugins.map(function(aEntryBaseKey) {
      var pattern = prefs.getPref(aEntryBaseKey + '.pattern');
      if (!pattern) return null;
      allPatterns.push(pattern);
      return {
        pattern :        new RegExp(pattern),
        shouldBeActive : prefs.getPref(aEntryBaseKey + '.status'),
        blocklisted :    prefs.getPref(aEntryBaseKey + '.blocklisted')
      };
    });
    controlledPlugins = controlledPlugins.filter(function(aControl) {
      return aControl;
    });

    allPatterns = new RegExp('(' + allPatterns.join('|') + ')');

    var PluginHost = Cc['@mozilla.org/plugin/host;1']
                      .getService(Ci.nsIPluginHost);
    var plugins = PluginHost.getPluginTags();
    plugins.forEach(function(aPluginTag) {
      if (!allPatterns.test(aPluginTag.name))
        return;

      controlledPlugins.some(function(aControl) {
        if (!aControl.pattern.test(aPluginTag.name))
          return false;
        if (aControl.shouldBeActive !== null &&
            aPluginTag.disabled !== !aControl.shouldBeActive) {
          aPluginTag.disabled = !aControl.shouldBeActive;
        }
        if (aControl.blocklisted !== null &&
            aPluginTag.blocklisted !== aControl.blocklisted) {
          aPluginTag.blocklisted = !!aControl.blocklisted;
        }
        return true;
      });
    });

    return Deferred;
  },

  registerListener : function()
  {
    AddonManager.addAddonListener(this);
  },
  deferredCheckStatus : function() {
    var self = this;
    return Deferred.next(function() {
      return self.checkStatus();
    });
  },
  onEnabling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onEnabled : function(aAddon) {
  },
  onDisabling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onDisabled : function(aAddon) {
  },
  onInstalling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onInstalled : function(aAddon) {
  },
  onUninstalling : function(aAddon, aNeedsRestart) {
    this.deferredCheckStatus();
  },
  onUninstalled : function(aAddon) {
  },
  onOperationCancelled : function(aAddon) {
    this.deferredCheckStatus();
  },
  onPropertyChanged : function(aAddon, aProperties) {
    this.deferredCheckStatus();
  },

  restart : function()
  {
    gLogger.log('try to restart');
    gLogger.log('  [restart (' + Ci.nsIAppStartup.eRestart + '), ' +
                   'force quit (' + Ci.nsIAppStartup.eForceQuit + ')]');
    gLogger.finalize();
    Cc['@mozilla.org/toolkit/app-startup;1']
      .getService(Ci.nsIAppStartup)
      .quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eForceQuit);
  },

  QueryInterface : function(aIID)
  {
    if(!aIID.equals(Ci.nsIObserver) &&
       !aIID.equals(Ci.nsISupports)) {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
    return this;
  }
};

Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ForceAddonStatusStartupService]);
<|endoftext|>


<commit_before>"use strict";

// Local tweet test override.
var canTweetFromLocal = false;

// Load libraries.
var _ = require('underscore');
var wordfilter = require('wordfilter');
var languageDetector = require('cld');
var SC = require('node-soundcloud');
var Twit = require('twit');

// Are we on production? Check if an important environment variable exists.
function isProduction () {
  return (typeof(process.env.TWITTER_CONSUMER_KEY) !== 'undefined');
}

// Use environment variables if we're on production, config files if we're local.
if (isProduction()) {
  SC.init({
    id:     process.env.SOUNDCLOUD_CLIENT_ID,
    secret: process.env.SOUNDCLOUD_SECRET
  });

  var twitter = new Twit({
    consumer_key: process.env.TWITTER_CONSUMER_KEY,
    consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
    access_token: process.env.TWITTER_ACCESS_TOKEN,
    access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
  });
}
else {
  SC.init(require('./soundcloud-config.js'));
  var twitter = new Twit(require('./twitter-config.js'));
}

// Get a handful of comments and choose one.
function makeAndPostTweet () {
  getRandomComment().then(function (results) {
    postTweet(results);
  }).catch(function (err) {
    console.log('ERROR:', err);
    makeAndPostTweet();
  });
}

// Pick a random comment. The API doesn't provide for this, but SoundCloud
// comment IDs are sequential! There are a lot of missing comments (deleted
// spam, etc.), but this has about a 40-50% success rate at finding an actual
// comment, which is pretty good!
function getRandomCommentID () {
  return String(_.random(100000000, 300000000));
}

function getRandomComment () {

  return new Promise (function (resolve, reject) {

    console.log('\n---\n');

    SC.get('/comments/' + getRandomCommentID(), function(err, comment) {
      // console.log('\n\nFULL RESPONSE:\n\n', comment)

      if (typeof(err) !== 'undefined') {
        reject('Comment does not exist.');
      }
      else {
        var comment = comment.body.trim();

        console.log('FOUND A COMMENT:', comment);

        if (comment.length < 1) {
          reject('Comment is too short.');
          return;
        }

        else if (comment.length > 141) {
          reject('Comment is too long');
          return;
        }

        else if (!isEnglish(comment)) {
          reject('Comment is not in English.');
          return;
        }

        else if (wordfilter.blacklisted(comment)) {
          reject('Comment is a reply, contains a bad word, or looks like spam.');
          return;
        }

        else {
          console.log('COMMENT IS USEABLE!');
          resolve(comment);
        }
      }

    });

  });

}

function postTweet (tweet) {

  console.log('NOW TWEETING:', tweet);

  if (isProduction() || canTweetFromLocal) {
    twitter.post('statuses/update', { status: tweet }, function (error) {
      if (error) {
        console.log('ERROR POSTING TWEET:', error);
      }
    });
  }

}

// Tweet on a regular schedule. 8 times a day means every 3 hours. Note that
// Because Heroku cycles dynos once per day, the bot's schedule will not be
// regular: https://devcenter.heroku.com/articles/how-heroku-works#dyno-manager
if (isProduction()) {
  var timesToTweetPerDay = 8;

  setInterval(function () {
    try {
      postTweet();
    }
    catch (err) {
      console.log('PROCESS UNSUCCESSFUL!', err);
    }
  }, (1000 * 60 * 60 * 24) / timesToTweetPerDay);
}

// Go!
makeAndPostTweet();

///

// Is this English?
function isEnglish (string) {

  var options = {
    isHTML       : false,
    encodingHint : 'UTF8UTF8',
    tldHint      : 'com'
  };

  return languageDetector.detect(string, options, function(err, result) {

    if (!_.isEmpty(err)) {
      console.log('LANGUAGE DETECTION ERROR:', err.message);

      // If the language couldn't be detected, just show the tweet. Most of the
      // time this means it's slang or internet-speak and is fine to print.
      if (err.message === 'Failed to identify language') {
        console.log('LANGUAGE DETECTION OVERRIDE: Approving as English anyway, just for kicks.');

        return true;
      }
    }
    else {
      return (result.languages[0].name === 'ENGLISH');
    }

  });

}

// Additional filters.
wordfilter.addWords([

  // To get a better set of comments, I'm filtering out a lot of things.
  // The lists below are used in addition to the default set:
  // https://github.com/dariusk/wordfilter/blob/master/lib/badwords.json

  // Spam.
  'follow',
  'listen to my',
  'check out',
  'check my',
  'subscribe',
  'channel',
  'my cloud',

  'soundcloud',
  'facebook',
  'twitter'
  'youtube',
  'instagram',
  'blog',

  'http',
  'www',
  '.co',
  '.net',

  'sex',
  'cam',
  'dollars',

  'free',
  'download',

  // Replies, SoundCloud links, tags, etc.
  'user-',
  '\@',
  '\n',
  '_',
  '#',

  // A few curses.
  'fuck'

]);
<commit_msg>Fix missing comma in list of words to filter.<commit_after>"use strict";

// Local tweet test override.
var canTweetFromLocal = false;

// Load libraries.
var _ = require('underscore');
var wordfilter = require('wordfilter');
var languageDetector = require('cld');
var SC = require('node-soundcloud');
var Twit = require('twit');

// Are we on production? Check if an important environment variable exists.
function isProduction () {
  return (typeof(process.env.TWITTER_CONSUMER_KEY) !== 'undefined');
}

// Use environment variables if we're on production, config files if we're local.
if (isProduction()) {
  SC.init({
    id:     process.env.SOUNDCLOUD_CLIENT_ID,
    secret: process.env.SOUNDCLOUD_SECRET
  });

  var twitter = new Twit({
    consumer_key: process.env.TWITTER_CONSUMER_KEY,
    consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
    access_token: process.env.TWITTER_ACCESS_TOKEN,
    access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
  });
}
else {
  SC.init(require('./soundcloud-config.js'));
  var twitter = new Twit(require('./twitter-config.js'));
}

// Get a handful of comments and choose one.
function makeAndPostTweet () {
  getRandomComment().then(function (results) {
    postTweet(results);
  }).catch(function (err) {
    console.log('ERROR:', err);
    makeAndPostTweet();
  });
}

// Pick a random comment. The API doesn't provide for this, but SoundCloud
// comment IDs are sequential! There are a lot of missing comments (deleted
// spam, etc.), but this has about a 40-50% success rate at finding an actual
// comment, which is pretty good!
function getRandomCommentID () {
  return String(_.random(100000000, 300000000));
}

function getRandomComment () {

  return new Promise (function (resolve, reject) {

    console.log('\n---\n');

    SC.get('/comments/' + getRandomCommentID(), function(err, comment) {
      // console.log('\n\nFULL RESPONSE:\n\n', comment)

      if (typeof(err) !== 'undefined') {
        reject('Comment does not exist.');
      }
      else {
        var comment = comment.body.trim();

        console.log('FOUND A COMMENT:', comment);

        if (comment.length < 1) {
          reject('Comment is too short.');
          return;
        }

        else if (comment.length > 141) {
          reject('Comment is too long');
          return;
        }

        else if (!isEnglish(comment)) {
          reject('Comment is not in English.');
          return;
        }

        else if (wordfilter.blacklisted(comment)) {
          reject('Comment is a reply, contains a bad word, or looks like spam.');
          return;
        }

        else {
          console.log('COMMENT IS USEABLE!');
          resolve(comment);
        }
      }

    });

  });

}

function postTweet (tweet) {

  console.log('NOW TWEETING:', tweet);

  if (isProduction() || canTweetFromLocal) {
    twitter.post('statuses/update', { status: tweet }, function (error) {
      if (error) {
        console.log('ERROR POSTING TWEET:', error);
      }
    });
  }

}

// Tweet on a regular schedule. 8 times a day means every 3 hours. Note that
// Because Heroku cycles dynos once per day, the bot's schedule will not be
// regular: https://devcenter.heroku.com/articles/how-heroku-works#dyno-manager
if (isProduction()) {
  var timesToTweetPerDay = 8;

  setInterval(function () {
    try {
      postTweet();
    }
    catch (err) {
      console.log('PROCESS UNSUCCESSFUL!', err);
    }
  }, (1000 * 60 * 60 * 24) / timesToTweetPerDay);
}

// Go!
makeAndPostTweet();

///

// Is this English?
function isEnglish (string) {

  var options = {
    isHTML       : false,
    encodingHint : 'UTF8UTF8',
    tldHint      : 'com'
  };

  return languageDetector.detect(string, options, function(err, result) {

    if (!_.isEmpty(err)) {
      console.log('LANGUAGE DETECTION ERROR:', err.message);

      // If the language couldn't be detected, just show the tweet. Most of the
      // time this means it's slang or internet-speak and is fine to print.
      if (err.message === 'Failed to identify language') {
        console.log('LANGUAGE DETECTION OVERRIDE: Approving as English anyway, just for kicks.');

        return true;
      }
    }
    else {
      return (result.languages[0].name === 'ENGLISH');
    }

  });

}

// Additional filters.
wordfilter.addWords([

  // To get a better set of comments, I'm filtering out a lot of things.
  // The lists below are used in addition to the default set:
  // https://github.com/dariusk/wordfilter/blob/master/lib/badwords.json

  // Promotion.
  'follow',
  'listen to my',
  'check out',
  'check my',
  'subscribe',
  'channel',
  'my cloud',

  'soundcloud',
  'facebook',
  'twitter',
  'youtube',
  'instagram',
  'blog',

  'free',
  'download',

  // Spam.
  'sex',
  'cam',
  'dollars',

  // URLs.
  'http',
  'www',
  '.co',
  '.net',

  // Replies, SoundCloud links, tags, etc.
  'user-',
  '\@',
  '\n',
  '_',
  '#',

  // A few curses.
  'fuck'

]);
<|endoftext|>


<commit_before>const Canvas = require('canvas');

class CanvasConstructor {

    constructor(width, height) {
        this.canvas = new Canvas(width, height);
        this.context = this.canvas.getContext('2d');
    }

    /**
     * Restore the context changes.
     * @returns {CanvasConstructor}
     * @chainable
     */
    restore() {
        this.context.restore();
        return this;
    }

    /**
     * Add a rectangle.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @returns {CanvasConstructor}
     * @chainable
     */
    fillRect(x, y, width, height) {
        this.context.fillRect(x, y, width, height);
        return this;
    }

    /**
     * Add a text.
     * @param {string} text    The text to write.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addText(text, x, y) {
        this.context.fillText(text, x, y);
        return this;
    }

    /**
     * Add stroked text.
     * @param {string} text    The text to write.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addStrokeText(text, x, y) {
        this.context.strokeText(text, x, y);
        return this;
    }

    /**
     * Set a colour for the canvas' context.
     * @param {string} color A canvas' colour resolvable.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setStroke(color) {
        this.context.strokeStyle = color;
        return this;
    }

    /**
     * Add an image.
     * @param {Buffer} buffer  The image's buffer.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @param {Object} options Options.
     * @param {number} options.radius The radius for the new image.
     * @param {'round'|'bevel'} options.type   The type for the new image.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addImage(buffer, x, y, width, height, options = {}) {
        if (options.type !== null) {
            if (options.type === 'round') return this.addRoundImage(buffer, x, y, width, height, options.radius);
            if (options.type === 'bevel') return this.addBevelImage(buffer, x, y, width, height, options.radius);
        }
        const image = new Canvas.Image();
        image.onload = () => this.context.drawImage(image, x, y, width, height);
        image.src = buffer;
        return this;
    }

    /**
     * Add a round image.
     * @param {Buffer} buffer  The image's buffer.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @param {number} radius  The radius for the new image.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addRoundImage(buffer, x, y, width, height, radius) {
        this.createRoundClip(x + radius, y + radius, radius);
        return this.addImage(buffer, x, y, width, height, radius, { type: null });
    }

    /**
     * Add a beveled image.
     * @param {Buffer} buffer  The image's buffer.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @param {number} radius  The radius for the new image.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addBevelImage(buffer, x, y, width, height, radius) {
        this.createBeveledClip(x, y, width, height, radius);
        return this.addImage(buffer, x, y, width, height, radius, { type: null });
    }

    /**
     * Create a round clip.
     * @param {number} x       The position x in the center of the clip's circle.
     * @param {number} y       The position y in the center of the clip's circle.
     * @param {number} radius  The radius for the clip.
     * @returns {CanvasConstructor}
     * @chainable
     */
    createRoundClip(x, y, radius) {
        this.context.save();
        this.context.beginPath();
        this.context.arc(x, y, radius, 0, Math.PI * 2, false);
        this.context.clip();
        return this;
    }

    /**
     * Create a round clip.
     * @param {number} x       The position x to start drawing clip.
     * @param {number} y       The position y to start drawing clip.
     * @param {number} width   The width of clip.
     * @param {number} height  The heigth of clip.
     * @param {number} radius  The radius for clip's rounded borders.
     * @returns {CanvasConstructor}
     * @chainable
     */
    createBeveledClip(x, y, width, height, radius) {
        if (width > 0 && height > 0) {
            radius = Math.min(radius, width / 2, height / 2);
            this.context.beginPath();
            this.context.moveTo(x + radius, y);
            this.context.lineTo(x + width - radius, y);
            this.context.quadraticCurveTo(x + width, y, x + width, y + radius);
            this.context.lineTo(x + width, y + height - radius);
            this.context.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
            this.context.lineTo(x + radius, y + height);
            this.context.quadraticCurveTo(x, y + height, x, y + height - radius);
            this.context.lineTo(x, y + radius);
            this.context.quadraticCurveTo(x, y, x + radius, y);
            this.context.closePath();
            this.context.clip();
        }
        return this;
    }

    /**
     * Register a new font.
     * @param {string} path The path for the font.
     * @param {string} family The font's family name.
     * @returns {CanvasConstructor}
     * @chainable
     */
    registerTextFont(path, family) {
        Canvas.registerFont(path, { family });
        return this;
    }

    /**
     * Set a colour for the canvas' context.
     * @param {string} color A canvas' colour resolvable.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setColor(color) {
        this.context.fillStyle = color;
        return this;
    }

    /**
     * Change the font.
     * @param {string} font The font's name to set.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setTextFont(font) {
        this.context.font = font;
        return this;
    }

    /**
     * Change the font alignment.
     * @param {('left'|'center'|'right'|'justified')} align The font's alignment to set.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setTextAlign(align) {
        this.context.textAlign = align;
        return this;
    }

    /**
     * Set the shadow's blur.
     * @param {number} radius The shadow's blur radius to set.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setShadowBlur(radius) {
        this.context.shadowBlur = radius;
        return this;
    }

    /**
     * Set the shadow's colour.
     * @param {string} colour A canvas' colour resolvable to set as shadow's colour.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setShadowColour(colour) {
        this.context.shadowColor = colour;
        return this;
    }

    /**
     * Reset the canvas' shadows.
     * @returns {CanvasConstructor}
     * @chainable
     */
    resetShadows() {
        return this.setShadowBlur(0).setShadowColour('#000000');
    }

    /**
     * Render the canvas into a buffer.
     * @param {Object} options The render's options.
     * @returns {Buffer}
     */
    toBuffer(options) {
        return this.canvas.toBuffer(options);
    }

}

module.exports = CanvasConstructor;
<commit_msg>Added addFont method<commit_after>const Canvas = require('canvas');

class CanvasConstructor {

    constructor(width, height) {
        this.canvas = new Canvas(width, height);
        this.context = this.canvas.getContext('2d');
    }

    /**
     * Restore the context changes.
     * @returns {CanvasConstructor}
     * @chainable
     */
    restore() {
        this.context.restore();
        return this;
    }

    /**
     * Add a rectangle.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @returns {CanvasConstructor}
     * @chainable
     */
    fillRect(x, y, width, height) {
        this.context.fillRect(x, y, width, height);
        return this;
    }

    /**
     * Add a text.
     * @param {string} text    The text to write.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addText(text, x, y) {
        this.context.fillText(text, x, y);
        return this;
    }

    /**
     * Add stroked text.
     * @param {string} text    The text to write.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addStrokeText(text, x, y) {
        this.context.strokeText(text, x, y);
        return this;
    }

    /**
     * Set a colour for the canvas' context.
     * @param {string} color A canvas' colour resolvable.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setStroke(color) {
        this.context.strokeStyle = color;
        return this;
    }

    /**
     * Add an image.
     * @param {Buffer} buffer  The image's buffer.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @param {Object} options Options.
     * @param {number} options.radius The radius for the new image.
     * @param {'round'|'bevel'} options.type   The type for the new image.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addImage(buffer, x, y, width, height, options = {}) {
        if (options.type !== null) {
            if (options.type === 'round') return this.addRoundImage(buffer, x, y, width, height, options.radius);
            if (options.type === 'bevel') return this.addBevelImage(buffer, x, y, width, height, options.radius);
        }
        const image = new Canvas.Image();
        image.onload = () => this.context.drawImage(image, x, y, width, height);
        image.src = buffer;
        return this;
    }

    /**
     * Add a round image.
     * @param {Buffer} buffer  The image's buffer.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @param {number} radius  The radius for the new image.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addRoundImage(buffer, x, y, width, height, radius) {
        this.createRoundClip(x + radius, y + radius, radius);
        return this.addImage(buffer, x, y, width, height, radius, { type: null });
    }

    /**
     * Add a beveled image.
     * @param {Buffer} buffer  The image's buffer.
     * @param {number} x       The position x to start drawing the element.
     * @param {number} y       The position y to start drawing the element.
     * @param {number} width   The width of the element.
     * @param {number} height  The heigth of the element.
     * @param {number} radius  The radius for the new image.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addBevelImage(buffer, x, y, width, height, radius) {
        this.createBeveledClip(x, y, width, height, radius);
        return this.addImage(buffer, x, y, width, height, radius, { type: null });
    }

    /**
     * Create a round clip.
     * @param {number} x       The position x in the center of the clip's circle.
     * @param {number} y       The position y in the center of the clip's circle.
     * @param {number} radius  The radius for the clip.
     * @returns {CanvasConstructor}
     * @chainable
     */
    createRoundClip(x, y, radius) {
        this.context.save();
        this.context.beginPath();
        this.context.arc(x, y, radius, 0, Math.PI * 2, false);
        this.context.clip();
        return this;
    }

    /**
     * Create a round clip.
     * @param {number} x       The position x to start drawing clip.
     * @param {number} y       The position y to start drawing clip.
     * @param {number} width   The width of clip.
     * @param {number} height  The heigth of clip.
     * @param {number} radius  The radius for clip's rounded borders.
     * @returns {CanvasConstructor}
     * @chainable
     */
    createBeveledClip(x, y, width, height, radius) {
        if (width > 0 && height > 0) {
            radius = Math.min(radius, width / 2, height / 2);
            this.context.beginPath();
            this.context.moveTo(x + radius, y);
            this.context.lineTo(x + width - radius, y);
            this.context.quadraticCurveTo(x + width, y, x + width, y + radius);
            this.context.lineTo(x + width, y + height - radius);
            this.context.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
            this.context.lineTo(x + radius, y + height);
            this.context.quadraticCurveTo(x, y + height, x, y + height - radius);
            this.context.lineTo(x, y + radius);
            this.context.quadraticCurveTo(x, y, x + radius, y);
            this.context.closePath();
            this.context.clip();
        }
        return this;
    }

    /**
     * Register a new font.
     * @param {string} path The path for the font.
     * @param {string} family The font's family name.
     * @returns {CanvasConstructor}
     * @chainable
     */
    registerTextFont(path, family) {
        Canvas.registerFont(path, { family });
        return this;
    }

    /**
     * Register a new font.
     * @param {string} path The path for the font.
     * @param {string} family The font's family name.
     * @returns {CanvasConstructor}
     * @chainable
     */
    addTextFont(path, family) {
        this.context.addFont(new Canvas.Font(family, path));
        return this;
    }

    /**
     * Set a colour for the canvas' context.
     * @param {string} color A canvas' colour resolvable.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setColor(color) {
        this.context.fillStyle = color;
        return this;
    }

    /**
     * Change the font.
     * @param {string} font The font's name to set.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setTextFont(font) {
        this.context.font = font;
        return this;
    }

    /**
     * Change the font alignment.
     * @param {('left'|'center'|'right'|'justified')} align The font's alignment to set.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setTextAlign(align) {
        this.context.textAlign = align;
        return this;
    }

    /**
     * Set the shadow's blur.
     * @param {number} radius The shadow's blur radius to set.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setShadowBlur(radius) {
        this.context.shadowBlur = radius;
        return this;
    }

    /**
     * Set the shadow's colour.
     * @param {string} colour A canvas' colour resolvable to set as shadow's colour.
     * @returns {CanvasConstructor}
     * @chainable
     */
    setShadowColour(colour) {
        this.context.shadowColor = colour;
        return this;
    }

    /**
     * Reset the canvas' shadows.
     * @returns {CanvasConstructor}
     * @chainable
     */
    resetShadows() {
        return this.setShadowBlur(0).setShadowColour('#000000');
    }

    /**
     * Render the canvas into a buffer.
     * @param {Object} options The render's options.
     * @returns {Buffer}
     */
    toBuffer(options) {
        return this.canvas.toBuffer(options);
    }

}

module.exports = CanvasConstructor;
<|endoftext|>


<commit_before>require("../scss/index.scss");

var React = require('react');
var Locale = require('grommet/utils/Locale');
var Cabler = require('./components/Cabler');

var locale = Locale.getCurrentLocale();
var localeData;
try {
  localeData = Locale.getLocaleData(require('../messages/' + locale));
} catch (e) {
  localeData = Locale.getLocaleData(require('../messages/en-US'));
}

var element = document.getElementById('content');
React.render(React.createElement(Cabler, {locales: localeData.locale, messages: localeData.messages}), element);

document.body.classList.remove('loading');
<commit_msg>Use react-dom package for rendering application<commit_after>require("../scss/index.scss");

var React = require('react');
var ReactDOM = require('react-dom');
var Locale = require('grommet/utils/Locale');
var Cabler = require('./components/Cabler');

var locale = Locale.getCurrentLocale();
var localeData;
try {
  localeData = Locale.getLocaleData(require('../messages/' + locale));
} catch (e) {
  localeData = Locale.getLocaleData(require('../messages/en-US'));
}

var element = document.getElementById('content');
ReactDOM.render(React.createElement(Cabler, {locales: localeData.locale, messages: localeData.messages}), element);

document.body.classList.remove('loading');
<|endoftext|>


<commit_before>/**
 * Populate the current user associated with the JWT
 */
const defaults = {
  userEndpoint: '/users',
  passwordField: 'password',
  idField: '_id'
};

export default function(options = {}){
  return function(hook) {
    // Try to get auth options from the app config
    const authOptions = hook.app.get('auth');

    options = Object.assign({}, defaults, authOptions, options);

    // If we already have a current user just pass through
    if (hook.params.user) {
      return Promise.resolve(hook);
    }

    let id;

    // If it's an after hook grab the id from the result
    if (hook.type === 'after') {
      id = hook.result[options.idField];
    }
    // Check to see if we have an id from a decoded JWT
    else if (hook.params.payload) {
      id = hook.params.payload[options.idField];
    }

    // If we didn't find an id then just pass through
    if (id === undefined) {
      return Promise.resolve(hook);
    }

    return new Promise(function(resolve, reject){
      hook.app.service(options.userEndpoint).get(id, {}).then(user => {
        // attach the user to the hook for use in other hooks or services
        hook.params.user = user;

        // If it's an after hook attach the user to the response
        if (hook.result) {
          hook.result.data = Object.assign({}, user = !user.toJSON ? user : user.toJSON());

          // format response
          delete hook.result[options.idField];
          delete hook.result.data[options.passwordField];
        }

        return resolve(hook);
      }).catch(reject);
    });
  };
}
<commit_msg>Make sure we always populate the user in case they were updated. Closes #102<commit_after>/**
 * Populate the current user associated with the JWT
 */
const defaults = {
  userEndpoint: '/users',
  passwordField: 'password',
  idField: '_id'
};

export default function(options = {}){
  return function(hook) {
    // Try to get auth options from the app config
    const authOptions = hook.app.get('auth');

    options = Object.assign({}, defaults, authOptions, options);

    let id;

    // If it's an after hook grab the id from the result
    if (hook.type === 'after') {
      id = hook.result[options.idField];
    }
    // Check to see if we have an id from a decoded JWT
    else if (hook.params.payload) {
      id = hook.params.payload[options.idField];
    }

    // If we didn't find an id then just pass through
    if (id === undefined) {
      return Promise.resolve(hook);
    }

    return new Promise(function(resolve, reject){
      hook.app.service(options.userEndpoint).get(id, {}).then(user => {
        // attach the user to the hook for use in other hooks or services
        hook.params.user = user;

        // If it's an after hook attach the user to the response
        if (hook.result) {
          hook.result.data = Object.assign({}, user = !user.toJSON ? user : user.toJSON());

          // format response
          delete hook.result[options.idField];
          delete hook.result.data[options.passwordField];
        }

        return resolve(hook);
      }).catch(reject);
    });
  };
}
<|endoftext|>


<commit_before>function boxGame_getMousePos(canvas, evt) {
	var rect = canvas.getBoundingClientRect();
    return {
        x: evt.clientX - rect.left,
        y: evt.clientY - rect.top
    };
}

function boxGame_getRandomColor() {
	var r = Math.round(Math.random() * 255);
    var g = Math.round(Math.random() * 255);
    var b = Math.round(Math.random() * 255);

    return 'rgb(' + r +',' + g + ',' + b + ')';
}

function boxGame_box(x,y,width,height) {
	this.x = x;
	this.y = y;
	this.width = width;
	this.height = height;
	this.color = getRandomColor();

	this.drawBox = function(ctx) {
		ctx.fillStyle=this.color;
		ctx.fillRect(this.x,this.y,this.width,this.height);
	};

	this.isPointInBox = function(x,y) {
		if(x < this.x)
			return false;
		if(y < this.y)
			return false;
		if(x > this.x + this.width)
			return false;
		if(y > this.y + this.height)
			return false;

		return true;
	};

	this.overLapsBox = function(box) {
		if(boxGame_box.isPointInBox(this.x, this.y))
			return true;
		if(boxGame_box.isPointInBox(this.x + this.width, this.y))
			return true;
		if(boxGame_box.isPointInBox(this.x, this.y + this.height))
			return true;
		if(boxGame_box.isPointInBox(this.x + this.width, this.y + this.height))
			return true;

		return false;
	};

	this.overLapsBoxes = function(boxes) {
		for(var i = 0; i < boxes.length; i++) {
			if(boxes[i].overLapsBox(this))
				return true;
		}

		return false;
	};
}

function boxGame_drawAllBoxes(boxes, ctx) {
	clearScreen(ctx);

	for(var i = 0; i < boxes.length; i++) {
		boxes[i].drawBox(ctx);
	}
}

function boxGame_clearScreen(ctx) {
	ctx.setTransform(1, 0, 0, 1, 0, 0);
	ctx.clearRect(0,0,canvas.width,canvas.height);
}

function boxGame_clearArray(array) {
	while(array.length)
		array.pop();
}

$(document).ready(function() {
	var canvas = $("#boxGame").get(0);
	var ctx = canvas.getContext('2d');
	var boxes = new Array();

	var score = 0;
	var peak = 0;

	var time = 1000;

	$("#boxGame").click(function(e) {
		var mousePos = getMousePos(canvas,e);

	    for(var i = boxes.length - 1; i >= 0; i--) {
	    	if(boxes[i].isPointInBox(mousePos.x,mousePos.y)) {
	    		boxes.splice(i, 1);
	    		boxGame_drawAllBoxes(boxes, ctx);
	    		score = score + 5;

	    		if(score > peak)
	    			peak = score;

	    		$("#peak").html(peak.toString());
	    		$("#score").html(score.toString());
	    		time = time - 10;

	    		if(time < 100)
	    			time = 100;

	    		return;
    		}
    	}

	    var newBox = new boxGame_box(mousePos.x - 25,mousePos.y - 25,50,50);
	    newBox.drawBox(ctx);

	    if(newBox.overLapsBoxes(boxes))
	    	score = score - 15;

	    boxes.push(newBox);
	    score = score - 10;
		$("#score").html(score.toString());
	});

	$("#clearButton").click(function() {
		boxGame_clearScreen(ctx);
		boxGame_clearArray(boxes);
		score = 0;
		$("#score").html(score.toString());
		time = 1000;
	});

	var boxAdder = function() {
		var x = Math.round(Math.random() * canvas.width);
		var y = Math.round(Math.random() * canvas.height);

		var newBox = new boxGame_box(x,y,50,50);
		newBox.drawBox(ctx);

		if(newBox.overLapsBoxes(boxes))
			score = score - 15;

		$("#score").html(score.toString());

		boxes.push(newBox);

		window.setTimeout(function(){boxAdder();}, time);
	};

	window.setTimeout(function(){boxAdder();},time);
});
<commit_msg>Update boxGame.js<commit_after>function boxGame_getMousePos(canvas, evt) {
	var rect = canvas.getBoundingClientRect();
    return {
        x: evt.clientX - rect.left,
        y: evt.clientY - rect.top
    };
}

function boxGame_getRandomColor() {
	var r = Math.round(Math.random() * 255);
    var g = Math.round(Math.random() * 255);
    var b = Math.round(Math.random() * 255);

    return 'rgb(' + r +',' + g + ',' + b + ')';
}

function boxGame_box(x,y,width,height) {
	this.x = x;
	this.y = y;
	this.width = width;
	this.height = height;
	this.color = boxGame_getRandomColor();

	this.drawBox = function(ctx) {
		ctx.fillStyle=this.color;
		ctx.fillRect(this.x,this.y,this.width,this.height);
	};

	this.isPointInBox = function(x,y) {
		if(x < this.x)
			return false;
		if(y < this.y)
			return false;
		if(x > this.x + this.width)
			return false;
		if(y > this.y + this.height)
			return false;

		return true;
	};

	this.overLapsBox = function(box) {
		if(boxGame_box.isPointInBox(this.x, this.y))
			return true;
		if(boxGame_box.isPointInBox(this.x + this.width, this.y))
			return true;
		if(boxGame_box.isPointInBox(this.x, this.y + this.height))
			return true;
		if(boxGame_box.isPointInBox(this.x + this.width, this.y + this.height))
			return true;

		return false;
	};

	this.overLapsBoxes = function(boxes) {
		for(var i = 0; i < boxes.length; i++) {
			if(boxes[i].overLapsBox(this))
				return true;
		}

		return false;
	};
}

function boxGame_drawAllBoxes(boxes, ctx) {
	clearScreen(ctx);

	for(var i = 0; i < boxes.length; i++) {
		boxes[i].drawBox(ctx);
	}
}

function boxGame_clearScreen(ctx) {
	ctx.setTransform(1, 0, 0, 1, 0, 0);
	ctx.clearRect(0,0,canvas.width,canvas.height);
}

function boxGame_clearArray(array) {
	while(array.length)
		array.pop();
}

$(document).ready(function() {
	var canvas = $("#boxGame").get(0);
	var ctx = canvas.getContext('2d');
	var boxes = new Array();

	var score = 0;
	var peak = 0;

	var time = 1000;

	$("#boxGame").click(function(e) {
		var mousePos = getMousePos(canvas,e);

	    for(var i = boxes.length - 1; i >= 0; i--) {
	    	if(boxes[i].isPointInBox(mousePos.x,mousePos.y)) {
	    		boxes.splice(i, 1);
	    		boxGame_drawAllBoxes(boxes, ctx);
	    		score = score + 5;

	    		if(score > peak)
	    			peak = score;

	    		$("#peak").html(peak.toString());
	    		$("#score").html(score.toString());
	    		time = time - 10;

	    		if(time < 100)
	    			time = 100;

	    		return;
    		}
    	}

	    var newBox = new boxGame_box(mousePos.x - 25,mousePos.y - 25,50,50);
	    newBox.drawBox(ctx);

	    if(newBox.overLapsBoxes(boxes))
	    	score = score - 15;

	    boxes.push(newBox);
	    score = score - 10;
		$("#score").html(score.toString());
	});

	$("#clearButton").click(function() {
		boxGame_clearScreen(ctx);
		boxGame_clearArray(boxes);
		score = 0;
		$("#score").html(score.toString());
		time = 1000;
	});

	var boxAdder = function() {
		var x = Math.round(Math.random() * canvas.width);
		var y = Math.round(Math.random() * canvas.height);

		var newBox = new boxGame_box(x,y,50,50);
		newBox.drawBox(ctx);

		if(newBox.overLapsBoxes(boxes))
			score = score - 15;

		$("#score").html(score.toString());

		boxes.push(newBox);

		window.setTimeout(function(){boxAdder();}, time);
	};

	window.setTimeout(function(){boxAdder();},time);
});
<|endoftext|>