<commit_before>/* jshint globalstrict: true */
/*
 *    (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
 *
 * 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.
 */
(function() {
  'use strict';

  describe('Launch Instance Post-Creation Step', function() {
  });

})();

<commit_msg>[Launch Instance Fix] Add Configuration Step Unit Tests<commit_after>/*
 *    (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
 *
 * 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.
 */
(function() {
  'use strict';

  describe('Launch Instance Configuration Step', function() {

    describe('LaunchInstanceConfigurationCtrl', function() {
      var scope, ctrl;

      beforeEach(module('hz.dashboard.launch-instance'));

      beforeEach(inject(function($controller) {
        scope = {
          model: {
            newInstanceSpec: {},
          }
        };

        ctrl = $controller('LaunchInstanceConfigurationCtrl', {
          $scope: scope
        });
      }));

      it('contains its own labels', function() {
        expect(ctrl.label).toBeDefined();
        expect(Object.keys(ctrl.label).length).toBeGreaterThan(0);
      });

      it('has correct disk configuration options', function() {
        expect(ctrl.diskConfigOptions).toBeDefined();
        expect(ctrl.diskConfigOptions.length).toBe(2);
        var vals = ctrl.diskConfigOptions.map(function(x) { return x.value; });
        expect(vals).toContain('AUTO');
        expect(vals).toContain('MANUAL');
      });

      it('defaults the disk configuration to "AUTO"', function() {
        expect(scope.model.newInstanceSpec.disk_config).toBe('AUTO');
      });

      it('defaults the config_drive configuration to false', function() {
        expect(scope.model.newInstanceSpec.config_drive).toBe(false);
      });

      it('defaults the user_data configuration to ""', function() {
        expect(scope.model.newInstanceSpec.user_data).toBe('');
      });

    });

    describe('LaunchInstanceConfigurationHelpCtrl', function() {
      var ctrl;

      beforeEach(module('hz.dashboard.launch-instance'));

      beforeEach(inject(function($controller) {
        ctrl = $controller('LaunchInstanceConfigurationHelpCtrl');
      }));

      it('defines the title', function() {
        expect(ctrl.title).toBeDefined();
      });

      it('has help paragraphs', function() {
        expect(ctrl.paragraphs).toBeDefined();
        expect(ctrl.paragraphs.length).toBeGreaterThan(0);
      });
    });

  });

})();

<|endoftext|>


<commit_before>const pmx = require('pmx').init({
  http: true,
  ignore_routes: [/socket\.io/],
  errors: true,
  custom_probes: true,
  network: true,
  ports: true,
});

const logger = require('./logger')();
const Discord = require('discord.js');
const fs = require('fs');
const path = require('path');
const notify = require('./notify');
const now = require('performance-now');
const perms = require('./permissions')();
const subGames = require('./subGames');
require('./memoryUsage');

logger.info('Starting Bot: PID:', process.pid);

// Create the Discord Client
const client = new Discord.Client({
  disableEveryone: true,
  disabledEvents: [
    'TYPING_START',
  ],
});

// Extent the client object
// circular Discord object for RichEmbeds
client.Discord = Discord;
client.config = require('../config');
client.utils = require('./utilities');
client.logger = logger;

client.commands = new Discord.Collection();
client.aliases = new Discord.Collection();
client.guildConfigs = new Discord.Collection();
client.commandPerms = new Discord.Collection();

// Shortcuts to different file paths
client.tempDir = path.join(process.cwd(), 'temp');
client.assetsDir = path.join(process.cwd(), 'assets');
client.commandsDir = path.join(process.cwd(), 'lib/cmds');

// Default Guild Config
client.defaultGuildConfig = {
  prefix: client.config.prefix,
};

// Create property to count items
client.count = {
  messages: 0,
  commands: 0,
  twitch: 0,
};

// Instantiate other modules into the client object
client.mongo = require('./mongo')(client);
client.twitch = require('./twitch')(client);

// A list of twitch channels that are waiting to sync with twitchClient
client.twitch.waiting = [];

client.utils.loadAllCommands(client)
  .then(() => {
    logger.info(`Loaded ${client.commands.size} command${client.commands.size === 1 ? '' : 's'}, ` +
      `with ${client.aliases.size} aliases.`);
  })
  .catch(err => {
    logger.error('Error loading commands', err);
  });

// Create a temp directory if one doesn't not exist on startup
if (!fs.existsSync(client.tempDir)) fs.mkdirSync(client.tempDir);

// Discord has disconnected
client.on('disconnect', () => {
  logger.warn('Discord disconnected');
});

// Discord general warning
client.on('warn', info => {
  logger.warn('Discord warning', info);
});

// Discord is reconnecting
client.on('reconnecting', () => {
  logger.info('Discord reconnecting');
});

// Discord has resumed
client.on('resumed', replayed => {
  logger.info(`Discord has resumed, replayed ${replayed} item(s)`);
});

// Discord has erred
client.on('error', err => {
  logger.error('Discord client error:', err ? err.stack : '');
});

client.on('ready', () => {
  logger.info(`Successful connection to Discord as '${client.user.username}'`);
});

// Run only on once on script launch
client.once('ready', () => {
  // If this bot is not DBKynd's test Bot...
  if (client.user.id !== '120105547633524736') {
    // Send the Owner a message that the Bot has started, indicates a crash or restart
    const owner = client.users.get(client.config.owner_id);
    if (owner) {
      owner.send(`I just started running. Did I crash? :worried:\nPID:\`\`${process.pid}\`\``);
    }
  }
  // Start the Twitch and Twitter Clients
  client.twitch.start();
  // Websocket client to post Twitch bans from AnneMunition's channel
  require('./twitchMessages')(client);
  // Timers
  require('./timers')(client);
});

// The bot has joined a guild, notify the owner
client.on('guildCreate', guild => {
  notify.guildCreate(client, guild);
});

// The client has left a guild, notify the owner
client.on('guildDelete', guild => {
  notify.guildDelete(client, guild);
});

// A new member has joined a connected server
client.on('guildMemberAdd', member => {
  notify.guildMemberAdd(client, member);
});

// A member has left/kick/ban from a connected server
client.on('guildMemberRemove', member => {
  notify.guildMemberRemove(client, member);
});

// A message has been deleted
client.on('messageDelete', message => {
  notify.messageDeleted(client, message);
});

// A message has been updated / edited
client.on('messageUpdate', (oldMessage, newMessage) => {
  notify.messageUpdated(client, oldMessage, newMessage);
});

// Received a message from a discord chat
client.on('message', msg => {
  // Don't listen to other Bots
  if (msg.author.bot) return;
  // Save time of message start process
  msg.startTime = now();
  // Increment the message count
  client.count.messages++;
  // Check for and run any prefixed commands
  prefixedCommands(msg);

  // Do other stuff with messages below...

  // AnneMunition Schedule Auto Pinning

  // If the message occurred in the #tweets channel
  // 140025699867164673 Test
  // 362349719663542272 #tweets
  if (msg.channel.id === '140025699867164673') {
    // If the new message has the correct terms
    if (contentHasScheduleTerms(msg)) {
      // Get all the pinned messages
      msg.channel.fetchPinnedMessages().then(messages => {
        // Sort for messages that has the correct terms
        messages.filter(x => contentHasScheduleTerms(x)).forEach(x => x.unpin());
        // Remove all pinned messages that meet the criteria
        // Pin the current message
        msg.pin().then(() => {
          // Find the Latest message that was a PINS_ADD
          const botMsg = msg.channel.messages.findOne(x => x.type === 'PINS_ADD');
          // And delete it if found
          if (botMsg) botMsg.delete();
        });
      });
    }
  }
});

function contentHasScheduleTerms(msg) {
  const content = msg.content.toLowerCase();
  return content.includes('schedule') && content.includes('mon') && content.includes('fri');
}

client.on('voiceStateUpdate', (oldMember, newMember) => {
  subGames().voiceStateUpdate(client, oldMember, newMember);
});

// Connect to Discord
logger.debug('Connecting to Discord...');
client.login(client.config.bot_token).catch(logger.error);

setInterval(() => {
  // Ensure we are logged every 5 minutes
  // Relog if the client is not in good status
  if (client.status !== 0) relog();
}, 1000 * 60 * 5);

async function relog() {
  logger.info('Re-logging to Discord');
  try {
    await client.destroy();
    client.login(client.config.bot_token);
  } catch (e) {
    logger.error(e);
  }
  return null;
}

function prefixedCommands(msg) {
  // Get the default guild config
  let conf = client.defaultGuildConfig;
  if (msg.channel.type === 'text') {
    // Get stored guild config if exists and not a dm channel
    if (client.guildConfigs.has(msg.guild.id)) {
      conf = client.guildConfigs.get(msg.guild.id);
    }
    // Exit if message doesn't use the guild prefix
    if (!msg.content.startsWith(conf.prefix)) return;
  } else if (msg.channel.type === 'dm') {
    // If a dm channel, ability to use a prefix from any of the setprefixs from all connected guilds
    if (!msg.content.startsWith(conf.prefix)) {
      // See if the prefix matches any stored from any guild ArmoryBot is in
      const anyConfig = client.guildConfigs.find(x => msg.content.startsWith(x.prefix));
      if (!anyConfig) return;
      // Store the prefix the DM user used
      conf.prefix = anyConfig.prefix;
    }
  }
  // Save the prefix used on this message so we can use it to build guild specific help commands
  msg.prefix = conf.prefix;
  // Split message into an array on any number of spaces
  const params = msg.content.split(/ +/g);
  // Pull first index and remove prefix
  const cmd = params.shift().slice(conf.prefix.length).toLowerCase();
  // Exit if no command was given (prefix only)
  if (!cmd) return;
  logger.debug('Command:', cmd, JSON.stringify(params));
  // Attempt to get command from Collection
  let command;
  if (client.commands.has(cmd)) {
    command = client.commands.get(cmd);
  } else if (client.aliases.has(cmd)) {
    command = client.aliases.get(cmd);
  }
  if (!command || !command.run || typeof command.run !== 'function') {
    logger.debug(`The cmd '${cmd}' does not exist, is not loaded, or is missing the 'run' function`);
    return;
  }
  // Store command name to the message object
  msg.cmd = cmd;
  // Log the command usage
  if (msg.channel.type === 'dm') {
    logger.info(`[DM] <${msg.author.tag}>: ${msg.content}`);
  } else {
    logger.info(`[${msg.guild.name}] (#${msg.channel.name}) ` +
      `<${msg.author.tag}>: ${msg.content}`);
  }
  logger.debug('Command Required Permissions:', command.info.permissions);
  // Ensure the bot client has every permissions required to run this command on Discord's end
  if (msg.channel.type === 'text' && command.info.permissions &&
    !msg.channel.permissionsFor(client.user).has(command.info.permissions)) {
    client.utils.ownerError('Permission Requirement', client, null, msg, cmd);
    logger.error('We do not have the required permissions in this channel to execute the command to completion.');
    return;
  }
  const allowed = perms.check(client, msg, cmd);
  if (allowed) {
    logger.info(`ALLOWED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    // Increment the commands counter
    client.count.commands++;
    // Attempt to run the command
    command.run(client, msg, params)
      .then(() => {
        // If the command completes OK run cleanup tasks
        logger.info(`${cmd} finished in: ${(now() - msg.startTime).toFixed(3)}ms`);
      })
      .catch(err => {
        // If the command fails for some reason, message the bot owner about it
        client.utils.ownerError('Command', client, err, msg, cmd);
      });
  } else {
    logger.info(`DENIED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    msg.reply('You do not have permission to run this command here.');
  }
}

// Message the bot owner on any unhandled errors
process.on('unhandledRejection', err => {
  client.utils.ownerError('Unhandled', client, err);
});

// Message the bot owner on any uncaught errors
process.on('uncaughtException', err => {
  client.utils.ownerError('Uncaught', client, err);
});
<commit_msg>delete all PINS_ADD<commit_after>const pmx = require('pmx').init({
  http: true,
  ignore_routes: [/socket\.io/],
  errors: true,
  custom_probes: true,
  network: true,
  ports: true,
});

const logger = require('./logger')();
const Discord = require('discord.js');
const fs = require('fs');
const path = require('path');
const notify = require('./notify');
const now = require('performance-now');
const perms = require('./permissions')();
const subGames = require('./subGames');
require('./memoryUsage');

logger.info('Starting Bot: PID:', process.pid);

// Create the Discord Client
const client = new Discord.Client({
  disableEveryone: true,
  disabledEvents: [
    'TYPING_START',
  ],
});

// Extent the client object
// circular Discord object for RichEmbeds
client.Discord = Discord;
client.config = require('../config');
client.utils = require('./utilities');
client.logger = logger;

client.commands = new Discord.Collection();
client.aliases = new Discord.Collection();
client.guildConfigs = new Discord.Collection();
client.commandPerms = new Discord.Collection();

// Shortcuts to different file paths
client.tempDir = path.join(process.cwd(), 'temp');
client.assetsDir = path.join(process.cwd(), 'assets');
client.commandsDir = path.join(process.cwd(), 'lib/cmds');

// Default Guild Config
client.defaultGuildConfig = {
  prefix: client.config.prefix,
};

// Create property to count items
client.count = {
  messages: 0,
  commands: 0,
  twitch: 0,
};

// Instantiate other modules into the client object
client.mongo = require('./mongo')(client);
client.twitch = require('./twitch')(client);

// A list of twitch channels that are waiting to sync with twitchClient
client.twitch.waiting = [];

client.utils.loadAllCommands(client)
  .then(() => {
    logger.info(`Loaded ${client.commands.size} command${client.commands.size === 1 ? '' : 's'}, ` +
      `with ${client.aliases.size} aliases.`);
  })
  .catch(err => {
    logger.error('Error loading commands', err);
  });

// Create a temp directory if one doesn't not exist on startup
if (!fs.existsSync(client.tempDir)) fs.mkdirSync(client.tempDir);

// Discord has disconnected
client.on('disconnect', () => {
  logger.warn('Discord disconnected');
});

// Discord general warning
client.on('warn', info => {
  logger.warn('Discord warning', info);
});

// Discord is reconnecting
client.on('reconnecting', () => {
  logger.info('Discord reconnecting');
});

// Discord has resumed
client.on('resumed', replayed => {
  logger.info(`Discord has resumed, replayed ${replayed} item(s)`);
});

// Discord has erred
client.on('error', err => {
  logger.error('Discord client error:', err ? err.stack : '');
});

client.on('ready', () => {
  logger.info(`Successful connection to Discord as '${client.user.username}'`);
});

// Run only on once on script launch
client.once('ready', () => {
  // If this bot is not DBKynd's test Bot...
  if (client.user.id !== '120105547633524736') {
    // Send the Owner a message that the Bot has started, indicates a crash or restart
    const owner = client.users.get(client.config.owner_id);
    if (owner) {
      owner.send(`I just started running. Did I crash? :worried:\nPID:\`\`${process.pid}\`\``);
    }
  }
  // Start the Twitch and Twitter Clients
  client.twitch.start();
  // Websocket client to post Twitch bans from AnneMunition's channel
  require('./twitchMessages')(client);
  // Timers
  require('./timers')(client);
});

// The bot has joined a guild, notify the owner
client.on('guildCreate', guild => {
  notify.guildCreate(client, guild);
});

// The client has left a guild, notify the owner
client.on('guildDelete', guild => {
  notify.guildDelete(client, guild);
});

// A new member has joined a connected server
client.on('guildMemberAdd', member => {
  notify.guildMemberAdd(client, member);
});

// A member has left/kick/ban from a connected server
client.on('guildMemberRemove', member => {
  notify.guildMemberRemove(client, member);
});

// A message has been deleted
client.on('messageDelete', message => {
  notify.messageDeleted(client, message);
});

// A message has been updated / edited
client.on('messageUpdate', (oldMessage, newMessage) => {
  notify.messageUpdated(client, oldMessage, newMessage);
});

// Received a message from a discord chat
client.on('message', msg => {
  // Don't listen to other Bots
  if (msg.author.bot) return;
  // Save time of message start process
  msg.startTime = now();
  // Increment the message count
  client.count.messages++;
  // Check for and run any prefixed commands
  prefixedCommands(msg);

  // Do other stuff with messages below...

  // AnneMunition Schedule Auto Pinning

  // If the message occurred in the #tweets channel
  // 140025699867164673 Test
  // 362349719663542272 #tweets
  if (msg.channel.id === '140025699867164673') {
    // If the new message has the correct terms
    if (contentHasScheduleTerms(msg)) {
      // Get all the pinned messages
      msg.channel.fetchPinnedMessages().then(messages => {
        // Sort for messages that has the correct terms
        messages.filter(x => contentHasScheduleTerms(x)).forEach(x => x.unpin());
        // Remove all pinned messages that meet the criteria
        // Pin the current message
        msg.pin().then(() => {
          // Find the Latest message that was a PINS_ADD
          msg.channel.messages.find(x => x.type === 'PINS_ADD').forEach(x => x.delete());
        });
      });
    }
  }
});

function contentHasScheduleTerms(msg) {
  const content = msg.content.toLowerCase();
  return content.includes('schedule') && content.includes('mon') && content.includes('fri');
}

client.on('voiceStateUpdate', (oldMember, newMember) => {
  subGames().voiceStateUpdate(client, oldMember, newMember);
});

// Connect to Discord
logger.debug('Connecting to Discord...');
client.login(client.config.bot_token).catch(logger.error);

setInterval(() => {
  // Ensure we are logged every 5 minutes
  // Relog if the client is not in good status
  if (client.status !== 0) relog();
}, 1000 * 60 * 5);

async function relog() {
  logger.info('Re-logging to Discord');
  try {
    await client.destroy();
    client.login(client.config.bot_token);
  } catch (e) {
    logger.error(e);
  }
  return null;
}

function prefixedCommands(msg) {
  // Get the default guild config
  let conf = client.defaultGuildConfig;
  if (msg.channel.type === 'text') {
    // Get stored guild config if exists and not a dm channel
    if (client.guildConfigs.has(msg.guild.id)) {
      conf = client.guildConfigs.get(msg.guild.id);
    }
    // Exit if message doesn't use the guild prefix
    if (!msg.content.startsWith(conf.prefix)) return;
  } else if (msg.channel.type === 'dm') {
    // If a dm channel, ability to use a prefix from any of the setprefixs from all connected guilds
    if (!msg.content.startsWith(conf.prefix)) {
      // See if the prefix matches any stored from any guild ArmoryBot is in
      const anyConfig = client.guildConfigs.find(x => msg.content.startsWith(x.prefix));
      if (!anyConfig) return;
      // Store the prefix the DM user used
      conf.prefix = anyConfig.prefix;
    }
  }
  // Save the prefix used on this message so we can use it to build guild specific help commands
  msg.prefix = conf.prefix;
  // Split message into an array on any number of spaces
  const params = msg.content.split(/ +/g);
  // Pull first index and remove prefix
  const cmd = params.shift().slice(conf.prefix.length).toLowerCase();
  // Exit if no command was given (prefix only)
  if (!cmd) return;
  logger.debug('Command:', cmd, JSON.stringify(params));
  // Attempt to get command from Collection
  let command;
  if (client.commands.has(cmd)) {
    command = client.commands.get(cmd);
  } else if (client.aliases.has(cmd)) {
    command = client.aliases.get(cmd);
  }
  if (!command || !command.run || typeof command.run !== 'function') {
    logger.debug(`The cmd '${cmd}' does not exist, is not loaded, or is missing the 'run' function`);
    return;
  }
  // Store command name to the message object
  msg.cmd = cmd;
  // Log the command usage
  if (msg.channel.type === 'dm') {
    logger.info(`[DM] <${msg.author.tag}>: ${msg.content}`);
  } else {
    logger.info(`[${msg.guild.name}] (#${msg.channel.name}) ` +
      `<${msg.author.tag}>: ${msg.content}`);
  }
  logger.debug('Command Required Permissions:', command.info.permissions);
  // Ensure the bot client has every permissions required to run this command on Discord's end
  if (msg.channel.type === 'text' && command.info.permissions &&
    !msg.channel.permissionsFor(client.user).has(command.info.permissions)) {
    client.utils.ownerError('Permission Requirement', client, null, msg, cmd);
    logger.error('We do not have the required permissions in this channel to execute the command to completion.');
    return;
  }
  const allowed = perms.check(client, msg, cmd);
  if (allowed) {
    logger.info(`ALLOWED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    // Increment the commands counter
    client.count.commands++;
    // Attempt to run the command
    command.run(client, msg, params)
      .then(() => {
        // If the command completes OK run cleanup tasks
        logger.info(`${cmd} finished in: ${(now() - msg.startTime).toFixed(3)}ms`);
      })
      .catch(err => {
        // If the command fails for some reason, message the bot owner about it
        client.utils.ownerError('Command', client, err, msg, cmd);
      });
  } else {
    logger.info(`DENIED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    msg.reply('You do not have permission to run this command here.');
  }
}

// Message the bot owner on any unhandled errors
process.on('unhandledRejection', err => {
  client.utils.ownerError('Unhandled', client, err);
});

// Message the bot owner on any uncaught errors
process.on('uncaughtException', err => {
  client.utils.ownerError('Uncaught', client, err);
});
<|endoftext|>


<commit_before>var Q = require('q');
var bufferUtils = require('./arraybuffer');

var FILETYPE = require('../constants/filetype');
var CHANGE_TYPE = require('../constants/changeType');
var Change = require('../models/change');
var File = require('../models/file');

var error = require('./error');
var ChangeUtils = require('./change');
var WorkingUtils = require('./working');
var BlobUtils = require('./blob');

/**
 * Fetch a file blob. Required for content access with
 * stat/read. No-op if the file is already fetched.
 * @param {RepositoryState} repoState
 * @param {Driver} driver
 * @param {Path} filepath
 * @return {Promise<RepositoryState>}
 */
function fetch(repoState, driver, filepath) {
    if (isFetched(repoState, filepath)) {
        // No op if already fetched
        return Q(repoState);
    }

    var workingState = repoState.getCurrentState();
    var blobSha = WorkingUtils.findSha(workingState, filepath);

    return BlobUtils.fetch(repoState, driver, blobSha);
}

/**
 * @param {RepositoryState} repoState
 * @param {Path} filepath
 * @return {Boolean} True if the file's content is in cache
 */
function isFetched(repoState, filepath) {
    var workingState = repoState.getCurrentState();
    var blobSha = WorkingUtils.findSha(workingState, filepath);
    // If sha is null then there are changes (those which are stored
    // and need not be fetched)
    return (blobSha === null) || BlobUtils.isFetched(repoState, blobSha);
}

/**
 * Stat details about a file.
 * @param {RepositoryState} repoState
 * @param {Path} filepath
 * @return {File}
 */
function stat(repoState, filepath) {
    if (!exists(repoState, filepath)) {
        throw error.fileNotFound(filepath);
    }

    var workingState = repoState.getCurrentState();

    // Lookup potential changes
    var change = workingState.getChanges().get(filepath);
    // Lookup file entry
    var treeEntry = workingState.getTreeEntries().get(filepath);

    // Determine SHA of the blob
    var blobSHA;
    if (change) {
        blobSHA = change.getSha();
    } else {
        blobSHA = treeEntry.getSha();
    }

    // Get the blob from change or cache
    var blob;
    if (blobSHA) {
        // Get content from cache
        blob = repoState.getCache().getBlob(blobSHA);
    } else {
        // No sha, so it must be in changes
        blob = change.getContent();
    }

    var fileSize;
    if (blob) {
        fileSize = blob.getByteLength();
    } else {
        // It might have been moved (but not fetched)
        var originalEntry = workingState.getTreeEntries().find(function (entry) {
            return entry.getSha() === blobSHA;
        });
        fileSize = originalEntry.getBlobSize();
    }

    return new File({
        type: FILETYPE.FILE,
        fileSize: fileSize,
        path: filepath,
        content: blob
    });
}

/**
 * Read content of a file
 * @param {Path} filepath
 * @return {Blob}
 */
function read(repoState, filepath) {
    var file = stat(repoState, filepath);
    return file.getContent();
}

/**
 * Read content of a file, returns a String
 * @return {String}
 */
function readAsString(repoState, filepath, encoding) {
    var blob = read(repoState, filepath);
    return blob.getAsString(encoding);
}

/**
 * Return true if file exists in working tree, false otherwise
 */
function exists(repoState, filepath) {
    var workingState = repoState.getCurrentState();
    var mergedFileSet = WorkingUtils.getMergedFileSet(workingState);

    return mergedFileSet.has(filepath);
}

/**
 * Create a new file (must not exists already)
 * @param {RepositoryState} repoState
 * @param {Path} filepath
 * @param {String} [content='']
 * @return {RepositoryState}
 */
function create(repoState, filepath, content) {
    content = content || '';
    if (exists(repoState, filepath)) {
        throw error.fileAlreadyExist(filepath);
    }
    var change = Change.createCreate(content);
    return ChangeUtils.setChange(repoState, filepath, change);
}

/**
 * Write a file (must exists)
 * @return {RepositoryState}
 */
function write(repoState, filepath, content) {
    if (!exists(repoState, filepath)) {
        throw error.fileNotFound(filepath);
    }

    var change = Change.createUpdate(content);
    return ChangeUtils.setChange(repoState, filepath, change);
}

/**
 * Remove a file
 * @return {RepositoryState}
 */
function remove(repoState, filepath) {
    if (!exists(repoState, filepath)) {
        throw error.fileNotFound(filepath);
    }

    var change = Change.createRemove();
    return ChangeUtils.setChange(repoState, filepath, change);
}

/**
 * Rename a file
 * @return {RepositoryState}
 */
function move(repoState, filepath, newFilepath) {
    if (filepath === newFilepath) {
        return repoState;
    }

    var initialWorkingState = repoState.getCurrentState();

    // Create new file, with Sha if possible
    var sha = WorkingUtils.findSha(initialWorkingState, filepath);
    var changeNewFile;
    if (sha) {
        changeNewFile = Change.createCreateFromSha(sha);
    } else {
        // Content not available as blob
        var contentBuffer = bufferUtils.toBuffer(read(repoState, filepath));
        changeNewFile = Change.createCreate(contentBuffer);
    }

    // Remove old file
    var removedRepoState = remove(repoState, filepath);
    // Add new file
    return ChangeUtils.setChange(removedRepoState, newFilepath, changeNewFile);
}

/**
 * Returns true if the given file has the same content in both
 * RepositoryState's current working state, or is absent from both.
 * @param {RepositoryState} previousState
 * @param {RepositoryState} newState
 * @param {Path} filepath
 * @return {Boolean}
 */
function hasChanged(previousState, newState, filepath) {
    var previouslyExists = exists(previousState, filepath);
    var newExists = exists(newState, filepath);
    if (!previouslyExists && !newExists) {
        // Still non existing
        return false;
    } else if (exists(previousState, filepath) !== exists(newState, filepath)) {
        // The file is absent from one
        return true;
    } else {
        // Both files exist
        var prevWorking = previousState.getCurrentState();
        var newWorking = newState.getCurrentState();

        var prevSha = WorkingUtils.findSha(prevWorking, filepath);
        var newSha = WorkingUtils.findSha(newWorking, filepath);
        if (prevSha === null && newSha === null) {
            // Both have are in pending changes. We can compare their contents
            return read(previousState, filepath).getAsString() !==
                read(newState, filepath).getAsString();
        } else {
            // Content changed if Shas are different, or one of them is null
            return prevSha !== newSha;
        }
    }
}

var FileUtils = {
    stat: stat,
    fetch: fetch,
    isFetched: isFetched,
    exists: exists,
    read: read,
    readAsString: readAsString,
    create: create,
    write: write,
    remove: remove,
    move: move,
    hasChanged: hasChanged
};
module.exports = FileUtils;
<commit_msg>Use mergedTreeEntries in exists for better perf<commit_after>var Q = require('q');
var bufferUtils = require('./arraybuffer');

var FILETYPE = require('../constants/filetype');
var CHANGE_TYPE = require('../constants/changeType');
var Change = require('../models/change');
var File = require('../models/file');

var error = require('./error');
var ChangeUtils = require('./change');
var WorkingUtils = require('./working');
var BlobUtils = require('./blob');

/**
 * Fetch a file blob. Required for content access with
 * stat/read. No-op if the file is already fetched.
 * @param {RepositoryState} repoState
 * @param {Driver} driver
 * @param {Path} filepath
 * @return {Promise<RepositoryState>}
 */
function fetch(repoState, driver, filepath) {
    if (isFetched(repoState, filepath)) {
        // No op if already fetched
        return Q(repoState);
    }

    var workingState = repoState.getCurrentState();
    var blobSha = WorkingUtils.findSha(workingState, filepath);

    return BlobUtils.fetch(repoState, driver, blobSha);
}

/**
 * @param {RepositoryState} repoState
 * @param {Path} filepath
 * @return {Boolean} True if the file's content is in cache
 */
function isFetched(repoState, filepath) {
    var workingState = repoState.getCurrentState();
    var blobSha = WorkingUtils.findSha(workingState, filepath);
    // If sha is null then there are changes (those which are stored
    // and need not be fetched)
    return (blobSha === null) || BlobUtils.isFetched(repoState, blobSha);
}

/**
 * Stat details about a file.
 * @param {RepositoryState} repoState
 * @param {Path} filepath
 * @return {File}
 */
function stat(repoState, filepath) {
    if (!exists(repoState, filepath)) {
        throw error.fileNotFound(filepath);
    }

    var workingState = repoState.getCurrentState();

    // Lookup potential changes
    var change = workingState.getChanges().get(filepath);
    // Lookup file entry
    var treeEntry = workingState.getTreeEntries().get(filepath);

    // Determine SHA of the blob
    var blobSHA;
    if (change) {
        blobSHA = change.getSha();
    } else {
        blobSHA = treeEntry.getSha();
    }

    // Get the blob from change or cache
    var blob;
    if (blobSHA) {
        // Get content from cache
        blob = repoState.getCache().getBlob(blobSHA);
    } else {
        // No sha, so it must be in changes
        blob = change.getContent();
    }

    var fileSize;
    if (blob) {
        fileSize = blob.getByteLength();
    } else {
        // It might have been moved (but not fetched)
        var originalEntry = workingState.getTreeEntries().find(function (entry) {
            return entry.getSha() === blobSHA;
        });
        fileSize = originalEntry.getBlobSize();
    }

    return new File({
        type: FILETYPE.FILE,
        fileSize: fileSize,
        path: filepath,
        content: blob
    });
}

/**
 * Read content of a file
 * @param {Path} filepath
 * @return {Blob}
 */
function read(repoState, filepath) {
    var file = stat(repoState, filepath);
    return file.getContent();
}

/**
 * Read content of a file, returns a String
 * @return {String}
 */
function readAsString(repoState, filepath, encoding) {
    var blob = read(repoState, filepath);
    return blob.getAsString(encoding);
}

/**
 * Return true if file exists in working tree, false otherwise
 */
function exists(repoState, filepath) {
    var workingState = repoState.getCurrentState();
    var mergedFileSet = WorkingUtils.getMergedTreeEntries(workingState);

    return mergedFileSet.has(filepath);
}

/**
 * Create a new file (must not exists already)
 * @param {RepositoryState} repoState
 * @param {Path} filepath
 * @param {String} [content='']
 * @return {RepositoryState}
 */
function create(repoState, filepath, content) {
    content = content || '';
    if (exists(repoState, filepath)) {
        throw error.fileAlreadyExist(filepath);
    }
    var change = Change.createCreate(content);
    return ChangeUtils.setChange(repoState, filepath, change);
}

/**
 * Write a file (must exists)
 * @return {RepositoryState}
 */
function write(repoState, filepath, content) {
    if (!exists(repoState, filepath)) {
        throw error.fileNotFound(filepath);
    }

    var change = Change.createUpdate(content);
    return ChangeUtils.setChange(repoState, filepath, change);
}

/**
 * Remove a file
 * @return {RepositoryState}
 */
function remove(repoState, filepath) {
    if (!exists(repoState, filepath)) {
        throw error.fileNotFound(filepath);
    }

    var change = Change.createRemove();
    return ChangeUtils.setChange(repoState, filepath, change);
}

/**
 * Rename a file
 * @return {RepositoryState}
 */
function move(repoState, filepath, newFilepath) {
    if (filepath === newFilepath) {
        return repoState;
    }

    var initialWorkingState = repoState.getCurrentState();

    // Create new file, with Sha if possible
    var sha = WorkingUtils.findSha(initialWorkingState, filepath);
    var changeNewFile;
    if (sha) {
        changeNewFile = Change.createCreateFromSha(sha);
    } else {
        // Content not available as blob
        var contentBuffer = bufferUtils.toBuffer(read(repoState, filepath));
        changeNewFile = Change.createCreate(contentBuffer);
    }

    // Remove old file
    var removedRepoState = remove(repoState, filepath);
    // Add new file
    return ChangeUtils.setChange(removedRepoState, newFilepath, changeNewFile);
}

/**
 * Returns true if the given file has the same content in both
 * RepositoryState's current working state, or is absent from both.
 * @param {RepositoryState} previousState
 * @param {RepositoryState} newState
 * @param {Path} filepath
 * @return {Boolean}
 */
function hasChanged(previousState, newState, filepath) {
    var previouslyExists = exists(previousState, filepath);
    var newExists = exists(newState, filepath);
    if (!previouslyExists && !newExists) {
        // Still non existing
        return false;
    } else if (exists(previousState, filepath) !== exists(newState, filepath)) {
        // The file is absent from one
        return true;
    } else {
        // Both files exist
        var prevWorking = previousState.getCurrentState();
        var newWorking = newState.getCurrentState();

        var prevSha = WorkingUtils.findSha(prevWorking, filepath);
        var newSha = WorkingUtils.findSha(newWorking, filepath);
        if (prevSha === null && newSha === null) {
            // Both have are in pending changes. We can compare their contents
            return read(previousState, filepath).getAsString() !==
                read(newState, filepath).getAsString();
        } else {
            // Content changed if Shas are different, or one of them is null
            return prevSha !== newSha;
        }
    }
}

var FileUtils = {
    stat: stat,
    fetch: fetch,
    isFetched: isFetched,
    exists: exists,
    read: read,
    readAsString: readAsString,
    create: create,
    write: write,
    remove: remove,
    move: move,
    hasChanged: hasChanged
};
module.exports = FileUtils;
<|endoftext|>


<commit_before>$(document).ready(function() {
  app.hookupMap();
  app.hookupSteps();
});

var app = app || {};
app.state = {
  channel: 'sms',
  geom: undefined,
  publisher_id: undefined,
};

app.eventMarkers = new L.FeatureGroup();

app.hookupMap = function() {
  var options = {
    zoom: 13,
    center: [47.604432, -122.336014],
    tileLayer: { detectRetina: true },
    scrollWheelZoom: false,
  };
  var mapId = $('meta[name=mapId]').attr('content');
  var map = app.map = L.mapbox.map('map', mapId, options);
};

app.hookupSteps = function() {
  $('.startButton').on('click', function() {
    app.scrollToElement($('#step1'));
  });

  $('.publisher:not(.soon)').on('click', function(event) {
    $('.publisher').removeClass('selected');

    var $publisher = $(event.currentTarget);
    app.state.publisher_id = $publisher.data('publisher-id');
    $publisher.addClass('selected');

    // Update the confirmation section with the name
    $('.confirmationType').html($publisher.data('publisher-title'));

    app.scrollToElement($('#step2'));

    // update events for the new publisher
    app.updateEvents(app.map.getBounds());
  });

  $('.mapButton').on('click', function() {
    app.scrollToElement($('#step3'));
  });

  $('.smsButton').on('click', function(event) {
    $(event.target).addClass('selected');
    $('.extraInfo').slideDown();
  });

  var finishSubscribe = function(e) {
    // TODO: animate the done checkmark at the same time
    e.preventDefault();

    // TODO: handle email and webhooks also
    app.state.phone_number = $('.phoneNumber').val();

    app.submitSubscription(function() {
      $('#confirmation').slideDown();
      app.scrollToElement($('#confirmation'));
    });
  };
  $('.subscribeButton').on('click', finishSubscribe);
  $('#subscribeForm').on('submit', finishSubscribe);

  $('.resetButton').on('click', function(event) {
    app.resetState();
    app.scrollToElement($('#step1'));

    // Hide after we've scrolled up.
    setTimeout(function() {
      $('#confirmation').hide();
    }, 800);
  });

  var prevMarker, prevCircle;
  var geolocate = function(e) {
    e && e.preventDefault();
    var city = $('.publisher.selected').data('publisher-city');
    var address = $('#geolocate').val();
    var radius = $('#user-selected-radius').val();
    var radiusMeters = parseFloat(radius) * 1609.344;
    var oneWeekAgo = new Date();
    oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);

    app.geocode(address+' '+city, function(latlng) {
      // Set the new app state
      var center = new LatLon(latlng[0], latlng[1]);
      var bboxWidth = 2 * parseFloat($('#user-selected-radius').val())
      var bbox = center.boundingBox(bboxWidth);
      app.state.geom = JSON.stringify({
        type: 'Polygon',
        coordinates: [bbox],
      });

      // Remove old layers
      if (prevMarker) app.map.removeLayer(prevMarker);
      if (prevCircle) app.map.removeLayer(prevCircle);

      // Preserve references to new layers
      prevMarker = L.marker(latlng).addTo(app.map);
      prevCircle = L.circle(latlng, radiusMeters).addTo(app.map);

      // fit bounds
      app.map.fitBounds(prevCircle.getBounds());

      // Frequency estimate
      app.getEventsCount(app.state.publisher_id, app.state.geom, oneWeekAgo, function(response) {
        $('#freqRadius').html(radius + ' mi');
        $('#freqAddress').html(address);
        $('#freqNum').html(response.events_count + ' citygrams');
      });

    });
  };

  app.map.on('zoomend', function() {
    app.updateEvents(app.map.getBounds());
  });
  $('.publisher:not(.soon)').on('click', function(e) {
    if ($('#geolocate').val().trim() !== '') geolocate();
  });
  $('#user-selected-radius').on('change', geolocate);
  $('#geolocateForm').on('submit', geolocate);
  $('.geolocateButton').on('click', geolocate);
  $('#geolocate').on('change', geolocate);
};

// Populate events
app.updateEvents = function(bounds) {
  var mapGeometry = {
    type: 'Polygon',
    coordinates: [[
      [bounds._southWest.lng, bounds._northEast.lat],
      [bounds._northEast.lng, bounds._northEast.lat],
      [bounds._northEast.lng, bounds._southWest.lat],
      [bounds._southWest.lng, bounds._southWest.lat],
      [bounds._southWest.lng, bounds._northEast.lat]
    ]]
  }

  app.getEventsForGeometry(JSON.stringify(mapGeometry), function(events) {
    app.eventMarkers.eachLayer(function(layer) {
      app.map.removeLayer(layer);
    });
    events.forEach(app.displayEventMarker);
  });
};

app.displayEventMarker = function(event) {
  var geometry = JSON.parse(event.geom);
  var html = "
"+event.title+"
"
  var marker = L.circleMarker([geometry.coordinates[1], geometry.coordinates[0]], { radius: 6 })
                 .addTo(app.map)
                 .bindPopup(html);

  app.eventMarkers.addLayer(marker);
  return marker;
}

app.getEventsForGeometry = function(geometry, callback){
  if (!app.state.publisher_id) return;
  $.getJSON('/publishers/'+app.state.publisher_id+'/events', { geometry: geometry }, callback);
};

app.getEventsCount = function(publisherId, geometry, since, callback){
  if (!publisherId) return;
  $.getJSON('/publishers/'+publisherId+'/events_count', { geometry: geometry, since: since }, callback);
};

app.scrollToElement = function(el) {
  $('html,body').animate({
    scrollTop: el.offset().top
  }, 800);
};

app.geocode = function(city, callback, context) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + encodeURIComponent(city);

  $.getJSON(url, function(response) {
    if (response.error || response.results.length === 0) {
      console.log('Unable to geocode city. Womp Womp.', response.error);
    }

    // Get the coordinates for the center of the city
    var location = response.results[0].geometry.location;
    var latlng = [location.lat, location.lng];
    callback.call(context || this, latlng);
  });
};

app.resetState = function() {
  app.state.publisher_id = undefined;
  $('.publisher').removeClass('selected');

  // Let's leave the location and phone number in place, for easy re-subscribe
};

app.submitSubscription = function(callback) {
  $.post('/subscriptions', { subscription: app.state }, callback);
};
<commit_msg>Rename radius variable<commit_after>$(document).ready(function() {
  app.hookupMap();
  app.hookupSteps();
});

var app = app || {};
app.state = {
  channel: 'sms',
  geom: undefined,
  publisher_id: undefined,
};

app.eventMarkers = new L.FeatureGroup();

app.hookupMap = function() {
  var options = {
    zoom: 13,
    center: [47.604432, -122.336014],
    tileLayer: { detectRetina: true },
    scrollWheelZoom: false,
  };
  var mapId = $('meta[name=mapId]').attr('content');
  var map = app.map = L.mapbox.map('map', mapId, options);
};

app.hookupSteps = function() {
  $('.startButton').on('click', function() {
    app.scrollToElement($('#step1'));
  });

  $('.publisher:not(.soon)').on('click', function(event) {
    $('.publisher').removeClass('selected');

    var $publisher = $(event.currentTarget);
    app.state.publisher_id = $publisher.data('publisher-id');
    $publisher.addClass('selected');

    // Update the confirmation section with the name
    $('.confirmationType').html($publisher.data('publisher-title'));

    app.scrollToElement($('#step2'));

    // update events for the new publisher
    app.updateEvents(app.map.getBounds());
  });

  $('.mapButton').on('click', function() {
    app.scrollToElement($('#step3'));
  });

  $('.smsButton').on('click', function(event) {
    $(event.target).addClass('selected');
    $('.extraInfo').slideDown();
  });

  var finishSubscribe = function(e) {
    // TODO: animate the done checkmark at the same time
    e.preventDefault();

    // TODO: handle email and webhooks also
    app.state.phone_number = $('.phoneNumber').val();

    app.submitSubscription(function() {
      $('#confirmation').slideDown();
      app.scrollToElement($('#confirmation'));
    });
  };
  $('.subscribeButton').on('click', finishSubscribe);
  $('#subscribeForm').on('submit', finishSubscribe);

  $('.resetButton').on('click', function(event) {
    app.resetState();
    app.scrollToElement($('#step1'));

    // Hide after we've scrolled up.
    setTimeout(function() {
      $('#confirmation').hide();
    }, 800);
  });

  var prevMarker, prevCircle;
  var geolocate = function(e) {
    e && e.preventDefault();
    var city = $('.publisher.selected').data('publisher-city');
    var address = $('#geolocate').val();
    var radiusMiles = parseFloat($('#user-selected-radius').val());
    var radiusMeters = radiusMiles * 1609.344;
    var oneWeekAgo = new Date();
    oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);

    app.geocode(address+' '+city, function(latlng) {
      // Set the new app state
      var center = new LatLon(latlng[0], latlng[1]);
      var bboxWidth = 2 * radiusMiles;
      var bbox = center.boundingBox(bboxWidth);
      app.state.geom = JSON.stringify({
        type: 'Polygon',
        coordinates: [bbox],
      });

      // Remove old layers
      if (prevMarker) app.map.removeLayer(prevMarker);
      if (prevCircle) app.map.removeLayer(prevCircle);

      // Preserve references to new layers
      prevMarker = L.marker(latlng).addTo(app.map);
      prevCircle = L.circle(latlng, radiusMeters).addTo(app.map);

      // fit bounds
      app.map.fitBounds(prevCircle.getBounds());

      // Frequency estimate
      app.getEventsCount(app.state.publisher_id, app.state.geom, oneWeekAgo, function(response) {
        $('#freqRadius').html(radiusMiles + ' mi');
        $('#freqAddress').html(address);
        $('#freqNum').html(response.events_count + ' citygrams');
      });

    });
  };

  app.map.on('zoomend', function() {
    app.updateEvents(app.map.getBounds());
  });
  $('.publisher:not(.soon)').on('click', function(e) {
    if ($('#geolocate').val().trim() !== '') geolocate();
  });
  $('#user-selected-radius').on('change', geolocate);
  $('#geolocateForm').on('submit', geolocate);
  $('.geolocateButton').on('click', geolocate);
  $('#geolocate').on('change', geolocate);
};

// Populate events
app.updateEvents = function(bounds) {
  var mapGeometry = {
    type: 'Polygon',
    coordinates: [[
      [bounds._southWest.lng, bounds._northEast.lat],
      [bounds._northEast.lng, bounds._northEast.lat],
      [bounds._northEast.lng, bounds._southWest.lat],
      [bounds._southWest.lng, bounds._southWest.lat],
      [bounds._southWest.lng, bounds._northEast.lat]
    ]]
  }

  app.getEventsForGeometry(JSON.stringify(mapGeometry), function(events) {
    app.eventMarkers.eachLayer(function(layer) {
      app.map.removeLayer(layer);
    });
    events.forEach(app.displayEventMarker);
  });
};

app.displayEventMarker = function(event) {
  var geometry = JSON.parse(event.geom);
  var html = "
"+event.title+"
"
  var marker = L.circleMarker([geometry.coordinates[1], geometry.coordinates[0]], { radius: 6 })
                 .addTo(app.map)
                 .bindPopup(html);

  app.eventMarkers.addLayer(marker);
  return marker;
}

app.getEventsForGeometry = function(geometry, callback){
  if (!app.state.publisher_id) return;
  $.getJSON('/publishers/'+app.state.publisher_id+'/events', { geometry: geometry }, callback);
};

app.getEventsCount = function(publisherId, geometry, since, callback){
  if (!publisherId) return;
  $.getJSON('/publishers/'+publisherId+'/events_count', { geometry: geometry, since: since }, callback);
};

app.scrollToElement = function(el) {
  $('html,body').animate({
    scrollTop: el.offset().top
  }, 800);
};

app.geocode = function(city, callback, context) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + encodeURIComponent(city);

  $.getJSON(url, function(response) {
    if (response.error || response.results.length === 0) {
      console.log('Unable to geocode city. Womp Womp.', response.error);
    }

    // Get the coordinates for the center of the city
    var location = response.results[0].geometry.location;
    var latlng = [location.lat, location.lng];
    callback.call(context || this, latlng);
  });
};

app.resetState = function() {
  app.state.publisher_id = undefined;
  $('.publisher').removeClass('selected');

  // Let's leave the location and phone number in place, for easy re-subscribe
};

app.submitSubscription = function(callback) {
  $.post('/subscriptions', { subscription: app.state }, callback);
};
<|endoftext|>


<commit_before>(function(jQuery) {

var $ = jQuery;

$.fn.echoButton = function() {
	var args  = arguments;
	return this.each(function(){
		var data = $(this).data("echoButton");
		if (!data || typeof args[0] === "object") {
			$(this).data("echoButton", new Button(this, args[0]));
		}
		if (typeof args[0] === "string") {
			data[args[0]].apply(data, Array.prototype.slice.call(args, 1));
		}
	});
};

var Button = function(element, params) {
	params = params || {};
	this.element = $(element);
	this.element.addClass("echo-sdk-button");
	$("<div>").appendTo(element).addClass("echo-label").css({
		"float": "left"
	});
	this.update({
		"label": params.label || this.element.text(),
		"icon": params.icon || "",
		"disabled": params.disabled || !!this.element.attr('disabled')
	});
};

Button.prototype.update = function(params) {
	params = params || {};
	this.label = params.label || "";
	this.icon = params.icon || "";
	this.disabled = params.disabled || false;
	this.render();
};

Button.prototype.render = function() {
	$(".echo-label", this.element).text(this.label);
	var iconElement = $(".echo-icon", this.element);
	var setBackground = function(element, icon) {
		element.css({
			"background" : "no-repeat center url(" + icon + ")"
		});
	};
	if (this.icon) {
		if (!iconElement.length) {
			iconElement = $("<div>").addClass("echo-icon").prependTo(this.element).css({
				"height": 16,
				"width": 16,
				"float": "left",
				"margin-right": 2,
				"margin-top": 2
			});
		}
		setBackground(iconElement, this.icon);
	} else {
		iconElement.remove();
	}
	this.element.attr("disabled", this.disabled);
};

})(Echo.jQuery);
<commit_msg>Minor fixes in $.echoButton<commit_after>(function(jQuery) {

var $ = jQuery;

$.fn.echoButton = function() {
	var args  = arguments;
	return this.each(function(){
		var data = $(this).data("echoButton");
		if (!data || typeof args[0] === "object") {
			$(this).data("echoButton", (data = new Button(this, args[0])));
		}
		if (typeof args[0] === "string") {
			data[args[0]].apply(data, Array.prototype.slice.call(args, 1));
		}
	});
};

var Button = function(element, params) {
	params = params || {};
	this.element = $(element);
	this.element.addClass("echo-sdk-button");
	$("<div>").appendTo(element).addClass("echo-label").css({
		"float": "left"
	});
	this.update({
		"label": params.label || this.element.text(),
		"icon": params.icon || "",
		"disabled": params.disabled || !!this.element.attr('disabled')
	});
};

Button.prototype.update = function(params) {
	params = params || {};
	this.label = params.label || "";
	this.icon = params.icon || "";
	this.disabled = params.disabled || false;
	this._render();
};

Button.prototype._render = function() {
	$(".echo-label", this.element).text(this.label);
	var iconElement = $(".echo-icon", this.element);
	var setBackground = function(element, icon) {
		element.css({
			"background" : "no-repeat center url(" + icon + ")"
		});
	};
	if (this.icon) {
		if (!iconElement.length) {
			iconElement = $("<div>").addClass("echo-icon").prependTo(this.element).css({
				"height": 16,
				"width": 16,
				"float": "left",
				"margin-right": 2,
				"margin-top": 2
			});
		}
		setBackground(iconElement, this.icon);
	} else {
		iconElement.remove();
	}
	this.element.attr("disabled", this.disabled);
};

})(Echo.jQuery);
<|endoftext|>


<commit_before>/*
 * Copyright (C)2012 D. Plaindoux.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*global define*/

define("Component/Factory",
    [
        "./composition/CompositionComponentFactory", "./core/CoreComponentFactory",
        "./bound/BoundComponentFactory", "./pipeline/PipelineComponentFactory",
        "./transducer/TransducerComponentFactory", "./switch/MultiComponentFactory"
    ],
    function (Composition, Core, Bound, Pipeline, Transducer, Switch) {
        "use strict";

        var Factory = {};

        Factory.core = {};
        Factory.core = Core;
        Factory.core.pipeline = Pipeline.component;

        Factory.compose = Composition.compose;
        Factory.initial = Bound.initial;
        Factory.terminal = Bound.terminal;
        Factory.transducer = Transducer.component;
        Factory.multi = Switch.multi;

        return Factory;

    });<commit_msg>Library problem fixed<commit_after>/*
 * Copyright (C)2012 D. Plaindoux.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*global define*/

define("Component/Factory",
    [
        "./composition/CompositionComponentFactory", "./core/CoreComponentFactory",
        "./bound/BoundComponentFactory", "./pipeline/PipelineComponentFactory",
        "./transducer/TransducerComponentFactory", "./multi/MultiComponentFactory"
    ],
    function (Composition, Core, Bound, Pipeline, Transducer, Switch) {
        "use strict";

        var Factory = {};

        Factory.core = {};
        Factory.core = Core;
        Factory.core.pipeline = Pipeline.component;

        Factory.compose = Composition.compose;
        Factory.initial = Bound.initial;
        Factory.terminal = Bound.terminal;
        Factory.transducer = Transducer.component;
        Factory.multi = Switch.multi;

        return Factory;

    });<|endoftext|>


<commit_before>/* PlayerManager.js
KC3改 Player Manager

Manages info about the player and all its holdings
Includes HQ, Fleet, Docks
Does not include Ships and Gears which are managed by other Managers
*/
(function(){
	"use strict";

	window.PlayerManager = {
		hq: {},
		consumables: {},
		fleets: [],
		fleetCount: 1,
		repairSlots: 2,
		repairShips: [-1,-1,-1,-1,-1],
		buildSlots: 2,
		combinedFleet: 0,
		statistics: {},
		lastRefresh: null,

		init :function(){
			this.hq = new KC3Player();
			this.consumables = {
				fcoin: 0,
				buckets : 0,
				devmats : 0,
				screws: 0,
				torch: 0,
				blueprints: 0
			};
			this.fleets = [
				new KC3Fleet(),
				new KC3Fleet(),
				new KC3Fleet(),
				new KC3Fleet()
			];
		},

		setHQ :function( data ){
			// Check if player suddenly changed
			if(this.hq.id !== 0 && this.hq.id != data.mid){
				this.hq.logout();
				this.hq = new KC3Player();
			}
			// Update player with new data
			this.hq.update( data );
			this.hq.save();
		},

		setFleets :function( data ){
			this.fleets[0].update( data[0] || {} );
			this.fleets[1].update( data[1] || {} );
			this.fleets[2].update( data[2] || {} );
			this.fleets[3].update( data[3] || {} );
			localStorage.fleets = JSON.stringify(this.fleets);
		},

		setRepairDocks :function( data ){
			var lastRepair = this.repairShips.map(function(x){return x;}); // clone
			this.repairShips = [];
			var dockingShips = [];
			var self = this;
			$.each(data, function(ctr, ndock){
				if(lastRepair[ndock.api_id] != ndock.api_ship_id) { // check if not in the list (repaired)
					KC3ShipManager.get(lastRepair[ndock.api_id]).applyRepair();
				}

				if(ndock.api_state > 0){
					self.repairShips[ ndock.api_id ] = ndock.api_ship_id;
					var repairInfo =
						{ id: ndock.api_ship_id,
						  completeTime: ndock.api_complete_time
						};
					dockingShips.push( repairInfo );
					KC3TimerManager.repair( ndock.api_id ).activate(
						ndock.api_complete_time,
						KC3ShipManager.get( ndock.api_ship_id ).masterId
					);
				}else{
					KC3TimerManager.repair( ndock.api_id ).deactivate();
				}
			});
			// "localStorage.dockingShips" is not supposed
			// to be modified,
			// it record the most recent docking ships
			// whenever a docking event comes
			localStorage.dockingShips = JSON.stringify(dockingShips);
		},

		setBuildDocks :function( data ){
			$.each(data, function(ctr, kdock){
				if(kdock.api_state > 0){
					KC3TimerManager.build( kdock.api_id ).activate(
						kdock.api_complete_time,
						kdock.api_created_ship_id
					);
					if(kdock.api_item1 > 999){
						KC3TimerManager.build( kdock.api_id ).lsc = true;
					}else{
						KC3TimerManager.build( kdock.api_id ).lsc = false;
					}
				}else{
					KC3TimerManager.build( kdock.api_id ).deactivate();
				}
			});
		},

		setResources :function( data, stime ){
			if(typeof localStorage.lastResource == "undefined"){ localStorage.lastResource = 0; }
			var ResourceHour = Math.floor(stime/3600);
			if(ResourceHour == localStorage.lastResource){ return false; }
			localStorage.lastResource = ResourceHour;
			KC3Database.Resource({
				rsc1 : data[0],
				rsc2 : data[1],
				rsc3 : data[2],
				rsc4 : data[3],
				hour : ResourceHour
			});
		},

		setConsumables :function( data, stime ){
			$.extend(this.consumables, data);

			if(typeof localStorage.lastUseitem == "undefined"){ localStorage.lastUseitem = 0; }
			var ResourceHour = Math.floor(stime/3600);
			if(ResourceHour == localStorage.lastUseitem){ return false; }
			localStorage.lastUseitem = ResourceHour;
			KC3Database.Useitem({
				torch : data.torch,
				screw : data.screws,
				bucket : data.buckets,
				devmat : data.devmats,
				hour : ResourceHour
			});
		},

		setStatistics :function( data ){
			var oldStatistics = JSON.parse(localStorage.statistics || "{\"exped\":{},\"pvp\":{},\"sortie\":{}}");
			var newStatistics = {
				exped: {
					rate: data.exped.rate || oldStatistics.exped.rate || 0,
					total: data.exped.total || oldStatistics.exped.total,
					success: data.exped.success || oldStatistics.exped.success
				},
				pvp: {
					rate: data.pvp.rate || oldStatistics.pvp.rate || 0,
					win: data.pvp.win || oldStatistics.pvp.win,
					lose: data.pvp.lose || oldStatistics.pvp.lose,
					attacked: data.pvp.attacked || oldStatistics.pvp.attacked,
					attacked_win: data.pvp.attacked_win || oldStatistics.pvp.attacked_win
				},
				sortie: {
					rate: data.sortie.rate || oldStatistics.sortie.rate || 0,
					win: data.sortie.win || oldStatistics.sortie.win,
					lose: data.sortie.lose || oldStatistics.sortie.lose
				}
			};
			if(newStatistics.sortie.rate===0){
				newStatistics.sortie.rate = Math.round(newStatistics.sortie.win / (newStatistics.sortie.win + newStatistics.sortie.lose) * 10000)/100;
			}
			if(newStatistics.pvp.rate===0){
				newStatistics.pvp.rate = Math.round(newStatistics.pvp.win / (newStatistics.pvp.win + newStatistics.pvp.lose) *10000)/100;
			}
			if(newStatistics.exped.rate===0){
				newStatistics.exped.rate =  Math.round(newStatistics.exped.success / newStatistics.exped.total * 10000)/100;
			}
			// console.log("rates", newStatistics.sortie.rate, newStatistics.pvp.rate, newStatistics.exped.rate);
			localStorage.statistics = JSON.stringify(newStatistics);
		},

		setNewsfeed :function( data, stime ){
			//console.log("newsfeed", data);
			$.each(data, function( index, element){
				if(parseInt(element.api_state, 10) !== 0){
					console.log("saved news", element);
					KC3Database.Newsfeed({
						type: element.api_type,
						message: element.api_message,
						time: stime
					});
				}
			});
		},

		portRefresh :function( data ){
		},

		loadFleets :function(){
			if(typeof localStorage.fleets != "undefined"){
				var oldFleets =JSON.parse( localStorage.fleets );
				this.fleets = [
					(new KC3Fleet()).defineFormatted(oldFleets[0]),
					(new KC3Fleet()).defineFormatted(oldFleets[1]),
					(new KC3Fleet()).defineFormatted(oldFleets[2]),
					(new KC3Fleet()).defineFormatted(oldFleets[3])
				];
			}
		}

	};

})();
<commit_msg>PlayerManager: load cached docking ships<commit_after>/* PlayerManager.js
KC3改 Player Manager

Manages info about the player and all its holdings
Includes HQ, Fleet, Docks
Does not include Ships and Gears which are managed by other Managers
*/
(function(){
	"use strict";

	window.PlayerManager = {
		hq: {},
		consumables: {},
		fleets: [],
		fleetCount: 1,
		repairSlots: 2,
		repairShips: [-1,-1,-1,-1,-1],
		buildSlots: 2,
		combinedFleet: 0,
		statistics: {},
		lastRefresh: null,

		init :function(){
			this.hq = new KC3Player();
			this.consumables = {
				fcoin: 0,
				buckets : 0,
				devmats : 0,
				screws: 0,
				torch: 0,
				blueprints: 0
			};
			this.fleets = [
				new KC3Fleet(),
				new KC3Fleet(),
				new KC3Fleet(),
				new KC3Fleet()
			];
		},

		setHQ :function( data ){
			// Check if player suddenly changed
			if(this.hq.id !== 0 && this.hq.id != data.mid){
				this.hq.logout();
				this.hq = new KC3Player();
			}
			// Update player with new data
			this.hq.update( data );
			this.hq.save();
		},

		setFleets :function( data ){
			this.fleets[0].update( data[0] || {} );
			this.fleets[1].update( data[1] || {} );
			this.fleets[2].update( data[2] || {} );
			this.fleets[3].update( data[3] || {} );
			localStorage.fleets = JSON.stringify(this.fleets);
		},

		setRepairDocks :function( data ){
			var lastRepair = this.repairShips.map(function(x){return x;}); // clone
			this.repairShips = [];
			var dockingShips = [];
			var self = this;
			$.each(data, function(ctr, ndock){
				if(lastRepair[ndock.api_id] != ndock.api_ship_id) { // check if not in the list (repaired)
					KC3ShipManager.get(lastRepair[ndock.api_id]).applyRepair();
				}

				if(ndock.api_state > 0){
					self.repairShips[ ndock.api_id ] = ndock.api_ship_id;
					var repairInfo =
						{ id: ndock.api_ship_id,
						  completeTime: ndock.api_complete_time
						};
					dockingShips.push( repairInfo );
					KC3TimerManager.repair( ndock.api_id ).activate(
						ndock.api_complete_time,
						KC3ShipManager.get( ndock.api_ship_id ).masterId
					);
				}else{
					KC3TimerManager.repair( ndock.api_id ).deactivate();
				}
			});
			// "localStorage.dockingShips" is not supposed
			// to be modified,
			// it record the most recent docking ships
			// whenever a docking event comes
			localStorage.dockingShips = JSON.stringify(dockingShips);
		},

		// cached docking ships' status
		// the return value is an object whose properties are "x{ship_id}"
		// with value set to the completeTime
		getCachedDockingShips: function() {
			var dockingShips = {};
			if (typeof localStorage.dockingShips !== "undefined") {
				try {
					var ndockData = JSON.parse( localStorage.dockingShips );
					$.each(ndockData, function (i, v) {
						var key = "x" + v.id.toString();
						dockingShips[key] = v.completeTime;
					});
				} catch (err) {
					console.log( "Error while processing cached docking ship" );
					console.log(err);
				}
			}
			return dockingShips;
		},

		setBuildDocks :function( data ){
			$.each(data, function(ctr, kdock){
				if(kdock.api_state > 0){
					KC3TimerManager.build( kdock.api_id ).activate(
						kdock.api_complete_time,
						kdock.api_created_ship_id
					);
					if(kdock.api_item1 > 999){
						KC3TimerManager.build( kdock.api_id ).lsc = true;
					}else{
						KC3TimerManager.build( kdock.api_id ).lsc = false;
					}
				}else{
					KC3TimerManager.build( kdock.api_id ).deactivate();
				}
			});
		},

		setResources :function( data, stime ){
			if(typeof localStorage.lastResource == "undefined"){ localStorage.lastResource = 0; }
			var ResourceHour = Math.floor(stime/3600);
			if(ResourceHour == localStorage.lastResource){ return false; }
			localStorage.lastResource = ResourceHour;
			KC3Database.Resource({
				rsc1 : data[0],
				rsc2 : data[1],
				rsc3 : data[2],
				rsc4 : data[3],
				hour : ResourceHour
			});
		},

		setConsumables :function( data, stime ){
			$.extend(this.consumables, data);

			if(typeof localStorage.lastUseitem == "undefined"){ localStorage.lastUseitem = 0; }
			var ResourceHour = Math.floor(stime/3600);
			if(ResourceHour == localStorage.lastUseitem){ return false; }
			localStorage.lastUseitem = ResourceHour;
			KC3Database.Useitem({
				torch : data.torch,
				screw : data.screws,
				bucket : data.buckets,
				devmat : data.devmats,
				hour : ResourceHour
			});
		},

		setStatistics :function( data ){
			var oldStatistics = JSON.parse(localStorage.statistics || "{\"exped\":{},\"pvp\":{},\"sortie\":{}}");
			var newStatistics = {
				exped: {
					rate: data.exped.rate || oldStatistics.exped.rate || 0,
					total: data.exped.total || oldStatistics.exped.total,
					success: data.exped.success || oldStatistics.exped.success
				},
				pvp: {
					rate: data.pvp.rate || oldStatistics.pvp.rate || 0,
					win: data.pvp.win || oldStatistics.pvp.win,
					lose: data.pvp.lose || oldStatistics.pvp.lose,
					attacked: data.pvp.attacked || oldStatistics.pvp.attacked,
					attacked_win: data.pvp.attacked_win || oldStatistics.pvp.attacked_win
				},
				sortie: {
					rate: data.sortie.rate || oldStatistics.sortie.rate || 0,
					win: data.sortie.win || oldStatistics.sortie.win,
					lose: data.sortie.lose || oldStatistics.sortie.lose
				}
			};
			if(newStatistics.sortie.rate===0){
				newStatistics.sortie.rate = Math.round(newStatistics.sortie.win / (newStatistics.sortie.win + newStatistics.sortie.lose) * 10000)/100;
			}
			if(newStatistics.pvp.rate===0){
				newStatistics.pvp.rate = Math.round(newStatistics.pvp.win / (newStatistics.pvp.win + newStatistics.pvp.lose) *10000)/100;
			}
			if(newStatistics.exped.rate===0){
				newStatistics.exped.rate =  Math.round(newStatistics.exped.success / newStatistics.exped.total * 10000)/100;
			}
			// console.log("rates", newStatistics.sortie.rate, newStatistics.pvp.rate, newStatistics.exped.rate);
			localStorage.statistics = JSON.stringify(newStatistics);
		},

		setNewsfeed :function( data, stime ){
			//console.log("newsfeed", data);
			$.each(data, function( index, element){
				if(parseInt(element.api_state, 10) !== 0){
					console.log("saved news", element);
					KC3Database.Newsfeed({
						type: element.api_type,
						message: element.api_message,
						time: stime
					});
				}
			});
		},

		portRefresh :function( data ){
		},

		loadFleets :function(){
			if(typeof localStorage.fleets != "undefined"){
				var oldFleets =JSON.parse( localStorage.fleets );
				this.fleets = [
					(new KC3Fleet()).defineFormatted(oldFleets[0]),
					(new KC3Fleet()).defineFormatted(oldFleets[1]),
					(new KC3Fleet()).defineFormatted(oldFleets[2]),
					(new KC3Fleet()).defineFormatted(oldFleets[3])
				];
			}
		}

	};

})();
<|endoftext|>


<commit_before>"use strict";

/*
  Block Quote
*/

var _ = require('../lodash');

var Block = require('../block');
var stToHTML = require('../to-html');

var template = _.template([
  '

',
  ' <%= i18n.t("blocks:quote:credit_field") %>',
  '<input maxlength="140" name="cite" placeholder="<%= i18n.t("blocks:quote:credit_field") %>"',
  ' class="st-input-string st-required js-cite-input" type="text" />'
].join("\n"));

module.exports = Block.extend({

  type: "quote",

  title: function() { return i18n.t('blocks:quote:title'); },

  icon_name: 'quote',

  editorHTML: function() {
    return template(this);
  },

  loadData: function(data){
    if (this.options.convertFromMarkdown && !data.isHtml) {
      this.setTextBlockHTML(stToHTML(data.text, this.type));
    } else {
      this.setTextBlockHTML(data.text);
    }

    this.$('.js-cite-input').val(data.cite);
  }
});
<commit_msg>Removed cite requirement<commit_after>"use strict";

/*
  Block Quote
*/

var _ = require('../lodash');

var Block = require('../block');
var stToHTML = require('../to-html');

var template = _.template([
  '

',
  ' <%= i18n.t("blocks:quote:credit_field") %>',
  '<input maxlength="140" name="cite" placeholder="<%= i18n.t("blocks:quote:credit_field") %>"',
  ' class="st-input-string js-cite-input" type="text" />'
].join("\n"));

module.exports = Block.extend({

  type: "quote",

  title: function() { return i18n.t('blocks:quote:title'); },

  icon_name: 'quote',

  editorHTML: function() {
    return template(this);
  },

  loadData: function(data){
    if (this.options.convertFromMarkdown && !data.isHtml) {
      this.setTextBlockHTML(stToHTML(data.text, this.type));
    } else {
      this.setTextBlockHTML(data.text);
    }
    if (this.options.citeRequired){
      this.$('.js-cite-input').addClass('st-required');
    }
    this.$('.js-cite-input').val(data.cite);
  }
});
<|endoftext|>


<commit_before>const version = require('../package.json').version;
const auth = require('../lib/auth');
var sites = require('../lib/sites');
var siteScripts = require('../lib/site-scripts');
var siteActions = require('../lib/site-actions');
var campaigns = require('../lib/campaigns');
var elements = require('../lib/elements');
var variants = require('../lib/variants');
var campaignScripts = require('../lib/campaign-scripts');
var campaignActions = require('../lib/campaign-actions');
var publish = require('../lib/publish');

function MMRestApi(config = {}) {
  const authHost = config.authHost || 'https://api-auth-eu.maxymiser.com/oauth2';
  const host = config.host || 'https://api-eu.maxymiser.com';
  const apiVersion = config.apiVersion || 1;
  const baseAuthPath = `${authHost}/v${apiVersion}`;
  const basePath = `${host}/v${apiVersion}`;
  const credentials = config.credentials;
  var authorize = auth.authorize(token, baseAuthPath, credentials);
  var token;

  // add authorization to the api methods
  sites = sites(basePath, authorize);
  siteScripts = siteScripts(basePath, authorize);
  siteActions = siteActions(basePath, authorize);
  campaigns = campaigns(basePath, authorize);
  elements = elements(basePath, authorize);
  variants = variants(basePath, authorize);
  campaignScripts = campaignScripts(basePath, authorize);
  campaignActions = campaignActions(basePath, authorize);
  publish = publish(basePath, authorize).publish;

  sites.actions = siteActions;
  sites.scripts = siteScripts;
  campaigns.elements = elements;
  campaigns.variants = variants;
  campaigns.scripts = campaignScripts;
  campaigns.actions = campaignActions;

  return {
    host,
    version,
    apiVersion,
    basePath,
    sites,
    campaigns,
    publish
  };
}

module.exports = MMRestApi;
<commit_msg>v1.0.2<commit_after>const version = require('../package.json').version;
const auth = require('../lib/auth');
var sites = require('../lib/sites');
var siteScripts = require('../lib/site-scripts');
var siteActions = require('../lib/site-actions');
var campaigns = require('../lib/campaigns');
var elements = require('../lib/elements');
var variants = require('../lib/variants');
var campaignScripts = require('../lib/campaign-scripts');
var campaignActions = require('../lib/campaign-actions');
var publish = require('../lib/publish');

function MMRestApi(config = {}) {
  const authHost = config.authHost || 'https://api-auth-eu.maxymiser.com/oauth2';
  const host = config.host || 'https://api-eu.maxymiser.com';
  const apiVersion = config.apiVersion || 1;
  const baseAuthPath = `${authHost}/v${apiVersion}`;
  const basePath = `${host}/v${apiVersion}`;
  const credentials = config.credentials;
  var authorize = auth.authorize(token, baseAuthPath, credentials);
  var token;

  // add authorization to the api methods
  var authSites = sites(basePath, authorize);
  var authSiteScripts = siteScripts(basePath, authorize);
  var authSiteActions = siteActions(basePath, authorize);
  var authCampaigns = campaigns(basePath, authorize);
  var authElements = elements(basePath, authorize);
  var authVariants = variants(basePath, authorize);
  var authCampaignScripts = campaignScripts(basePath, authorize);
  var authCampaignActions = campaignActions(basePath, authorize);
  var authPublish = publish(basePath, authorize).publish;

  authSites.actions = siteActions;
  authSites.scripts = siteScripts;
  authCampaigns.elements = elements;
  authCampaigns.variants = variants;
  authCampaigns.scripts = campaignScripts;
  authCampaigns.actions = campaignActions;

  return {
    host,
    version,
    apiVersion,
    basePath,
    sites: authSites,
    campaigns: authCampaigns,
    publish: authPublish
  };
}

module.exports = MMRestApi;
<|endoftext|>


<commit_before>Ext4.define('Kwf.Ext4.Controller.Binding.BindableToGrid', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    requires: [ 'Kwf.Ext4.Data.StoreSyncQueue' ],
    gridController: null,
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
        this.init();
    },

    init: function()
    {
        var grid = this.gridController.grid;
        var bindable = this.bindable;
        bindable.disable();

        if (!this.saveButton && bindable.getPanel()) this.saveButton = bindable.getPanel().down('button#save');
        if (!this.addButton) this.addButton = grid.down('button#add');

        if (this.saveButton) this.saveButton.disable();

        grid.on('selectionchange', function(model, rows) {
            if (rows[0]) {
                var row = rows[0];
                if (bindable.getLoadedRecord() !== row) {
                    bindable.load(row);
                }
                bindable.enable();
                if (this.saveButton) this.saveButton.enable();
            } else {
                bindable.disable();
                if (this.saveButton) this.saveButton.disable();
            }
        }, this);

        grid.on('beforeselect', function(sm, record) {
            if (bindable.getLoadedRecord() !== record && bindable.isDirty()) {
                Ext4.Msg.show({
                    title: trlKwf('Save'),
                    msg: trlKwf('Do you want to save the changes?'),
                    buttons: Ext4.Msg.YESNOCANCEL,
                    scope: this,
                    fn: function(button) {
                        if (button == 'yes') {
                            if (this.save()) {
                                bindable.reset();
                                grid.getSelectionModel().select(record);
                            } else {
                                //validation failed re-select
                                grid.getSelectionModel().select(bindable.getLoadedRecord());
                            }
                        } else if (button == 'no') {
                            bindable.reset();
                            grid.getSelectionModel().select(record);
                        } else if (button == 'cancel') {
                            grid.getSelectionModel().select(bindable.getLoadedRecord());
                        }
                    }
                });
                return false;
            }
        }, this);

        this.gridController.on('bindstore', this.onBindStore, this);
        if (grid.getStore()) this.onBindStore(grid.getStore());

        if (this.saveButton) {
            this.saveButton.on('click', function() {
                var syncQueue = new Kwf.Ext4.Data.StoreSyncQueue();
                this.save(syncQueue);
                syncQueue.start({
                    success: function() {
                        this.fireEvent('savesuccess');
                    },
                    scope: this
                });

            }, this);
        }
        if (this.addButton) {
            this.addButton.on('click', function() {
                if (!bindable.isValid()) {
                    return false;
                }

                var s = grid.getStore();
                var row = s.model.create();
                s.add(row);
                this.fireEvent('add', row);

                grid.getSelectionModel().select(row);
                bindable.onAdd(row);

            }, this);
        }
    },

    onRefreshStore: function()
    {
        var curr = this.bindable.getLoadedRecord();
        if (curr) {
            var newRow = this.gridController.grid.getStore().getById(curr.getId());
            //A refresh that loads the current row, a new object is created.
            //Load the new row, dirty values should be kept by the bindable
            if (newRow && newRow !== curr) {
                this.bindable.load(newRow);
            }
        }
    },

    onBindStore: function(store)
    {
        store.on('refresh', this.onRefreshStore, this);
    },

    save: function(syncQueue)
    {
        if (!this.bindable.isValid()) {
            Ext4.Msg.alert(trlKwf('Save'),
                trlKwf("Can't save, please fill all red underlined fields correctly."));
            return false;
        }

        if (syncQueue) {
            syncQueue.add(this.gridController.grid.getStore()); //sync this.gridController.grid store first
            this.bindable.save(syncQueue);         //then bindables (so bindable grid is synced second)
                                                   //bindable forms can still update the row as the sync is not yet started
        } else {
            this.bindable.save();                  //bindables first to allow form updating the row before sync
            this.gridController.grid.getStore().sync({
                success: function() {
                    this.fireEvent('savesuccess');
                },
                scope: this
            });
        }

        this.fireEvent('save');

        return true;
    },

    isDirty: function()
    {
        return this.bindable.isDirty();
    },

    reset: function()
    {
        return this.bindable.reset();
    }
});
<commit_msg>Ext4: BindableToGrid: attach to syncQueue event to get success<commit_after>Ext4.define('Kwf.Ext4.Controller.Binding.BindableToGrid', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    requires: [ 'Kwf.Ext4.Data.StoreSyncQueue' ],
    gridController: null,
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
        this.init();
    },

    init: function()
    {
        var grid = this.gridController.grid;
        var bindable = this.bindable;
        bindable.disable();

        if (!this.saveButton && bindable.getPanel()) this.saveButton = bindable.getPanel().down('button#save');
        if (!this.addButton) this.addButton = grid.down('button#add');

        if (this.saveButton) this.saveButton.disable();

        grid.on('selectionchange', function(model, rows) {
            if (rows[0]) {
                var row = rows[0];
                if (bindable.getLoadedRecord() !== row) {
                    bindable.load(row);
                }
                bindable.enable();
                if (this.saveButton) this.saveButton.enable();
            } else {
                bindable.disable();
                if (this.saveButton) this.saveButton.disable();
            }
        }, this);

        grid.on('beforeselect', function(sm, record) {
            if (bindable.getLoadedRecord() !== record && bindable.isDirty()) {
                Ext4.Msg.show({
                    title: trlKwf('Save'),
                    msg: trlKwf('Do you want to save the changes?'),
                    buttons: Ext4.Msg.YESNOCANCEL,
                    scope: this,
                    fn: function(button) {
                        if (button == 'yes') {
                            if (this.save()) {
                                bindable.reset();
                                grid.getSelectionModel().select(record);
                            } else {
                                //validation failed re-select
                                grid.getSelectionModel().select(bindable.getLoadedRecord());
                            }
                        } else if (button == 'no') {
                            bindable.reset();
                            grid.getSelectionModel().select(record);
                        } else if (button == 'cancel') {
                            grid.getSelectionModel().select(bindable.getLoadedRecord());
                        }
                    }
                });
                return false;
            }
        }, this);

        this.gridController.on('bindstore', this.onBindStore, this);
        if (grid.getStore()) this.onBindStore(grid.getStore());

        if (this.saveButton) {
            this.saveButton.on('click', function() {
                var syncQueue = new Kwf.Ext4.Data.StoreSyncQueue();
                this.save(syncQueue);
                syncQueue.start();

            }, this);
        }
        if (this.addButton) {
            this.addButton.on('click', function() {
                if (!bindable.isValid()) {
                    return false;
                }

                var s = grid.getStore();
                var row = s.model.create();
                s.add(row);
                this.fireEvent('add', row);

                grid.getSelectionModel().select(row);
                bindable.onAdd(row);

            }, this);
        }
    },

    onRefreshStore: function()
    {
        var curr = this.bindable.getLoadedRecord();
        if (curr) {
            var newRow = this.gridController.grid.getStore().getById(curr.getId());
            //A refresh that loads the current row, a new object is created.
            //Load the new row, dirty values should be kept by the bindable
            if (newRow && newRow !== curr) {
                this.bindable.load(newRow);
            }
        }
    },

    onBindStore: function(store)
    {
        store.on('refresh', this.onRefreshStore, this);
    },

    save: function(syncQueue)
    {
        if (!this.bindable.isValid()) {
            Ext4.Msg.alert(trlKwf('Save'),
                trlKwf("Can't save, please fill all red underlined fields correctly."));
            return false;
        }

        if (syncQueue) {
            syncQueue.add(this.gridController.grid.getStore()); //sync this.gridController.grid store first
            this.bindable.save(syncQueue);         //then bindables (so bindable grid is synced second)
                                                   //bindable forms can still update the row as the sync is not yet started
            syncQueue.on('finished', function(syncQueue) {
                if (!syncQueue.hasException) {
                    this.fireEvent('savesuccess');
                }
            }, this, { single: true });
        } else {
            this.bindable.save();                  //bindables first to allow form updating the row before sync
            this.gridController.grid.getStore().sync({
                success: function() {
                    this.fireEvent('savesuccess');
                },
                scope: this
            });
        }

        this.fireEvent('save');

        return true;
    },

    isDirty: function()
    {
        return this.bindable.isDirty();
    },

    reset: function()
    {
        return this.bindable.reset();
    }
});
<|endoftext|>