<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var _ = require('underscore');
var fs = require('fs');
var path = require('path');
var util = require('util');

var adalAuth = require('../authentication/adalAuth');
var Environment = require('./environment');
var Subscription = require('./subscription');
var publishSettings = require('./publishSettings');
var cacheUtils = require('../cacheUtils');
var log = require('../logging');
var StringSet = require('../set');
var utils = require('../utils');
var $ = utils.getLocaleString;

//
// Profile object - this manages the serialization of environment
// and subscription data for the current user.
//

function Profile() {
  var self = this;
  self.environments = {};
  Environment.publicEnvironments.forEach(function (env) {
    self.addEnvironment(env);
  });
  self.subscriptions = {};

  self.onSubscriptionUpdated = this.save.bind(this);
}

Object.defineProperty(Profile.prototype, 'currentSubscription', {
  enumerable: true,
  get: function () {
    return _.chain(this.subscriptions)
      .values()
      .filter(function (s) { return s.isDefault; })
      .first()
      .value() || null;
  },

  set: function (value) {
    _.values(this.subscriptions)
      .forEach(function (s) { s.isDefault = false; });
    if (value) {
      value.isDefault = true;
    }
  }
});

_.extend(Profile.prototype, {
  addEnvironment: function (env) {
    this.environments[env.name] = env;
  },

  getEnvironment: function (envName) {
    if (!envName) {
      return this.environments.AzureCloud;
    }
    var key = _.keys(this.environments)
      .filter(function (env) { return utils.ignoreCaseEquals(env, envName); })[0];
    return this.environments[key];
  },

  deleteEnvironment: function (environmentOrName) {
    if (_.isString(environmentOrName)) {
      delete this.environments[environmentOrName];
    } else {
      delete this.environments[environmentOrName.name];
    }
  },

  addSubscription: function (subscription) {
    var existingSubscription = _.values(this.subscriptions)
      .filter(function (s) { return s.id === subscription.id; })[0];

    if (existingSubscription) {
      existingSubscription.removeListener('updated', this.onSubscriptionUpdated);

      if (subscription.name !== existingSubscription.name) {
        delete this.subscriptions[existingSubscription.name];
      }

      _.extend(existingSubscription, subscription);
      subscription = existingSubscription;
    }

    if (subscription.isDefault) {
      this.currentSubscription = null;
    }
    this.subscriptions[subscription.name] = subscription;
    subscription.on('updated', this.onSubscriptionUpdated);
  },

  deleteSubscription: function (subscriptionOrName) {
    var subscription = subscriptionOrName;
    if (_.isString(subscriptionOrName)) {
      subscription = this.subscriptions[subscriptionOrName];
    }

    if (subscription.isDefault) {
      var remainingSubscriptions = _.values(this.subscriptions)
        .filter(function (sub) { return sub.name !== subscription.name; });
      if (_.first(remainingSubscriptions)) {
        remainingSubscriptions[0].isDefault = true;
      }
    }

    subscription.removeListener('updated', this.onSubscriptionUpdated);
    delete this.subscriptions[subscription.name];
  },

  logoutUser: function (username) {
    username = adalAuth.normalizeUserName(username);
    var subscriptions = _.values(this.subscriptions);
    var subscriptionsToRemove = [];

    //construct impacted subscription list, we will make sure to remove the default subscription at last
    //so that the logic of reselecting the new default one will run only once.
    subscriptions.forEach(function (subData) {
      if (subData.username === username){
        if (subData.isDefault){
          subscriptionsToRemove.unshift(subData);
        } else {
          subscriptionsToRemove.push(subData);
        }
      }
    });

    var logoutOccurred = false;
    for (var i = subscriptionsToRemove.length-1; i>=0; i--){
      var subscription = subscriptionsToRemove[i];
      if (subscription.accessToken){
        if (subscription.managementCertificate) {
          delete subscription.accessToken;
        } else {
          this.deleteSubscription(subscription.name);
        }
        logoutOccurred = true;
      }
    }

    return logoutOccurred;
  },

  getSubscription: function (idOrName) {
    var subscription;
    if (!idOrName) {
      subscription = this.currentSubscription;
      if (!subscription) {
        throw new Error($('There is no current subscription. Please use the azure account login command to set your current subscription.'));
      }
    } else {
      subscription = this.subscriptions[idOrName] ||
      _.values(this.subscriptions)
      .filter(function (s) { return utils.ignoreCaseEquals(s.id, idOrName); })[0];
      if (!subscription) {
        throw new Error(util.format(
          $('The subscription \'%s\' was not found. Please check your spelling, or use the azure account login command to set your subscription.'),
          idOrName));
      }
    }
    return subscription;
  },

  importPublishSettings: function (fileName) {
    var self = this;
    _.each(publishSettings.import(fileName), function (subData) {
      var newSubscription = new Subscription(subData, self._findEnvironment(subData));
      self.addSubscription(newSubscription);
      if (!self.currentSubscription) {
        newSubscription.isDefault = true;
      }
    });
  },

  saveToStream: function (stream) {
    stream.write(JSON.stringify(this._getSaveData(), null, 4), 'utf8');
    stream.end();
  },

  save: function (fileName) {
    if (!fileName) {
      fileName = defaultProfileFile;
    }

    fs.writeFileSync(fileName, JSON.stringify(this._getSaveData(), null, 4));
  },

  _getSaveData: function () {
    return {
      environments: _.values(this.environments)
        .filter(function (e) { return !e.isPublicEnvironment; })
        .map(function (e) { return e.toJSON(); }),
      subscriptions: _.values(this.subscriptions).map(function (s) { return s.toJSON(); })
    };
  },

  /**
  * Find an environment with a matching management endpoint
  * @param {object} subscriptionData subscription data from publishsettings file
  *
  * @returns corresponding environment object or throws if not found.
  */
  _findEnvironment: function (subscriptionData) {
    var trimmedEndpoint = utils.stringTrimEnd(subscriptionData.managementEndpointUrl, '/');

    var found = _.values(this.environments).filter(function (e) {
      return utils.ignoreCaseEquals(trimmedEndpoint, utils.stringTrimEnd(e.managementEndpointUrl));
    });
    if (found.length === 0) {
      throw new Error(util.format(
        $('Could not find an environment with management endpoint %s. Create one and import this publishSettings file again.'),
        subscriptionData.managementEndpointUrl));
    }
    return found[0];
  }
});

//
// Profile loading functions
//

function load(fileNameOrData) {
  var profile = new Profile();
  if (_.isUndefined(fileNameOrData) || fileNameOrData === defaultProfileFile) {
    return loadDefaultProfile(profile);
  } else if (_.isString(fileNameOrData)) {
    return loadProfileFromFile(profile, fileNameOrData);
  } else {
    return loadProfileFromObject(profile, fileNameOrData);
  }
}

function loadDefaultProfile(profile) {
  profile.fileName = defaultProfileFile;
  if (utils.pathExistsSync(defaultProfileFile)) {
    return loadProfileFromFile(profile, defaultProfileFile);
  }
  return profile;
}

function loadProfileFromFile(profile, fileName) {
  profile.fileName = fileName;
  if (!utils.pathExistsSync(fileName)) {
    throw new Error(util.format($('Profile file %s does not exist'), fileName));
  }
  return loadProfileFromObject(profile, JSON.parse(fs.readFileSync(fileName, 'utf8')));
}

function loadProfileFromObject(profile, data) {
  if (data.environments) {
    data.environments.forEach(function (envData) {
      var e = new Environment(envData);
      profile.addEnvironment(e);
    });
  }
  if (data.subscriptions) {
    data.subscriptions.forEach(function (subData) {
      profile.addSubscription(new Subscription(subData, profile.environments[subData.environmentName]));
    });
    if(!profile.currentSubscription && data.subscriptions.length > 0) {
      profile.getSubscription(data.subscriptions[0].id).isDefault = true;
    }
  }
  return profile;
}

function clearAzureDir() {
  function deleteIfExists(file, isDir) {
    if (utils.pathExistsSync(file)) {
      log.silly(util.format($('Removing %s'), file));
      (isDir ? fs.rmdirSync : fs.unlinkSync)(file);
      return true;
    } else {
      log.silly(util.format($('%s does not exist'), file));
    }
  }

  var azureDirectory = utils.azureDir();
  var pemPath = path.join(azureDirectory, 'managementCertificate.pem');
  var publishSettingsFilePath = path.join(azureDirectory, 'publishSettings.xml');

  var isDeleted = deleteIfExists(pemPath);
  isDeleted = deleteIfExists(publishSettingsFilePath) || isDeleted; // in this order only
  isDeleted = utils.clearConfig() || isDeleted;
  isDeleted = cacheUtils.clear() || isDeleted;
  isDeleted = deleteIfExists(defaultProfileFile) || isDeleted;

  try {
    deleteIfExists(azureDirectory, true);
  } catch (err) {
    log.warn(util.format($('Couldn\'t remove %s'), azureDirectory));
  }

  log.info(isDeleted ? $('Account settings cleared successfully')
      : $('Account settings are already clear'));
}

var defaultProfileFile = path.join(utils.azureDir(), 'azureProfile.json');

var currentProfile = load(defaultProfileFile);

//
// Resource management
//
function toLowerCase(s) { return s.toLowerCase(); }

var knownResourceNamespaces = new StringSet(toLowerCase);
var knownProviders = new StringSet(toLowerCase);

function addKnownResourceNamespace() {
  knownResourceNamespaces.add(Array.prototype.slice.call(arguments, 0));
}

function addKnownProvider() {
  knownProviders.add(Array.prototype.slice.call(arguments, 0));
}

_.extend(module.exports, {
  load: load,
  defaultProfileFile: defaultProfileFile,
  Profile: Profile,
  Subscription: Subscription,
  Environment: Environment,
  current: currentProfile,
  clearAzureDir: clearAzureDir,
  getSubscription: function (subscription) {
    return currentProfile.getSubscription(subscription);
  },
  addKnownResourceNamespace: addKnownResourceNamespace,
  addKnownProvider: addKnownProvider,
  knownResourceNamespaces: function () { return knownResourceNamespaces.keys(); },
  knownProviders: function() { return knownProviders.keys(); },
  providerKeyTransform: toLowerCase
});
<commit_msg>Updated help text when subscription is missing<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var _ = require('underscore');
var fs = require('fs');
var path = require('path');
var util = require('util');

var adalAuth = require('../authentication/adalAuth');
var Environment = require('./environment');
var Subscription = require('./subscription');
var publishSettings = require('./publishSettings');
var cacheUtils = require('../cacheUtils');
var log = require('../logging');
var StringSet = require('../set');
var utils = require('../utils');
var $ = utils.getLocaleString;

//
// Profile object - this manages the serialization of environment
// and subscription data for the current user.
//

function Profile() {
  var self = this;
  self.environments = {};
  Environment.publicEnvironments.forEach(function (env) {
    self.addEnvironment(env);
  });
  self.subscriptions = {};

  self.onSubscriptionUpdated = this.save.bind(this);
}

Object.defineProperty(Profile.prototype, 'currentSubscription', {
  enumerable: true,
  get: function () {
    return _.chain(this.subscriptions)
      .values()
      .filter(function (s) { return s.isDefault; })
      .first()
      .value() || null;
  },

  set: function (value) {
    _.values(this.subscriptions)
      .forEach(function (s) { s.isDefault = false; });
    if (value) {
      value.isDefault = true;
    }
  }
});

_.extend(Profile.prototype, {
  addEnvironment: function (env) {
    this.environments[env.name] = env;
  },

  getEnvironment: function (envName) {
    if (!envName) {
      return this.environments.AzureCloud;
    }
    var key = _.keys(this.environments)
      .filter(function (env) { return utils.ignoreCaseEquals(env, envName); })[0];
    return this.environments[key];
  },

  deleteEnvironment: function (environmentOrName) {
    if (_.isString(environmentOrName)) {
      delete this.environments[environmentOrName];
    } else {
      delete this.environments[environmentOrName.name];
    }
  },

  addSubscription: function (subscription) {
    var existingSubscription = _.values(this.subscriptions)
      .filter(function (s) { return s.id === subscription.id; })[0];

    if (existingSubscription) {
      existingSubscription.removeListener('updated', this.onSubscriptionUpdated);

      if (subscription.name !== existingSubscription.name) {
        delete this.subscriptions[existingSubscription.name];
      }

      _.extend(existingSubscription, subscription);
      subscription = existingSubscription;
    }

    if (subscription.isDefault) {
      this.currentSubscription = null;
    }
    this.subscriptions[subscription.name] = subscription;
    subscription.on('updated', this.onSubscriptionUpdated);
  },

  deleteSubscription: function (subscriptionOrName) {
    var subscription = subscriptionOrName;
    if (_.isString(subscriptionOrName)) {
      subscription = this.subscriptions[subscriptionOrName];
    }

    if (subscription.isDefault) {
      var remainingSubscriptions = _.values(this.subscriptions)
        .filter(function (sub) { return sub.name !== subscription.name; });
      if (_.first(remainingSubscriptions)) {
        remainingSubscriptions[0].isDefault = true;
      }
    }

    subscription.removeListener('updated', this.onSubscriptionUpdated);
    delete this.subscriptions[subscription.name];
  },

  logoutUser: function (username) {
    username = adalAuth.normalizeUserName(username);
    var subscriptions = _.values(this.subscriptions);
    var subscriptionsToRemove = [];

    //construct impacted subscription list, we will make sure to remove the default subscription at last
    //so that the logic of reselecting the new default one will run only once.
    subscriptions.forEach(function (subData) {
      if (subData.username === username){
        if (subData.isDefault){
          subscriptionsToRemove.unshift(subData);
        } else {
          subscriptionsToRemove.push(subData);
        }
      }
    });

    var logoutOccurred = false;
    for (var i = subscriptionsToRemove.length-1; i>=0; i--){
      var subscription = subscriptionsToRemove[i];
      if (subscription.accessToken){
        if (subscription.managementCertificate) {
          delete subscription.accessToken;
        } else {
          this.deleteSubscription(subscription.name);
        }
        logoutOccurred = true;
      }
    }

    return logoutOccurred;
  },

  getSubscription: function (idOrName) {
    var subscription;
    if (!idOrName) {
      subscription = this.currentSubscription;
      if (!subscription) {
        throw new Error($('There is no current subscription. Please use the azure login command to set your current subscription.'));
      }
    } else {
      subscription = this.subscriptions[idOrName] ||
      _.values(this.subscriptions)
      .filter(function (s) { return utils.ignoreCaseEquals(s.id, idOrName); })[0];
      if (!subscription) {
        throw new Error(util.format(
          $('The subscription \'%s\' was not found. Please check your spelling, or use the azure login command to set your subscription.'),
          idOrName));
      }
    }
    return subscription;
  },

  importPublishSettings: function (fileName) {
    var self = this;
    _.each(publishSettings.import(fileName), function (subData) {
      var newSubscription = new Subscription(subData, self._findEnvironment(subData));
      self.addSubscription(newSubscription);
      if (!self.currentSubscription) {
        newSubscription.isDefault = true;
      }
    });
  },

  saveToStream: function (stream) {
    stream.write(JSON.stringify(this._getSaveData(), null, 4), 'utf8');
    stream.end();
  },

  save: function (fileName) {
    if (!fileName) {
      fileName = defaultProfileFile;
    }

    fs.writeFileSync(fileName, JSON.stringify(this._getSaveData(), null, 4));
  },

  _getSaveData: function () {
    return {
      environments: _.values(this.environments)
        .filter(function (e) { return !e.isPublicEnvironment; })
        .map(function (e) { return e.toJSON(); }),
      subscriptions: _.values(this.subscriptions).map(function (s) { return s.toJSON(); })
    };
  },

  /**
  * Find an environment with a matching management endpoint
  * @param {object} subscriptionData subscription data from publishsettings file
  *
  * @returns corresponding environment object or throws if not found.
  */
  _findEnvironment: function (subscriptionData) {
    var trimmedEndpoint = utils.stringTrimEnd(subscriptionData.managementEndpointUrl, '/');

    var found = _.values(this.environments).filter(function (e) {
      return utils.ignoreCaseEquals(trimmedEndpoint, utils.stringTrimEnd(e.managementEndpointUrl));
    });
    if (found.length === 0) {
      throw new Error(util.format(
        $('Could not find an environment with management endpoint %s. Create one and import this publishSettings file again.'),
        subscriptionData.managementEndpointUrl));
    }
    return found[0];
  }
});

//
// Profile loading functions
//

function load(fileNameOrData) {
  var profile = new Profile();
  if (_.isUndefined(fileNameOrData) || fileNameOrData === defaultProfileFile) {
    return loadDefaultProfile(profile);
  } else if (_.isString(fileNameOrData)) {
    return loadProfileFromFile(profile, fileNameOrData);
  } else {
    return loadProfileFromObject(profile, fileNameOrData);
  }
}

function loadDefaultProfile(profile) {
  profile.fileName = defaultProfileFile;
  if (utils.pathExistsSync(defaultProfileFile)) {
    return loadProfileFromFile(profile, defaultProfileFile);
  }
  return profile;
}

function loadProfileFromFile(profile, fileName) {
  profile.fileName = fileName;
  if (!utils.pathExistsSync(fileName)) {
    throw new Error(util.format($('Profile file %s does not exist'), fileName));
  }
  return loadProfileFromObject(profile, JSON.parse(fs.readFileSync(fileName, 'utf8')));
}

function loadProfileFromObject(profile, data) {
  if (data.environments) {
    data.environments.forEach(function (envData) {
      var e = new Environment(envData);
      profile.addEnvironment(e);
    });
  }
  if (data.subscriptions) {
    data.subscriptions.forEach(function (subData) {
      profile.addSubscription(new Subscription(subData, profile.environments[subData.environmentName]));
    });
    if(!profile.currentSubscription && data.subscriptions.length > 0) {
      profile.getSubscription(data.subscriptions[0].id).isDefault = true;
    }
  }
  return profile;
}

function clearAzureDir() {
  function deleteIfExists(file, isDir) {
    if (utils.pathExistsSync(file)) {
      log.silly(util.format($('Removing %s'), file));
      (isDir ? fs.rmdirSync : fs.unlinkSync)(file);
      return true;
    } else {
      log.silly(util.format($('%s does not exist'), file));
    }
  }

  var azureDirectory = utils.azureDir();
  var pemPath = path.join(azureDirectory, 'managementCertificate.pem');
  var publishSettingsFilePath = path.join(azureDirectory, 'publishSettings.xml');

  var isDeleted = deleteIfExists(pemPath);
  isDeleted = deleteIfExists(publishSettingsFilePath) || isDeleted; // in this order only
  isDeleted = utils.clearConfig() || isDeleted;
  isDeleted = cacheUtils.clear() || isDeleted;
  isDeleted = deleteIfExists(defaultProfileFile) || isDeleted;

  try {
    deleteIfExists(azureDirectory, true);
  } catch (err) {
    log.warn(util.format($('Couldn\'t remove %s'), azureDirectory));
  }

  log.info(isDeleted ? $('Account settings cleared successfully')
      : $('Account settings are already clear'));
}

var defaultProfileFile = path.join(utils.azureDir(), 'azureProfile.json');

var currentProfile = load(defaultProfileFile);

//
// Resource management
//
function toLowerCase(s) { return s.toLowerCase(); }

var knownResourceNamespaces = new StringSet(toLowerCase);
var knownProviders = new StringSet(toLowerCase);

function addKnownResourceNamespace() {
  knownResourceNamespaces.add(Array.prototype.slice.call(arguments, 0));
}

function addKnownProvider() {
  knownProviders.add(Array.prototype.slice.call(arguments, 0));
}

_.extend(module.exports, {
  load: load,
  defaultProfileFile: defaultProfileFile,
  Profile: Profile,
  Subscription: Subscription,
  Environment: Environment,
  current: currentProfile,
  clearAzureDir: clearAzureDir,
  getSubscription: function (subscription) {
    return currentProfile.getSubscription(subscription);
  },
  addKnownResourceNamespace: addKnownResourceNamespace,
  addKnownProvider: addKnownProvider,
  knownResourceNamespaces: function () { return knownResourceNamespaces.keys(); },
  knownProviders: function() { return knownProviders.keys(); },
  providerKeyTransform: toLowerCase
});
<|endoftext|>


<commit_before>var util = require('util');

var ValidaInvalidError = module.exports = function (validationErrors) {
  Error.call(this);

  this.name = this.constructor.name;
  Error.captureStackTrace(this, this.constructor);

  this.validationErrors = validationErrors;
};

util.inherits(ValidaInvalidError, Error);
<commit_msg>missing constructor name<commit_after>var util = require('util');

var ValidaInvalidError = module.exports = function ValidaInvalidError (validationErrors) {
  Error.call(this);

  this.name = this.constructor.name;
  Error.captureStackTrace(this, this.constructor);

  this.validationErrors = validationErrors;
};

util.inherits(ValidaInvalidError, Error);
<|endoftext|>


<commit_before>const KEYS_USED_FOR_ASSIGNMENT =
  new Set(['id', 'imported', 'local', 'params', 'key']);
const KEYS_USED_IN_REFERENCE_TO_OBJECTS = new Set(['property']);

function normalizeNode(node, key, definedInScope) {
  if (node.type === 'JSXOpeningElement') {
    return {
      name: node.name.name || node.name.object.name,
      isJSX: true,
      definedInScope,
    };
  }

  if (node.type !== 'Identifier') {
    return undefined;
  }

  const isAssignment = KEYS_USED_FOR_ASSIGNMENT.has(key);
  if (isAssignment) {
    definedInScope.add(node.name);
  }

  return {
    isReference: KEYS_USED_IN_REFERENCE_TO_OBJECTS.has(key),
    isAssignment,
    definedInScope,
    name: node.name,
  };
}

export default function visitIdentifierNodes(
  node, visitor, definedInScope = new Set(['module']), key = 'root') {
  if (!node || typeof node !== 'object') {
    return;
  }

  if (Array.isArray(node)) {
    node.forEach((child) => {
      visitIdentifierNodes(child, visitor, definedInScope, key);
    });
    return;
  }

  const normalizedNode = normalizeNode(node, key, definedInScope);
  if (normalizedNode) {
    visitor(normalizedNode);
  }

  Object.keys(node).forEach((key) => {
    const newDefinedInScope = key === 'body' ?
      new Set([...definedInScope]) : definedInScope;

    visitIdentifierNodes(node[key], visitor, newDefinedInScope, key);
  });
}
<commit_msg>Switch to Object.entries while recursing over ast nodes<commit_after>const KEYS_USED_FOR_ASSIGNMENT =
  new Set(['id', 'imported', 'local', 'params', 'key']);
const KEYS_USED_IN_REFERENCE_TO_OBJECTS = new Set(['property']);

function normalizeNode(node, key, definedInScope) {
  if (node.type === 'JSXOpeningElement') {
    return {
      name: node.name.name || node.name.object.name,
      isJSX: true,
      definedInScope,
    };
  }

  if (node.type !== 'Identifier') {
    return undefined;
  }

  const isAssignment = KEYS_USED_FOR_ASSIGNMENT.has(key);
  if (isAssignment) {
    definedInScope.add(node.name);
  }

  return {
    isReference: KEYS_USED_IN_REFERENCE_TO_OBJECTS.has(key),
    isAssignment,
    definedInScope,
    name: node.name,
  };
}

export default function visitIdentifierNodes(
  node, visitor, definedInScope = new Set(['module']), key = 'root') {
  if (!node || typeof node !== 'object') {
    return;
  }

  if (Array.isArray(node)) {
    node.forEach((child) => {
      visitIdentifierNodes(child, visitor, definedInScope, key);
    });
    return;
  }

  const normalizedNode = normalizeNode(node, key, definedInScope);
  if (normalizedNode) {
    visitor(normalizedNode);
  }

  Object.entries(node).forEach(([key, value]) => {
    const newDefinedInScope = key === 'body' ?
      new Set([...definedInScope]) : definedInScope;

    visitIdentifierNodes(value, visitor, newDefinedInScope, key);
  });
}
<|endoftext|>


<commit_before>const os = require('os');
const changelog = require('changelog');
const cacache = require('cacache/en');
const npmRegistry = require('../../manager/npm/registry');
const { addReleaseNotes } = require('./release-notes');
const semver = require('semver');

module.exports = {
  getChangeLogJSON,
};

async function getChangeLogJSON(depName, fromVersion, newVersion) {
  logger.debug(`getChangeLogJSON(${depName}, ${fromVersion}, ${newVersion})`);
  if (!fromVersion || fromVersion === newVersion) {
    return null;
  }
  const semverString = `>${fromVersion} <=${newVersion}`;
  logger.debug(`semverString: ${semverString}`);
  const cachePath =
    (process.env.RENOVATE_TMPDIR || os.tmpdir()) + '/renovate-changelog-cache';
  const cacheKey = `${depName}-${fromVersion}-${newVersion}`;

  // Return from cache if present
  try {
    const cacheVal = await cacache.get(cachePath, cacheKey);
    logger.debug(`Returning cached version of ${depName}`);
    const cachedResult = JSON.parse(cacheVal.data.toString());
    return addReleaseNotes(cachedResult);
  } catch (err) {
    logger.debug('Cache miss');
  }
  let res = null;
  try {
    res = await changelog.generate(depName, semverString);
    if (!res) {
      logger.info({ depName, fromVersion, newVersion }, 'No changelog found');
      return null;
    }
    // Sort from oldest to newest
    if (Array.isArray(res.versions)) {
      res.versions.reverse();
      res.versions.forEach(version => {
        if (Array.isArray(version.changes)) {
          version.changes.reverse();
        }
      });
    }
    await cacache.put(cachePath, cacheKey, JSON.stringify(res));
  } catch (err) {
    logger.debug({ err }, `getChangeLogJSON error`);
  }
  if (!res) {
    logger.debug('Checking for github source URL manually');
    const dep = await npmRegistry.getDependency(depName);
    // istanbul ignore if
    if (
      dep &&
      dep.repositoryUrl &&
      dep.repositoryUrl.startsWith('https://github.com/')
    ) {
      logger.info('Found github URL manually');
      const github = dep.repositoryUrl
        .replace('https://github.com/', '')
        .replace(/#.*/, '');
      res = {
        project: {
          github,
        },
        versions: Object.keys(dep.versions)
          .filter(v => semver.satisfies(v, semverString))
          .map(version => ({ version, changes: [] })),
      };
      logger.debug({ res }, 'Manual res');
    } else {
      logger.debug('No repo found manually');
    }
  }
  return addReleaseNotes(res);
}
<commit_msg>fix: add extra check for manual github url<commit_after>const os = require('os');
const changelog = require('changelog');
const cacache = require('cacache/en');
const npmRegistry = require('../../manager/npm/registry');
const { addReleaseNotes } = require('./release-notes');
const semver = require('semver');

module.exports = {
  getChangeLogJSON,
};

async function getChangeLogJSON(depName, fromVersion, newVersion) {
  logger.debug(`getChangeLogJSON(${depName}, ${fromVersion}, ${newVersion})`);
  if (!fromVersion || fromVersion === newVersion) {
    return null;
  }
  const semverString = `>${fromVersion} <=${newVersion}`;
  logger.debug(`semverString: ${semverString}`);
  const cachePath =
    (process.env.RENOVATE_TMPDIR || os.tmpdir()) + '/renovate-changelog-cache';
  const cacheKey = `${depName}-${fromVersion}-${newVersion}`;

  // Return from cache if present
  try {
    const cacheVal = await cacache.get(cachePath, cacheKey);
    logger.debug(`Returning cached version of ${depName}`);
    const cachedResult = JSON.parse(cacheVal.data.toString());
    return addReleaseNotes(cachedResult);
  } catch (err) {
    logger.debug('Cache miss');
  }
  let res = null;
  try {
    res = await changelog.generate(depName, semverString);
    if (!res) {
      logger.info({ depName, fromVersion, newVersion }, 'No changelog found');
      return null;
    }
    // Sort from oldest to newest
    if (Array.isArray(res.versions)) {
      res.versions.reverse();
      res.versions.forEach(version => {
        if (Array.isArray(version.changes)) {
          version.changes.reverse();
        }
      });
    }
    await cacache.put(cachePath, cacheKey, JSON.stringify(res));
  } catch (err) {
    logger.debug({ err }, `getChangeLogJSON error`);
  }
  if (!res) {
    logger.debug('Checking for github source URL manually');
    const dep = await npmRegistry.getDependency(depName);
    // istanbul ignore if
    if (
      dep &&
      dep.repositoryUrl &&
      dep.repositoryUrl.startsWith('https://github.com/')
    ) {
      logger.info({ url: dep.repositoryUrl }, 'Found github URL manually');
      const github = dep.repositoryUrl
        .replace('https://github.com/', '')
        .replace(/#.*/, '');
      if (github.split('/').length === 2) {
        res = {
          project: {
            github,
          },
          versions: Object.keys(dep.versions)
            .filter(v => semver.satisfies(v, semverString))
            .map(version => ({ version, changes: [] })),
        };
        logger.debug({ res }, 'Manual res');
      } else {
        logger.debug('Invalid github URL found');
      }
    } else {
      logger.debug('No repo found manually');
    }
  }
  return addReleaseNotes(res);
}
<|endoftext|>


<commit_before>/*$AMPERSAND_VERSION*/
var contains = require('amp-contains');
var difference = require('amp-difference');
var each = require('amp-each');
var every = require('amp-every');
var extend = require('amp-extend');
var isArray = require('amp-is-array');
var isEqual = require('amp-is-object-equal');
var keys = require('amp-keys');
var reduce = require('amp-reduce');
var sortBy = require('amp-sort-by');
var sortedInsert = require('amp-sorted-insert');
var union = require('amp-union');
var classExtend = require('ampersand-class-extend');
var underscoreMixins = require('ampersand-collection-underscore-mixin');
var Events = require('ampersand-events');

var slice = Array.prototype.slice;

function FilteredCollection(collection, spec) {
    this.collection = collection;
    this.indexes = collection.indexes;
    this._indexes = {};
    this._resetIndexes(this._indexes);
    this.mainIndex = collection.mainIndex;
    this.models = []; //Our filtered, models
    this.configure(spec || {}, true);
    this.listenTo(this.collection, 'all', this._onCollectionEvent);
}

extend(FilteredCollection.prototype, Events, underscoreMixins, {
    // Public API

    // add a filter function directly
    addFilter: function (filter) {
        this.swapFilters([filter], []);
    },

    // remove filter function directly
    removeFilter: function (filter) {
        this.swapFilters([], [filter]);
    },

    // clears filters fires events for each add/remove
    clearFilters: function () {
        this._resetFilters();
        this._runFilters();
    },

    // Swap out a set of old filters with a set of
    // new filters
    swapFilters: function (newFilters, oldFilters) {
        var self = this;

        if (!oldFilters) {
            oldFilters = this._filters;
        } else if (!isArray(oldFilters)) {
            oldFilters = [oldFilters];
        }

        if (!newFilters) {
            newFilters = [];
        } else if (!isArray(newFilters)) {
            newFilters = [newFilters];
        }

        oldFilters.forEach(function (filter) {
            self._removeFilter(filter);
        });

        newFilters.forEach(function (filter) {
            self._addFilter(filter);
        });

        this._runFilters();
    },

    // Update config with potentially new filters/where/etc
    configure: function (opts, clear) {
        if (clear) this._resetFilters(clear);
        this._parseSpec(opts);
        if (clear) this._runFilters();
    },

    // gets a model at a given index
    at: function (index) {
        return this.models[index];
    },

    // proxy `get` method to the underlying collection
    get: function (query, indexName) {
        var model = this.collection.get(query, indexName);
        if (model && this.contains(model)) return model;
    },

    // clear all filters, reset everything
    reset: function () {
        this.configure({}, true);
    },

    // Internal API

    // try to get a model by index
    _indexedGet: function (query, indexName) {
        if (!query) return;
        var index = this._indexes[indexName || this.mainIndex];
        return index[query] || index[query[this.mainIndex]] || this._indexes.cid[query] || this._indexes.cid[query.cid];
    },

    _parseSpec: function (spec) {
        if (spec.watched) this._watch(spec.watched);
        //this.comparator = this.collection.comparator;
        if (spec.comparator) this.comparator = spec.comparator;
        if (spec.where) {
            each(spec.where, function (value, item) {
                this._addFilter(function (model) {
                    return (model.get ? model.get(item) : model[item]) === value;
                });
            }, this);
            // also make sure we watch all `where` keys
            this._watch(keys(spec.where));
        }
        if (spec.filter) {
            this._addFilter(spec.filter);
        }
        if (spec.filters) {
            spec.filters.forEach(this._addFilter, this);
        }
    },
    // internal method registering new filter function
    _addFilter: function (filter) {
        this._filters.push(filter);
    },

    // remove filter if found
    _removeFilter: function (filter) {
        var index = this._filters.indexOf(filter);
        if (index !== -1) {
            this._filters.splice(index, 1);
        }
    },

    // just reset filters, no model changes
    _resetFilters: function (resetComparator) {
        this._filters = [];
        this._watched = [];
        if (resetComparator) this.comparator = undefined;
    },

    // adds a property or array of properties to watch, ensures uniquness.
    _watch: function (items) {
        this._watched = union(this._watched, items);
    },

    // removes a watched property
    _unwatch: function (item) {
        this._watched = difference(this._watched, isArray(item) ? item : [item]);
    },

    _sortModels: function (newModels) {
        var comparator = this.comparator || this.collection.comparator;
        if (comparator) {
            newModels = sortBy(newModels, comparator);
        } else {
            // This only happens when parent got a .set with options.at defined
            this._runFilters();
        }
        return newModels;
    },

    //Add a model to this filtered collection that has already passed the filters
    _addModel: function (model, options, eventName) {
        var newModels = slice.call(this.models);
        var comparator = this.comparator || this.collection.comparator;
        //Whether or not we are to expect a sort event from our collection later
        var sortable = eventName === 'add' && this.collection.comparator && (options.at == null) && options.sort !== false;
        if (!sortable) {
            sortedInsert(newModels, model, comparator);
        } else {
            newModels.push(model);
            if (options.at) newModels = this._sortModels(newModels);
        }

        this.models = newModels;
        this._addIndex(this._indexes, model);
        if (this.comparator && !sortable) {
            this.trigger('sort', this);
        }
    },

    //Remove a model if it's in this filtered collection
    _removeModel: function (model) {
        var newModels = slice.call(this.models);
        var modelIndex = newModels.indexOf(model);
        if (modelIndex > -1) {
            newModels.splice(modelIndex, 1);
            this.models = newModels;
            this._removeIndex(this._indexes, model);
            return true;
        }
        return false;
    },

    //Test if a model passes our filters
    _testModel: function (model) {
        if (this._filters.length === 0) {
            return true;
        }
        return every(this._filters, function (filter) {
            return filter(model);
        });
    },

    _addIndex: function (newIndexes, model) {
        for (var name in this._indexes) {
            var indexVal = model[name] || (model.get && model.get(name));
            if (indexVal) newIndexes[name][indexVal] = model;
        }
    },

    _removeIndex: function (newIndexes, model) {
        for (var name in this._indexes) {
            delete this._indexes[name][model[name] || (model.get && model.get(name))];
        }
    },

    _resetIndexes: function (newIndexes) {
        for (var i = 0; i < this.indexes.length; i++) {
            newIndexes[this.indexes[i]] = {};
        }
    },

    // Re-run the filters on all our parent's models
    _runFilters: function () {
        // make a copy of the array for comparisons
        var existingModels = slice.call(this.models);
        var rootModels = slice.call(this.collection.models);
        var newIndexes = {};
        var newModels, toAdd, toRemove;

        this._resetIndexes(newIndexes);

        // reduce base model set by applying filters
        if (this._filters.length) {
            newModels = reduce(this._filters, function (startingArray, filterFunc) {
                return startingArray.filter(filterFunc);
            }, rootModels);
        } else {
            newModels = slice.call(rootModels);
        }

        // sort it
        if (this.comparator) newModels = sortBy(newModels, this.comparator);

        newModels.forEach(function (model) {
            this._addIndex(newIndexes, model);
        }, this);

        // Cache a reference to the full filtered set to allow this._filtered.length. Ref: #6
        if (rootModels.length) {
            this._filtered = newModels;
            this._indexes = newIndexes;
        } else {
            this._filtered = [];
            this._resetIndexes(this._indexes);
        }

        // now we've got our new models time to compare
        toAdd = difference(newModels, existingModels);
        toRemove = difference(existingModels, newModels);

        // save 'em
        this.models = newModels;

        each(toRemove, function (model) {
            this.trigger('remove', model, this);
        }, this);

        each(toAdd, function (model) {
            this.trigger('add', model, this);
        }, this);

        // unless we have the same models in same order trigger `sort`
        if (!isEqual(existingModels, newModels) && this.comparator) {
            this.trigger('sort', this);
        }
    },

    _onCollectionEvent: function (eventName, model, that, options) {
        /*jshint -W030 */
        options || (options = {});
        var accepted;
        var propName = eventName.split(':')[1];
        var action = eventName;
        var alreadyHave = this._indexedGet(model);
        //Whether or not we are to expect a sort event from our collection later
        var sortable = this.collection.comparator && (options.at == null) && (options.sort !== false);
        var add = options.add;
        var remove = options.remove;
        var ordered = !sortable && add && remove;

        if (
            (propName !== undefined && propName === this.comparator) ||
            contains(this._watched, propName)
        ) { //If a property we care about changed
            accepted = this._testModel(model);

            if (!alreadyHave && accepted) {
                action = 'add';
            } else if (alreadyHave && !accepted) {
                action = 'remove';
            } else {
                action = 'ignore';
            }
        } else if (action === 'add') { //See if we really want to add
            if (!this._testModel(model) || alreadyHave) {
                action = 'ignore';
            }
        }

        // action has now passed the filters

        if (action === 'reset') return this._runFilters();

        if (action === 'add') {
            if (this.models.length === 0) {
                this._runFilters();
            } else {
                this._addModel(model, options, eventName);
                this.trigger('add', model, this);
            }
            return;
        }

        if (action === 'remove') {
           if (this._removeModel(model)) {
               this.trigger('remove', model, this);
           }
           return;
        }

        if (action !== 'ignore') this.trigger.apply(this, arguments);

        //If we were asked to sort, or we aren't gonna get a sort later and had a sortable property change
        if (action === 'sort' || (propName && !sortable && contains([this.comparator, this.collection.comparator]), propName))
       {
            if (ordered && model.isNew) return; //We'll get a sort later
            this.models = this._sortModels(this.models);
            if (this.comparator && action !== 'sort') {
                this.trigger('sort', this);
            }
        }

    }

});

Object.defineProperty(FilteredCollection.prototype, 'length', {
    get: function () {
        return this.models.length;
    }
});

Object.defineProperty(FilteredCollection.prototype, 'isCollection', {
    get: function () {
        return true;
    }
});

FilteredCollection.extend = classExtend;

module.exports = FilteredCollection;
<commit_msg>Fix indexing issues caused by ampersand-collection@1.4.2<commit_after>/*$AMPERSAND_VERSION*/
var contains = require('amp-contains');
var difference = require('amp-difference');
var each = require('amp-each');
var every = require('amp-every');
var extend = require('amp-extend');
var isArray = require('amp-is-array');
var isEqual = require('amp-is-object-equal');
var keys = require('amp-keys');
var reduce = require('amp-reduce');
var sortBy = require('amp-sort-by');
var sortedInsert = require('amp-sorted-insert');
var union = require('amp-union');
var classExtend = require('ampersand-class-extend');
var underscoreMixins = require('ampersand-collection-underscore-mixin');
var Events = require('ampersand-events');

var slice = Array.prototype.slice;

function FilteredCollection(collection, spec) {
    this.collection = collection;
    this.indexes = collection.indexes || [];
    this._indexes = {};
    this._resetIndexes(this._indexes);
    this.mainIndex = collection.mainIndex;
    this.models = []; //Our filtered, models
    this.configure(spec || {}, true);
    this.listenTo(this.collection, 'all', this._onCollectionEvent);
}

extend(FilteredCollection.prototype, Events, underscoreMixins, {
    // Public API

    // add a filter function directly
    addFilter: function (filter) {
        this.swapFilters([filter], []);
    },

    // remove filter function directly
    removeFilter: function (filter) {
        this.swapFilters([], [filter]);
    },

    // clears filters fires events for each add/remove
    clearFilters: function () {
        this._resetFilters();
        this._runFilters();
    },

    // Swap out a set of old filters with a set of
    // new filters
    swapFilters: function (newFilters, oldFilters) {
        var self = this;

        if (!oldFilters) {
            oldFilters = this._filters;
        } else if (!isArray(oldFilters)) {
            oldFilters = [oldFilters];
        }

        if (!newFilters) {
            newFilters = [];
        } else if (!isArray(newFilters)) {
            newFilters = [newFilters];
        }

        oldFilters.forEach(function (filter) {
            self._removeFilter(filter);
        });

        newFilters.forEach(function (filter) {
            self._addFilter(filter);
        });

        this._runFilters();
    },

    // Update config with potentially new filters/where/etc
    configure: function (opts, clear) {
        if (clear) this._resetFilters(clear);
        this._parseSpec(opts);
        if (clear) this._runFilters();
    },

    // gets a model at a given index
    at: function (index) {
        return this.models[index];
    },

    // proxy `get` method to the underlying collection
    get: function (query, indexName) {
        var model = this.collection.get(query, indexName);
        if (model && this.contains(model)) return model;
    },

    // clear all filters, reset everything
    reset: function () {
        this.configure({}, true);
    },

    // Internal API

    // try to get a model by index
    _indexedGet: function (query, indexName) {
        if (!query) return;
        var index = this._indexes[indexName || this.mainIndex];
        return index[query] || index[query[this.mainIndex]] || this._indexes.cid[query] || this._indexes.cid[query.cid];
    },

    _parseSpec: function (spec) {
        if (spec.watched) this._watch(spec.watched);
        //this.comparator = this.collection.comparator;
        if (spec.comparator) this.comparator = spec.comparator;
        if (spec.where) {
            each(spec.where, function (value, item) {
                this._addFilter(function (model) {
                    return (model.get ? model.get(item) : model[item]) === value;
                });
            }, this);
            // also make sure we watch all `where` keys
            this._watch(keys(spec.where));
        }
        if (spec.filter) {
            this._addFilter(spec.filter);
        }
        if (spec.filters) {
            spec.filters.forEach(this._addFilter, this);
        }
    },
    // internal method registering new filter function
    _addFilter: function (filter) {
        this._filters.push(filter);
    },

    // remove filter if found
    _removeFilter: function (filter) {
        var index = this._filters.indexOf(filter);
        if (index !== -1) {
            this._filters.splice(index, 1);
        }
    },

    // just reset filters, no model changes
    _resetFilters: function (resetComparator) {
        this._filters = [];
        this._watched = [];
        if (resetComparator) this.comparator = undefined;
    },

    // adds a property or array of properties to watch, ensures uniquness.
    _watch: function (items) {
        this._watched = union(this._watched, items);
    },

    // removes a watched property
    _unwatch: function (item) {
        this._watched = difference(this._watched, isArray(item) ? item : [item]);
    },

    _sortModels: function (newModels) {
        var comparator = this.comparator || this.collection.comparator;
        if (comparator) {
            newModels = sortBy(newModels, comparator);
        } else {
            // This only happens when parent got a .set with options.at defined
            this._runFilters();
        }
        return newModels;
    },

    //Add a model to this filtered collection that has already passed the filters
    _addModel: function (model, options, eventName) {
        var newModels = slice.call(this.models);
        var comparator = this.comparator || this.collection.comparator;
        //Whether or not we are to expect a sort event from our collection later
        var sortable = eventName === 'add' && this.collection.comparator && (options.at == null) && options.sort !== false;
        if (!sortable) {
            sortedInsert(newModels, model, comparator);
        } else {
            newModels.push(model);
            if (options.at) newModels = this._sortModels(newModels);
        }

        this.models = newModels;
        this._addIndex(this._indexes, model);
        if (this.comparator && !sortable) {
            this.trigger('sort', this);
        }
    },

    //Remove a model if it's in this filtered collection
    _removeModel: function (model) {
        var newModels = slice.call(this.models);
        var modelIndex = newModels.indexOf(model);
        if (modelIndex > -1) {
            newModels.splice(modelIndex, 1);
            this.models = newModels;
            this._removeIndex(this._indexes, model);
            return true;
        }
        return false;
    },

    //Test if a model passes our filters
    _testModel: function (model) {
        if (this._filters.length === 0) {
            return true;
        }
        return every(this._filters, function (filter) {
            return filter(model);
        });
    },

    _addIndex: function (newIndexes, model) {
        for (var name in this._indexes) {
            var indexVal = model[name] || (model.get && model.get(name));
            if (indexVal) newIndexes[name][indexVal] = model;
        }
    },

    _removeIndex: function (newIndexes, model) {
        for (var name in this._indexes) {
            delete this._indexes[name][model[name] || (model.get && model.get(name))];
        }
    },

    _resetIndexes: function (newIndexes) {
        var list = slice.call(this.indexes);
        list.push(this.mainIndex);
        list.push('cid');
        for (var i = 0; i < list.length; i++) {
            newIndexes[list[i]] = {};
        }
    },

    // Re-run the filters on all our parent's models
    _runFilters: function () {
        // make a copy of the array for comparisons
        var existingModels = slice.call(this.models);
        var rootModels = slice.call(this.collection.models);
        var newIndexes = {};
        var newModels, toAdd, toRemove;

        this._resetIndexes(newIndexes);

        // reduce base model set by applying filters
        if (this._filters.length) {
            newModels = reduce(this._filters, function (startingArray, filterFunc) {
                return startingArray.filter(filterFunc);
            }, rootModels);
        } else {
            newModels = slice.call(rootModels);
        }

        // sort it
        if (this.comparator) newModels = sortBy(newModels, this.comparator);

        newModels.forEach(function (model) {
            this._addIndex(newIndexes, model);
        }, this);

        // Cache a reference to the full filtered set to allow this._filtered.length. Ref: #6
        if (rootModels.length) {
            this._filtered = newModels;
            this._indexes = newIndexes;
        } else {
            this._filtered = [];
            this._resetIndexes(this._indexes);
        }

        // now we've got our new models time to compare
        toAdd = difference(newModels, existingModels);
        toRemove = difference(existingModels, newModels);

        // save 'em
        this.models = newModels;

        each(toRemove, function (model) {
            this.trigger('remove', model, this);
        }, this);

        each(toAdd, function (model) {
            this.trigger('add', model, this);
        }, this);

        // unless we have the same models in same order trigger `sort`
        if (!isEqual(existingModels, newModels) && this.comparator) {
            this.trigger('sort', this);
        }
    },

    _onCollectionEvent: function (eventName, model, that, options) {
        /*jshint -W030 */
        options || (options = {});
        var accepted;
        var propName = eventName.split(':')[1];
        var action = eventName;
        var alreadyHave = this._indexedGet(model);
        //Whether or not we are to expect a sort event from our collection later
        var sortable = this.collection.comparator && (options.at == null) && (options.sort !== false);
        var add = options.add;
        var remove = options.remove;
        var ordered = !sortable && add && remove;

        if (
            (propName !== undefined && propName === this.comparator) ||
            contains(this._watched, propName)
        ) { //If a property we care about changed
            accepted = this._testModel(model);

            if (!alreadyHave && accepted) {
                action = 'add';
            } else if (alreadyHave && !accepted) {
                action = 'remove';
            } else {
                action = 'ignore';
            }
        } else if (action === 'add') { //See if we really want to add
            if (!this._testModel(model) || alreadyHave) {
                action = 'ignore';
            }
        }

        // action has now passed the filters

        if (action === 'reset') return this._runFilters();

        if (action === 'add') {
            if (this.models.length === 0) {
                this._runFilters();
            } else {
                this._addModel(model, options, eventName);
                this.trigger('add', model, this);
            }
            return;
        }

        if (action === 'remove') {
           if (this._removeModel(model)) {
               this.trigger('remove', model, this);
           }
           return;
        }

        if (action !== 'ignore') this.trigger.apply(this, arguments);

        //If we were asked to sort, or we aren't gonna get a sort later and had a sortable property change
        if (action === 'sort' || (propName && !sortable && contains([this.comparator, this.collection.comparator]), propName))
       {
            if (ordered && model.isNew) return; //We'll get a sort later
            this.models = this._sortModels(this.models);
            if (this.comparator && action !== 'sort') {
                this.trigger('sort', this);
            }
        }

    }

});

Object.defineProperty(FilteredCollection.prototype, 'length', {
    get: function () {
        return this.models.length;
    }
});

Object.defineProperty(FilteredCollection.prototype, 'isCollection', {
    get: function () {
        return true;
    }
});

FilteredCollection.extend = classExtend;

module.exports = FilteredCollection;
<|endoftext|>


<commit_before>const DomReady = require('domready');
const GhostContentApi = require('@tryghost/content-api');
const layer2 = require('@tryghost/members-layer2');

function reload() {
    window.location.reload();
}

function show (el) {
    el.style.display = 'block';
}
function hide (el) {
    el.style.display = 'none';
}

const version = 'v2';

DomReady(function () {
    const members = layer2({membersUrl: window.membersUrl});

    const [hashMatch, hash, query] = window.location.hash.match(/^#([^?]+)\??(.*)$/) || [];

    if (hashMatch && hash === 'reset-password') {
        const [tokenMatch, token] = query.match(/token=([a-zA-z-_]+.[a-zA-Z-_]+.[a-zA-Z-_]+)/) || [];
        if (tokenMatch) {
            return members.resetPassword({token})
                .then(reload);
        }
    }

    const membersContentElements = Array.from(document.querySelectorAll("[data-members]")) // TODO use data-members-content;

    var signinBtn = document.querySelector('[data-members-signin]');
    var signinCta = document.querySelector('[data-members-signin-cta]');
    var signoutBtn = document.querySelector('[data-members-signout]');

    members.on('signedin', function () {
        show(signoutBtn);
        hide(signinBtn);
    });

    members.on('signedout', function () {
        show(signinBtn);
        hide(signoutBtn);
    });

    function signout(event) {
        event.preventDefault();
        members.signout()
            .then(reload);
    }

    function signin(event) {
        event.preventDefault();
        members.signin()
            .then(reload);
    }

    signoutBtn.addEventListener('click', signout);
    signinBtn.addEventListener('click', signin);
    signinCta.addEventListener('click', signin);

    membersContentElements.forEach(function (element) {
        console.log(element);
        const resourceType = element.getAttribute('data-members-resource-type');
        const resourceId = element.getAttribute('data-members-resource-id');
        const host = element.getAttribute('data-members-content-host');

        const api = GhostContentApi.create({
            host,
            version
        });

        const audience = new URL(host).origin
        console.log(audience);
        members.getToken({audience}).then((token) => {
            console.log(token);
            if (!token) {
                return;
            }

            console.log({resourceType, resourceId});

            api[resourceType].read({ id: resourceId }, {}, token).then(({html}) => {
                element.innerHTML = html;
            }).catch((err) => {
                element.innerHTML = err.message;
            });
        });
    });
});
<commit_msg>Fixed JWT regex<commit_after>const DomReady = require('domready');
const GhostContentApi = require('@tryghost/content-api');
const layer2 = require('@tryghost/members-layer2');

function reload() {
    window.location.reload();
}

function show (el) {
    el.style.display = 'block';
}
function hide (el) {
    el.style.display = 'none';
}

const version = 'v2';

DomReady(function () {
    const members = layer2({membersUrl: window.membersUrl});

    const [hashMatch, hash, query] = window.location.hash.match(/^#([^?]+)\??(.*)$/) || [];

    if (hashMatch && hash === 'reset-password') {
        const [tokenMatch, token] = query.match(/token=([a-zA-Z0-9-_]+.[a-zA-Z0-9-_]+.[a-zA-Z0-9-_]+)/) || [];
        if (tokenMatch) {
            return members.resetPassword({token})
                .then(reload);
        }
    }

    const membersContentElements = Array.from(document.querySelectorAll("[data-members]")) // TODO use data-members-content;

    var signinBtn = document.querySelector('[data-members-signin]');
    var signinCta = document.querySelector('[data-members-signin-cta]');
    var signoutBtn = document.querySelector('[data-members-signout]');

    members.on('signedin', function () {
        show(signoutBtn);
        hide(signinBtn);
    });

    members.on('signedout', function () {
        show(signinBtn);
        hide(signoutBtn);
    });

    function signout(event) {
        event.preventDefault();
        members.signout()
            .then(reload);
    }

    function signin(event) {
        event.preventDefault();
        members.signin()
            .then(reload);
    }

    signoutBtn.addEventListener('click', signout);
    signinBtn.addEventListener('click', signin);
    signinCta.addEventListener('click', signin);

    membersContentElements.forEach(function (element) {
        console.log(element);
        const resourceType = element.getAttribute('data-members-resource-type');
        const resourceId = element.getAttribute('data-members-resource-id');
        const host = element.getAttribute('data-members-content-host');

        const api = GhostContentApi.create({
            host,
            version
        });

        const audience = new URL(host).origin
        console.log(audience);
        members.getToken({audience}).then((token) => {
            console.log(token);
            if (!token) {
                return;
            }

            console.log({resourceType, resourceId});

            api[resourceType].read({ id: resourceId }, {}, token).then(({html}) => {
                element.innerHTML = html;
            }).catch((err) => {
                element.innerHTML = err.message;
            });
        });
    });
});
<|endoftext|>


<commit_before>const { getPolyfillString } = require('polyfill-service')
const pathname = '/polyfill.js'
const pathnameMinified = '/polyfill.min.js'

export default function withPolyfill(Wrapped) {
  WithPolyfill.routes = {
    match: async (location, request) => {
      const uaString = request.get('User-Agent')

      if ([pathname, pathnameMinified].includes(location.pathname)) {
        const minify = location.pathname === pathnameMinified
        return polyfillResponse({ uaString, query: request.query, minify })
      }

      if (Wrapped.routes) {
        const { status, headers, data: originalData } = await Wrapped.routes.match(location, request)
        return { status, headers, data: { uaString, originalData }}
      }

      return { status: 200, data: null }
    }
  }

  function WithPolyfill({ location, data: { polyfill, uaString, originalData } }) {
    return polyfill || <Wrapped location={location} data={originalData} polyfill={getPolyfill} />

    function getPolyfill(features = [], { minify = true } = {}) {
      const path = minify ? pathnameMinified : pathname
      return (<script defer src={`${path}?features=${features.join(',')}&cache=${encodeURIComponent(uaString)}`} />)
    }
  }

  return WithPolyfill
}

async function polyfillResponse({uaString, minify, query = {} }) {
  const features = (query.features || '').split(',').filter(Boolean).reduce((res, x) => ({ ...res, [x]: { flags: ['gated'] }}), {})
  const cache = !!query.cache

  const polyfill = await getPolyfillString({ uaString, minify, features })
  const headers = {
    'Content-Type': 'application/javascript;charset=utf-8',
    'Content-Length': polyfill.length,
    ...cache && { 'Cache-Control': 'max-age=31536000' }, // 365 days
  }

  return {
    status: 200,
    headers,
    data: { polyfill, uaString }
  }
}
<commit_msg>WithPolyfill HOC will pass on other props<commit_after>const { getPolyfillString } = require('polyfill-service')
const pathname = '/polyfill.js'
const pathnameMinified = '/polyfill.min.js'

export default function withPolyfill(Wrapped) {
  WithPolyfill.routes = {
    match: async (location, request) => {
      const uaString = request.get('User-Agent')

      if ([pathname, pathnameMinified].includes(location.pathname)) {
        const minify = location.pathname === pathnameMinified
        return polyfillResponse({ uaString, query: request.query, minify })
      }

      if (Wrapped.routes) {
        const { status, headers, data: originalData } = await Wrapped.routes.match(location, request)
        return { status, headers, data: { uaString, originalData }}
      }

      return { status: 200, data: null }
    }
  }

  function WithPolyfill({ location, data: { polyfill, uaString, originalData }, ...props }) {
    return polyfill || <Wrapped {...props} location={location} data={originalData} polyfill={getPolyfill} />

    function getPolyfill(features = [], { minify = true } = {}) {
      const path = minify ? pathnameMinified : pathname
      return (<script defer src={`${path}?features=${features.join(',')}&cache=${encodeURIComponent(uaString)}`} />)
    }
  }

  return WithPolyfill
}

async function polyfillResponse({uaString, minify, query = {} }) {
  const features = (query.features || '').split(',').filter(Boolean).reduce((res, x) => ({ ...res, [x]: { flags: ['gated'] }}), {})
  const cache = !!query.cache

  const polyfill = await getPolyfillString({ uaString, minify, features })
  const headers = {
    'Content-Type': 'application/javascript;charset=utf-8',
    'Content-Length': polyfill.length,
    ...cache && { 'Cache-Control': 'max-age=31536000' }, // 365 days
  }

  return {
    status: 200,
    headers,
    data: { polyfill, uaString }
  }
}
<|endoftext|>


<commit_before>var eid = -1;
var bgmEditorTpl =
    '\

\
    <form>\

\

\
                番剧名称\
                <input type="text" id="bgmName" class="u-full-width" value="{{bgmTitle}}">\

\

\
                已看到\
                <input type="number" id="bgmCurEpi" class="u-full-width" value="{{bgmCurEpi}}">\

\

\
                更新于\
                <select id="bgmOnAir" class="u-full-width">\
                    <option value="1">周一</option>\
                    <option value="2">周二</option>\
                    <option value="3">周三</option>\
                    <option value="4">周四</option>\
                    <option value="5">周五</option>\
                    <option value="6">周六</option>\
                    <option value="7">周日</option>\
                    <option value="9">不定期</option>\
                    <option value="0">已完结</option>\
                </select>\

\

\

\

设置检查器
\

保存 删除 取消\
    </form>\

\
    ';

var bgmRowTpl =
    '

[{{bgmOnAir}}] {{bgmTitle}} ({{bgmCurEpi}})

+1 -1 检查

';

var bgmSeekerEditorTpl =
    '
\

{{seekerName}} 检查器
\
        检查关键字 <input type="text" value="{{seekerChkKey}}" />\

';

var bgmChkModalTpl =
    '\

\

{{ChkStatus}}
\

\
    ';

var seekers = [];

anicolle = {

    urls: {
        getAni: "/action/get/",
        modify: "/action/modify/",
        plus: "/action/plus/",
        decrease: "/action/decrease/",
        add: "/action/add",
        remove: "/action/remove/",
        chkup: "/action/chkup/",
        getSeekers: "/action/get_seekers/"
    },

    getSeekers: function() {
        return $.ajax({
            url: this.urls.getSeekers,
            type: "GET",
            dataType: "json"
        })
    },

    getAni: function(bid){
        return $.ajax( {
            url: this.urls.getAni + bid,
            type: "GET",
            dataType: "json"
        });
    },

    plus: function( bid ) {
        return $.ajax({
            url: this.urls.plus + bid,
            type: "GET",
            dataType: "json"
        });
    },

    decrease: function( bid ) {
        return $.ajax({
            url: this.urls.decrease + bid,
            type: "GET",
            dataType: "json"
        });
    },

    modify: function( bid, name, cur_epi, on_air, seeker) {
        return $.ajax({
            url: this.urls.modify + bid,
            type: "POST",
            data: { "name": name, "cur_epi": cur_epi,
                    "on_air": on_air, "seeker": JSON.stringify(seeker)}
        });
    },

    add: function( name, cur_epi, on_air, seeker ) {
        return $.ajax({
            url: this.urls.add,
            type: "POST",
            data: { "name": name, "cur_epi": cur_epi,
                    "on_air": on_air, "seeker": JSON.stringify(seeker)}
        });
    },

    remove: function( bid ) {
        return $.ajax({
            url: this.urls.remove + bid,
            type: "GET"
        });
    },

    chkup: function( bid ) {
        return $.ajax({
            url: this.urls.chkup + bid,
            type: "GET",
            dataType: "json"
        });
    }
};

function showBgmEditor(obj) {
    $(".bgm-editor").remove();
    var beh = bgmEditorTpl;
    if( $(obj).hasClass('bgm-row') ) {
        var r = anicolle.getAni( getBid(obj) );
        r.done(function(data){
            beh = beh.replace(/{{bgmBid}}/g, data['id']);
            beh = beh.replace(/{{bgmTitle}}/g, escapeQuote(data['name']));
            beh = beh.replace(/{{bgmCurEpi}}/g, data['cur_epi']);
            $(obj).append(beh);
            var data_seeker = JSON.parse(data['seeker']);;
            seekers.forEach(function(item, index){
                var bseh = bgmSeekerEditorTpl;
                bseh = bseh.replace(/{{seekerIdx}}/g, index);
                bseh = bseh.replace(/{{seekerName}}/g, item);
                bseh = bseh.replace(/{{seekerChkKey}}/g, getChkkeyFromSeekerData(data_seeker, item));
                $('.bgm-seeker-editor-ctn').append(bseh);
            });
            $(".bgm-editor select option[value="+data[3]+"]").attr("selected", true);
        });
    } else {
        beh = beh.replace(/{{bgmBid}}/g, 0);
        beh = beh.replace(/{{bgmTitle}}/g, "");
        beh = beh.replace(/{{bgmCurEpi}}/g, "");
        beh = beh.replace(/{{bgmChkKey}}/g, "");
        $(obj).append(beh);
        seekers.forEach(function(item, index){
            var bseh = bgmSeekerEditorTpl;
            bseh = bseh.replace(/{{seekerIdx}}/g, index);
            bseh = bseh.replace(/{{seekerName}}/g, item);
                bseh = bseh.replace(/{{seekerChkKey}}/g, "");
            $('.bgm-seeker-editor-ctn').append(bseh);
        });
        $(".bgm-editor select option[value=0]").attr("selected", true);
        $(".bgm-editor a.button:eq(1)").remove();
    }
}

function hideBgmEditor() {
    $('.bgm-editor').remove();
}

function genBgmRow( row ) {
    var brh = bgmRowTpl;
    brh = brh.replace(/{{bgmBid}}/g, row['id']);
    brh = brh.replace(/{{bgmTitle}}/g, escapeQuote(row['name']));
    brh = brh.replace(/{{bgmCurEpi}}/g, row['cur_epi']);
    var onAirDict = [ '结', '一', '二', '三', '四', '五', '六', '日', '无', '不定' ];
    var onAir = onAirDict[row['on_air_day']];
    brh = brh.replace(/{{bgmOnAir}}/g, onAir);
    brh = brh.replace(/{{bgmOnAirCode}}/g, row['on_air_day']);
    return brh;
}

function showBgm() {
    $('.row.bgm-row').remove();
    $(".action-row input").val("");

    var bgmRowH = "";

    var r = anicolle.getAni('');
    r.done( function(data){
        data.forEach( function(row){
            bgmRowH += genBgmRow(row);
        });
        $('.row.action-row').after(bgmRowH);
        $('.bgm-title').click(function(){
            bgmO = $(this).parents(".row");
            showBgmEditor(bgmO);
        });
    });
}

function doPlus(bid) {
    var r = anicolle.plus( bid );
    var curEpi = $( getObjByBid(bid) ).find( ".cur-epi" ).text();
    curEpi  = parseFloat(curEpi);
    curEpi += 1;

    $( getObjByBid(bid) ).find(".cur-epi")
    .animate({opacity: 0})
    .text(curEpi)
    .animate({opacity: 1});
}

function doDecrease(bid) {
    var r = anicolle.decrease( bid );
    var curEpi = $( getObjByBid(bid) ).find( ".cur-epi" ).text();
    curEpi  = parseFloat(curEpi);
    curEpi -= 1;

    $( getObjByBid(bid) ).find(".cur-epi")
    .animate({opacity: 0})
    .text(curEpi)
    .animate({opacity: 1});
}

function doModify(bid) {
    if(!bid) {
        doAdd();
        return;
    }
    var r = anicolle.modify( bid, $("#bgmName").val(), $("#bgmCurEpi").val(), $("#bgmOnAir").val(), getBgmSeekerInput() );
    r.done(function(){
        hideBgmEditor();
        showBgm();
    });
}

function doAdd() {
    var r = anicolle.add( $("#bgmName").val(), $("#bgmCurEpi").val(), $("#bgmOnAir").val(), getBgmSeekerInput() );
    r.done(function(){
        hideBgmEditor();
        showBgm();
    });
}

function doRemove(bid) {
    if( confirm("你确定要删除这个番剧吗？" ) ) {
        var r = anicolle.remove( bid );
        r.done(function(){
            showBgm();
        });
    }
}

function doChk(bid) {
    obj = getObjByBid(bid);
    obj = $(obj).find('.button:eq(2)');
    $(obj).text("检查中...");
    var r = anicolle.chkup( bid );
    var bcmh = bgmChkModalTpl;
    r.done(function(data){
        $(obj).text("检查");
        if( data ) {
            //prompt("找到更新 " + data.magname, data.maglink);
            bcmh = bcmh.replace(/{{ChkStatus}}/, "找到更新");
        } else {
            bcmh = bcmh.replace(/{{ChkStatus}}/, "未找到更新");
        }
        $('body').append(bcmh);
        if(data) {
            data.forEach(function(item){
             $('.bgm-chk-modal').append("
" + item['magname'] + "
" )
                .append("<input type='text' onclick='this.select()' value='" + item['maglink'] + "' />")
            });
        }
        $('.bgm-chk-modal').append('
关闭
')
        $('.body-hover').fadeIn();
    }).fail(function(){
         $(obj).text("检查失败");
    });
}

function hideChkModal() {
     $('.bgm-chk-modal').remove();
     $('.body-hover').fadeOut();
}

function initSearch() {
    $(".action-row input").keyup(function(){
        var key = $(this).val();
        if(key) {
            if( key.match(/^w\d$/) ) {
                key = key.replace("w", "");
                $('.bgm-row').stop().fadeOut(function(){
                    $('.bgm-row[data-onair='+key+']').stop().fadeIn();
                });
            } else {
                $('.bgm-row').each(function(){
                    var title = $(this).find('.bgm-title').text();
                    var re = new RegExp(".*"+key+".*", "gi");
                    if( !re.test(title) ){
                        $(this).stop().fadeOut();
                    } else {
                        $(this).stop().fadeIn();
                    }
                });
            }
        } else {
            $('.bgm-row').stop().fadeIn();
        }
    });
}

$(document).ready(function(){
    anicolle.getSeekers().done(function(data){
         seekers = data;
    });
    showBgm();
    initSearch();
});

function escapeQuote(str) {
    return str.replace( /"/g, """ );
}

function getBid(obj) {
    return $(obj).attr("data-bid");
}

function getObjByBid(bid) {
    return $('.bgm-row[data-bid=' + bid + ']');
}

function getChkkeyFromSeekerData(data, item) {
    var r = "";
    data.every(function(seeker){
        if (seeker['seeker']==item) {
            r = seeker['chk_key'];
            return 0;
        }
        return 1;
    });
    return r;
}

function getBgmSeekerInput() {
    var r = [];
    $(".bgm-seeker-editor").each(function(){
        var seekerName = seekers[parseInt($(this).attr('data-id'))];
        var chkkey = $(this).find('input').val();
        if(chkkey) {
            r.push({
                'seeker': seekerName,
                'chk_key': chkkey
            });
        }
    });
    return r;
}
<commit_msg>Hotfix<commit_after>var eid = -1;
var bgmEditorTpl =
    '\

\
    <form>\

\

\
                番剧名称\
                <input type="text" id="bgmName" class="u-full-width" value="{{bgmTitle}}">\

\

\
                已看到\
                <input type="number" id="bgmCurEpi" class="u-full-width" value="{{bgmCurEpi}}">\

\

\
                更新于\
                <select id="bgmOnAir" class="u-full-width">\
                    <option value="1">周一</option>\
                    <option value="2">周二</option>\
                    <option value="3">周三</option>\
                    <option value="4">周四</option>\
                    <option value="5">周五</option>\
                    <option value="6">周六</option>\
                    <option value="7">周日</option>\
                    <option value="9">不定期</option>\
                    <option value="0">已完结</option>\
                </select>\

\

\

\

设置检查器
\

保存 删除 取消\
    </form>\

\
    ';

var bgmRowTpl =
    '

[{{bgmOnAir}}] {{bgmTitle}} ({{bgmCurEpi}})

+1 -1 检查

';

var bgmSeekerEditorTpl =
    '
\

{{seekerName}} 检查器
\
        检查关键字 <input type="text" value="{{seekerChkKey}}" />\

';

var bgmChkModalTpl =
    '\

\

{{ChkStatus}}
\

\
    ';

var seekers = [];

anicolle = {

    urls: {
        getAni: "/action/get/",
        modify: "/action/modify/",
        plus: "/action/plus/",
        decrease: "/action/decrease/",
        add: "/action/add",
        remove: "/action/remove/",
        chkup: "/action/chkup/",
        getSeekers: "/action/get_seekers/"
    },

    getSeekers: function() {
        return $.ajax({
            url: this.urls.getSeekers,
            type: "GET",
            dataType: "json"
        })
    },

    getAni: function(bid){
        return $.ajax( {
            url: this.urls.getAni + bid,
            type: "GET",
            dataType: "json"
        });
    },

    plus: function( bid ) {
        return $.ajax({
            url: this.urls.plus + bid,
            type: "GET",
            dataType: "json"
        });
    },

    decrease: function( bid ) {
        return $.ajax({
            url: this.urls.decrease + bid,
            type: "GET",
            dataType: "json"
        });
    },

    modify: function( bid, name, cur_epi, on_air, seeker) {
        return $.ajax({
            url: this.urls.modify + bid,
            type: "POST",
            data: { "name": name, "cur_epi": cur_epi,
                    "on_air": on_air, "seeker": JSON.stringify(seeker)}
        });
    },

    add: function( name, cur_epi, on_air, seeker ) {
        return $.ajax({
            url: this.urls.add,
            type: "POST",
            data: { "name": name, "cur_epi": cur_epi,
                    "on_air": on_air, "seeker": JSON.stringify(seeker)}
        });
    },

    remove: function( bid ) {
        return $.ajax({
            url: this.urls.remove + bid,
            type: "GET"
        });
    },

    chkup: function( bid ) {
        return $.ajax({
            url: this.urls.chkup + bid,
            type: "GET",
            dataType: "json"
        });
    }
};

function showBgmEditor(obj) {
    $(".bgm-editor").remove();
    var beh = bgmEditorTpl;
    if( $(obj).hasClass('bgm-row') ) {
        var r = anicolle.getAni( getBid(obj) );
        r.done(function(data){
            beh = beh.replace(/{{bgmBid}}/g, data['id']);
            beh = beh.replace(/{{bgmTitle}}/g, escapeQuote(data['name']));
            beh = beh.replace(/{{bgmCurEpi}}/g, data['cur_epi']);
            $(obj).append(beh);
            var data_seeker = JSON.parse(data['seeker']);;
            seekers.forEach(function(item, index){
                var bseh = bgmSeekerEditorTpl;
                bseh = bseh.replace(/{{seekerIdx}}/g, index);
                bseh = bseh.replace(/{{seekerName}}/g, item);
                bseh = bseh.replace(/{{seekerChkKey}}/g, getChkkeyFromSeekerData(data_seeker, item));
                $('.bgm-seeker-editor-ctn').append(bseh);
            });
            $(".bgm-editor select option[value="+data[3]+"]").attr("selected", true);
        });
    } else {
        beh = beh.replace(/{{bgmBid}}/g, 0);
        beh = beh.replace(/{{bgmTitle}}/g, "");
        beh = beh.replace(/{{bgmCurEpi}}/g, "");
        beh = beh.replace(/{{bgmChkKey}}/g, "");
        $(obj).append(beh);
        seekers.forEach(function(item, index){
            var bseh = bgmSeekerEditorTpl;
            bseh = bseh.replace(/{{seekerIdx}}/g, index);
            bseh = bseh.replace(/{{seekerName}}/g, item);
                bseh = bseh.replace(/{{seekerChkKey}}/g, "");
            $('.bgm-seeker-editor-ctn').append(bseh);
        });
        $(".bgm-editor select option[value=0]").attr("selected", true);
        $(".bgm-editor a.button:eq(1)").remove();
    }
}

function hideBgmEditor() {
    $('.bgm-editor').remove();
}

function genBgmRow( row ) {
    var brh = bgmRowTpl;
    brh = brh.replace(/{{bgmBid}}/g, row['id']);
    brh = brh.replace(/{{bgmTitle}}/g, escapeQuote(row['name']));
    brh = brh.replace(/{{bgmCurEpi}}/g, row['cur_epi']);
    var onAirDict = [ '结', '一', '二', '三', '四', '五', '六', '日', '无', '不定' ];
    var onAir = onAirDict[row['on_air_day']];
    brh = brh.replace(/{{bgmOnAir}}/g, onAir);
    brh = brh.replace(/{{bgmOnAirCode}}/g, row['on_air_day']);
    return brh;
}

function showBgm() {
    $('.row.bgm-row').remove();
    $(".action-row input").val("");

    var bgmRowH = "";

    var r = anicolle.getAni('');
    r.done( function(data){
        data.forEach( function(row){
            bgmRowH += genBgmRow(row);
        });
        $('.row.action-row').after(bgmRowH);
        $('.bgm-title').click(function(){
            bgmO = $(this).parents(".row");
            showBgmEditor(bgmO);
        });
    });
}

function doPlus(bid) {
    var r = anicolle.plus( bid );
    var curEpi = $( getObjByBid(bid) ).find( ".cur-epi" ).text();
    curEpi  = parseFloat(curEpi);
    curEpi += 1;

    $( getObjByBid(bid) ).find(".cur-epi")
    .animate({opacity: 0})
    .text(curEpi)
    .animate({opacity: 1});
}

function doDecrease(bid) {
    var r = anicolle.decrease( bid );
    var curEpi = $( getObjByBid(bid) ).find( ".cur-epi" ).text();
    curEpi  = parseFloat(curEpi);
    curEpi -= 1;

    $( getObjByBid(bid) ).find(".cur-epi")
    .animate({opacity: 0})
    .text(curEpi)
    .animate({opacity: 1});
}

function doModify(bid) {
    if(!bid) {
        doAdd();
        return;
    }
    var r = anicolle.modify( bid, $("#bgmName").val(), $("#bgmCurEpi").val(), $("#bgmOnAir").val(), getBgmSeekerInput() );
    r.done(function(){
        hideBgmEditor();
        showBgm();
    });
}

function doAdd() {
    var r = anicolle.add( $("#bgmName").val(), $("#bgmCurEpi").val(), $("#bgmOnAir").val(), getBgmSeekerInput() );
    r.done(function(){
        hideBgmEditor();
        showBgm();
    });
}

function doRemove(bid) {
    if( confirm("你确定要删除这个番剧吗？" ) ) {
        var r = anicolle.remove( bid );
        r.done(function(){
            showBgm();
        });
    }
}

function doChk(bid) {
    obj = getObjByBid(bid);
    obj = $(obj).find('.button:eq(2)');
    $(obj).text("检查中...");
    var r = anicolle.chkup( bid );
    var bcmh = bgmChkModalTpl;
    r.done(function(data){
        $(obj).text("检查");
        if( data.length ) {
            //prompt("找到更新 " + data.magname, data.maglink);
            bcmh = bcmh.replace(/{{ChkStatus}}/, "找到更新");
        } else {
            bcmh = bcmh.replace(/{{ChkStatus}}/, "未找到更新");
        }
        $('body').append(bcmh);
        if(data) {
            data.forEach(function(item){
             $('.bgm-chk-modal').append("
" + item['magname'] + "
" )
                .append("<input type='text' onclick='this.select()' value='" + item['maglink'] + "' />")
            });
        }
        $('.bgm-chk-modal').append('
关闭
')
        $('.body-hover').fadeIn();
    }).fail(function(){
         $(obj).text("检查失败");
    });
}

function hideChkModal() {
     $('.bgm-chk-modal').remove();
     $('.body-hover').fadeOut();
}

function initSearch() {
    $(".action-row input").keyup(function(){
        var key = $(this).val();
        if(key) {
            if( key.match(/^w\d$/) ) {
                key = key.replace("w", "");
                $('.bgm-row').stop().fadeOut(function(){
                    $('.bgm-row[data-onair='+key+']').stop().fadeIn();
                });
            } else {
                $('.bgm-row').each(function(){
                    var title = $(this).find('.bgm-title').text();
                    var re = new RegExp(".*"+key+".*", "gi");
                    if( !re.test(title) ){
                        $(this).stop().fadeOut();
                    } else {
                        $(this).stop().fadeIn();
                    }
                });
            }
        } else {
            $('.bgm-row').stop().fadeIn();
        }
    });
}

$(document).ready(function(){
    anicolle.getSeekers().done(function(data){
         seekers = data;
    });
    showBgm();
    initSearch();
});

function escapeQuote(str) {
    return str.replace( /"/g, """ );
}

function getBid(obj) {
    return $(obj).attr("data-bid");
}

function getObjByBid(bid) {
    return $('.bgm-row[data-bid=' + bid + ']');
}

function getChkkeyFromSeekerData(data, item) {
    var r = "";
    data.every(function(seeker){
        if (seeker['seeker']==item) {
            r = seeker['chk_key'];
            return 0;
        }
        return 1;
    });
    return r;
}

function getBgmSeekerInput() {
    var r = [];
    $(".bgm-seeker-editor").each(function(){
        var seekerName = seekers[parseInt($(this).attr('data-id'))];
        var chkkey = $(this).find('input').val();
        if(chkkey) {
            r.push({
                'seeker': seekerName,
                'chk_key': chkkey
            });
        }
    });
    return r;
}
<|endoftext|>


<commit_before>var Promise = require("nodegit-promise");
var promisify = require("promisify-node");
var path = require("path");
var fs = require("fs");

var checkPrepared = require("./checkPrepared");
var whichNativeNodish = require("which-native-nodish");
var prepareForBuild = require("./prepareForBuild");

var exec = promisify(function(command, opts, callback) {
  return require("child_process").exec(command, opts, callback);
});
var nwVersion = null;
var asVersion = null;

var local = path.join.bind(path, __dirname);

return whichNativeNodish("..")
  .then(function(results) {
    nwVersion = results.nwVersion;
    asVersion = results.asVersion;
  })
  .then(function() {
    if (nwVersion) {
      console.info("[nodegit] Must build for node-webkit/nw.js");
      return checkAndBuild();
    }
    else if (asVersion) {
      console.info("[nodegit] Must build for atom-shell");
      return checkAndBuild();
    }
    if (fs.existsSync(local("../.didntcomefromthenpmregistry"))) {
      return checkAndBuild();
    }
    if (process.env.BUILD_DEBUG) {
      console.info("[nodegit] Doing a debug build, no fetching allowed.");
      return checkAndBuild();
    }
    if (process.env.BUILD_ONLY) {
      console.info("[nodegit] BUILD_ONLY is set to true, no fetching allowed.");
      return checkAndBuild();
    }
    console.info("[nodegit] Fetching binary from S3.");
    return exec("node-pre-gyp install")
      .then(
        function() {
          console.info("[nodegit] Completed installation successfully.");
        },
        function() {
          console.info("[nodegit] Failed to install prebuilt binary, " +
            "building manually.");
          return checkAndBuild();
        }
      );
  });

function checkAndBuild() {
  console.info("[nodegit] Making sure dependencies are available and native " +
    "code is generated");

  return checkPrepared.checkAll()
    .then(function(allGood) {
      if (allGood) {
        return Promise.resolve();
      }
      else {
        console.info("[nodegit] Something is missing, retrieving " +
        "dependencies and regenerating code");
        return prepareForBuild();
      }
    })
    .then(function() {
      return build();
    });
}

function build() {
  console.info("[nodegit] Everything is ready to go, attempting compilation");
  if (nwVersion) {
    console.info("[nodegit] Building native node-webkit module.");
  }
  else {
    console.info("[nodegit] Building native node module.");
  }

  var opts = {
    cwd: ".",
    maxBuffer: Number.MAX_VALUE
  };

  var prefix = "";
  var target = "";
  var debug = (process.env.BUILD_DEBUG ? " --debug" : "");
  var builder = "pangyp";
  var distUrl = "";

  if (asVersion) {
    prefix = (process.platform == "win32" ?
      "SET HOME=%HOME%\\.atom-shell-gyp&& " :
      "HOME=~/.atom-shell-gyp");

    target = "--target=" + asVersion;

    distUrl = "--dist-url=https://gh-contractor-zcbenz.s3." +
      "amazonaws.com/atom-shell/dist";
  }
  else if (nwVersion) {
    builder = "nw-gyp";
    target = "--target=" + nwVersion;
  }

  return exec("npm install " + builder)
    .then(function() {
      builder = path.resolve(".", "node_modules", ".bin", builder);
      builder = builder.replace(/\s/g, "\\$&");
      var cmd = [prefix, builder, "rebuild", target, debug, distUrl]
        .join(" ").trim();

      return exec(cmd, opts);
    })
    .then(function() {
      console.info("[nodegit] Compilation complete.");
      console.info("[nodegit] Completed installation successfully.");
    },
    function(err, stderr) {
      console.error(err);
      console.error(stderr);
    });
}
<commit_msg>Download all dev dependencies before build<commit_after>var Promise = require("nodegit-promise");
var promisify = require("promisify-node");
var path = require("path");
var fs = require("fs");

var checkPrepared = require("./checkPrepared");
var whichNativeNodish = require("which-native-nodish");
var prepareForBuild = require("./prepareForBuild");

var exec = promisify(function(command, opts, callback) {
  return require("child_process").exec(command, opts, callback);
});
var nwVersion = null;
var asVersion = null;

var local = path.join.bind(path, __dirname);

return whichNativeNodish("..")
  .then(function(results) {
    nwVersion = results.nwVersion;
    asVersion = results.asVersion;
  })
  .then(function() {
    if (nwVersion) {
      console.info("[nodegit] Must build for node-webkit/nw.js");
      return checkAndBuild();
    }
    else if (asVersion) {
      console.info("[nodegit] Must build for atom-shell");
      return checkAndBuild();
    }
    if (fs.existsSync(local("../.didntcomefromthenpmregistry"))) {
      return checkAndBuild();
    }
    if (process.env.BUILD_DEBUG) {
      console.info("[nodegit] Doing a debug build, no fetching allowed.");
      return checkAndBuild();
    }
    if (process.env.BUILD_ONLY) {
      console.info("[nodegit] BUILD_ONLY is set to true, no fetching allowed.");
      return checkAndBuild();
    }
    console.info("[nodegit] Fetching binary from S3.");
    return exec("node-pre-gyp install")
      .then(
        function() {
          console.info("[nodegit] Completed installation successfully.");
        },
        function() {
          console.info("[nodegit] Failed to install prebuilt binary, " +
            "building manually.");
          return checkAndBuild();
        }
      );
  });

function checkAndBuild() {
  console.info("[nodegit] Making sure dependencies are available and native " +
    "code is generated");

  return checkPrepared.checkAll()
    .then(function(allGood) {
      if (allGood) {
        return Promise.resolve();
      }
      else {
        console.info("[nodegit] Something is missing, retrieving " +
        "dependencies and regenerating code");
        return prepareForBuild();
      }
    })
    .then(function() {
      return build();
    });
}

function build() {
  console.info("[nodegit] Everything is ready to go, attempting compilation");
  if (nwVersion) {
    console.info("[nodegit] Building native node-webkit module.");
  }
  else {
    console.info("[nodegit] Building native node module.");
  }

  var opts = {
    cwd: ".",
    maxBuffer: Number.MAX_VALUE
  };

  var prefix = "";
  var target = "";
  var debug = (process.env.BUILD_DEBUG ? " --debug" : "");
  var builder = "pangyp";
  var distUrl = "";

  if (asVersion) {
    prefix = (process.platform == "win32" ?
      "SET HOME=%HOME%\\.atom-shell-gyp&& " :
      "HOME=~/.atom-shell-gyp");

    target = "--target=" + asVersion;

    distUrl = "--dist-url=https://gh-contractor-zcbenz.s3." +
      "amazonaws.com/atom-shell/dist";
  }
  else if (nwVersion) {
    builder = "nw-gyp";
    target = "--target=" + nwVersion;
  }

  return exec("npm install --ignore-scripts")
    .then(function() {
      builder = path.resolve(".", "node_modules", ".bin", builder);
      builder = builder.replace(/\s/g, "\\$&");
      var cmd = [prefix, builder, "rebuild", target, debug, distUrl]
        .join(" ").trim();

      return exec(cmd, opts);
    })
    .then(function() {
      console.info("[nodegit] Compilation complete.");
      console.info("[nodegit] Completed installation successfully.");
    },
    function(err, stderr) {
      console.error(err);
      console.error(stderr);
    });
}
<|endoftext|>


<commit_before>// Determine if two singly linked lists intersect (return intersecting node)

// LINKED LIST

// define constructor
function Node(data) {
	this.data = data;
	this.next = null;
}

function LinkedList() {
	this._length = 0; // assign number of nodes in linked list
	this.head = null; // points to head of linked list (node at front of linked list)
}

LinkedList.prototype.add = function(val) {
	var node = new Node(val), // create new instance of node
		currentNode = this.head;

	// first case: if linked list is initially empty
	if (!currentNode) {
		this.head = node; // make new node head of linked list
		this._length++;

		return node;
	}

	// second case: if linked list is initially not empty
	while (currentNode.next) {
		currentNode = currentNode.next; // iterate through entire non-empty linked list to get to end of linked list
	}

	currentNode.next = node; // add new node to end of linked list

	this._length++;

	return node;
}

// remove node from linked list
LinkedList.prototype.remove = function(position) {
	var currentNode = this.head,
		length = this.length,
		count = 0,
		message = {failure: 'Failure: non-existent node in this list'},
		beforeNodeToDelete = null,
		nodeToDelete = null,
		deletedNode = null;

	// first case: invalid position
	if (position < 0 || position > length) {
		throw new Error(message.failure);
	}

	// second case: first node is removed
	if (position === 1) {
		this.head = currentNode.next; // head reassigned
		deletedNode = currentNode;
		currentNode = null;
		this._length--;

		return deletedNode;
	}

	// third case: any other node is removed
	while (count < position) { // loop until we reach node at position we want to remove
		beforeNodeToDelete = currentNode;
		nodeToDelete = currentNode.next;
		count++;
	}

	beforeNodeToDelete.next = nodeToDelete.next;
	deletedNode = nodeToDelete;
	nodeToDelete = null;
	this._length--;

	return deletedNode;
}

function peekStack(stack) {
	return stack[stack.length - 1];
}

function findIntersection(headOne, headTwo) {
	var stackOne = [],
		stackTwo = [];

	while (headOne !== null) {
		stackOne.push(headOne);
		headOne = headOne.next;
	}

	while (headTwo !== null) {
		stackTwo.push(headTwo);
		headTwo = headTwo.next;
	}

	// if no intersection, return undefined
	if (stackOne.length === 0 || stackTwo.length ===0) {
		return undefined;
	} else if (peekStack(stackOne) !== peekStack(stackTwo)) {
		return undefined;
	} else { // if there is an intersection
		var intersect;
		while (peekStack(stackOne) === peekStack(stackTwo)) {
			intersect = peekStack(stackOne);
			stackOne.pop();
			stackTwo.pop();
		}
		return intersect; // return intersecting node
	}
}

<commit_msg>Add test case (not working yet)<commit_after>// Determine if two singly linked lists intersect (return intersecting node)

// LINKED LIST

// define constructor
function Node(data) {
	this.data = data;
	this.next = null;
}

function LinkedList() {
	this._length = 0; // assign number of nodes in linked list
	this.head = null; // points to head of linked list (node at front of linked list)
}

LinkedList.prototype.add = function(val) {
	var node = new Node(val), // create new instance of node
		currentNode = this.head;

	// first case: if linked list is initially empty
	if (!currentNode) {
		this.head = node; // make new node head of linked list
		this._length++;

		return node;
	}

	// second case: if linked list is initially not empty
	while (currentNode.next) {
		currentNode = currentNode.next; // iterate through entire non-empty linked list to get to end of linked list
	}

	currentNode.next = node; // add new node to end of linked list

	this._length++;

	return node;
}

// remove node from linked list
LinkedList.prototype.remove = function(position) {
	var currentNode = this.head,
		length = this.length,
		count = 0,
		message = {failure: 'Failure: non-existent node in this list'},
		beforeNodeToDelete = null,
		nodeToDelete = null,
		deletedNode = null;

	// first case: invalid position
	if (position < 0 || position > length) {
		throw new Error(message.failure);
	}

	// second case: first node is removed
	if (position === 1) {
		this.head = currentNode.next; // head reassigned
		deletedNode = currentNode;
		currentNode = null;
		this._length--;

		return deletedNode;
	}

	// third case: any other node is removed
	while (count < position) { // loop until we reach node at position we want to remove
		beforeNodeToDelete = currentNode;
		nodeToDelete = currentNode.next;
		count++;
	}

	beforeNodeToDelete.next = nodeToDelete.next;
	deletedNode = nodeToDelete;
	nodeToDelete = null;
	this._length--;

	return deletedNode;
}

function peekStack(stack) {
	return stack[stack.length - 1];
}

function findIntersection(linkedlistOne, linkedlistTwo) {
	var headOne = linkedlistOne.head,
		headTwo = linkedlistTwo.head,
		stackOne = [],
		stackTwo = [],
		intersect;

	while (headOne !== null) {
		stackOne.push(headOne);
		headOne = headOne.next;
	}

	while (headTwo !== null) {
		stackTwo.push(headTwo);
		headTwo = headTwo.next;
	}

	console.log(stackOne);
	console.log(stackTwo);

	if (stackOne.length === 0 || stackTwo.length ===0) { // if either stack/linked list is empty
		intersect = undefined;
	} else if (peekStack(stackOne) !== peekStack(stackTwo)) { // if no intersection
		intersect = undefined;
		console.log("ELSE IF STATEMENT");
	} else { // if there is an intersection
		while (peekStack(stackOne) === peekStack(stackTwo)) {
			intersect = peekStack(stackOne);
			stackOne.pop();
			stackTwo.pop();
		}
	}
	return intersect; // return intersecting node
}

// test case
var testOne = new LinkedList(),
	testTwo = new LinkedList();
	testNode = new Node(4);

testOne.add(1);
testOne.add(2);
testOne.add(3);
testOne.add(testNode);

testTwo.add(2);
testTwo.add(3);
testTwo.add(testNode);
testTwo.add(5);
testTwo.add(6);

findIntersection(testOne, testTwo); // expect to return testNode
<|endoftext|>