<commit_before>var $ = require('jquery');
var moment = require('moment');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var _ = require('underscore');
var PrivacyWarningView = require('builder/components/modals/privacy-warning/privacy-warning-view');
var ModalsServiceModel = require('builder/components/modals/modals-service-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var template = require('./publish-button.tpl');

var REQUIRED_OPTS = [
  'visDefinitionModel',
  'mapcapsCollection',
  'configModel',
  'userModel'
];

module.exports = CoreView.extend({

  events: {
    'click .js-button': '_onUpdate'
  },

  initialize: function (opts) {
    _.each(REQUIRED_OPTS, function (item) {
      if (opts[item] === undefined) throw new Error(item + ' is required');
      this['_' + item] = opts[item];
    }, this);

    // We recreate this modal stack to be able to
    // have two modals stacked on screen
    this._modals = new ModalsServiceModel();

    this.model = new Backbone.Model({
      status: 'idle'
    });

    this._initBinds();
  },

  render: function () {
    var publishedOn = this._mapcapsCollection.length > 0
      ? _t('components.modals.publish.share.last-published', { date:
                        this.model.get('status') === 'updated'
                          ? moment(this._mapcapsCollection.first().get('created_at')).fromNow()
                          : moment(this._mapcapsCollection.first().get('created_at')).format('Do MMMM YYYY, HH:mm')
      })
      : _t('components.modals.publish.share.unpublished');

    this.clearSubViews();
    this.$el.html(template({
      isPublished: this._isPublished(),
      isLoading: this._isLoading(),
      isDisabled: this._isDisabled(),
      publishedOn: publishedOn
    }));
    return this;
  },

  _initBinds: function () {
    this._mapcapsCollection.on('reset', this.render, this);
    this.add_related_model(this._mapcapsCollection);
    this.model.on('change:status', this.render, this);
  },

  _isPublished: function () {
    return this._mapcapsCollection.length > 0;
  },

  _isLoading: function () {
    return this.model.get('status') === 'loading';
  },

  _isDisabled: function () {
    return this.model.get('status') === 'updated';
  },

  _onUpdate: function () {
    var self = this;
    var url = this._visDefinitionModel.mapcapsURL();
    var data = {
      api_key: this._configModel.get('api_key')
    };

    if (this._isDisabled()) return false;

    if (this._shouldShowPrivacyWarning()) {
      this._checkPrivacyChange(
        function () { self._createMapCap(url, data); }
      );
    } else {
      this._createMapCap(url, data);
    }
  },

  _checkPrivacyChange: function (confirmCallback, dismissCallback) {
    this._modals.create(function (modalModel) {
      return new PrivacyWarningView({
        modalModel: modalModel,
        privacyType: this._visDefinitionModel.get('privacy'),
        onConfirm: confirmCallback,
        onDismiss: dismissCallback
      });
    });
  },

  _createMapCap: function (url, data) {
    var self = this;
    this.model.set({status: 'loading'});

    $.post(url, data)
      .done(function (data) {
        self._visDefinitionModel.set('visChanges', 0);
        self._mapcapsCollection.add(data, {at: 0});
        self.model.set({status: 'updated'});
      })
      .fail(function () {
        self.model.set({status: 'error'});
      });
  },

  _shouldShowPrivacyWarning: function () {
    return !this._userModel.hasAccountType('PERSONAL30') &&
      !this._isPublished() &&
      VisDefinitionModel.isPubliclyAvailable(this._visDefinitionModel.get('privacy'));
  },

  clean: function () {
    this._modals.destroy();
    CoreView.prototype.clean.apply(this, arguments);
  }
});
<commit_msg>Inject newPrivacyStatus to _checkPrivacyChange in PublishButtonView<commit_after>var $ = require('jquery');
var moment = require('moment');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var _ = require('underscore');
var PrivacyWarningView = require('builder/components/modals/privacy-warning/privacy-warning-view');
var ModalsServiceModel = require('builder/components/modals/modals-service-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
var template = require('./publish-button.tpl');

var REQUIRED_OPTS = [
  'visDefinitionModel',
  'mapcapsCollection',
  'configModel',
  'userModel'
];

module.exports = CoreView.extend({

  events: {
    'click .js-button': '_onUpdate'
  },

  initialize: function (opts) {
    _.each(REQUIRED_OPTS, function (item) {
      if (opts[item] === undefined) throw new Error(item + ' is required');
      this['_' + item] = opts[item];
    }, this);

    // We recreate this modal stack to be able to
    // have two modals stacked on screen
    this._modals = new ModalsServiceModel();

    this.model = new Backbone.Model({
      status: 'idle'
    });

    this._initBinds();
  },

  render: function () {
    var publishedOn = this._mapcapsCollection.length > 0
      ? _t('components.modals.publish.share.last-published', { date:
                        this.model.get('status') === 'updated'
                          ? moment(this._mapcapsCollection.first().get('created_at')).fromNow()
                          : moment(this._mapcapsCollection.first().get('created_at')).format('Do MMMM YYYY, HH:mm')
      })
      : _t('components.modals.publish.share.unpublished');

    this.clearSubViews();
    this.$el.html(template({
      isPublished: this._isPublished(),
      isLoading: this._isLoading(),
      isDisabled: this._isDisabled(),
      publishedOn: publishedOn
    }));
    return this;
  },

  _initBinds: function () {
    this._mapcapsCollection.on('reset', this.render, this);
    this.add_related_model(this._mapcapsCollection);
    this.model.on('change:status', this.render, this);
  },

  _isPublished: function () {
    return this._mapcapsCollection.length > 0;
  },

  _isLoading: function () {
    return this.model.get('status') === 'loading';
  },

  _isDisabled: function () {
    return this.model.get('status') === 'updated';
  },

  _onUpdate: function () {
    var self = this;
    var url = this._visDefinitionModel.mapcapsURL();
    var data = {
      api_key: this._configModel.get('api_key')
    };

    if (this._isDisabled()) return false;

    if (this._shouldShowPrivacyWarning()) {
      this._checkPrivacyChange(
        this._visDefinitionModel.get('privacy'),
        function () { self._createMapCap(url, data); }
      );
    } else {
      this._createMapCap(url, data);
    }
  },

  _checkPrivacyChange: function (newPrivacyStatus, confirmCallback, dismissCallback) {
    this._modals.create(function (modalModel) {
      return new PrivacyWarningView({
        modalModel: modalModel,
        privacyType: newPrivacyStatus,
        onConfirm: confirmCallback,
        onDismiss: dismissCallback
      });
    });
  },

  _createMapCap: function (url, data) {
    var self = this;
    this.model.set({status: 'loading'});

    $.post(url, data)
      .done(function (data) {
        self._visDefinitionModel.set('visChanges', 0);
        self._mapcapsCollection.add(data, {at: 0});
        self.model.set({status: 'updated'});
      })
      .fail(function () {
        self.model.set({status: 'error'});
      });
  },

  _shouldShowPrivacyWarning: function () {
    return !this._userModel.hasAccountType('PERSONAL30') &&
      !this._isPublished() &&
      VisDefinitionModel.isPubliclyAvailable(this._visDefinitionModel.get('privacy'));
  },

  clean: function () {
    this._modals.destroy();
    CoreView.prototype.clean.apply(this, arguments);
  }
});
<|endoftext|>


<commit_before>/* dependencies */
var http = require("http")
  , HTTPClient = require(path.join(require.resolve("socket.io"), "..", "test", "common.js"))
  , io = require("socket.io");

/* constructor */
module.exports = TestClient = function(test) {
  this._test = test;

  this._server_port = test._port;

  this._http_client = new HTTPClient(this._server_port);
  this._cookie = null;

  this._socket = null;
  this._connected = false;
  this._heartbeats = 0;
}

/* @returns {Number} cumulative heartbeats */
TestClient.prototype.__defineGetter__("heartbeats", function() { return this._heartbeats });

/* @returns {Number} socket id */
TestClient.prototype.__defineGetter__("sid", function() { return this._socket ? this._socket.sid : null });

/* @returns {String} cookie id */
TestClient.prototype.__defineGetter__("cookie", function() { return this._cookie });

/* issue get request
 *
 * @param {String} path
 * @param {Function} callback given response object
 * @api public
 */
TestClient.prototype.get = function(path, callback) {
  var self = this;
  ext.Sync.wait_for(function() { return self._test.bound }, function() {
    var options = {host: "127.0.0.1", port: self._server_port, path: path};
    if (self._cookie !== null) options.headers = {"Cookie": self._cookie };

    var request = http.get(options, function(response) {
      try {
        if (response.headers["set-cookie"]) {
          self._cookie = response.headers["set-cookie"][0].split("; ")[0].split("=")[1];
        }
        callback(response);
      } catch(e) {
        self._test._error = e;
      }
    });
    request.on("error", function(e) { self._test._error = e });
  });
}

/* handshake with server
 *
 * @param {Object} opts
 * @param {Function} callback called after websocket is created
 * @param {Object} test caller
 * @api public
 */
TestClient.prototype.ws_handshake = function(opts, callback) {
  var self = this;
  ext.Sync.wait_for(function() { return self._test.bound }, function() {
    self._http_client.handshake(opts, function(sid) {
      self._socket = websocket(self._http_client, sid);

      self._socket.on("message", function(msg) {
        if (msg && msg.type && msg.type === "connect") {
          self._connected = true;

        } else if (msg && msg.type && msg.type === "heartbeat") {
          self._socket.packet({ type: 'heartbeat' });
          self._heartbeats++;
        }
      });

      if (callback) callback();
    });
  });
}

/* connect with server
 *
 * @param {Function} callback called after open event
 * @param {Object} test caller
 * @api public
 */
TestClient.prototype.ws_connect = function(callback) {
  var self = this;
  this.ws_handshake({}, function() {
    self._socket.on("open", function() {
      try {
        callback();
      } catch(e) {
        self._test._error = e;
      }
    });

    self._socket.on("error", function (e) { self._test._error = e });
  });
}

/* sends a packet to the server
 *
 * @param {Object} packet for a comprehensive list of packet types, see lib/parser.js in socket.io
 * @api public
 */
TestClient.prototype.ws_send_packet = function(packet) {
  var self = this;
  ext.Sync.wait_for(function() { return self._connected }, function() {
    self._socket.packet(packet);
  });
}

/* emits an event of name with data
 *
 * @param {String} name
 * @param {Object} data
 * @api public
 */
TestClient.prototype.ws_emit = function(name, data) {
  this.ws_send_packet({type: "event", name: name, args: [data]});
}

/* @api private */
TestClient.prototype.end = function() {
  this._http_client.end();
  if (this._socket) this._socket.finishClose();
}
<commit_msg>documentation clean up<commit_after>/* dependencies */
var http = require("http")
  , HTTPClient = require(path.join(require.resolve("socket.io"), "..", "test", "common.js"))
  , io = require("socket.io");

/* constructor */
module.exports = TestClient = function(test) {
  this._test = test;

  this._server_port = test._port;

  this._http_client = new HTTPClient(this._server_port);
  this._cookie = null;

  this._socket = null;
  this._connected = false;
  this._heartbeats = 0;
}

/* @returns {Number} cumulative heartbeats */
TestClient.prototype.__defineGetter__("heartbeats", function() { return this._heartbeats });

/* @returns {Number} socket id */
TestClient.prototype.__defineGetter__("sid", function() { return this._socket ? this._socket.sid : null });

/* @returns {String} cookie id */
TestClient.prototype.__defineGetter__("cookie", function() { return this._cookie });

/* issue get request
 *
 * @param {String} path
 * @param {Function} callback
 ** response
 * @api public
 */
TestClient.prototype.get = function(path, callback) {
  var self = this;
  ext.Sync.wait_for(function() { return self._test.bound }, function() {
    var options = {host: "127.0.0.1", port: self._server_port, path: path};
    if (self._cookie !== null) options.headers = {"Cookie": self._cookie };

    var request = http.get(options, function(response) {
      try {
        if (response.headers["set-cookie"]) {
          self._cookie = response.headers["set-cookie"][0].split("; ")[0].split("=")[1];
        }
        callback(response);
      } catch(e) {
        self._test._error = e;
      }
    });
    request.on("error", function(e) { self._test._error = e });
  });
}

/* handshake with server
 *
 * @param {Object} opts
 * @param {Function} callback called after websocket is created
 * @api public
 */
TestClient.prototype.ws_handshake = function(opts, callback) {
  var self = this;
  ext.Sync.wait_for(function() { return self._test.bound }, function() {
    self._http_client.handshake(opts, function(sid) {
      self._socket = websocket(self._http_client, sid);

      self._socket.on("message", function(msg) {
        if (msg && msg.type && msg.type === "connect") {
          self._connected = true;

        } else if (msg && msg.type && msg.type === "heartbeat") {
          self._socket.packet({ type: 'heartbeat' });
          self._heartbeats++;
        }
      });

      if (callback) callback();
    });
  });
}

/* connect with server
 *
 * @param {Function} callback called after open event
 * @param {Object} test caller
 * @api public
 */
TestClient.prototype.ws_connect = function(callback) {
  var self = this;
  this.ws_handshake({}, function() {
    self._socket.on("open", function() {
      try {
        callback();
      } catch(e) {
        self._test._error = e;
      }
    });

    self._socket.on("error", function (e) { self._test._error = e });
  });
}

/* sends a packet to the server
 *
 * for a comprehensive list of packet types, see lib/parser.js in socket.io
 *
 * @param {Object} packet
 * @api public
 */
TestClient.prototype.ws_send_packet = function(packet) {
  var self = this;
  ext.Sync.wait_for(function() { return self._connected }, function() {
    self._socket.packet(packet);
  });
}

/* emits an event of name with data
 *
 * @param {String} name
 * @param {Object} data
 * @api public
 */
TestClient.prototype.ws_emit = function(name, data) {
  this.ws_send_packet({type: "event", name: name, args: [data]});
}

/* @api private */
TestClient.prototype.end = function() {
  this._http_client.end();
  if (this._socket) this._socket.finishClose();
}
<|endoftext|>


<commit_before>var $ = require('jquery');
var CustomListCollection = require('../../../../../javascripts/cartodb3/components/custom-list/custom-list-collection');
var ContextMenuView = require('../../../../../javascripts/cartodb3/components/context-menu/context-menu-view');

var simulateEscapeKeyPress = function () {
  var e = $.Event('keydown');
  e.keyCode = e.which = $.ui.keyCode.ESCAPE;
  $(document).trigger(e);
};

describe('components/context-menu/context-menu-view', function () {
  beforeEach(function () {
    var self = this;

    this.collection = new CustomListCollection([
      {
        label: 'Label 1',
        val: 'value-1'
      }, {
        label: 'Label 2',
        val: 'value-2'
      }
    ]);

    this.triggerElement = $('

');
    this.button = $('<button class="js-show-menu"></button>');
    this.triggerElement.append(this.button);

    $('body').append(this.triggerElement);

    this.view = new ContextMenuView({
      collection: this.collection,
      triggerElementID: this.triggerElement[0].id
    });

    this.triggerElement.on('click', function (ev) {
      console.log("paco", ev.target);
      self.view.toggle();
    });

    this.view.render();
    $('body').append(this.view.el);

    this.view.$el.appendTo(document.body);
  });

  afterEach(function () {
    this.triggerElement.remove();
    this.view.clean();
  });

  it('should render a list with the given items', function () {
    expect(this.view.$el.find('button[title="Label 1"]')).toBeDefined();
    expect(this.view.$el.find('button[title="Label 2"]')).toBeDefined();
  });

  it('should be toggled when clicking on the triggerElement', function () {
    expect(this.view.$el.css('display')).toEqual('none');

    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');

    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be toggled when clicking on an element inside of the triggerElement', function () {
    expect(this.view.$el.css('display')).toEqual('none');

    this.button.click();

    console.log(this.view.isVisible());
    expect(this.view.$el.css('display')).toEqual('block');

    this.button.click();

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be hidden when clicking on the document', function () {
    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');

    $('body').click();
    spyOn(this.view, '_onDocumentElementClicked');

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be hidden when hitting ESCAPE', function () {
    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');

    simulateEscapeKeyPress();

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be hidden when clicking on one of the items', function () {
    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');
    expect(this.collection.at(0).get('selected')).toBe(false);
    expect(this.collection.at(1).get('selected')).toBe(false);

    this.view.$el.find('button[title="Label 1"]').click();
    expect(this.collection.at(0).get('selected')).toBe(true);
    expect(this.collection.at(1).get('selected')).toBe(false);

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should apply the given offset', function () {
    this.view = new ContextMenuView({
      collection: this.collection,
      triggerElementID: this.triggerElement.attr('id'),
      offset: { top: '0px', right: '1px', bottom: '2px', left: '3px', wadus: 'wadus' }
    });

    this.view.render();

    expect(this.view.$el.css('top')).toEqual('0px');
    expect(this.view.$el.css('right')).toEqual('1px');
    expect(this.view.$el.css('bottom')).toEqual('2px');
    expect(this.view.$el.css('left')).toEqual('3px');
    expect(this.view.$el.css('wadus')).toBeUndefined();
  });

  it('should not try to hide the context menu when clicking somewhere else', function () {
    spyOn(this.view, 'hide');
    $('body').click();
    expect(this.view.hide).not.toHaveBeenCalled();
    this.view.show();
    $('body').click();
    expect(this.view.hide).toHaveBeenCalled();
  });

  it('should not try to hide the context menu when ESC key is pressed', function () {
    spyOn(this.view, 'hide');
    simulateEscapeKeyPress();
    expect(this.view.hide).not.toHaveBeenCalled();
    this.view.show();
    simulateEscapeKeyPress();
    expect(this.view.hide).toHaveBeenCalled();
  });

  describe('.clean', function () {
    beforeEach(function () {
      this.view.show();
    });

    it('should unbind document clicks', function () {
      spyOn(this.view, 'hide');

      $('body').click();

      expect(this.view.hide).toHaveBeenCalled();
      this.view.hide.calls.reset();

      this.view.clean();

      $('body').click();

      expect(this.view.hide).not.toHaveBeenCalled();
    });

    it('should unbind ESC key', function () {
      spyOn(this.view, 'hide');

      simulateEscapeKeyPress();

      expect(this.view.hide).toHaveBeenCalled();
      this.view.hide.calls.reset();

      this.view.clean();

      simulateEscapeKeyPress();

      expect(this.view.hide).not.toHaveBeenCalled();
    });
  });
});
<commit_msg>Removed unnecessary consoles<commit_after>var $ = require('jquery');
var CustomListCollection = require('../../../../../javascripts/cartodb3/components/custom-list/custom-list-collection');
var ContextMenuView = require('../../../../../javascripts/cartodb3/components/context-menu/context-menu-view');

var simulateEscapeKeyPress = function () {
  var e = $.Event('keydown');
  e.keyCode = e.which = $.ui.keyCode.ESCAPE;
  $(document).trigger(e);
};

describe('components/context-menu/context-menu-view', function () {
  beforeEach(function () {
    var self = this;

    this.collection = new CustomListCollection([
      {
        label: 'Label 1',
        val: 'value-1'
      }, {
        label: 'Label 2',
        val: 'value-2'
      }
    ]);

    this.triggerElement = $('

');
    this.button = $('<button class="js-show-menu"></button>');
    this.triggerElement.append(this.button);

    $('body').append(this.triggerElement);

    this.view = new ContextMenuView({
      collection: this.collection,
      triggerElementID: this.triggerElement[0].id
    });

    this.triggerElement.on('click', function (ev) {
      self.view.toggle();
    });

    this.view.render();
    $('body').append(this.view.el);

    this.view.$el.appendTo(document.body);
  });

  afterEach(function () {
    this.triggerElement.remove();
    this.view.clean();
  });

  it('should render a list with the given items', function () {
    expect(this.view.$el.find('button[title="Label 1"]')).toBeDefined();
    expect(this.view.$el.find('button[title="Label 2"]')).toBeDefined();
  });

  it('should be toggled when clicking on the triggerElement', function () {
    expect(this.view.$el.css('display')).toEqual('none');

    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');

    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be toggled when clicking on an element inside of the triggerElement', function () {
    expect(this.view.$el.css('display')).toEqual('none');

    this.button.click();

    expect(this.view.$el.css('display')).toEqual('block');

    this.button.click();

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be hidden when clicking on the document', function () {
    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');

    $('body').click();
    spyOn(this.view, '_onDocumentElementClicked');

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be hidden when hitting ESCAPE', function () {
    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');

    simulateEscapeKeyPress();

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should be hidden when clicking on one of the items', function () {
    this.triggerElement.click();

    expect(this.view.$el.css('display')).toEqual('block');
    expect(this.collection.at(0).get('selected')).toBe(false);
    expect(this.collection.at(1).get('selected')).toBe(false);

    this.view.$el.find('button[title="Label 1"]').click();
    expect(this.collection.at(0).get('selected')).toBe(true);
    expect(this.collection.at(1).get('selected')).toBe(false);

    expect(this.view.$el.css('display')).toEqual('none');
  });

  it('should apply the given offset', function () {
    this.view = new ContextMenuView({
      collection: this.collection,
      triggerElementID: this.triggerElement.attr('id'),
      offset: { top: '0px', right: '1px', bottom: '2px', left: '3px', wadus: 'wadus' }
    });

    this.view.render();

    expect(this.view.$el.css('top')).toEqual('0px');
    expect(this.view.$el.css('right')).toEqual('1px');
    expect(this.view.$el.css('bottom')).toEqual('2px');
    expect(this.view.$el.css('left')).toEqual('3px');
    expect(this.view.$el.css('wadus')).toBeUndefined();
  });

  it('should not try to hide the context menu when clicking somewhere else', function () {
    spyOn(this.view, 'hide');
    $('body').click();
    expect(this.view.hide).not.toHaveBeenCalled();
    this.view.show();
    $('body').click();
    expect(this.view.hide).toHaveBeenCalled();
  });

  it('should not try to hide the context menu when ESC key is pressed', function () {
    spyOn(this.view, 'hide');
    simulateEscapeKeyPress();
    expect(this.view.hide).not.toHaveBeenCalled();
    this.view.show();
    simulateEscapeKeyPress();
    expect(this.view.hide).toHaveBeenCalled();
  });

  describe('.clean', function () {
    beforeEach(function () {
      this.view.show();
    });

    it('should unbind document clicks', function () {
      spyOn(this.view, 'hide');

      $('body').click();

      expect(this.view.hide).toHaveBeenCalled();
      this.view.hide.calls.reset();

      this.view.clean();

      $('body').click();

      expect(this.view.hide).not.toHaveBeenCalled();
    });

    it('should unbind ESC key', function () {
      spyOn(this.view, 'hide');

      simulateEscapeKeyPress();

      expect(this.view.hide).toHaveBeenCalled();
      this.view.hide.calls.reset();

      this.view.clean();

      simulateEscapeKeyPress();

      expect(this.view.hide).not.toHaveBeenCalled();
    });
  });
});
<|endoftext|>


<commit_before>import glob from 'glob';
import path from 'path';
import fs from 'fs-extra';
import each from 'lodash/collection/each';
import map from 'lodash/collection/map';
import config from '../config';
import Parse from '../parse';
import * as CONSTANTS from '../constants';
import Asset from './asset';

export default class Theme {
  constructor() {
    /**
     * The current theme name.
     * @type {string}
     */
    this.name = '';

    /**
     * Raw object that holds the theme config object.
     * @type {Object}
     */
    this.config;

    /**
     * Collection of all assets for this theme.
     * @type {Object.<string, Asset>}
     */
    this.assets = Object.create(null);

    /**
     * Data of asset names and paths that is exposed to templates.
     * @type {Object}
     */
    this.data = Object.create(null);
  }

  update() {
    this.name = config.theme;

    // Find all theme config files that might exist.
    let files = glob.sync(config.path.themes + `/**/${CONSTANTS.YAML.THEME}`, {
      nodir: true
    }).reduce((allFiles, file) => {
      let parsedFile = Parse.fromYaml(
        fs.readFileSync(file, 'utf8')
      );

      // If the found theme config file name matches our set theme name
      // then save it.
      if (parsedFile.name === this.name) {
        // Save the directory where the correct theme file was found.
        parsedFile.path[CONSTANTS.KEY.SOURCE] = path.dirname(file);
        allFiles.push(parsedFile);
      }

      return allFiles;
    }, []);

    if (files.length === 0) {
      throw new Error(`Did not find any theme named '${this.name}' installed.`);
    } else if (files.length !== 1) {
      throw new Error('Found multiple themes with the same name: ' +
        files.join(','));
    }

    let parsedFile = files[0];

    // Save raw theme config.
    this.config = parsedFile;

    // Calculate absolute path of 'paths' keys.
    each(this.config.path, (val, key) => {
      if (key !== CONSTANTS.KEY.SOURCE) {
        // The root of the theme's destination is the site's destination.
        let rootPath = key === CONSTANTS.KEY.DESTINATION ?
          config.path.destination : this.config.path.source;

        this.config.path[key] = path.resolve(
          rootPath,
          this.config.path[key]
        );
      }
    });

    this._createAssets();
  }

  _createAssets() {
    // Instantiate an Asset for every asset the theme configures.
    each(this.config.assets, (assetConfig, assetType) => {
      assetConfig.destination = path.resolve(
        this.config.path.destination,
        assetConfig.source
      );

      assetConfig.source = path.resolve(
        this.config.path.source,
        assetConfig.source
      );

      let asset = new Asset(assetType, assetConfig);
      this.assets[asset.id] = asset;
    });
  }

  async read() {
    // Have every asset write itself to the destination folder.
    await Promise.all(
      map(this.assets, assetProcessor => {
        return assetProcessor.process();
      })
    );

    // Expose every asset's data onto the theme's data object.
    each(this.assets, assetProcessor => {
      if (assetProcessor.data) {
        this.data[assetProcessor.type] = assetProcessor.data.url;
      }
    });
  }

  async write() {
    // Have every asset write itself to the destination folder.
    await Promise.all(
      map(this.assets, assetProcessor => {
        return assetProcessor.write();
      })
    );
  }
}
<commit_msg>Allow loading themes from package.json.<commit_after>import glob from 'glob';
import path from 'path';
import fs from 'fs-extra';
import each from 'lodash/collection/each';
import map from 'lodash/collection/map';
import config from '../config';
import {yarnPackageNameRegex, getYarnPackageNames} from '../json';
import Parse from '../parse';
import * as CONSTANTS from '../constants';
import Asset from './asset';

export default class Theme {
  constructor() {
    /**
     * The current theme name.
     * @type {string}
     */
    this.name = '';

    /**
     * Raw object that holds the theme config object.
     * @type {Object}
     */
    this.config;

    /**
     * Collection of all assets for this theme.
     * @type {Object.<string, Asset>}
     */
    this.assets = Object.create(null);

    /**
     * Data of asset names and paths that is exposed to templates.
     * @type {Object}
     */
    this.data = Object.create(null);
  }

  /**
   * Find all theme.yml files in node_modules.
   * @param {string} directory Directory to load package.json from.
   * @return {Array.<string>} Array of paths to theme.yml files.
   */
  _getThemesFromPackageJson(directory = '') {
    const yarnThemePackageNameRegex = /^yarn\-theme\-/;

    // Path to node_modules.
    const nodeModulesPath = path.resolve(config.path.source, 'node_modules');

    // All yarn packages that exist in our root package.json file.
    const yarnPackages = getYarnPackageNames(directory);

    // Return only the path to the theme.yml file.
    return yarnPackages.reduce((packageThemes, packageName) => {
      if (!yarnThemePackageNameRegex.test(packageName)) {
        return packageThemes;
      }

      const yamlPath = path.join(
        nodeModulesPath,
        packageName,
        CONSTANTS.YAML.THEME
      );

      packageThemes.push(yamlPath);

      return packageThemes;
    }, []);
  }

  update() {
    this.name = config.theme;

    // Find all theme config files that might exist.
    const fileSystemYamls = glob.sync(
      config.path.themes + `/**/${CONSTANTS.YAML.THEME}`,
      { nodir: true }
    );

    const moduleYamls = this._getThemesFromPackageJson(config.path.source);

    const allYamls = [].concat(fileSystemYamls, moduleYamls);

    let files = allYamls.reduce((allFiles, file) => {
      let parsedFile = Parse.fromYaml(
        fs.readFileSync(file, 'utf8')
      );

      // If the found theme config file name matches our set theme name
      // then save it.
      if (parsedFile.name === this.name) {
        // Save the directory where the correct theme file was found.
        parsedFile.path[CONSTANTS.KEY.SOURCE] = path.dirname(file);
        allFiles.push(parsedFile);
      }

      return allFiles;
    }, []);

    if (files.length === 0) {
      throw new Error(`Did not find any theme named '${this.name}' installed.`);
    } else if (files.length !== 1) {
      throw new Error('Found multiple themes with the same name: ' +
        files.join(','));
    }

    let parsedFile = files[0];

    // Save raw theme config.
    this.config = parsedFile;

    // Calculate absolute path of 'paths' keys.
    each(this.config.path, (val, key) => {
      if (key !== CONSTANTS.KEY.SOURCE) {
        // The root of the theme's destination is the site's destination.
        let rootPath = key === CONSTANTS.KEY.DESTINATION ?
          config.path.destination : this.config.path.source;

        this.config.path[key] = path.resolve(
          rootPath,
          this.config.path[key]
        );
      }
    });

    this._createAssets();
  }

  _createAssets() {
    // Instantiate an Asset for every asset the theme configures.
    each(this.config.assets, (assetConfig, assetType) => {
      assetConfig.destination = path.resolve(
        this.config.path.destination,
        assetConfig.source
      );

      assetConfig.source = path.resolve(
        this.config.path.source,
        assetConfig.source
      );

      let asset = new Asset(assetType, assetConfig);
      this.assets[asset.id] = asset;
    });
  }

  async read() {
    // Have every asset write itself to the destination folder.
    await Promise.all(
      map(this.assets, assetProcessor => {
        return assetProcessor.process();
      })
    );

    // Expose every asset's data onto the theme's data object.
    each(this.assets, assetProcessor => {
      if (assetProcessor.data) {
        this.data[assetProcessor.type] = assetProcessor.data.url;
      }
    });
  }

  async write() {
    // Have every asset write itself to the destination folder.
    await Promise.all(
      map(this.assets, assetProcessor => {
        return assetProcessor.write();
      })
    );
  }
}
<|endoftext|>


<commit_before>(function () {"use strict";})();
/**
 * Created by apizzimenti on 7/15/16.
 */

/**
 * @author Anthony Pizzimenti
 *
 * @desc Game's mouse object.
 *
 * @param game {object} Current Phaser game instance.
 * @param map {Map} Game's Map object.
 * @param group {object} Sprite group.
 *
 * @property game {object} Game instance this mouse belongs to.
 * @property map {Map} Game's Map object.
 * @property group {object} Sprite group.
 * @property pos {object} Initial Isometric Point3.
 * @property twoD {object} Phaser 2d pointer position.
 * @property threeD {object} Derived 3d pointer position.
 * @property switch {boolean} Is selected mode on or off?
 * @property tile {object} Tiling system for the mouse pointer.
 * @property tile.center {sprite} Tile currently under the mouse pointer (when selected mode is on).
 * @property mouse {object} Phaser input object.
 *
 * @class {object} Mouse
 * @this Mouse
 * @constructor
 */

function Mouse (game, map, group) {

    this.game = game;
    this.map = map;
    this.group = group;
    this.pos = new Phaser.Plugin.Isometric.Point3();

    var x = this.game.input.mousePointer.x,
        y = this.game.input.mousePointer.y;

    this.twoD = new Phaser.Point(x, y);
    this.threeD = this.game.iso.unproject(this.twoD, this.pos);

    this.switch = false;

    this.tile = {};

    this.mouse = this.game.input;
}

/**
 * @author Anthony Pizzimenti
 *
 * @desc Updates 2d (twoD) and 3d (threeD) positions as well as world boundaries.
 *
 * @property twoD.x {number} 2d x position.
 * @property twoD.y {number} 2d y position.
 * @property threeD.x {number} 3d x position.
 * @property threeD.y {number} 3d y position.
 * @property inBounds {boolean} Is the mouse pointer within the world boundaries?
 *
 * @this Mouse
 */

Mouse.prototype.update = function () {

    this.twoD.x = this.game.input.mousePointer.x;
    this.twoD.y = this.game.input.mousePointer.y;
    this.threeD = this.game.iso.unproject(this.twoD, this.pos);

    var backX = this.game.physics.isoArcade.bounds.backX,
        backY = this.game.physics.isoArcade.bounds.backY,
        frontX = this.game.physics.isoArcade.bounds.frontX,
        frontY = this.game.physics.isoArcade.bounds.frontY;

    this.inBounds = (this.threeD.x > backX && this.threeD.x < frontX) && (this.threeD.y > backY && this.threeD.y < frontY);
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc If the mouse is in selected mode (i.e. switch is true), this determines the tile to animate.
 *
 * @param [animation] {Object} Container for custom mouse on-select animation.
 *
 * @example
 * // values shown in this example animation object are the default values for the system
 *
 * var animation = {
 *         tint: 0x98FB98,                                          // hexadecimal color
 *         alpha: 1.3 + Math.sin(this.game.time.now * 0.007),       // value (or function) applied to tile transparency
 *         tween: [{isoZ: 5}, 20, Phaser.Easing.Linear.None, true]  // tween arguments to modify tile physics properties
 *     }
 *
 * @this Mouse
 */

Mouse.prototype.selected = function (animation) {

    var notExist = Globals.paramNotExist(animation, "object");

    if (this.inBounds) {

        this.group.forEach((tile) => {

            if (tile.type === "tile") {

                var inside = tile.isoBounds.containsXY(this.threeD.x + this.map.tileSize, this.threeD.y + this.map.tileSize);

                if (inside) {

                    this.tile = tile;

                    this.row = tile.row;
                    this.col = tile.col;

                    tile.tint = notExist ? 0x98FB98 : animation.tint || 0x98FB98 ;
                    tile.alpha = notExist ? 1.3 + Math.sin(this.game.time.now * 0.007) : animation.alpha || 1.3 + Math.sin(this.game.time.now * 0.007)

                } else if (!inside) {
                    tile.discovered ? tile.tint = 0xFFFFFF : tile.tint = 0x571F57;
                    tile.alpha = 1;
                }
            }
        });

    } else {
        this.reset();
    }

};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Resets all tile sprites in the group.
 *
 * @this Mouse
 */

Mouse.prototype.reset = function () {

    this.group.forEach((tile) => {

        if (tile.type === "tile") {
            tile.discovered ? tile.tint = 0xFFFFFF : tile.tint = 0x571F57;
            tile.alpha = 1;
            tile.isoZ = 0;
            this.game.canvas.style.cursor = "default";
        }
    });
};
<commit_msg>comments within constructor and prototypal methods<commit_after>(function () {"use strict";})();
/**
 * Created by apizzimenti on 7/15/16.
 */

/**
 * @author Anthony Pizzimenti
 *
 * @desc Game's mouse object.
 *
 * @param game {object} Current Phaser game instance.
 * @param map {Map} Game's Map object.
 * @param group {object} Sprite group.
 *
 * @property game {object} Game instance this mouse belongs to.
 * @property map {Map} Game's Map object.
 * @property group {object} Sprite group.
 * @property pos {object} Initial Isometric Point3.
 * @property twoD {object} Phaser 2d pointer position.
 * @property threeD {object} Derived 3d pointer position.
 * @property switch {boolean} Is selected mode on or off?
 * @property tile {object} Tiling system for the mouse pointer.
 * @property tile.center {sprite} Tile currently under the mouse pointer (when selected mode is on).
 * @property mouse {object} Phaser input object.
 *
 * @class {object} Mouse
 * @this Mouse
 * @constructor
 */

function Mouse (game, map, group) {

    this.game = game;
    this.map = map;
    this.group = group;

    // create a new Point3 to project 2d position onto isometric map
    this.pos = new Phaser.Plugin.Isometric.Point3();

    var x = this.game.input.mousePointer.x,
        y = this.game.input.mousePointer.y;

    // note 2d and 3d points
    this.twoD = new Phaser.Point(x, y);
    this.threeD = this.game.iso.unproject(this.twoD, this.pos);

    this.switch = false;

    this.tile = {};

    this.mouse = this.game.input;
}

/**
 * @author Anthony Pizzimenti
 *
 * @desc Updates 2d (twoD) and 3d (threeD) positions as well as world boundaries.
 *
 * @property twoD.x {number} 2d x position.
 * @property twoD.y {number} 2d y position.
 * @property threeD.x {number} 3d x position.
 * @property threeD.y {number} 3d y position.
 * @property inBounds {boolean} Is the mouse pointer within the world boundaries?
 *
 * @this Mouse
 */

Mouse.prototype.update = function () {

    this.twoD.x = this.game.input.mousePointer.x;
    this.twoD.y = this.game.input.mousePointer.y;
    this.threeD = this.game.iso.unproject(this.twoD, this.pos);

    var backX = this.game.physics.isoArcade.bounds.backX,
        backY = this.game.physics.isoArcade.bounds.backY,
        frontX = this.game.physics.isoArcade.bounds.frontX,
        frontY = this.game.physics.isoArcade.bounds.frontY;

    // assigns inBounds property to check if mouse is within physics world bounds.
    this.inBounds = (this.threeD.x > backX && this.threeD.x < frontX) && (this.threeD.y > backY && this.threeD.y < frontY);
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc If the mouse is in selected mode (i.e. switch is true), this determines the tile to animate.
 *
 * @param [animation] {Object} Container for custom mouse on-select animation.
 *
 * @example
 * // values shown in this example animation object are the default values for the system
 *
 * var animation = {
 *         tint: 0x98FB98,                                          // hexadecimal color
 *         alpha: 1.3 + Math.sin(this.game.time.now * 0.007),       // value applied to tile transparency
 *         tween: [{isoZ: 5}, 20, Phaser.Easing.Linear.None, true]  // tween arguments to modify tile physics properties
 *     }
 *
 * @this Mouse
 */

Mouse.prototype.selected = function (animation) {

    var notExist = Globals.paramNotExist(animation, "object");

    if (this.inBounds) {

        this.group.forEach((tile) => {

            if (tile.type === "tile") {

                var inside = tile.isoBounds.containsXY(this.threeD.x + this.map.tileSize, this.threeD.y + this.map.tileSize);

                if (inside) {

                    this.tile = tile;

                    this.row = tile.row;
                    this.col = tile.col;

                    tile.tint = notExist ? 0x98FB98 : animation.tint || 0x98FB98 ;
                    tile.alpha = notExist ? 1.3 + Math.sin(this.game.time.now * 0.007) : animation.alpha || 1.3 + Math.sin(this.game.time.now * 0.007)

                } else if (!inside) {
                    tile.discovered ? tile.tint = 0xFFFFFF : tile.tint = 0x571F57;
                    tile.alpha = 1;
                }
            }
        });

    } else {
        this.reset();
    }

};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Resets all tile sprites in the group.
 *
 * @this Mouse
 */

Mouse.prototype.reset = function () {

    this.group.forEach((tile) => {

        if (tile.type === "tile") {
            tile.discovered ? tile.tint = 0xFFFFFF : tile.tint = 0x571F57;
            tile.alpha = 1;
            tile.isoZ = 0;
            this.game.canvas.style.cursor = "default";
        }
    });
};
<|endoftext|>


<commit_before>"use babel";

import ignore from "ignore";
import { CompositeDisposable } from "atom";
import $ from "jquery";

let oPackageConfig,
    fActivate, fDeactivate,
    _oDisposables, _oAtomIgnoreFileDisposables,
    _oMutationObserver,
    _bIsWindowsPlatform = document.body.classList.contains( "platform-win32" ),
    _bEnabled,
    _oAtomIgnoreFiles,
    _fUpdate, _fApply, _fTreeViewHasMutated, _fProjectChangedPaths,
    _fAddAtomIgnoreFiles, _fNormalizePath, _fHandleIgnoreFile, _fUnhideParents;

oPackageConfig = {
    "enabled": {
        "type": "boolean",
        "default": true
    },
    "ignoreFileName": {
        "type": "string",
        "default": ".atomignore"
    }
};

fActivate = function() {
    _oDisposables && _oDisposables.dispose();
    _oDisposables = new CompositeDisposable();

    _oDisposables.add( atom.commands.add( "atom-workspace", {
        "tree-ignore:toggle": _fApply.bind( null, !_bEnabled ),
        "tree-ignore:enable": _fApply.bind( null, true ),
        "tree-ignore:disable": _fApply.bind( null, false )
    } ) );

    _oDisposables.add( atom.commands.add( ".platform-win32, .platform-linux, .platform-darwin", {
        "tree-view:toggle": _fUpdate.bind( null )
    } ) );

    _bEnabled = atom.config.get( "tree-ignore.enabled" ) || false;

    _oMutationObserver = new MutationObserver( _fTreeViewHasMutated );

    atom.packages.onDidActivateInitialPackages( () => {
        _oDisposables.add( atom.project.onDidChangePaths( _fProjectChangedPaths ) );

        _fProjectChangedPaths();

        atom.config.observe( "tree-ignore.enabled", ( bNewValue ) => {
            if ( bNewValue !== _bEnabled ) {
                _fApply( bNewValue );
            }
        } );

        _fUpdate();
    } );
};

fDeactivate = function() {
    _oDisposables && _oDisposables.dispose();
    _oAtomIgnoreFileDisposables && _oAtomIgnoreFileDisposables.dispose();
    _oMutationObserver.disconnect();
};

_fApply = function( bValue ) {
    atom.config.set( "tree-ignore.enabled", _bEnabled = bValue );
    _fUpdate();
};

_fUpdate = function() {
    let oIgnore,
        oIgnoredItems = {},
        sProjectRoot = "",
        bHandleProject;

    if ( _bEnabled && document.querySelector( ".tree-view" ) ) {
        _oMutationObserver.observe( document.querySelector( ".tree-view" ), {
            "childList": true,
            "subtree": true
        } );
    } else {
        _oMutationObserver.disconnect();
    }

    $( atom.views.getView( atom.workspace ) )
        .find( ".tree-view li.entry .name" ).each( function() {
            let sPath,
                $this,
                bFiltered = false;

            if ( sPath = ( $this = $( this ) ).data( "path" ) ) {
                let $parent = $this.parents( "li.entry" ).first();

                sPath = _fNormalizePath( sPath );
                if ( $parent.hasClass( "directory" ) ) {
                    sPath += "/";
                }
                if ( $parent.hasClass( "project-root" ) ) {
                    sProjectRoot = sPath;
                    oIgnore = _fHandleIgnoreFile( sProjectRoot );
                    bHandleProject = oIgnore !== null;
                }
                if ( oIgnore !== null ) {
                    sPath = sPath.substring( sProjectRoot.length );
                    bFiltered = oIgnore.filter( [ sPath ] ).length === 0;
                    if ( bFiltered ) {
                        if ( sPath.endsWith( "/" ) ) {
                            oIgnoredItems[ sPath ] = $parent;
                        }
                    } else {
                        _fUnhideParents( oIgnoredItems, sPath );
                    }
                }
                $parent.toggleClass( "tree-ignore-element", _bEnabled && bHandleProject && bFiltered );
            }
        } );
};

_fTreeViewHasMutated = function() {
    _fUpdate();
};

_fProjectChangedPaths = function() {
    _fAddAtomIgnoreFiles();
    _fUpdate();
};

_fAddAtomIgnoreFiles = function() {
    let sIgnoreFileName = atom.config.get( "tree-ignore.ignoreFileName" );

    _oAtomIgnoreFileDisposables && _oAtomIgnoreFileDisposables.dispose();
    _oAtomIgnoreFileDisposables = new CompositeDisposable();
    _oAtomIgnoreFiles = {};

    atom.project.getDirectories().forEach( ( oDirectory ) => {
        let oIgnoreFile = oDirectory.getFile( sIgnoreFileName );

        _oAtomIgnoreFiles[ _fNormalizePath( `${ oDirectory.getPath() }/` ) ] = oIgnoreFile;
        _oAtomIgnoreFileDisposables.add( oIgnoreFile.onDidChange( _fUpdate ) );
    } );
};

_fNormalizePath = function( sPath ) {
    if ( _bIsWindowsPlatform ) {
        return sPath.replace( /\\/g, "/" );
    }
    return sPath;
};

_fHandleIgnoreFile = function( sPath ) {
    let sIgnoreFile = _oAtomIgnoreFiles[ sPath ];

    if ( sIgnoreFile == null ) {
        // New root / project path, look if it has an ignore file
        return null; // Don't handle this project
    }
    if ( !sIgnoreFile.existsSync() ) {
        return null; // Removed, unhide everything
    }

    return ignore().addIgnoreFile( sIgnoreFile.getPath() );
};

_fUnhideParents = function( oIgnoredItems, sPath ) {
    const getParent = function( sChildPath ) {
        if ( sChildPath.endsWith( "/" ) ) {
            return sChildPath.replace( /[^\/]*\/$/, "" );
        }
        return sChildPath.replace( /\/.*?$/, "/" );
    };
    let sParent = sPath, oItem;

    while ( sParent.includes( "/" ) ) {
        sParent = getParent( sParent );
        oItem = oIgnoredItems[ sParent ];
        if ( oItem !== undefined ) {
            oItem.removeClass( "tree-ignore-element" );
        }
    }
};

export {
    oPackageConfig as config,
    fActivate as activate,
    fDeactivate as deactivate
};
<commit_msg>Fix closure issue with state<commit_after>"use babel";

import ignore from "ignore";
import { CompositeDisposable } from "atom";
import $ from "jquery";

let oPackageConfig,
    fActivate, fDeactivate,
    _oDisposables, _oAtomIgnoreFileDisposables,
    _oMutationObserver,
    _bIsWindowsPlatform = document.body.classList.contains( "platform-win32" ),
    _oAtomIgnoreFiles,
    _fUpdate, _fApply, _fTreeViewHasMutated, _fProjectChangedPaths,
    _fAddAtomIgnoreFiles, _fNormalizePath, _fHandleIgnoreFile, _fUnhideParents;

oPackageConfig = {
    "enabled": {
        "type": "boolean",
        "default": true
    },
    "ignoreFileName": {
        "type": "string",
        "default": ".atomignore"
    }
};

fActivate = function() {
    _oDisposables && _oDisposables.dispose();
    _oDisposables = new CompositeDisposable();

    _oDisposables.add( atom.commands.add( "atom-workspace", {
        "tree-ignore:toggle": () => {
            _fApply( !atom.config.get( "tree-ignore.enabled" ) );
        },
        "tree-ignore:enable": _fApply.bind( null, true ),
        "tree-ignore:disable": _fApply.bind( null, false )
    } ) );

    _oDisposables.add( atom.commands.add( ".platform-win32, .platform-linux, .platform-darwin", {
        "tree-view:toggle": _fUpdate.bind( null )
    } ) );

    _oMutationObserver = new MutationObserver( _fTreeViewHasMutated );

    atom.packages.onDidActivateInitialPackages( () => {
        _oDisposables.add( atom.project.onDidChangePaths( _fProjectChangedPaths ) );

        _fProjectChangedPaths();

        atom.config.observe( "tree-ignore.enabled", ( bNewValue ) => {
            if ( bNewValue !== atom.config.get( "tree-ignore.enabled" ) ) {
                _fApply( bNewValue );
            }
        } );

        _fUpdate( atom.config.get( "tree-ignore.enabled" ) );
    } );
};

fDeactivate = function() {
    _oDisposables && _oDisposables.dispose();
    _oAtomIgnoreFileDisposables && _oAtomIgnoreFileDisposables.dispose();
    _oMutationObserver.disconnect();
};

_fApply = function( bValue ) {
    atom.config.set( "tree-ignore.enabled", bValue );
    _fUpdate( bValue );
};

_fUpdate = function( bState ) {
    let oIgnore,
        oIgnoredItems = {},
        sProjectRoot = "",
        bHandleProject;

    if ( bState && document.querySelector( ".tree-view" ) ) {
        _oMutationObserver.observe( document.querySelector( ".tree-view" ), {
            "childList": true,
            "subtree": true
        } );
    } else {
        _oMutationObserver.disconnect();
    }

    $( atom.views.getView( atom.workspace ) )
        .find( ".tree-view li.entry .name" ).each( function() {
            let sPath,
                $this,
                bFiltered = false;

            if ( sPath = ( $this = $( this ) ).data( "path" ) ) {
                let $parent = $this.parents( "li.entry" ).first();

                sPath = _fNormalizePath( sPath );
                if ( $parent.hasClass( "directory" ) ) {
                    sPath += "/";
                }
                if ( $parent.hasClass( "project-root" ) ) {
                    sProjectRoot = sPath;
                    oIgnore = _fHandleIgnoreFile( sProjectRoot );
                    bHandleProject = oIgnore != null;
                }
                if ( oIgnore != null ) {
                    sPath = sPath.substring( sProjectRoot.length );
                    bFiltered = oIgnore.filter( [ sPath ] ).length === 0;
                    if ( bFiltered ) {
                        if ( sPath.endsWith( "/" ) ) {
                            oIgnoredItems[ sPath ] = $parent;
                        }
                    } else {
                        _fUnhideParents( oIgnoredItems, sPath );
                    }
                }
                $parent.toggleClass( "tree-ignore-element", bState && bHandleProject && bFiltered );
            }
        } );
};

_fTreeViewHasMutated = function() {
    _fUpdate();
};

_fProjectChangedPaths = function() {
    _fAddAtomIgnoreFiles();
    _fUpdate();
};

_fAddAtomIgnoreFiles = function() {
    let sIgnoreFileName = atom.config.get( "tree-ignore.ignoreFileName" );

    _oAtomIgnoreFileDisposables && _oAtomIgnoreFileDisposables.dispose();
    _oAtomIgnoreFileDisposables = new CompositeDisposable();
    _oAtomIgnoreFiles = {};

    atom.project.getDirectories().forEach( ( oDirectory ) => {
        let oIgnoreFile = oDirectory.getFile( sIgnoreFileName );

        _oAtomIgnoreFiles[ _fNormalizePath( `${ oDirectory.getPath() }/` ) ] = oIgnoreFile;
        _oAtomIgnoreFileDisposables.add( oIgnoreFile.onDidChange( _fUpdate ) );
    } );
};

_fNormalizePath = function( sPath ) {
    if ( _bIsWindowsPlatform ) {
        return sPath.replace( /\\/g, "/" );
    }
    return sPath;
};

_fHandleIgnoreFile = function( sPath ) {
    let sIgnoreFile = _oAtomIgnoreFiles[ sPath ];

    if ( sIgnoreFile == null ) {
        // New root / project path, look if it has an ignore file
        return null; // Don't handle this project
    }
    if ( !sIgnoreFile.existsSync() ) {
        return null; // Removed, unhide everything
    }

    return ignore().addIgnoreFile( sIgnoreFile.getPath() );
};

_fUnhideParents = function( oIgnoredItems, sPath ) {
    const getParent = function( sChildPath ) {
        if ( sChildPath.endsWith( "/" ) ) {
            return sChildPath.replace( /[^\/]*\/$/, "" );
        }
        return sChildPath.replace( /\/.*?$/, "/" );
    };
    let sParent = sPath, oItem;

    while ( sParent.includes( "/" ) ) {
        sParent = getParent( sParent );
        oItem = oIgnoredItems[ sParent ];
        if ( oItem != null ) {
            oItem.removeClass( "tree-ignore-element" );
        }
    }
};

export {
    oPackageConfig as config,
    fActivate as activate,
    fDeactivate as deactivate
};
<|endoftext|>


<commit_before>// tasks:
//    no number overflow/underflow
//    no duplicate keys in maps
//    number literals to numbers
//    unicode escape sequences to chars
//    characters to chars
//    escapes to chars
//    join up string literal
//    object/array at top level
// implementation:
//   similar to the logging monad

"use strict";

var _escapes = {'"': '"',  '\\': '\\',
                '/': '/',  'b': '\b' ,
                'f': '\f', 'n': '\n' ,
                'r': '\r', 't': '\t'  };

function concat(first, second) {
    second.map(function(e) {
        first.push(e);
    });
}

function make_error(element, message, text, position) {
    return {
        'element' : element ,
        'message' : message ,
        'text'    : text    ,
        'position': position
    };
}

function ret_err(errors, value) {
    return {
        'errors': errors,
        'value': value
    };
}

function unicode(val) {
    return String.fromCharCode(parseInt(val.join(''), 16));
}

function escape(val) {
    if ( !(val in _escapes) ) {
        throw new Error('invalid character escape -- ' + val);
    }
    return _escapes[val];
}

var _chars = {
    'escape': escape,
    'unicode escape': unicode,
    'character': function(c) {return c;}
};

function t_char(node) {
    return _chars[node._name](node.value);
}

function t_string(node) {
    return ret_err([], node.value.map(t_char).join(''));
}

function format_number(sign, i, d, exp) {
    return [sign, i, d ? ( '.' + d ) : '', exp].join('');
}

function t_number(node) {
    // check that node _name is number (optional)
    var errors = [],
        sign = node.sign ? node.sign : '',
        i = node.integer.join(''),
        pos = node._start;
    var d = node.decimal ? node.decimal.digits.join('') : '',
        exp = '';
    if ( node.exponent ) {
        exp += node.exponent.letter;
        if ( node.exponent.sign ) {
            exp += node.exponent.sign;
        }
        exp += node.exponent.power.join('');
    }
    var val = [sign, i, '.', d, exp].join(''),
        // convert to a float
        num = parseFloat(val);
    // check for overflow
    if ( num === Infinity || num === -Infinity ) {
        errors.push(make_error('number',
                               'overflow',
                               format_number(sign, i, d, exp),
                               pos));
    }
    // underflow check may be incorrect:
    // ??? false negatives ??? other IEEE 0's or NaN's or something ???
    if ( num === 0 && node.exponent ) {
        errors.push(make_error('number',
                               'possible underflow',
                               format_number(sign, i, d, exp),
                               pos));
    }
    return ret_err(errors, num);
}

var _keywords = {
        'true' : true,
        'false': false,
        'null' : null
    };

function t_keyword(node) {
    if ( node.value in _keywords ) {
        return ret_err([], _keywords[node.value]);
    }
    throw new Error('invalid keyword -- ' + node.value);
}

function extractValues(body) {
    return (body !== null) ? [body[0]].concat(body[1].map(([_, v]) => v)) : [];
}

function t_array(node) {
    var errors = [],
        vals = [];
    const values = extractValues(node.body);
    values.map(function(v) {
        var e = t_value(v);
        concat(errors, e.errors);
        vals.push(e.value);
    });
    return ret_err(errors, vals); // array position not important
}

function t_pair(node) {
    var errors = [],
        s = t_string(node.key), // what if ... node.key is not a string CST?
        v = t_value(node.value);
    concat(errors, s.errors);
    concat(errors, v.errors);
    var kv_pair = [s.value, v.value];
    return ret_err(errors, kv_pair); // key/val position unimportant
}

function t_build_object(pairs) {
    var errors = [],
        obj = {},
        positions = {},
        seen_twice = {};
    pairs.map(function(pair) {
        var p = t_pair(pair),
            key = p.value[0];
        concat(errors, p.errors);
        if ( key in positions ) {
            seen_twice[key] = true;
        } else {
            positions[key] = [];
            obj[key] = p.value[1];
        }
        positions[key].push(pair.key._start);
    });
    for (var key in seen_twice) {
        errors.push(make_error('object', 'duplicate key', key, positions[key]));
    }
    return ret_err(errors, obj);
}

function t_object(node) {
    const values = extractValues(node.body);
    return t_build_object(values); // what about the object position?
}

var NODE_ACTIONS = {
        'keyword': t_keyword,
        'number' : t_number ,
        'string' : t_string ,
        'array'  : t_array  ,
        'object' : t_object
    };

function t_value(node) {
    return NODE_ACTIONS[node._name](node);
}

var TOP_LEVEL = {'array': 1, 'object': 1},
    TOP_LEVEL_WARNING = 'top-level element should be object or array';

function t_json(node) {
    var val = t_value(node.value);
    if ( !TOP_LEVEL.hasOwnProperty(node.value._name) ) {
        val.errors.push(make_error(node._name, TOP_LEVEL_WARNING, '', node._start));
    }
    return val;
}

module.exports = {
    't_json'    : t_json,
    't_value'   : t_value,
    'make_error': make_error,
    'ret_err'   : ret_err,
    't_char'    : t_char
};

<commit_msg>update to ES6 syntax<commit_after>// tasks:
//    no number overflow/underflow
//    no duplicate keys in maps
//    number literals to numbers
//    unicode escape sequences to chars
//    characters to chars
//    escapes to chars
//    join up string literal
//    object/array at top level
// implementation:
//   similar to the logging monad

"use strict";

const _escapes = new Map([
        ['"' , '"' ],
        ['\\', '\\'],
        ['/' , '/' ],
        ['b' , '\b'],
        ['f' , '\f'],
        ['n' , '\n'],
        ['r' , '\r'],
        ['t' , '\t']
    ]);

function concat(first, second) {
    second.forEach((e) => first.push(e));
}

function make_error(element, message, text, position) {
    return {
        'element' : element ,
        'message' : message ,
        'text'    : text    ,
        'position': position
    };
}

function ret_err(errors, value) {
    return {
        'errors': errors,
        'value': value
    };
}

function unicode(val) {
    return String.fromCharCode(parseInt(val.join(''), 16));
}

function escape(val) {
    if ( !_escapes.has(val)) {
        throw new Error('invalid character escape -- ' + val);
    }
    return _escapes.get(val);
}

var _chars = new Map([
    ['escape', escape],
    ['unicode escape', unicode],
    ['character', (c) => c]
]);

function t_char(node) {
    return _chars.get(node._name)(node.value);
}

function t_string(node) {
    return ret_err([], node.value.map(t_char).join(''));
}

function format_number(sign, i, d, exp) {
    return [sign, i, d ? ( '.' + d ) : '', exp].join('');
}

function t_number(node) {
    // check that node _name is number (optional)
    var errors = [],
        sign = node.sign ? node.sign : '',
        i = node.integer.join(''),
        pos = node._start;
    var d = node.decimal ? node.decimal.digits.join('') : '',
        exp = '';
    if ( node.exponent ) {
        exp += node.exponent.letter;
        if ( node.exponent.sign ) {
            exp += node.exponent.sign;
        }
        exp += node.exponent.power.join('');
    }
    var val = [sign, i, '.', d, exp].join(''),
        // convert to a float
        num = parseFloat(val);
    // check for overflow
    if ( num === Infinity || num === -Infinity ) {
        errors.push(make_error('number',
                               'overflow',
                               format_number(sign, i, d, exp),
                               pos));
    }
    // underflow check may be incorrect:
    // ??? false negatives ??? other IEEE 0's or NaN's or something ???
    if ( num === 0 && node.exponent ) {
        errors.push(make_error('number',
                               'possible underflow',
                               format_number(sign, i, d, exp),
                               pos));
    }
    return ret_err(errors, num);
}

const _keywords = new Map([
        ['true' , true ],
        ['false', false],
        ['null' , null ]
    ]);

function t_keyword(node) {
    if ( _keywords.has(node.value) ) {
        return ret_err([], _keywords.get(node.value));
    }
    throw new Error('invalid keyword -- ' + node.value);
}

function extractValues(body) {
    return (body !== null) ? [body[0]].concat(body[1].map(([_, v]) => v)) : [];
}

function t_array(node) {
    var errors = [],
        vals = [];
    const values = extractValues(node.body);
    values.map(function(v) {
        var e = t_value(v);
        concat(errors, e.errors);
        vals.push(e.value);
    });
    return ret_err(errors, vals); // array position not important
}

function t_pair(node) {
    var errors = [],
        s = t_string(node.key), // what if ... node.key is not a string CST?
        v = t_value(node.value);
    concat(errors, s.errors);
    concat(errors, v.errors);
    var kv_pair = [s.value, v.value];
    return ret_err(errors, kv_pair); // key/val position unimportant
}

function t_build_object(pairs) {
    var errors = [],
        obj = {},
        positions = {},
        seen_twice = {};
    pairs.map(function(pair) {
        var p = t_pair(pair),
            key = p.value[0];
        concat(errors, p.errors);
        if ( key in positions ) {
            seen_twice[key] = true;
        } else {
            positions[key] = [];
            obj[key] = p.value[1];
        }
        positions[key].push(pair.key._start);
    });
    for (var key in seen_twice) {
        errors.push(make_error('object', 'duplicate key', key, positions[key]));
    }
    return ret_err(errors, obj);
}

function t_object(node) {
    const values = extractValues(node.body);
    return t_build_object(values); // what about the object position?
}

const NODE_ACTIONS = new Map([
        ['keyword', t_keyword],
        ['number' , t_number ],
        ['string' , t_string ],
        ['array'  , t_array  ],
        ['object' , t_object ]
    ]);

function t_value(node) {
    return NODE_ACTIONS.get(node._name)(node);
}

const TOP_LEVEL = new Set(['array', 'object']);
const TOP_LEVEL_WARNING = 'top-level element should be object or array';

function t_json(node) {
    var val = t_value(node.value);
    if ( !TOP_LEVEL.has(node.value._name) ) {
        val.errors.push(make_error(node._name, TOP_LEVEL_WARNING, '', node._start));
    }
    return val;
}

module.exports = {
    't_json'    : t_json,
    't_value'   : t_value,
    'make_error': make_error,
    'ret_err'   : ret_err,
    't_char'    : t_char
};

<|endoftext|>


<commit_before>var
  jsonDiff = require("./json-diff"),
  Promise = require("bluebird"),
  _ = require("lodash");

function getSelectedHandler (routeName) {
  var route = _.find(this.routes, { name: routeName });
  return route.handlers[route.selectedHandler];
}

function getSurrogateRequest (request) {
  return _.chain(request)
    .pick("payload", "params", "query")
    .cloneDeep()
    .value();
}

function getHandlerContext (request) {
  return {
    state: this.state,
    cookies: _.cloneDeep(request.state) || {}
  };
}

function updateCookieState (replyObj, originalCookies, modifiedCookies, cookieEncoding) {
  var
    cookiesDiff = jsonDiff(originalCookies, modifiedCookies);
  _.each(cookiesDiff.add, function (val, key) {
    // jsonDiff does deep-diff; we just want shallow modifiedCookies[key]
    replyObj.state(key, modifiedCookies[key], { encoding: cookieEncoding });
  });
  _.each(cookiesDiff.del, function (val, key) {
    replyObj.state(key, "", { encoding: cookieEncoding });
  });
}

function getHandlerArgs (request, handler) {
  var handlerArgs, selectedOptions;

  handlerArgs = [ getSurrogateRequest(request) ];

  if (handler.optionsType === "selectOne") {
    handlerArgs = handlerArgs.concat(
      handler.options[handler.selectedOption]
    );
  } else if (handler.optionsType === "selectMany") {
    selectedOptions = _.map(handler.selectedOptions, function (optionName) {
      return handler.options[optionName];
    });
    handlerArgs = Array.prototype.concat.apply(handlerArgs, selectedOptions);
  }

  return handlerArgs;
}

function defineDynamicRoutes () {
  var self = this;

  // use `this._routes` because `this.routes` will not be available when
  // defining user routes
  _.each(this._routes, function (routeCfg) {
    this.server.route({
      method: routeCfg.method,
      path: routeCfg.path,
      handler: _.bind(function (request, reply) {
        var
          surrogateHandler = getSelectedHandler.call(this, routeCfg.name),
          handlerContext = getHandlerContext.call(this, request),
          handlerArgs = getHandlerArgs.call(this, request, surrogateHandler);

        Promise.resolve(surrogateHandler.handler.apply(handlerContext, handlerArgs))
          .then(function (response) {
            if (!_.isNull(self.delay)) {
              setTimeout(function () {
                var replyObj = reply(response)
                  .type(routeCfg.type)
                  .code(200);
                updateCookieState(
                  replyObj,
                  request.state || {},
                  handlerContext.cookies,
                  self.cookieEncoding
                );
              }, self.delay);
            }
          });
      }, this)
    });
  }, this);

}

function defineHapiRoutes () {
  _.each(this._hapiRoutes, function (routeCfg) {
    this.server.route(routeCfg);
  }, this);
}

module.exports = function () {
  defineDynamicRoutes.call(this);
  defineHapiRoutes.call(this);
};
<commit_msg>support static directory routes<commit_after>var
  jsonDiff = require("./json-diff"),
  path = require("path"),
  Promise = require("bluebird"),
  _ = require("lodash");

function getSelectedHandler (routeName) {
  var route = _.find(this.routes, { name: routeName });
  return route.handlers[route.selectedHandler];
}

function getSurrogateRequest (request) {
  return _.chain(request)
    .pick("payload", "params", "query")
    .cloneDeep()
    .value();
}

function getHandlerContext (request) {
  return {
    state: this.state,
    cookies: _.cloneDeep(request.state) || {}
  };
}

function updateCookieState (replyObj, originalCookies, modifiedCookies, cookieEncoding) {
  var
    cookiesDiff = jsonDiff(originalCookies, modifiedCookies);
  _.each(cookiesDiff.add, function (val, key) {
    // jsonDiff does deep-diff; we just want shallow modifiedCookies[key]
    replyObj.state(key, modifiedCookies[key], { encoding: cookieEncoding });
  });
  _.each(cookiesDiff.del, function (val, key) {
    replyObj.state(key, "", { encoding: cookieEncoding });
  });
}

function getHandlerArgs (request, handler) {
  var handlerArgs, selectedOptions;

  handlerArgs = [ getSurrogateRequest(request) ];

  if (handler.optionsType === "selectOne") {
    handlerArgs = handlerArgs.concat(
      handler.options[handler.selectedOption]
    );
  } else if (handler.optionsType === "selectMany") {
    selectedOptions = _.map(handler.selectedOptions, function (optionName) {
      return handler.options[optionName];
    });
    handlerArgs = Array.prototype.concat.apply(handlerArgs, selectedOptions);
  }

  return handlerArgs;
}

function defineDynamicRoutes () {
  var self = this;

  // use `this._routes` because `this.routes` will not be available when
  // defining user routes
  _.each(this._routes, function (routeCfg) {
    this.server.route({
      method: routeCfg.method,
      path: routeCfg.path,
      handler: _.bind(function (request, reply) {
        var
          surrogateHandler = getSelectedHandler.call(this, routeCfg.name),
          handlerContext = getHandlerContext.call(this, request),
          handlerArgs = getHandlerArgs.call(this, request, surrogateHandler);

        Promise.resolve(surrogateHandler.handler.apply(handlerContext, handlerArgs))
          .then(function (response) {
            if (!_.isNull(self.delay)) {
              setTimeout(function () {
                var replyObj = reply(response)
                  .type(routeCfg.type)
                  .code(200);
                updateCookieState(
                  replyObj,
                  request.state || {},
                  handlerContext.cookies,
                  self.cookieEncoding
                );
              }, self.delay);
            }
          });
      }, this)
    });
  }, this);

}

function defineHapiRoutes () {
  _.each(this._hapiRoutes, function (routeCfg) {
    this.server.route(routeCfg);
  }, this);
}

function defineStaticRoutes () {
  _.each(this._staticRoutes, function (staticRoute) {
    if (staticRoute.type === "directory") {
      this.server.route({
        method: "GET",
        path: path.join(staticRoute.urlPath, "{param*}"),
        handler: {
          directory: {
            path: staticRoute.filePath
          }
        }
      });
    }
  }, this);
}

module.exports = function () {
  defineDynamicRoutes.call(this);
  defineHapiRoutes.call(this);
  defineStaticRoutes.call(this);
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

var delimsMap = require('delimiter-map');
var language = require('lang-map');
var through = require('through2');
var globby = require('globby');
var path = require('path');
var _ = require('lodash');

/**
 * Expose globby
 */

exports.glob = globby;

/**
 * Expose stream
 */

exports.stream = require('./stream');

/**
 * @param  {*} `value`
 * @return {*}
 * @api private
 */

exports.noop = function (value) {
  return value;
};

/**
 * Inspired by gulp-util
 *
 * @api private
 */

exports.noopStream = function () {
  return through.obj();
};

/**
 * Convenience method for debugging vinyl file objects
 * without the `contents` property.
 *
 * @api private
 */

exports.debug = function (obj, omit) {
  var o = _.omit(obj, ['contents', '_contents', 'orig'].concat(omit || []));
  return JSON.stringify(o, null, 2);
};

/**
 * Temporarily fix the node.js v0.11.x drive letter and file path bug
 * see: https://github.com/joyent/node/issues/7880
 *
 * @return {[type]}
 */

exports.fixPath = function () {
  var args = [].slice.call(arguments);
  var filepath = path.join.apply(path, args);
  var segments = filepath.split(':\\');
  segments[0] = segments[0].toUpperCase();
  return segments.join(':\\\\');
};

/**
 * Parse a filepath into an object with properties
 * matching the methods on the node.js path module,
 * with a few extras:
 *
 *   - `dirname`
 *   - `basename` (with extension)
 *   - `name` (without extension)
 *   - `extname` (src)
 *   - `ext` (dest)
 *
 * @api private
 */

exports.parsePath = require('parse-filepath');

/**
 * Coerce value to an array
 *
 * @api private
 */

exports.arrayify = function arrayify(val) {
  return !Array.isArray(val)
    ? [val]
    : val;
};

/**
 * Check if `path` looks absolute.
 *
 * @param {String} path
 * @return {Boolean}
 * @api private
 */

exports.isAbsolute = require('is-absolute');

/**
 * Parse front matter from a string.
 *
 * @api private
 */

exports.matter = require('gray-matter');

/**
 * Return the destination extension for the given `file`.
 *
 * @param {Object} `file` A file object with `ext`/`dest` properties.
 * @param {Object} `assemble`
 * @api public
 */

exports.getDestExt = function(file, assemble) {
  var data = file && file.data;
  var dest = data.dest;

  if (!dest) {
    return assemble.option('destExt');
  }

  var ext = file.ext || data.src.ext;
  if (ext) {
    ext = exports.formatExt(ext);

    var engine = assemble.engines[ext];
    if (engine) {
      ext = engine.options.destExt;
      if (ext) {
        return ext;
      }
      ext = assemble.option('destExt');
      if (ext) {
        return ext;
      }
    }
  }

  return file.ext || dest.ext || assemble.option('destExt');
};

/**
 * Ensure that a file extension is formatted properly.
 *
 * @param {String} `ext`
 * @api private
 */

exports.formatExt = function(ext) {
  if (ext && ext[0] !== '.') {
    ext = '.' + ext;
  }
  return ext;
};

/**
 * Remove the dot from a file extension.
 *
 * @param {String} `ext`
 * @api private
 */

exports.stripExtDot = function (ext) {
  if (ext[0] === '.') {
    return ext.slice(1);
  }
  return ext;
};

/**
 * Returns `true` if the file extension is mapped to
 * a template engine with known delimiters.
 *
 * @param {String} `ext`
 * @api private
 */

exports.isValidEngineExt = function(ext) {
  var lang = language.lang(exports.stripExtDot(ext));
  return !!(/md/.test(ext) || (delimsMap(lang) &&
    delimsMap(lang).length));
};

/**
 * Remove the dot from a file extension.
 *
 * @param {String} `ext`
 * @api private
 */

exports.engineDelims = function (ext) {
	return delimsMap(exports.stripExtDot(ext));
};
<commit_msg>cleanup utils, related tests<commit_after>'use strict';

/**
 * Module dependencies
 */

var path = require('path');
var _ = require('lodash');

/**
 * Parse a filepath into an object with properties
 * matching the methods on the node.js path module,
 * with a few extras:
 *
 *   - `dirname`
 *   - `basename` (with extension)
 *   - `name` (without extension)
 *   - `extname` (src)
 *   - `ext` (dest)
 *
 * @api private
 */

exports.parsePath = require('parse-filepath');

/**
 * Coerce value to an array
 *
 * @api private
 */

exports.arrayify = function arrayify(val) {
  return !Array.isArray(val)
    ? [val]
    : val;
};

/**
 * Return the destination extension for the given `file`.
 *
 * @param {Object} `file` A file object with `ext`/`dest` properties.
 * @param {Object} `assemble`
 * @api public
 */

exports.getDestExt = function(file, assemble) {
  var data = file && file.data;
  var dest = data.dest;

  if (!dest) {
    return assemble.option('destExt');
  }

  var ext = file.ext || data.src.ext;
  if (ext) {
    ext = exports.formatExt(ext);

    var engine = assemble.engines[ext];
    if (engine) {
      ext = engine.options.destExt;
      if (ext) {
        return ext;
      }
      ext = assemble.option('destExt');
      if (ext) {
        return ext;
      }
    }
  }

  return file.ext || dest.ext || assemble.option('destExt');
};

/**
 * Ensure that a file extension is formatted properly.
 *
 * @param {String} `ext`
 * @api private
 */

exports.formatExt = function(ext) {
  if (ext && ext[0] !== '.') {
    ext = '.' + ext;
  }
  return ext;
};

/**
 * Remove the dot from a file extension.
 *
 * @param {String} `ext`
 * @api private
 */

exports.stripExtDot = function (ext) {
  if (ext[0] === '.') {
    return ext.slice(1);
  }
  return ext;
};
<|endoftext|>


<commit_before>'use strict';

const moment = require('moment');
const _ = require('lodash');
const rarityfilter = require('./filter/rarityfilter');
const logger = require('../logger');
const config = require('../../config');

module.exports = {

    /**
     * @function Parses the raw content of a sales file and returns a JSON array representation of the data for further use
     * @param {Array} body - Raw sales data content
     * @param {Object} articleHashMap - A hashmap of all articles mapped by their UUIDs (eg. <UUID> => {name: 'Argus, Herald of Doom', rarity: 'Legendary', ...})
     * @returns {Array} - An array of sale objects ready to insert into the database
     */
    parseSales: function(body, articleHashMap) {

        const sales = []; // array which holds all sales
        const rows = body.split('\n');

        const notFoundArticles = [];

        for (var i = 0; i < rows.length; i++) {

            const row = rows[i];
            // skip empty rows
            if(row.length < 5) {
                continue;
            }

            const content = row.split(',');
            // skip short rows
            if (content.length < 6) {
                continue;
            }
            const uuid = content[5].trim();

            let internalName = content[0].trim();
            config.articleNameSpecialCharacters.forEach(function(sc) {
                internalName = internalName.split(sc).join('');
            });
            let sale = {}; // holds sale information
            sale.aa = false; // set default
            const mappedArticle = articleHashMap[uuid];

            if(!mappedArticle) {
                notFoundArticles.push(internalName);
                continue;
            }

            /*
             * First grab all article related data
             */

            // UUID of the article should be highly relevant
            sale.uuid = mappedArticle.uuid;
            // Actual name of the article
            sale.name = mappedArticle.name;
            // determine more properties
            if (parseInt(content[1].trim()) === 5) {
                sale.name = sale.name + ' AA';
                sale.aa = true;
            }
            // Rarity
            sale.rarity = mappedArticle.rarity;
            // Set
            sale.set = mappedArticle.set_number.split('_').join(' ');
            // Type
            if (parseInt(content[1].trim()) !== 0) {
                sale.type = 'Card';
            } else if (sale.name.indexOf('Set') !== -1 && sale.name.indexOf('Pack') !== -1) {
                sale.type = 'Pack';
            } else if (mappedArticle.type.indexOf('Equipment') !== -1) {
                sale.type = 'Equipment';
            } else {
                sale.type = 'Other'
            }

            /*
             * Second grab all sale related data
             */

            // Currency
            sale.currency = module.exports.capitalizeFirstLetter(content[2]);
            // Price
            sale.price = parseInt(content[3].trim());
            // Date
            sale.date = content[4].trim();

            // add new sale
            sales.push(sale);

        }

        if(notFoundArticles.length > 0) {
            logger.info(`Could not find any matching articles in hexdb for ${_.uniq(notFoundArticles)}`);
        }

        // console.log(sortedSales);
        return sales;

    },

    // /**
    //  * @function Finds a numeric value for a string representation of a rarity to sort through it
    //  * @param {string} rarity The rarity to find a numeric representation for
    //  * @returns {integer} An integer representing the given rarity as a number
    //  */
    // getNumericRarity: function(rarity) {
    //     switch (rarity) {
    //         case 'Common':
    //             return 2;
    //         case 'Uncommon':
    //             return 3;
    //         case 'Rare':
    //             return 4;
    //         case 'Epic':
    //             return 5;
    //         case 'Legendary':
    //             return 6;
    //         case 'Normal':
    //             return -1;
    //         case 'Primal':
    //             return -2;
    //         default:
    //             return 0;
    //     }
    // },

    capitalizeFirstLetter: function(string) {
        string = string.trim();
        return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
    }
};
<commit_msg>added name mapping for old set pack dump names<commit_after>'use strict';

const moment = require('moment');
const _ = require('lodash');
const rarityfilter = require('./filter/rarityfilter');
const logger = require('../logger');
const config = require('../../config');

module.exports = {

    /**
     * @function Parses the raw content of a sales file and returns a JSON array representation of the data for further use
     * @param {Array} body - Raw sales data content
     * @param {Object} articleHashMap - A hashmap of all articles mapped by their UUIDs (eg. <UUID> => {name: 'Argus, Herald of Doom', rarity: 'Legendary', ...})
     * @returns {Array} - An array of sale objects ready to insert into the database
     */
    parseSales: function(body, articleHashMap) {

        const sales = []; // array which holds all sales
        const rows = body.split('\n');

        const notFoundArticles = [];

        for (var i = 0; i < rows.length; i++) {

            const row = rows[i];
            // skip empty rows
            if(row.length < 5) {
                continue;
            }

            const content = row.split(',');
            // skip short rows
            if (content.length < 6) {
                continue;
            }
            const uuid = content[5].trim();

            let internalName = content[0].trim();
            config.articleNameSpecialCharacters.forEach(function(sc) {
                internalName = internalName.split(sc).join('');
            });
            let sale = {}; // holds sale information
            sale.aa = false; // set default
            const mappedArticle = articleHashMap[uuid];

            if(!mappedArticle) {
                notFoundArticles.push(internalName);
                continue;
            }

            /*
             * First grab all article related data
             */

            // UUID of the article should be highly relevant
            sale.uuid = mappedArticle.uuid;
            // Actual name of the article
            sale.name = mappedArticle.name;

            // CUSTOM NAME MAPPING
            // As of 2016-09-08 packs are now called '<Set name> <Booster|Primal> Pack', so we should map older names to
            // the new format
            if(sale.name.match(/^Set.*(Booster|Primal) Pack$/)) {

                sale.name = module.exports.mapSetName(sale.name);

            }

            // determine more properties
            if (parseInt(content[1].trim()) === 5) {
                sale.name = sale.name + ' AA';
                sale.aa = true;
            }
            // Rarity
            sale.rarity = mappedArticle.rarity;
            // Set
            sale.set = mappedArticle.set_number.split('_').join(' ');
            // Type
            if (parseInt(content[1].trim()) !== 0) {
                sale.type = 'Card';
            } else if (sale.name.indexOf('Set') !== -1 && sale.name.indexOf('Pack') !== -1) {
                sale.type = 'Pack';
            } else if (mappedArticle.type.indexOf('Equipment') !== -1) {
                sale.type = 'Equipment';
            } else {
                sale.type = 'Other'
            }

            /*
             * Second grab all sale related data
             */

            // Currency
            sale.currency = module.exports.capitalizeFirstLetter(content[2]);
            // Price
            sale.price = parseInt(content[3].trim());
            // Date
            sale.date = content[4].trim();

            // add new sale
            sales.push(sale);

        }

        if(notFoundArticles.length > 0) {
            logger.info(`Could not find any matching articles in hexdb for ${_.uniq(notFoundArticles)}`);
        }

        // console.log(sortedSales);
        return sales;

    },

    // /**
    //  * @function Finds a numeric value for a string representation of a rarity to sort through it
    //  * @param {string} rarity The rarity to find a numeric representation for
    //  * @returns {integer} An integer representing the given rarity as a number
    //  */
    // getNumericRarity: function(rarity) {
    //     switch (rarity) {
    //         case 'Common':
    //             return 2;
    //         case 'Uncommon':
    //             return 3;
    //         case 'Rare':
    //             return 4;
    //         case 'Epic':
    //             return 5;
    //         case 'Legendary':
    //             return 6;
    //         case 'Normal':
    //             return -1;
    //         case 'Primal':
    //             return -2;
    //         default:
    //             return 0;
    //     }
    // },

    capitalizeFirstLetter: function(string) {
        string = string.trim();
        return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
    },

    mapSetName: function(string) {
        switch(string) {
            case 'Set 001 Booster Pack': return 'Shards of Fate Booster Pack';
            case 'Set 001 Primal Pack': return 'Shards of Fate Primal Pack';
            case 'Set 002 Booster Pack': return 'Shattered Destiny Booster Pack';
            case 'Set 002 Primal Pack': return 'Shattered Destiny Primal Pack';
            case 'Set 003 Booster Pack': return 'Armies of Myth Booster Pack';
            case 'Set 003 Primal Pack': return 'Armies of Myth Primal Pack';
            case 'Set 004 Booster Pack': return 'Primal Dawn Booster Pack';
            case 'Set 004 Primal Pack': return 'Primal Dawn Primal Pack';
        }
    }

};
<|endoftext|>


<commit_before>// Modified from https://github.com/kbhomes/google-music-mac/blob/v1.1.3/google-music-mac/js/main.js
// under MIT license
function GoogleMusic(win) {
  this.win = win;
  this.doc = win.document;
}
GoogleMusic.prototype = {
  // References to the media playback elements.
  get _eplayPause () { return doc.querySelector('button[data-id="play-pause"]'); },
  get _eforward () { return doc.querySelector('button[data-id="forward"]'); },
  get _erewind () { return doc.querySelector('button[data-id="rewind"]'); },
  get _eshuffle () { return doc.querySelector('button[data-id="shuffle"]'); },
  get _erepeat () { return this.doc.querySelector('button[data-id="repeat"]'); },

  // Repeat modes.
  LIST_REPEAT: 'LIST_REPEAT',
  SINGLE_REPEAT: 'SINGLE_REPEAT',
  NO_REPEAT: 'NO_REPEAT',

  // Shuffle modes.
  ALL_SHUFFLE: 'ALL_SHUFFLE',
  NO_SHUFFLE: 'NO_SHUFFLE',

  // Playback functions.
  playPause: function () {
    this._eplayPause.click();
  },
  forward: function () {
    this._eforward.click();
  },
  rewind: function () {
    this._erewind.click();
  },

  getShuffle: function () {
    return this._eshuffle.value;
  },
  toggleShuffle: function () {
    this._eshuffle.click();
  },

  getRepeat: function () {
    return this._erepeat.value;
  },

  changeRepeat: function (mode) {
    if (!mode) {
      // Toggle between repeat modes once.
      this._erepeat.click();
    } else {
      // Toggle between repeat modes until the desired mode is activated.
      while (this.getRepeat() !== mode) {
        this._erepeat.click();
      }
    }
  },

  // Taken from the Google Play Music page.
  toggleVisualization: function() {
    this.win.SJBpost('toggleVisualization');
  }
};
  //     var lastTitle = "";
  //     var lastArtist = "";
  //     var lastAlbum = "";

  //     var addObserver = new MutationObserver(function(mutations) {
  //         mutations.forEach(function(m) {
  //             for (var i = 0; i < m.addedNodes.length; i++) {
  //                 var target = m.addedNodes[i];
  //                 var name = target.id || target.className;

  //                 if (name == 'text-wrapper')  {
  //                     var now = new Date();

  //                     var title = doc.querySelector('#playerSongTitle');
  //                     var artist = doc.querySelector('#player-artist');
  //                     var album = doc.querySelector('.player-album');
  //                     var art = doc.querySelector('#playingAlbumArt');
  //                     var duration = parseInt(doc.querySelector('#player #slider').getAttribute('aria-valuemax')) / 1000;

  //                     title = (title) ? title.innerText : 'Unknown';
  //                     artist = (artist) ? artist.innerText : 'Unknown';
  //                     album = (album) ? album.innerText : 'Unknown';
  //                     art = (art) ? art.src : null;

  //                     // The art may be a protocol-relative URL, so normalize it to HTTPS.
  //                     if (art && art.slice(0, 2) === '//') {
  //                         art = 'https:' + art;
  //                     }

  //                     // Make sure that this is the first of the notifications for the
  //                     // insertion of the song information elements.
  //                     if (lastTitle != title || lastArtist != artist || lastAlbum != album) {
  //                         // TODO: Emit event
  //                         // win.GoogleMusicApp.notifySong(title, artist, album, art, duration);

  //                         lastTitle = title;
  //                         lastArtist = artist;
  //                         lastAlbum = album;
  //                     }
  //                 }
  //             }
  //         });
  //     });

  //     var playbackObserver = new MutationObserver(function(mutations) {
  //         mutations.forEach(function(m) {
  //             var target = m.target;
  //             var id = target.dataset.id;

  //             if (id == 'play-pause') {
  //                 var playing = target.classList.contains('playing');
  //                 // TODO: Emit event
  //                 // win.GoogleMusicApp.playbackChanged(playing ? 1 : 0);
  //             }
  //         });
  //     });

  //     addObserver.observe(doc.querySelector('#playerSongInfo'), { childList: true, subtree: true });
  //     playbackObserver.observe(doc.querySelector('#player button[data-id="play-pause"]'), { attributes: true });
  // }
module.exports = GoogleMusic;
<commit_msg>Fixed up another lint error and everything is looking shiny<commit_after>// Modified from https://github.com/kbhomes/google-music-mac/blob/v1.1.3/google-music-mac/js/main.js
// under MIT license
function GoogleMusic(win) {
  this.win = win;
  this.doc = win.document;
}
GoogleMusic.prototype = {
  // References to the media playback elements.
  get _eplayPause () { return this.doc.querySelector('button[data-id="play-pause"]'); },
  get _eforward () { return this.doc.querySelector('button[data-id="forward"]'); },
  get _erewind () { return this.doc.querySelector('button[data-id="rewind"]'); },
  get _eshuffle () { return this.doc.querySelector('button[data-id="shuffle"]'); },
  get _erepeat () { return this.doc.querySelector('button[data-id="repeat"]'); },

  // Repeat modes.
  LIST_REPEAT: 'LIST_REPEAT',
  SINGLE_REPEAT: 'SINGLE_REPEAT',
  NO_REPEAT: 'NO_REPEAT',

  // Shuffle modes.
  ALL_SHUFFLE: 'ALL_SHUFFLE',
  NO_SHUFFLE: 'NO_SHUFFLE',

  // Playback functions.
  playPause: function () {
    this._eplayPause.click();
  },
  forward: function () {
    this._eforward.click();
  },
  rewind: function () {
    this._erewind.click();
  },

  getShuffle: function () {
    return this._eshuffle.value;
  },
  toggleShuffle: function () {
    this._eshuffle.click();
  },

  getRepeat: function () {
    return this._erepeat.value;
  },

  changeRepeat: function (mode) {
    if (!mode) {
      // Toggle between repeat modes once.
      this._erepeat.click();
    } else {
      // Toggle between repeat modes until the desired mode is activated.
      while (this.getRepeat() !== mode) {
        this._erepeat.click();
      }
    }
  },

  // Taken from the Google Play Music page.
  toggleVisualization: function() {
    this.win.SJBpost('toggleVisualization');
  }
};
  //     var lastTitle = "";
  //     var lastArtist = "";
  //     var lastAlbum = "";

  //     var addObserver = new MutationObserver(function(mutations) {
  //         mutations.forEach(function(m) {
  //             for (var i = 0; i < m.addedNodes.length; i++) {
  //                 var target = m.addedNodes[i];
  //                 var name = target.id || target.className;

  //                 if (name == 'text-wrapper')  {
  //                     var now = new Date();

  //                     var title = doc.querySelector('#playerSongTitle');
  //                     var artist = doc.querySelector('#player-artist');
  //                     var album = doc.querySelector('.player-album');
  //                     var art = doc.querySelector('#playingAlbumArt');
  //                     var duration = parseInt(doc.querySelector('#player #slider').getAttribute('aria-valuemax')) / 1000;

  //                     title = (title) ? title.innerText : 'Unknown';
  //                     artist = (artist) ? artist.innerText : 'Unknown';
  //                     album = (album) ? album.innerText : 'Unknown';
  //                     art = (art) ? art.src : null;

  //                     // The art may be a protocol-relative URL, so normalize it to HTTPS.
  //                     if (art && art.slice(0, 2) === '//') {
  //                         art = 'https:' + art;
  //                     }

  //                     // Make sure that this is the first of the notifications for the
  //                     // insertion of the song information elements.
  //                     if (lastTitle != title || lastArtist != artist || lastAlbum != album) {
  //                         // TODO: Emit event
  //                         // win.GoogleMusicApp.notifySong(title, artist, album, art, duration);

  //                         lastTitle = title;
  //                         lastArtist = artist;
  //                         lastAlbum = album;
  //                     }
  //                 }
  //             }
  //         });
  //     });

  //     var playbackObserver = new MutationObserver(function(mutations) {
  //         mutations.forEach(function(m) {
  //             var target = m.target;
  //             var id = target.dataset.id;

  //             if (id == 'play-pause') {
  //                 var playing = target.classList.contains('playing');
  //                 // TODO: Emit event
  //                 // win.GoogleMusicApp.playbackChanged(playing ? 1 : 0);
  //             }
  //         });
  //     });

  //     addObserver.observe(doc.querySelector('#playerSongInfo'), { childList: true, subtree: true });
  //     playbackObserver.observe(doc.querySelector('#player button[data-id="play-pause"]'), { attributes: true });
  // }
module.exports = GoogleMusic;
<|endoftext|>