<commit_before>define(function(require) {

/**
 * Handles updates between versions of the extension.
 */
Updater = {
  updates: {},
  firstInstall: function() {
    var notification = window.webkitNotifications.createNotification(
      'img/icon48.png',                      // The image.
      'Tab Wrangler is installed',
      'Tab wrangler is now auto-closing tabs after ' + TW.settings.get('minutesInactive') + ' minutes. \n\
  To change this setting, click on the new icon on your URL bar.'
      );
    notification.show();
  },
  //@todo: refactor this into a couple functions
  run: function() {
    console.log('running updater');
    var self = this;
    chrome.storage.sync.get('version', function(items) {
      // Whatever is set in chrome.storage (if anything)
      var currentVersion;

      // The version from the manifest file
      var manifestVersion = parseFloat(chrome.app.getDetails().version);

      // If items[version] is undefined, the app has either not been installed,
      // or it is an upgrade from when we were not storing the version.
      if (typeof items['version'] != 'undefined') {
        currentVersion = items['version'];
      }

      if (!currentVersion) {
        // Hardcoded here to make the code simpler.
        // This is the first update for users upgrading from when we didn't store
        // a version.
        if (localStorage['minutes_inactive']) {
          // This is the ancient 1.x version
          this.updates[2.1].fx();
        }
        if (localStorage['minutesInactive']) {
          // This is an update from the 2.1 version
          currentVersion = 2.1;
        }
      }
      self.runUpdates(currentVersion, manifestVersion);
    });
  },
  runUpdates: function(currentVersion, manifestVersion) {
    var self = this;
    if (!currentVersion) {
      chrome.storage.sync.set({
        'version': manifestVersion
      },function() {
        self.firstInstall();
      });
    } else if (currentVersion < manifestVersion) {
      for (var i in this.updates) {
        if (this.updates.hasOwnProperty(i)) {
          if (i > currentVersion) {
            this.updates[i].fx();
          }

          // This is the version we are updating to.
          if (i == manifestVersion) {
            // Post 2.0 updates.
            chrome.storage.sync.set({
              'version': manifestVersion
            },function() {
              if (typeof self.updates[i].finished == 'function') {
                self.updates[i].finished();
              }
            });
          }
        }
      }
    }
  }
};

Updater.getNotification = function(message, items) {
  message = message || "Tab Wrangler Updates";
  items = items || [];
  return {
      type: "list",
      title: "Tab Wrangler",
      message: message,
      iconUrl: "img/icon128.png",
      items: [],
      buttons: [
        {iconUrl: 'img/star.png', title: "Review Tab Wrangler"},
        {iconUrl: 'img/notes.png', title: "See all release notes / be a tester"}
      ],
    };
};

// These are also run for users with no currentVersion set.
// This update is for the 1.x -> 2.x users
Updater.updates[2.1] = {
  fx: function() {
    var map = {
      'minutes_inactive' : 'minutesInactive',
      'closed_tab_ids' : null,
      'closed_tab_titles': null,
      'closed_tab_urls' : null,
      'closed_tab_icons' : null,
      'closed_tab_actions': null,
      'locked_ids' : 'lockedIds',
      'popup_view' : null
    }

    var oldValue;

    for (var i in map) {
      if (map.hasOwnProperty(i)) {
        oldValue = localStorage[i];
        if (oldValue) {
          if (map[i] != null) {
            localStorage[map[i]] = oldValue;
          }
          localStorage.removeItem(i);
        }
      }
    }
  }
}

Updater.updates[2.2] = {
  fx: function() {
    // Move localStorage to chrome.storage.sync
    var items = {}
    var val;
    for(var i in localStorage) {
      val = String(localStorage[i]);
      try {
        items[i] = JSON.parse(val);
      } catch(err) {
        items[i] = val;
      }
    }
    chrome.storage.sync.set(items, function() {
      localStorage.clear();
      TW.settings.init();
    });
  },

  finished: function() {

    var updateTxt = ''
    + 'Big changes:'
    + '
'
    + '
 Resets timer when minTabs is reached Feature
'
    + '
 Syncs settings between computers Feature
'
    + '
 Right-click to lock tab Feature
'
    + '
  See all changes
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.2&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.3] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Minor release:'
    + '
'
  + '
 See all changes
'
    + '
 Fixes version requirement for (Chrome 20+ required) Bug
'
    + '
 Adds a search box to Tab Corral Feature
'
    + '
 Various consmetic improvements Feature
'

    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.3&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.4] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'New features!:'
    + '
'
  + '
 See all changes
'
    + '
 Tabs now open in background Feature
'
    + '
 Tabs are grouped by time closed - restore multiple Feature
'
    + '
 Tab lock counter counts down Feature
'
    + '
  Review tab wrangler! Info
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.4&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.5] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Minor release:'
    + '
'
    + '
 See all changes
'
    + '
 Fixed error handling on options form Bug
'
    + '
 Clear link was brokenBug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.5&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.6] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Critical bug fix:'
    + '
'
    + '
 See all changes
'
    + '
 Syntax error in updater caused new installs to fail Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.6&message=' + updateTxt
    );
    notification.show();
  }
};

Updater.updates[2.8] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Updates'
    + '
'
    + '
 See all changes
'
    + '
 Right click on page to immediately close and save a tab. Feature
'
    + '
 Added a pause feature Feature
'
    + '
 Restoring tabs w/o using TabWrangler now removes them from Corral Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.8&message=' + updateTxt
    );
    notification.show();
  }
};

Updater.updates[2.9] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Updates'
    + '
'
    + '
 See all changes
'
    + '
 Tabs were getting cleared at quit, even when setting was on. Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.9&message=' + updateTxt
    );
    notification.show();

  }
}

Updater.updates[3.0] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Updates'
    + '
'
    + '
 See all changes
'
    + '
 Added ability to remove tabs from Corral. Feature
'
    + '
 Pinned tabs don\'t count towards minimum tabs Feature
'
    + '
 Usability improvements on auto-lock page  Feature
'
    + '
 Opening more than minimum tabs, does not cause an auto-close.  Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = Updater.getNotification("Tab Wrangler 3.0 updates");
    notification.items.push({title: "New", message: "Added ability to remove tabs from Corral"});
    notification.items.push({title: "New", message: "Pinned tabs don\'t count towards minimum tabs"});
    notification.items.push({title: "New", message: "Usability improvements on auto-lock page"});
    notification.items.push({title: "Fix", message: "Opening more than minimum tabs, does not cause an auto-close."});
    chrome.notifications.create('3.0Update', notification, function(a) {console.log(a)});
  }
}

return Updater;

});
<commit_msg>Rich Notifications now working for version 3.1<commit_after>define(function(require) {

/**
 * Handles updates between versions of the extension.
 */
Updater = {
  updates: {},
  firstInstall: function() {
    var notification = window.webkitNotifications.createNotification(
      'img/icon48.png',                      // The image.
      'Tab Wrangler is installed',
      'Tab wrangler is now auto-closing tabs after ' + TW.settings.get('minutesInactive') + ' minutes. \n\
  To change this setting, click on the new icon on your URL bar.'
      );
    notification.show();
  },
  //@todo: refactor this into a couple functions
  run: function() {
    console.log('running updater');
    var self = this;
    chrome.storage.sync.get('version', function(items) {
      // Whatever is set in chrome.storage (if anything)
      var currentVersion;

      // The version from the manifest file
      var manifestVersion = parseFloat(chrome.app.getDetails().version);

      // If items[version] is undefined, the app has either not been installed,
      // or it is an upgrade from when we were not storing the version.
      if (typeof items['version'] != 'undefined') {
        currentVersion = items['version'];
      }

      if (!currentVersion) {
        // Hardcoded here to make the code simpler.
        // This is the first update for users upgrading from when we didn't store
        // a version.
        if (localStorage['minutes_inactive']) {
          // This is the ancient 1.x version
          this.updates[2.1].fx();
        }
        if (localStorage['minutesInactive']) {
          // This is an update from the 2.1 version
          currentVersion = 2.1;
        }
      }
      self.runUpdates(currentVersion, manifestVersion);
    });
  },
  runUpdates: function(currentVersion, manifestVersion) {
    var self = this;
    if (!currentVersion) {
      chrome.storage.sync.set({
        'version': manifestVersion
      },function() {
        self.firstInstall();
      });
    } else if (currentVersion < manifestVersion) {
      for (var i in this.updates) {
        if (this.updates.hasOwnProperty(i)) {
          if (i > currentVersion) {
            this.updates[i].fx();
          }

          // This is the version we are updating to.
          if (i == manifestVersion) {
            // Post 2.0 updates.
            chrome.storage.sync.set({
              'version': manifestVersion
            },function() {
              if (typeof self.updates[i].finished == 'function') {
                self.updates[i].finished();
              }
            });
          }
        }
      }
    }
  }
};

Updater.getNotification = function(title, items) {
  title = title || "Tab Wrangler Updates";
  items = items || [];
  return {
      type: "list",
      title: title,
      message: "Tab wrangler updates",
      iconUrl: "img/icon128.png",
      items: [],
      buttons: [],
    };
};

Updater.addCommonButtons = function(notification) {
  notification.buttons.push({iconUrl: 'img/star.png', title: "Review Tab Wrangler"});
  notification.buttons.push({iconUrl: 'img/notes.png', title: "See all release notes / be a tester"});
};

Updater.notificationIdPrefix = "updater-";

Updater.commonButtonHandler = function(id, buttonIdx) {
  // If an updater is creating a notification it should
  // do so by prefixing the Id with updater--.
  // This is a little hacky, so there is a helper function
  // which launches the notification with this.
  if(id.indexOf(Updater.notificationIdPrefix) === 0) {
    if (buttonIdx === 0) {
      // Reviewing TabWrangler
      // Launch the Chrome store page
      chrome.tabs.create({url: 'https://chrome.google.com/webstore/detail/egnjhciaieeiiohknchakcodbpgjnchh/reviews', active: true});
    }

    if (buttonIdx === 1) {
      chrome.tabs.create({url: 'http://www.jacobsingh.name/tabwrangler/release-notes', active: true});
    }
  }
};

Updater.launchNotification = function(id, notification, addButtons) {
  var cb = function(){};
  addButtons = typeof(addButtons) == 'undefined' ? false : true;
  if (addButtons) {
    this.addCommonButtons(notification);
    chrome.notifications.onButtonClicked.addListener(this.commonButtonHandler);
  }
  chrome.notifications.create(this.notificationIdPrefix + id, notification, cb);
};

// These are also run for users with no currentVersion set.
// This update is for the 1.x -> 2.x users
Updater.updates[2.1] = {
  fx: function() {
    var map = {
      'minutes_inactive' : 'minutesInactive',
      'closed_tab_ids' : null,
      'closed_tab_titles': null,
      'closed_tab_urls' : null,
      'closed_tab_icons' : null,
      'closed_tab_actions': null,
      'locked_ids' : 'lockedIds',
      'popup_view' : null
    };

    var oldValue;

    for (var i in map) {
      if (map.hasOwnProperty(i)) {
        oldValue = localStorage[i];
        if (oldValue) {
          if (map[i] != null) {
            localStorage[map[i]] = oldValue;
          }
          localStorage.removeItem(i);
        }
      }
    }
  }
};

Updater.updates[2.2] = {
  fx: function() {
    // Move localStorage to chrome.storage.sync
    var items = {};
    var val;
    for(var i in localStorage) {
      val = String(localStorage[i]);
      try {
        items[i] = JSON.parse(val);
      } catch(err) {
        items[i] = val;
      }
    }
    chrome.storage.sync.set(items, function() {
      localStorage.clear();
      TW.settings.init();
    });
  },

  finished: function() {

    var updateTxt = ''
    + 'Big changes:'
    + '
'
    + '
 Resets timer when minTabs is reached Feature
'
    + '
 Syncs settings between computers Feature
'
    + '
 Right-click to lock tab Feature
'
    + '
  See all changes
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.2&message=' + updateTxt
    );
    notification.show();
  }
};

Updater.updates[2.3] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Minor release:'
    + '
'
    + '
 See all changes
'
    + '
 Fixes version requirement for (Chrome 20+ required) Bug
'
    + '
 Adds a search box to Tab Corral Feature
'
    + '
 Various consmetic improvements Feature
'

    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.3&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.4] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'New features!:'
    + '
'
  + '
 See all changes
'
    + '
 Tabs now open in background Feature
'
    + '
 Tabs are grouped by time closed - restore multiple Feature
'
    + '
 Tab lock counter counts down Feature
'
    + '
  Review tab wrangler! Info
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.4&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.5] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Minor release:'
    + '
'
    + '
 See all changes
'
    + '
 Fixed error handling on options form Bug
'
    + '
 Clear link was brokenBug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.5&message=' + updateTxt
    );
    notification.show();
  }
}

Updater.updates[2.6] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Critical bug fix:'
    + '
'
    + '
 See all changes
'
    + '
 Syntax error in updater caused new installs to fail Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.6&message=' + updateTxt
    );
    notification.show();
  }
};

Updater.updates[2.8] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Updates'
    + '
'
    + '
 See all changes
'
    + '
 Right click on page to immediately close and save a tab. Feature
'
    + '
 Added a pause feature Feature
'
    + '
 Restoring tabs w/o using TabWrangler now removes them from Corral Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.8&message=' + updateTxt
    );
    notification.show();
  }
};

Updater.updates[2.9] = {
  fx: function() {

  },

  finished: function() {

    var updateTxt = ''
    + 'Updates'
    + '
'
    + '
 See all changes
'
    + '
 Tabs were getting cleared at quit, even when setting was on. Bug
'
    + '
  Review tab wrangler!
'
    + '
';

    var notification = window.webkitNotifications.createHTMLNotification(
      'notification.html?title=Version 2.9&message=' + updateTxt
    );
    notification.show();

  }
}

Updater.updates[3.0] = {
  fx: function() {

  },

  finished: function() {

    var notification = Updater.getNotification("Tab Wrangler 3.0 updates");
    notification.items.push({title: "New", message: "Remove tabs from Corral"});
    notification.items.push({title: "New", message: "Pinned tabs not counted"});
    notification.items.push({title: "New", message: "Auto-lock page UX"});
    notification.items.push({title: "Fix", message: "No auto-close when > minTabs."});
    Updater.launchNotification("3.0", notification, true);
  }
}

return Updater;

});
<|endoftext|>


<commit_before>function GameManager(size, InputManager, Actuator, ScoreManager) {
  this.size         = size; // Size of the grid
  this.inputManager = new InputManager;
  this.scoreManager = new ScoreManager;
  this.actuator     = new Actuator;

  this.startTiles   = 2;

  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();
}

// Restart the game
GameManager.prototype.restart = function () {
  this.actuator.continue();
  this.setup();
};

// Keep playing after winning
GameManager.prototype.keepPlaying = function () {
  this.keepPlaying = true;
  this.actuator.continue();
};

GameManager.prototype.isGameTerminated = function () {
  if (this.over || (this.won && !this.keepPlaying)) {
    return true;
  } else {
    return false;
  }
};

// Set up the game
GameManager.prototype.setup = function () {
  this.grid        = new Grid(this.size);

  this.score       = 0;
  this.over        = false;
  this.won         = false;
  this.keepPlaying = false;

  // Add the initial tiles
  this.addStartTiles();

  // Update the actuator
  this.actuate();
};

// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
  for (var i = 0; i < this.startTiles; i++) {
    this.addRandomTile();
  }
};

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    var value = Math.random() < 0.9 ? 1024 : 2048;
    var tile = new Tile(this.grid.randomAvailableCell(), value);

    this.grid.insertTile(tile);
  }
};

// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
  if (this.scoreManager.get() < this.score) {
    this.scoreManager.set(this.score);
  }

  this.actuator.actuate(this.grid, {
    score:      this.score,
    over:       this.over,
    won:        this.won,
    bestScore:  this.scoreManager.get(),
    terminated: this.isGameTerminated()
  });

};

// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
  this.grid.eachCell(function (x, y, tile) {
    if (tile) {
      tile.mergedFrom = null;
      tile.savePosition();
    }
  });
};

// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
  this.grid.cells[tile.x][tile.y] = null;
  this.grid.cells[cell.x][cell.y] = tile;
  tile.updatePosition(cell);
};

// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
  // 0: up, 1: right, 2:down, 3: left
  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);
  var traversals = this.buildTraversals(vector);
  var moved      = false;

  // Save the current tile positions and remove merger information
  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles
  traversals.x.forEach(function (x) {
    traversals.y.forEach(function (y) {
      cell = { x: x, y: y };
      tile = self.grid.cellContent(cell);

      if (tile) {
        var positions = self.findFarthestPosition(cell, vector);
        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?
        if (next && next.value === tile.value && !next.mergedFrom) {
          var merged = new Tile(positions.next, tile.value * 2);
          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);
          self.grid.removeTile(tile);

          // Converge the two tiles' positions
          tile.updatePosition(positions.next);

          // Update the score
          self.score += merged.value;

          // The mighty 2048 tile
          if (merged.value === 2048) self.won = true;
        } else {
          self.moveTile(tile, positions.farthest);
        }

        if (!self.positionsEqual(cell, tile)) {
          moved = true; // The tile moved from its original cell!
        }
      }
    });
  });

  if (moved) {
    this.addRandomTile();

    if (!this.movesAvailable()) {
      this.over = true; // Game over!
    }

    this.actuate();
  }
};

// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
  // Vectors representing tile movement
  var map = {
    0: { x: 0,  y: -1 }, // up
    1: { x: 1,  y: 0 },  // right
    2: { x: 0,  y: 1 },  // down
    3: { x: -1, y: 0 }   // left
  };

  return map[direction];
};

// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {
    traversals.x.push(pos);
    traversals.y.push(pos);
  }

  // Always traverse from the farthest cell in the chosen direction
  if (vector.x === 1) traversals.x = traversals.x.reverse();
  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;
};

GameManager.prototype.findFarthestPosition = function (cell, vector) {
  var previous;

  // Progress towards the vector direction until an obstacle is found
  do {
    previous = cell;
    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  } while (this.grid.withinBounds(cell) &&
           this.grid.cellAvailable(cell));

  return {
    farthest: previous,
    next: cell // Used to check if a merge is required
  };
};

GameManager.prototype.movesAvailable = function () {
  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};

// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {
    for (var y = 0; y < this.size; y++) {
      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {
        for (var direction = 0; direction < 4; direction++) {
          var vector = self.getVector(direction);
          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {
            return true; // These two tiles can be merged
          }
        }
      }
    }
  }

  return false;
};

GameManager.prototype.positionsEqual = function (first, second) {
  return first.x === second.x && first.y === second.y;
};
<commit_msg>negative are as awesome<commit_after>function GameManager(size, InputManager, Actuator, ScoreManager) {
  this.size         = size; // Size of the grid
  this.inputManager = new InputManager;
  this.scoreManager = new ScoreManager;
  this.actuator     = new Actuator;

  this.startTiles   = 2;

  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();
}

// Restart the game
GameManager.prototype.restart = function () {
  this.actuator.continue();
  this.setup();
};

// Keep playing after winning
GameManager.prototype.keepPlaying = function () {
  this.keepPlaying = true;
  this.actuator.continue();
};

GameManager.prototype.isGameTerminated = function () {
  if (this.over || (this.won && !this.keepPlaying)) {
    return true;
  } else {
    return false;
  }
};

// Set up the game
GameManager.prototype.setup = function () {
  this.grid        = new Grid(this.size);

  this.score       = 0;
  this.over        = false;
  this.won         = false;
  this.keepPlaying = false;

  // Add the initial tiles
  this.addStartTiles();

  // Update the actuator
  this.actuate();
};

// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
  for (var i = 0; i < this.startTiles; i++) {
    this.addRandomTile();
  }
};

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    var value = Math.random() < 0.9 ? -2 : -4;
    var tile = new Tile(this.grid.randomAvailableCell(), value);

    this.grid.insertTile(tile);
  }
};

// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
  if (this.scoreManager.get() < this.score) {
    this.scoreManager.set(this.score);
  }

  this.actuator.actuate(this.grid, {
    score:      this.score,
    over:       this.over,
    won:        this.won,
    bestScore:  this.scoreManager.get(),
    terminated: this.isGameTerminated()
  });

};

// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
  this.grid.eachCell(function (x, y, tile) {
    if (tile) {
      tile.mergedFrom = null;
      tile.savePosition();
    }
  });
};

// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
  this.grid.cells[tile.x][tile.y] = null;
  this.grid.cells[cell.x][cell.y] = tile;
  tile.updatePosition(cell);
};

// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
  // 0: up, 1: right, 2:down, 3: left
  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);
  var traversals = this.buildTraversals(vector);
  var moved      = false;

  // Save the current tile positions and remove merger information
  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles
  traversals.x.forEach(function (x) {
    traversals.y.forEach(function (y) {
      cell = { x: x, y: y };
      tile = self.grid.cellContent(cell);

      if (tile) {
        var positions = self.findFarthestPosition(cell, vector);
        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?
        if (next && next.value === tile.value && !next.mergedFrom) {
          var merged = new Tile(positions.next, tile.value * 2);
          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);
          self.grid.removeTile(tile);

          // Converge the two tiles' positions
          tile.updatePosition(positions.next);

          // Update the score
          self.score += merged.value;

          // The mighty 2048 tile
          if (merged.value === 2048) self.won = true;
        } else {
          self.moveTile(tile, positions.farthest);
        }

        if (!self.positionsEqual(cell, tile)) {
          moved = true; // The tile moved from its original cell!
        }
      }
    });
  });

  if (moved) {
    this.addRandomTile();

    if (!this.movesAvailable()) {
      this.over = true; // Game over!
    }

    this.actuate();
  }
};

// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
  // Vectors representing tile movement
  var map = {
    0: { x: 0,  y: -1 }, // up
    1: { x: 1,  y: 0 },  // right
    2: { x: 0,  y: 1 },  // down
    3: { x: -1, y: 0 }   // left
  };

  return map[direction];
};

// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {
    traversals.x.push(pos);
    traversals.y.push(pos);
  }

  // Always traverse from the farthest cell in the chosen direction
  if (vector.x === 1) traversals.x = traversals.x.reverse();
  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;
};

GameManager.prototype.findFarthestPosition = function (cell, vector) {
  var previous;

  // Progress towards the vector direction until an obstacle is found
  do {
    previous = cell;
    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  } while (this.grid.withinBounds(cell) &&
           this.grid.cellAvailable(cell));

  return {
    farthest: previous,
    next: cell // Used to check if a merge is required
  };
};

GameManager.prototype.movesAvailable = function () {
  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};

// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {
    for (var y = 0; y < this.size; y++) {
      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {
        for (var direction = 0; direction < 4; direction++) {
          var vector = self.getVector(direction);
          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {
            return true; // These two tiles can be merged
          }
        }
      }
    }
  }

  return false;
};

GameManager.prototype.positionsEqual = function (first, second) {
  return first.x === second.x && first.y === second.y;
};
<|endoftext|>


<commit_before>var net = require('net'),
	events = require('events'),
	util = require('util');

function NodeThinkGearError(message){
  Error.call(this);
  Error.captureStackTrace(this, arguments.callee);
  this.message = message;
  this.name = 'NodeThinkGearError';
};

NodeThinkGearError.prototype.__proto__ = Error.prototype;

var ThinkGearClient = function(opts){
	opts || (opts = {});

	this.port = opts.port || 13854;
	this.host = opts.host || 'localhost';

	if(typeof(opts.appName) !== 'string') throw new NodeThinkGearError('Must specify appName');
	if(typeof(opts.appKey) !== 'string') throw new NodeThinkGearError('Must specify appKey');

	this.auth = {
		appName:opts.appName,
		appKey:opts.appKey
	};

	this.config = {
		enableRawOutput: false,
		format: "Json"
	};

	events.EventEmitter.call(this);
};

util.inherits(ThinkGearClient, events.EventEmitter);

ThinkGearClient.prototype.connect = function(){
	var self = this;

	var client = this.client = net.connect(this.port,this.host,function(){
		client.write(JSON.stringify(self.auth));
	});

	client.on('data',function(data){
		if(!self.configSent){
			self.configSent = true;
			client.write(JSON.stringify(self.config));
		} else {
			try{
				self.emit('data',JSON.parse(data.toString()));
			}catch(e){
				self.emit('parse_error',data.toString());
			}
		}
	});

    client.on('error', function(err) {
        console.log('Error connecting to ThinkGear client. Try starting the ThinkGear Connector app.\n', err);
        process.exit(1);
    });
};

exports.ThinkGearClient = ThinkGearClient;

exports.createClient = function(opts){
	return new ThinkGearClient(opts || {});
};<commit_msg>update module name in source file<commit_after>var net = require('net'),
	events = require('events'),
	util = require('util');

function NodeNeuroSkyError(message){
  Error.call(this);
  Error.captureStackTrace(this, arguments.callee);
  this.message = message;
  this.name = 'NodeThinkGearError';
};

NodeNeuroSkyError.prototype.__proto__ = Error.prototype;

var NeuroSkyClient = function(opts){
	opts || (opts = {});

	this.port = opts.port || 13854;
	this.host = opts.host || 'localhost';

	if(typeof(opts.appName) !== 'string') throw new NodeNeuroSkyError('Must specify appName');
	if(typeof(opts.appKey) !== 'string') throw new NodeNeuroSkyError('Must specify appKey');

	this.auth = {
		appName:opts.appName,
		appKey:opts.appKey
	};

	this.config = {
		enableRawOutput: false,
		format: "Json"
	};

	events.EventEmitter.call(this);
};

util.inherits(NeuroSkyClient, events.EventEmitter);

NeuroSkyClient.prototype.connect = function(){
	var self = this;

	var client = this.client = net.connect(this.port,this.host,function(){
		client.write(JSON.stringify(self.auth));
	});

	client.on('data',function(data){
		if(!self.configSent){
			self.configSent = true;
			client.write(JSON.stringify(self.config));
		} else {
			try{
				self.emit('data',JSON.parse(data.toString()));
			}catch(e){
				self.emit('parse_error',data.toString());
			}
		}
	});

  client.on('error', function(err) {
      console.log('Error connecting to ThinkGear client. Try starting the ThinkGear Connector app.\n', err);
      process.exit(1);
  });
};

exports.NeuroSkyClient = NeuroSkyClient;

exports.createClient = function(opts){
	return new NeuroSkyClient(opts || {});
};<|endoftext|>


<commit_before>'use strict';

var http = require('http');
var https = require('https');
var qs = require('qs');
var url = require('url');
var objectMerge = require('object-merge');

if (typeof Promise === 'undefined')
	var Promise = require('q').Promise;

module.exports = function (baseUrl, opts) {
	var client = new Client(baseUrl, opts);
	return client.request.bind(client);
};

var Client = function (baseUrl, opts) {
	this.baseUrl = baseUrl;

	this.options = objectMerge({
		headers: { Accept: 'application/json' }
	}, opts || {});
};

Client.prototype.request = function (method, path, params, body, options) {
	var query = params ? '?' + qs.stringify(params) : '';
	path = path + query;

	var resolved = url.resolve(this.baseUrl, path);
	resolved = url.parse(resolved);

	var headers = {};

	if (typeof body === 'object') {
		body = JSON.stringify(body);
		headers['Content-Type'] = 'application/json';
	}

	if (body)
		headers['Content-Length'] = Buffer.byteLength(body, 'utf8');

	options = objectMerge(this.options, options || {}, {
		method: method,
		path: resolved.path,
		hostname: resolved.hostname,
		port: resolved.port || (resolved.protocol == 'https:' ? 443 : 80),
		headers: headers
	});

	var newReq = this.request.bind(this);
	var proto = resolved.protocol == 'https:' ? https : http;

	return new Promise(function (resolve, reject) {
		var request = proto.request(options);
		request.on('error', reject);

		if (body)
			request.end(body, 'utf8');
		else
			request.end();

		request.on('response', function (response) {
			var data = '';
			response.setEncoding('utf8');

			response.on('data', function (chunk) {
				data += chunk;
			});

			response.on('end', function () {
				var error;
				var code = response.statusCode;

				// do we need to catch 1xx codes?

				// 2xx - success
				if (code < 300) {
					if (!data || !data.length) {
						resolve(null);
						return;
					}

					try {
						data = JSON.parse(data);
					} catch (e) {
						error = new Error('invalid json');
						error.statusCode = code;
						error.data = data;
						reject(error);
						return;
					}

					resolve(data);
					return;
				}

				// 3xx - redirection
				// assumes that infinite loops won't happen
				if (code < 400) {
					switch (code) {
						case 304: // not modified
						case 305: // use proxy (not supported)
						case 306: // switch proxy (not supported)
							error = new Error('HTTP ' + code + ': ' + http.STATUS_CODES[code]);
							error.code = error.statusCode = code;
							error.data = data;
							reject(error);
							return;

						case 307: // temporary redirect - maintain method
						case 308: // permanent redirect - maintain method
							newReq(method, response.headers.location, null, body, options)
							.then(resolve, reject);
							return;

						default: // redirect - change method to GET
							newReq('get', response.headers.location, null, null, options)
							.then(resolve, reject);
							return;
					}
				}

				// 4xx 5xx - failure
				error = new Error('HTTP ' + code + ': ' + http.STATUS_CODES[code]);
				error.statusCode = code;

				try {
					error.object = JSON.parse(data);
				} catch (e) {
					error.data = data;
				}

				reject(error);
			});
		});
	});
};
<commit_msg>copy parsed JSON object keys to root of error<commit_after>'use strict';

var http = require('http');
var https = require('https');
var qs = require('qs');
var url = require('url');
var objectMerge = require('object-merge');

if (typeof Promise === 'undefined')
	var Promise = require('q').Promise;

module.exports = function (baseUrl, opts) {
	var client = new Client(baseUrl, opts);
	return client.request.bind(client);
};

var Client = function (baseUrl, opts) {
	this.baseUrl = baseUrl;

	this.options = objectMerge({
		headers: { Accept: 'application/json' }
	}, opts || {});
};

Client.prototype.request = function (method, path, params, body, options) {
	var query = params ? '?' + qs.stringify(params) : '';
	path = path + query;

	var resolved = url.resolve(this.baseUrl, path);
	resolved = url.parse(resolved);

	var headers = {};

	if (typeof body === 'object') {
		body = JSON.stringify(body);
		headers['Content-Type'] = 'application/json';
	}

	if (body)
		headers['Content-Length'] = Buffer.byteLength(body, 'utf8');

	options = objectMerge(this.options, options || {}, {
		method: method,
		path: resolved.path,
		hostname: resolved.hostname,
		port: resolved.port || (resolved.protocol == 'https:' ? 443 : 80),
		headers: headers
	});

	var newReq = this.request.bind(this);
	var proto = resolved.protocol == 'https:' ? https : http;

	return new Promise(function (resolve, reject) {
		var request = proto.request(options);
		request.on('error', reject);

		if (body)
			request.end(body, 'utf8');
		else
			request.end();

		request.on('response', function (response) {
			var data = '';
			response.setEncoding('utf8');

			response.on('data', function (chunk) {
				data += chunk;
			});

			response.on('end', function () {
				var error;
				var code = response.statusCode;

				// do we need to catch 1xx codes?

				// 2xx - success
				if (code < 300) {
					if (!data || !data.length) {
						resolve(null);
						return;
					}

					try {
						data = JSON.parse(data);
					} catch (e) {
						error = new Error('invalid json');
						error.statusCode = code;
						error.data = data;
						reject(error);
						return;
					}

					resolve(data);
					return;
				}

				// 3xx - redirection
				// assumes that infinite loops won't happen
				if (code < 400) {
					switch (code) {
						case 304: // not modified
						case 305: // use proxy (not supported)
						case 306: // switch proxy (not supported)
							error = new Error('HTTP ' + code + ': ' + http.STATUS_CODES[code]);
							error.code = error.statusCode = code;
							error.data = data;
							reject(error);
							return;

						case 307: // temporary redirect - maintain method
						case 308: // permanent redirect - maintain method
							newReq(method, response.headers.location, null, body, options)
							.then(resolve, reject);
							return;

						default: // redirect - change method to GET
							newReq('get', response.headers.location, null, null, options)
							.then(resolve, reject);
							return;
					}
				}

				// 4xx 5xx - failure
				error = new Error('HTTP ' + code + ': ' + http.STATUS_CODES[code]);
				error.statusCode = code;

				try {
					const json = JSON.parse(data);

					for (var key in Object.keys(json))
						error[key] = json[key];
				} catch (e) {
					error.data = data;
				}

				reject(error);
			});
		});
	});
};
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import Doc from './doc';
import Paper from 'material-ui/Paper';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { viewerStyle } from '../styles';

let Viewer = React.createClass({
  propTypes: {
    docs: React.PropTypes.array,
    config: React.PropTypes.object
  },

  render() {
    let { docs, config } = this.props;

    return (
      <Paper zDepth={0} style={viewerStyle} >
        {docs.map(d => (<Doc key={d._id} doc={d} config={config} />))}
      </Paper>
    );
  }
});

const mapStateToProps = state => ({
  docs: state.docs,
  config: state.config,
  length: state.length,
  filter: state.filter
});

const mergeProps = createSelector(
  state => state.docs,
  state => state.config,
  state => state.length,
  state => state.filter,
  (docs, config, length, filters) => ({
    config,
    docs: docs.filter(d => {
      // lang may be undefined in some documents
      let langs = d.lang || [];

      // If there are no filters, there's nothing to filter.
      if (filters.length === 0) {
        return true;
      } else {
        // Test every filter against the langs of the doc
        return filters.every(f => langs.indexOf(f) >= 0);
      }
    }).slice(0, length)
  })
);

export default connect(mapStateToProps, {}, mergeProps)(Viewer);
<commit_msg>removing config from viewer, now that header subscribes to it<commit_after>'use strict';

import React from 'react';
import Doc from './doc';
import Paper from 'material-ui/Paper';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { viewerStyle } from '../styles';

let Viewer = React.createClass({
  propTypes: {
    docs: React.PropTypes.array
  },

  render() {
    let { docs } = this.props;

    return (
      <Paper zDepth={0} style={viewerStyle} >
        {docs.map(d => (<Doc key={d._id} doc={d} />))}
      </Paper>
    );
  }
});

const mapStateToProps = state => ({
  docs: state.docs,
  length: state.length,
  filter: state.filter
});

const mergeProps = createSelector(
  state => state.docs,
  state => state.length,
  state => state.filter,
  (docs, length, filters) => ({
    docs: docs.filter(d => {
      // lang may be undefined in some documents
      let langs = d.lang || [];

      // If there are no filters, there's nothing to filter.
      if (filters.length === 0) {
        return true;
      } else {
        // Test every filter against the langs of the doc
        return filters.every(f => langs.indexOf(f) >= 0);
      }
    }).slice(0, length)
  })
);

export default connect(mapStateToProps, {}, mergeProps)(Viewer);
<|endoftext|>


<commit_before>var _ = require('underscore');
var KeenRequests = require('./requests');

/*!
* -----------------
* Keen IO Query JS
* -----------------
*/

var Keen = {};

// ------------------------------
// Keen.Request
// ------------------------------

Keen.Request = function(){
  this.data = {};
  this.configure.apply(this, arguments);
}

Keen.Request.prototype.configure = function(client, queries, callback){
  this.client = client;
  this.queries = queries;
  this.callback = callback;
  this.run();
  return this;
};

Keen.Request.prototype.run = function(){
  var self = this,
      completions = 0,
      response = [];

  var handleResponse = function(err, res){
    if (err && self.callback) {
      return self.callback(err, null);
    }
    response[arguments[2]] = res, completions++;
    if (completions == self.queries.length) {
      self.data = (self.queries.length == 1) ? response[0] : response;
      if (self.callback) self.callback(null, self.data);
    }
  };

  _.each(self.queries, function(query, index){
    var data, path = '/projects/' + self.client.projectId;
    var callbackSequencer = function(err, res){
      handleResponse(err, res, index);
    };

    if (query instanceof Keen.Query) {
      path += query.path;
      data = query.params || {};
    }
    /* TODO: Test and deploy this
    else if (_.isString(query)) {
      path += '/saved_queries/' + query + '/result';
      data = { api_key: self.client.readKey };
    }*/
    else {
      throw new Error('Query #' + (index+1)  +' is not valid');

    }

    KeenRequests.get.call(self.client, self.client.readKey, path, data, callbackSequencer);
  });

  return self;
};

// ------------------------------
// Keen.Query
// ------------------------------

Keen.Query = function(){
  this.configure.apply(this, arguments);
};

Keen.Query.prototype.configure = function(analysisType, params){
  //if (!collection) throw new Error('Event Collection name is required');
  var self = this;
  self.path = '/queries/' + analysisType;
  self.params = {};
  self.set(params);
  return self;
};

Keen.Query.prototype.get = function(attribute) {
  if (this.params) {
    return this.params[attribute] || null;
  }
};

Keen.Query.prototype.set = function(attributes) {
  var self = this;
  _.each(attributes, function(v, k){
    var key = k, value = v;
    if (k.match(new RegExp("[A-Z]"))) {
      key = k.replace(/([A-Z])/g, function($1) { return "_"+$1.toLowerCase(); });
    }
    self.params[key] = value;

    if (_.isArray(value)) {
      _.each(value, function(dv, index){
        if (_.isObject(dv)) {
          _.each(dv, function(deepValue, deepKey){
            if (deepKey.match(new RegExp("[A-Z]"))) {
              var _deepKey = deepKey.replace(/([A-Z])/g, function($1) { return "_"+$1.toLowerCase(); });
              delete self.params[key][index][deepKey];
              self.params[key][index][_deepKey] = deepValue;
            }
          });
        }
      });
    }

  });
  return self;
};

// Export Methods
// ------------------------------
module.exports = {
  client: {
    run: function(query, callback){
      if (!query) throw new Error('At least one query is required');
      var queries = (_.isArray(query)) ? query : [query];
      return new Keen.Request(this, queries, callback);
    }
  },
  Query: Keen.Query
};
<commit_msg>Eliminate instanceof test<commit_after>var _ = require('underscore');
var KeenRequests = require('./requests');

/*!
* -----------------
* Keen IO Query JS
* -----------------
*/

var Keen = {};

// ------------------------------
// Keen.Request
// ------------------------------

Keen.Request = function(){
  this.data = {};
  this.configure.apply(this, arguments);
}

Keen.Request.prototype.configure = function(client, queries, callback){
  this.client = client;
  this.queries = queries;
  this.callback = callback;
  this.run();
  return this;
};

Keen.Request.prototype.run = function(){
  var self = this,
      completions = 0,
      response = [];

  var handleResponse = function(err, res){
    if (err && self.callback) {
      return self.callback(err, null);
    }
    response[arguments[2]] = res, completions++;
    if (completions == self.queries.length) {
      self.data = (self.queries.length == 1) ? response[0] : response;
      if (self.callback) self.callback(null, self.data);
    }
  };

  _.each(self.queries, function(query, index){
    var data, path = '/projects/' + self.client.projectId;
    var callbackSequencer = function(err, res){
      handleResponse(err, res, index);
    };

    if (query.path && query.params) {
      path += query.path;
      data = query.params || {};
    }
    /* TODO: Test and deploy this
    else if (_.isString(query)) {
      path += '/saved_queries/' + query + '/result';
      data = { api_key: self.client.readKey };
    }*/
    else {
      throw new Error('Query #' + (index+1)  +' is not valid');

    }

    KeenRequests.get.call(self.client, self.client.readKey, path, data, callbackSequencer);
  });

  return self;
};

// ------------------------------
// Keen.Query
// ------------------------------

Keen.Query = function(){
  this.configure.apply(this, arguments);
};

Keen.Query.prototype.configure = function(analysisType, params){
  //if (!collection) throw new Error('Event Collection name is required');
  var self = this;
  self.path = '/queries/' + analysisType;
  self.params = {};
  self.set(params);
  return self;
};

Keen.Query.prototype.get = function(attribute) {
  if (this.params) {
    return this.params[attribute] || null;
  }
};

Keen.Query.prototype.set = function(attributes) {
  var self = this;
  _.each(attributes, function(v, k){
    var key = k, value = v;
    if (k.match(new RegExp("[A-Z]"))) {
      key = k.replace(/([A-Z])/g, function($1) { return "_"+$1.toLowerCase(); });
    }
    self.params[key] = value;

    if (_.isArray(value)) {
      _.each(value, function(dv, index){
        if (_.isObject(dv)) {
          _.each(dv, function(deepValue, deepKey){
            if (deepKey.match(new RegExp("[A-Z]"))) {
              var _deepKey = deepKey.replace(/([A-Z])/g, function($1) { return "_"+$1.toLowerCase(); });
              delete self.params[key][index][deepKey];
              self.params[key][index][_deepKey] = deepValue;
            }
          });
        }
      });
    }

  });
  return self;
};

// Export Methods
// ------------------------------
module.exports = {
  client: {
    run: function(query, callback){
      if (!query) throw new Error('At least one query is required');
      var queries = (_.isArray(query)) ? query : [query];
      return new Keen.Request(this, queries, callback);
    }
  },
  Query: Keen.Query
};
<|endoftext|>


<commit_before>Version = { id: '182c862' };
<commit_msg>Bump up version<commit_after>Version = { id: '074cf35' };
<|endoftext|>


<commit_before>// Copyright 2014-2020, University of Colorado Boulder

/**
 * Main file for the Griddle library demo.
 *
 * @deprecated - please use bamboo
 */

import Property from '../../axon/js/Property.js';
import Screen from '../../joist/js/Screen.js';
import Sim from '../../joist/js/Sim.js';
import simLauncher from '../../joist/js/simLauncher.js';
import deprecationWarning from '../../phet-core/js/deprecationWarning.js';
import GriddleDemoScreenView from './demo/GriddleDemoScreenView.js';
import griddleStrings from './griddleStrings.js';

const simOptions = {
  credits: {
    leadDesign: 'PhET'
  }
};

simLauncher.launch( function() {
  assert && deprecationWarning( 'Please use bamboo' );
  new Sim( griddleStrings.griddle.title, [
    new Screen(
      function() { return {}; },
      function() { return new GriddleDemoScreenView(); },
      {
        name: 'Griddle Demo',
        backgroundColorProperty: new Property( '#e4fcf4' )
      }
    )
  ], simOptions ).start();
} );<commit_msg>ESLint: Autofix prefer-arrow-callback rule, see https://github.com/phetsims/chipper/issues/814<commit_after>// Copyright 2014-2020, University of Colorado Boulder

/**
 * Main file for the Griddle library demo.
 *
 * @deprecated - please use bamboo
 */

import Property from '../../axon/js/Property.js';
import Screen from '../../joist/js/Screen.js';
import Sim from '../../joist/js/Sim.js';
import simLauncher from '../../joist/js/simLauncher.js';
import deprecationWarning from '../../phet-core/js/deprecationWarning.js';
import GriddleDemoScreenView from './demo/GriddleDemoScreenView.js';
import griddleStrings from './griddleStrings.js';

const simOptions = {
  credits: {
    leadDesign: 'PhET'
  }
};

simLauncher.launch( () => {
  assert && deprecationWarning( 'Please use bamboo' );
  new Sim( griddleStrings.griddle.title, [
    new Screen(
      ( () => {
        return {};
      } ),
      ( () => new GriddleDemoScreenView() ),
      {
        name: 'Griddle Demo',
        backgroundColorProperty: new Property( '#e4fcf4' )
      }
    )
  ], simOptions ).start();
} );<|endoftext|>


<commit_before>//
// Dotty makes it easy to programmatically access arbitrarily nested objects and
// their properties.
//

//
// `object` is an object, `path` is the path to the property you want to check
// for existence of.
//
// `path` can be provided as either a `"string.separated.with.dots"` or as
// `["an", "array"]`.
//
// Returns `true` if the path can be completely resolved, `false` otherwise.
//

var exists = module.exports.exists = function exists(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return false;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return false;
  }

  if (path.length === 0) {
    return Object.hasOwnProperty.apply(object, [key]);
  } else {
    return exists(object[key], path);
  }
};

//
// These arguments are the same as those for `exists`.
//
// The return value, however, is the property you're trying to access, or
// `undefined` if it can't be found. This means you won't be able to tell
// the difference between an unresolved path and an undefined property, so you
// should not use `get` to check for the existence of a property. Use `exists`
// instead.
//

var get = module.exports.get = function get(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (path.length === 0) {
    return object[key];
  }

  if (path.length) {
    return get(object[key], path);
  }
};

//
// Arguments are similar to `exists` and `get`, with the exception that path
// components are regexes with some special cases. If a path component is `"*"`
// on its own, it'll be converted to `/.*/`.
//
// The return value is an array of values where the key path matches the
// specified criterion. If none match, an empty array will be returned.
//

var search = module.exports.search = function search(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (key === "*") {
    key = ".*";
  }

  if (typeof key === "string") {
    key = new RegExp(key);
  }

  if (path.length === 0) {
    return Object.keys(object).filter(key.test.bind(key)).map(function(k) { return object[k]; });
  } else {
    return Array.prototype.concat.apply([], Object.keys(object).filter(key.test.bind(key)).map(function(k) { return search(object[k], path); }));
  }
};

//
// The first two arguments for `put` are the same as `exists` and `get`.
//
// The third argument is a value to `put` at the `path` of the `object`.
// Objects in the middle will be created if they don't exist, or added to if
// they do. If a value is encountered in the middle of the path that is *not*
// an object, it will not be overwritten.
//
// The return value is `true` in the case that the value was `put`
// successfully, or `false` otherwise.
//

var put = module.exports.put = function put(object, path, value) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return false;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return false;
  }

  if (path.length === 0) {
    object[key] = value;
  } else {
    if (typeof object[key] === "undefined") {
      object[key] = {};
    }

    if (typeof object[key] !== "object" || object[key] === null) {
      return false;
    }

    return put(object[key], path, value);
  }
};

//
// `remove` is like `put` in reverse!
//
// The return value is `true` in the case that the value existed and was removed
// successfully, or `false` otherwise.
//

var remove = module.exports.remove = function remove(object, path, value) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return false;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return false;
  }

  if (path.length === 0) {
    if (!Object.hasOwnProperty.call(object, key)) {
      return false;
    }

    delete object[key];

    return true;
  } else {
    return remove(object[key], path, value);
  }
};

//
// `deepKeys` creates a list of all possible key paths for a given object.
//
// The return value is always an array, the members of which are paths in array
// format. If you want them in dot-notation format, do something like this:
//
// ```js
// dotty.deepKeys(obj).map(function(e) {
//   return e.join(".");
// });
// ```
//
// *Note: this will probably explode on recursive objects. Be careful.*
//

var deepKeys = module.exports.deepKeys = function deepKeys(object, prefix) {
  if (typeof prefix === "undefined") {
    prefix = [];
  }

  var keys = [];

  for (var k in object) {
    if (!Object.hasOwnProperty.call(object, k)) {
      continue;
    }

    keys.push(prefix.concat([k]));

    if (typeof object[k] === "object" && object[k] !== null) {
      keys = keys.concat(deepKeys(object[k], prefix.concat([k])));
    }
  }

  return keys;
};
<commit_msg>add leavesOnly option to deepKeys<commit_after>//
// Dotty makes it easy to programmatically access arbitrarily nested objects and
// their properties.
//

//
// `object` is an object, `path` is the path to the property you want to check
// for existence of.
//
// `path` can be provided as either a `"string.separated.with.dots"` or as
// `["an", "array"]`.
//
// Returns `true` if the path can be completely resolved, `false` otherwise.
//

var exists = module.exports.exists = function exists(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return false;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return false;
  }

  if (path.length === 0) {
    return Object.hasOwnProperty.apply(object, [key]);
  } else {
    return exists(object[key], path);
  }
};

//
// These arguments are the same as those for `exists`.
//
// The return value, however, is the property you're trying to access, or
// `undefined` if it can't be found. This means you won't be able to tell
// the difference between an unresolved path and an undefined property, so you
// should not use `get` to check for the existence of a property. Use `exists`
// instead.
//

var get = module.exports.get = function get(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (path.length === 0) {
    return object[key];
  }

  if (path.length) {
    return get(object[key], path);
  }
};

//
// Arguments are similar to `exists` and `get`, with the exception that path
// components are regexes with some special cases. If a path component is `"*"`
// on its own, it'll be converted to `/.*/`.
//
// The return value is an array of values where the key path matches the
// specified criterion. If none match, an empty array will be returned.
//

var search = module.exports.search = function search(object, path) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return;
  }

  if (key === "*") {
    key = ".*";
  }

  if (typeof key === "string") {
    key = new RegExp(key);
  }

  if (path.length === 0) {
    return Object.keys(object).filter(key.test.bind(key)).map(function(k) { return object[k]; });
  } else {
    return Array.prototype.concat.apply([], Object.keys(object).filter(key.test.bind(key)).map(function(k) { return search(object[k], path); }));
  }
};

//
// The first two arguments for `put` are the same as `exists` and `get`.
//
// The third argument is a value to `put` at the `path` of the `object`.
// Objects in the middle will be created if they don't exist, or added to if
// they do. If a value is encountered in the middle of the path that is *not*
// an object, it will not be overwritten.
//
// The return value is `true` in the case that the value was `put`
// successfully, or `false` otherwise.
//

var put = module.exports.put = function put(object, path, value) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return false;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return false;
  }

  if (path.length === 0) {
    object[key] = value;
  } else {
    if (typeof object[key] === "undefined") {
      object[key] = {};
    }

    if (typeof object[key] !== "object" || object[key] === null) {
      return false;
    }

    return put(object[key], path, value);
  }
};

//
// `remove` is like `put` in reverse!
//
// The return value is `true` in the case that the value existed and was removed
// successfully, or `false` otherwise.
//

var remove = module.exports.remove = function remove(object, path, value) {
  if (typeof path === "string") {
    path = path.split(".");
  }

  if (!(path instanceof Array) || path.length === 0) {
    return false;
  }

  path = path.slice();

  var key = path.shift();

  if (typeof object !== "object" || object === null) {
    return false;
  }

  if (path.length === 0) {
    if (!Object.hasOwnProperty.call(object, key)) {
      return false;
    }

    delete object[key];

    return true;
  } else {
    return remove(object[key], path, value);
  }
};

//
// `deepKeys` creates a list of all possible key paths for a given object.
//
// The return value is always an array, the members of which are paths in array
// format. If you want them in dot-notation format, do something like this:
//
// ```js
// dotty.deepKeys(obj).map(function(e) {
//   return e.join(".");
// });
// ```
//
// *Note: this will probably explode on recursive objects. Be careful.*
//

var deepKeys = module.exports.deepKeys = function deepKeys(object, prefix, leavesOnly) {
  if (typeof prefix === "undefined") {
    prefix = [];
  }

  var keys = [];

  for (var k in object) {
    if (!Object.hasOwnProperty.call(object, k)) {
      continue;
    }

    if (!leavesOnly || typeof object[k] !== "object") {
      keys.push(prefix.concat([k]));
    }

    if (typeof object[k] === "object" && object[k] !== null) {
      keys = keys.concat(deepKeys(object[k], prefix.concat([k])));
    }
  }

  return keys;
};
<|endoftext|>


<commit_before>module.exports = class Radar {
  constructor(github, config) {
    this.github = github;
    this.config = Object.assign({}, require('./defaults'), config || {});
    this.logger = config.logger || console;
    this.issue_body = '## Here is the radar for the week\n'
  }

  //return most recent issues with select labels
  async getIssuesWithLabel(){
    const {owner, repo, labels} = this.config;
    this.logger.debug(owner, repo, labels);
    var all_issues = {};

    await Promise.all(labels.map( async (label) => {
      var query = `repo:${owner}/${repo} is:issue is:open label:${label} `;
      const params = {
        q: query,
        sort: 'updated',
        order: 'desc',
        per_page: 100
      };

      this.logger.debug(params, 'searching %s/%s for issues', owner, repo);
      var issues = await this.github.search.issues(params);
      if(!all_issues[label]){
        all_issues[label] = issues;
      }else{
        all_issues[label] = all_issues.concat(issues);
      }
    }));

    this.logger.debug('all_issues:', all_issues);
    return all_issues;
  }

  async getIssueTitleLink(issues){
    issues.items.forEach(issue =>{
      this.logger.debug('issue_title', issue.title);
      this.logger.debug('issue_title', issue.html_url);
      this.issue_body += `[${issue.title}](${issue.html_url})\n`
    });
  }

  // Create radar issue with issue titles links
  async createRadarIssue(){
    // Check if we need to create an issue
    //TODO  isRadarIssueOpen
    const {owner, radar} = this.config;
    const data = await this.getIssuesWithLabel();
    var
    Object.keys(data).forEach(async (label) =>{
      this.issue_body += `### ${label}`
      await this.getIssueTitleLink(data[label]);
    });

    var params = {
      title: 'Radar for the week',
      labels: radar.labels,
      body: this.issue_body
    }
    this.github.issues.create({owner, radar.repo, params})
  }

  // Close a radar issue if it's open for more than x days
  async closeRadarIssue(){

  }

  //check if there is an open radar issue in the radar repo
  async isRadarIssueOpen(){
    const {owner, radar} = this.config;

    // Get all radar labels
    var radar_labels = radar.labels.join(' label:')
    this.logger.debug('radar_labels: ', radar_labels)

    // Check if the radar issue has an open radar label issue
    var query = `repo:${owner}/${radar.repo} is:issue is:open label:${radar_labels} `
    const params = {
      q: query,
      sort: 'updated',
      order: 'desc',
      per_page: 1
    };

    var radar_issues = await this.github.search.issues(params);

    if(radar_issues.count === 1){
      this.logger.info('Found atleast 1 open radar open issue', radar_issues);
      return true;
    }
    return false;
  }
};
<commit_msg>fix stray var and fix log heading for link<commit_after>module.exports = class Radar {
  constructor(github, config) {
    this.github = github;
    this.config = Object.assign({}, require('./defaults'), config || {});
    this.logger = config.logger || console;
    this.issue_body = '## Here is the radar for the week\n'
  }

  //return most recent issues with select labels
  async getIssuesWithLabel(){
    const {owner, repo, labels} = this.config;
    this.logger.debug(owner, repo, labels);
    var all_issues = {};

    await Promise.all(labels.map( async (label) => {
      var query = `repo:${owner}/${repo} is:issue is:open label:${label} `;
      const params = {
        q: query,
        sort: 'updated',
        order: 'desc',
        per_page: 100
      };

      this.logger.debug(params, 'searching %s/%s for issues', owner, repo);
      var issues = await this.github.search.issues(params);
      if(!all_issues[label]){
        all_issues[label] = issues;
      }else{
        all_issues[label] = all_issues.concat(issues);
      }
    }));

    this.logger.debug('all_issues:', all_issues);
    return all_issues;
  }

  async getIssueTitleLink(issues){
    issues.items.forEach(issue =>{
      this.logger.debug('issue_title', issue.title);
      this.logger.debug('issue_link', issue.html_url);
      this.issue_body += `[${issue.title}](${issue.html_url})\n`
    });
  }

  // Create radar issue with issue titles links
  async createRadarIssue(){
    // Check if we need to create an issue
    //TODO  isRadarIssueOpen
    const {owner, radar} = this.config;
    const data = await this.getIssuesWithLabel();
    Object.keys(data).forEach(async (label) =>{
      this.issue_body += `### ${label}`
      await this.getIssueTitleLink(data[label]);
    });

    var params = {
      title: 'Radar for the week',
      labels: radar.labels,
      body: this.issue_body
    }
    this.github.issues.create({owner, radar.repo, params})
  }

  // Close a radar issue if it's open for more than x days
  async closeRadarIssue(){

  }

  //check if there is an open radar issue in the radar repo
  async isRadarIssueOpen(){
    const {owner, radar} = this.config;

    // Get all radar labels
    var radar_labels = radar.labels.join(' label:')
    this.logger.debug('radar_labels: ', radar_labels)

    // Check if the radar issue has an open radar label issue
    var query = `repo:${owner}/${radar.repo} is:issue is:open label:${radar_labels} `
    const params = {
      q: query,
      sort: 'updated',
      order: 'desc',
      per_page: 1
    };

    var radar_issues = await this.github.search.issues(params);

    if(radar_issues.count === 1){
      this.logger.info('Found atleast 1 open radar open issue', radar_issues);
      return true;
    }
    return false;
  }
};
<|endoftext|>


<commit_before>Version = { id: 'd5c8446' };
<commit_msg>Bump up version<commit_after>Version = { id: '0a1833c' };
<|endoftext|>


<commit_before>iD.ui.preset = function(context, entity, preset) {
    var original = context.graph().base().entities[entity.id],
        event = d3.dispatch('change', 'close'),
        fields = [],
        tags = {},
        formwrap,
        formbuttonwrap;

    function UIField(field, show) {
        field = _.clone(field);

        field.input = iD.ui.preset[field.type](field, context)
            .on('close', event.close)
            .on('change', event.change);

        if (field.type === 'address') {
            field.input.entity(entity);
        }

        field.keys = field.keys || [field.key];

        field.show = show;

        field.shown = function() {
            return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
        };

        field.modified = function() {
            return _.any(field.keys, function(key) {
                return original ? tags[key] !== original.tags[key] : tags[key];
            });
        };

        return field;
    }

    fields.push(UIField(context.presets().field('name')));

    var geometry = entity.geometry(context.graph());
    preset.fields.forEach(function(field) {
        if (field.matchGeometry(geometry)) {
            fields.push(UIField(field, true));
        }
    });

    context.presets().universal().forEach(function(field) {
        if (fields.indexOf(field) < 0) {
            fields.push(UIField(field));
        }
    });

    function fieldKey(field) {
        return field.id;
    }

    function shown() {
        return fields.filter(function(field) { return field.shown(); });
    }

    function notShown() {
        return fields.filter(function(field) { return !field.shown(); });
    }

    function show(field) {
        field.show = true;
        render();
        field.input.focus();
    }

    function revert(field) {
        d3.event.stopPropagation();
        d3.event.preventDefault();
        var t = {};
        field.keys.forEach(function(key) {
            t[key] = original ? original.tags[key] : undefined;
        });
        event.change(t);
    }

    function toggleReference(field) {
        d3.event.stopPropagation();
        d3.event.preventDefault();
        _.forEach(fields, function(other) {
            if (other.id === field.id) {
                other.showingReference = !other.showingReference;
            } else {
                other.showingReference = false;
            }
        });

        render();
    }

    function render() {
        var selection = formwrap.selectAll('.form-field')
            .data(shown(), fieldKey);

        var enter = selection.enter()
            .insert('div', '.more-buttons')
            .style('opacity', 0)
            .attr('class', function(field) {
                return 'form-field form-field-' + field.id + ' fillL col12';
            });

        enter.transition()
            .style('max-height', '0px')
            .style('padding-top', '0px')
            .style('opacity', '0')
            .transition()
            .duration(200)
            .style('padding-top', '20px')
            .style('max-height', '200px')
            .style('opacity', '1');

        var label = enter.append('label')
            .attr('class', 'form-label')
            .attr('for', function(field) { return 'preset-input-' + field.id; })
            .text(function(field) { return field.label(); });

        label.append('button')
            .attr('class', 'tag-reference-button minor')
            .attr('tabindex', -1)
            .on('click', toggleReference)
            .append('span')
            .attr('class', 'icon inspect');

        label.append('button')
            .attr('class', 'modified-icon minor')
            .attr('tabindex', -1)
            .on('click', revert)
            .append('div')
            .attr('class','icon undo');

        enter.each(function(field) {
            d3.select(this).call(field.input);
        });

        enter.append('div')
            .attr('class', 'tag-help');

        selection
            .classed('modified', function(field) {
                return field.modified();
            });

        selection.selectAll('.tag-help')
            .each(function(field) {
                if (field.showingReference) {
                    d3.select(this)
                        .call(iD.ui.TagReference(entity, {key: field.key}))
                        .style('max-height', '0px')
                        .style('padding-top', '0px')
                        .style('opacity', '0')
                        .transition()
                        .duration(200)
                        .style('padding-top', '20px')
                        .style('max-height', '200px')
                        .style('opacity', '1');
                } else {
                    d3.select(this)
                        .transition()
                        .duration(200)
                        .style('max-height', '0px')
                        .style('padding-top', '0px')
                        .style('opacity', '0');
                }
            });

        selection.exit()
            .remove();

        var addFields = formbuttonwrap.selectAll('.preset-add-field')
            .data(notShown(), fieldKey);

        addFields.enter()
            .append('button')
            .attr('class', 'preset-add-field')
            .on('click', show)
            .call(bootstrap.tooltip()
                .placement('top')
                .title(function(d) { return d.label(); }))
            .append('span')
            .attr('class', function(d) { return 'icon ' + d.icon; });

        addFields.exit()
            .transition()
            .style('opacity', 0)
            .remove();

        return selection;
    }

    function presets(selection) {
        selection.html('');

        formwrap = selection;

        formbuttonwrap = selection.append('div')
            .attr('class', 'col12 more-buttons inspector-inner');

        render();
    }

    presets.rendered = function() {
        return _.flatten(shown().map(function(field) { return field.keys; }));
    };

    presets.preset = function(_) {
        if (!arguments.length) return preset;
        preset = _;
        return presets;
    };

    presets.change = function(_) {
        tags = _;

        fields.forEach(function(field) {
            if (field.shown()) {
                field.input.tags(_);
            }
        });

        render();

        return presets;
    };

    return d3.rebind(presets, event, 'on');
};
<commit_msg>Fix preset input error<commit_after>iD.ui.preset = function(context, entity, preset) {
    var original = context.graph().base().entities[entity.id],
        event = d3.dispatch('change', 'close'),
        fields = [],
        tags = {},
        formwrap,
        formbuttonwrap;

    function UIField(field, show) {
        field = _.clone(field);

        field.input = iD.ui.preset[field.type](field, context)
            .on('close', event.close)
            .on('change', event.change);

        if (field.type === 'address') {
            field.input.entity(entity);
        }

        field.keys = field.keys || [field.key];

        field.show = show;

        field.shown = function() {
            return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
        };

        field.modified = function() {
            return _.any(field.keys, function(key) {
                return original ? tags[key] !== original.tags[key] : tags[key];
            });
        };

        return field;
    }

    fields.push(UIField(context.presets().field('name')));

    var geometry = entity.geometry(context.graph());
    preset.fields.forEach(function(field) {
        if (field.matchGeometry(geometry)) {
            fields.push(UIField(field, true));
        }
    });

    context.presets().universal().forEach(function(field) {
        if (fields.indexOf(field) < 0) {
            fields.push(UIField(field));
        }
    });

    function fieldKey(field) {
        return field.id;
    }

    function shown() {
        return fields.filter(function(field) { return field.shown(); });
    }

    function notShown() {
        return fields.filter(function(field) { return !field.shown(); });
    }

    function show(field) {
        field.show = true;
        render();
        field.input.focus();
    }

    function revert(field) {
        d3.event.stopPropagation();
        d3.event.preventDefault();
        var t = {};
        field.keys.forEach(function(key) {
            t[key] = original ? original.tags[key] : undefined;
        });
        event.change(t);
    }

    function toggleReference(field) {
        d3.event.stopPropagation();
        d3.event.preventDefault();
        _.forEach(fields, function(other) {
            if (other.id === field.id) {
                other.showingReference = !other.showingReference;
            } else {
                other.showingReference = false;
            }
        });

        render();
    }

    function render() {
        var selection = formwrap.selectAll('.form-field')
            .data(shown(), fieldKey);

        var enter = selection.enter()
            .insert('div', '.more-buttons')
            .style('opacity', 0)
            .attr('class', function(field) {
                return 'form-field form-field-' + field.id + ' fillL col12';
            });

        enter.transition()
            .style('max-height', '0px')
            .style('padding-top', '0px')
            .style('opacity', '0')
            .transition()
            .duration(200)
            .style('padding-top', '20px')
            .style('max-height', '200px')
            .style('opacity', '1');

        var label = enter.append('label')
            .attr('class', 'form-label')
            .attr('for', function(field) { return 'preset-input-' + field.id; })
            .text(function(field) { return field.label(); });

        label.append('button')
            .attr('class', 'tag-reference-button minor')
            .attr('tabindex', -1)
            .on('click', toggleReference)
            .append('span')
            .attr('class', 'icon inspect');

        label.append('button')
            .attr('class', 'modified-icon minor')
            .attr('tabindex', -1)
            .on('click', revert)
            .append('div')
            .attr('class','icon undo');

        enter.each(function(field) {
            d3.select(this).call(field.input);
        });

        enter.append('div')
            .attr('class', 'tag-help');

        selection
            .each(function(field) {
                field.input.tags(tags);
            })
            .classed('modified', function(field) {
                return field.modified();
            });

        selection.selectAll('.tag-help')
            .each(function(field) {
                if (field.showingReference) {
                    d3.select(this)
                        .call(iD.ui.TagReference(entity, {key: field.key}))
                        .style('max-height', '0px')
                        .style('padding-top', '0px')
                        .style('opacity', '0')
                        .transition()
                        .duration(200)
                        .style('padding-top', '20px')
                        .style('max-height', '200px')
                        .style('opacity', '1');
                } else {
                    d3.select(this)
                        .transition()
                        .duration(200)
                        .style('max-height', '0px')
                        .style('padding-top', '0px')
                        .style('opacity', '0');
                }
            });

        selection.exit()
            .remove();

        var addFields = formbuttonwrap.selectAll('.preset-add-field')
            .data(notShown(), fieldKey);

        addFields.enter()
            .append('button')
            .attr('class', 'preset-add-field')
            .on('click', show)
            .call(bootstrap.tooltip()
                .placement('top')
                .title(function(d) { return d.label(); }))
            .append('span')
            .attr('class', function(d) { return 'icon ' + d.icon; });

        addFields.exit()
            .transition()
            .style('opacity', 0)
            .remove();

        return selection;
    }

    function presets(selection) {
        selection.html('');

        formwrap = selection;

        formbuttonwrap = selection.append('div')
            .attr('class', 'col12 more-buttons inspector-inner');

        render();
    }

    presets.rendered = function() {
        return _.flatten(shown().map(function(field) { return field.keys; }));
    };

    presets.preset = function(_) {
        if (!arguments.length) return preset;
        preset = _;
        return presets;
    };

    presets.change = function(_) {
        tags = _;
        render();
        return presets;
    };

    return d3.rebind(presets, event, 'on');
};
<|endoftext|>


<commit_before>var BloggifyLib = module.exports = {};

BloggifyLib.clearCache = function (options) {

    // Require config
    var Config = global.Config = Utils.requireNoCache(options.root + "/config");

    // Set log level
    Debug._config.logLevel = Config.logLevel;

    // start parsing paths
    Utils.parsePaths(Config.gitSite.paths, ["parsed"]);

    // Attach core pages
    Config.gitSite.parsed.roots.pages["/login"] = {
        url: "/core/html/login.html"
      , visible: false
      , slug: "login"
    };

    Config.gitSite.parsed.roots.pages["/blog"] = {
        url: "/core/html/blog.html"
      , label: "Blog"
      , order: 19
      , slug: "blog"
    };

    Config.gitSite.parsed.roots.pages["/admin"] = {
        url: "/core/html/admin.html"
      , label: "Admin"
      , order: 21
      , loggedIn: true
      , slug: "admin"
    };

    // statique config
    Statique
        .server({ root: options.root + Config.gitSite.url })
        .setRoutes(Config.gitSite.parsed.roots.pages)
      ;
};

/**
 * start
 *
 * @name start
 * @function
 * @param {Object} options The options object containing the following fields:
 *  - root: the bloggify root directory
 * @return
 */
BloggifyLib.start = function (options) {

    // Clear cache
    BloggifyLib.clearCache(options);

    // Handle uncaught exceptions
    if (Config.logLevel < 4) {
        process.on("uncaughtException", function (err) {
            Debug.log(err, "error");
        });
    }

    // Output
    Debug.log("Started Bloggify platform", "error");
};
<commit_msg>Info message instead of error<commit_after>var BloggifyLib = module.exports = {};

BloggifyLib.clearCache = function (options) {

    // Require config
    var Config = global.Config = Utils.requireNoCache(options.root + "/config");

    // Set log level
    Debug._config.logLevel = Config.logLevel;

    // start parsing paths
    Utils.parsePaths(Config.gitSite.paths, ["parsed"]);

    // Attach core pages
    Config.gitSite.parsed.roots.pages["/login"] = {
        url: "/core/html/login.html"
      , visible: false
      , slug: "login"
    };

    Config.gitSite.parsed.roots.pages["/blog"] = {
        url: "/core/html/blog.html"
      , label: "Blog"
      , order: 19
      , slug: "blog"
    };

    Config.gitSite.parsed.roots.pages["/admin"] = {
        url: "/core/html/admin.html"
      , label: "Admin"
      , order: 21
      , loggedIn: true
      , slug: "admin"
    };

    // statique config
    Statique
        .server({ root: options.root + Config.gitSite.url })
        .setRoutes(Config.gitSite.parsed.roots.pages)
      ;
};

/**
 * start
 *
 * @name start
 * @function
 * @param {Object} options The options object containing the following fields:
 *  - root: the bloggify root directory
 * @return
 */
BloggifyLib.start = function (options) {

    // Clear cache
    BloggifyLib.clearCache(options);

    // Handle uncaught exceptions
    if (Config.logLevel < 4) {
        process.on("uncaughtException", function (err) {
            Debug.log(err, "error");
        });
    }

    // Output
    Debug.log("Started Bloggify platform", "info");
};
<|endoftext|>


<commit_before>'use strict';
/**
 * This object contains all the handlers to use for this providers
 */
var googleapis = require('googleapis');
var rarity = require('rarity');
var async = require('async');
var OAuth2Client = googleapis.OAuth2Client;

var config = require('../config/configuration.js');
var retrieveContacts = require('./helpers/retrieve.js');
var uploadContact = require('./helpers/upload.js');

var redirectToService = function(callbackUrl, cb) {
  googleapis.execute(function(err) {
    if(err) {
      return cb(err);
    }

    var oauth2Client = new OAuth2Client(config.googleId, config.googleSecret, callbackUrl);

    // generate consent page url for Google Contacts access, even when user is not connected (offline)
    var redirectUrl = oauth2Client.generateAuthUrl({
      access_type: 'offline',
      scope: 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.email',
      approval_prompt: 'force', // Force resending a refresh_token
    });

    cb(null, redirectUrl, {redirectUrl: redirectUrl, callbackUrl: callbackUrl});
  });
};

var retrieveTokens = function(reqParams, storedParams, cb) {
  async.waterfall([
    function(cb) {
      googleapis.discover('oauth2', 'v2').execute(cb);
    },
    function(client, cb) {
      var oauth2Client = new OAuth2Client(config.googleId, config.googleSecret, storedParams.callbackUrl);
      oauth2Client.getToken(reqParams.code, rarity.carryAndSlice([oauth2Client, client], 4, cb));
    },
    function(oauth2Client, client, tokens, cb) {
      oauth2Client.credentials = tokens;
      client.oauth2.userinfo.get().withAuthClient(oauth2Client).execute(rarity.carryAndSlice([tokens], 3, cb));
    },
    function(tokens, data, cb) {
      cb(null, data.email, tokens);
    }
  ], cb);
};

var updateAccount = function(serviceData, cursor, queues, cb) {
  // Retrieve all contacts since last call
  if(!cursor) {
    cursor = new Date(1970);
  }
  var newCursor = new Date();

  retrieveContacts(serviceData.refresh_token, cursor, function(err, contacts) {
    if(contacts) {
      contacts.forEach(function(contact) {
        if(contact.deleted) {
          queues.deletion.push(contact);
        }
        else {
          queues.addition.push(contact);
        }
      });
    }

    cb(err, newCursor);
  });
};

var additionQueueWorker = function(job, cb) {
  uploadContact(job.task, job.anyfetchClient, job.serviceData.refresh_token, cb);
};

var deletionQueueWorker = function(job, cb) {
  job.anyfetchClient.deleteDocument(job.task.url, rarity.slice(1, cb));
};

module.exports = {
  connectFunctions: {
    redirectToService: redirectToService,
    retrieveTokens: retrieveTokens
  },
  updateAccount: updateAccount,
  workers: {
    addition: additionQueueWorker,
    deletion: deletionQueueWorker
  },

  config: config
};
<commit_msg>Named functions<commit_after>'use strict';
/**
 * This object contains all the handlers to use for this providers
 */
var googleapis = require('googleapis');
var rarity = require('rarity');
var async = require('async');
var OAuth2Client = googleapis.OAuth2Client;

var config = require('../config/configuration.js');
var retrieveContacts = require('./helpers/retrieve.js');
var uploadContact = require('./helpers/upload.js');

var redirectToService = function(callbackUrl, cb) {
  googleapis.execute(function(err) {
    if(err) {
      return cb(err);
    }

    var oauth2Client = new OAuth2Client(config.googleId, config.googleSecret, callbackUrl);

    // generate consent page url for Google Contacts access, even when user is not connected (offline)
    var redirectUrl = oauth2Client.generateAuthUrl({
      access_type: 'offline',
      scope: 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.email',
      approval_prompt: 'force', // Force resending a refresh_token
    });

    cb(null, redirectUrl, {redirectUrl: redirectUrl, callbackUrl: callbackUrl});
  });
};

var retrieveTokens = function(reqParams, storedParams, cb) {
  async.waterfall([
    function getClient(cb) {
      googleapis.discover('oauth2', 'v2').execute(cb);
    },
    function getToken(client, cb) {
      var oauth2Client = new OAuth2Client(config.googleId, config.googleSecret, storedParams.callbackUrl);
      oauth2Client.getToken(reqParams.code, rarity.carryAndSlice([oauth2Client, client], 4, cb));
    },
    function getUserInfo(oauth2Client, client, tokens, cb) {
      oauth2Client.credentials = tokens;
      client.oauth2.userinfo.get().withAuthClient(oauth2Client).execute(rarity.carryAndSlice([tokens], 3, cb));
    },
    function callFinalCb(tokens, data, cb) {
      cb(null, data.email, tokens);
    }
  ], cb);
};

var updateAccount = function(serviceData, cursor, queues, cb) {
  // Retrieve all contacts since last call
  if(!cursor) {
    cursor = new Date(1970);
  }
  var newCursor = new Date();

  retrieveContacts(serviceData.refresh_token, cursor, function(err, contacts) {
    if(contacts) {
      contacts.forEach(function(contact) {
        if(contact.deleted) {
          queues.deletion.push(contact);
        }
        else {
          queues.addition.push(contact);
        }
      });
    }

    cb(err, newCursor);
  });
};

var additionQueueWorker = function(job, cb) {
  uploadContact(job.task, job.anyfetchClient, job.serviceData.refresh_token, cb);
};

var deletionQueueWorker = function(job, cb) {
  job.anyfetchClient.deleteDocument(job.task.url, rarity.slice(1, cb));
};

module.exports = {
  connectFunctions: {
    redirectToService: redirectToService,
    retrieveTokens: retrieveTokens
  },
  updateAccount: updateAccount,
  workers: {
    addition: additionQueueWorker,
    deletion: deletionQueueWorker
  },

  config: config
};
<|endoftext|>