<commit_before>/*
 Copyright 2017 Aleksandar Jankovic

 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';

const mongoose = require('mongoose');
const dbConnector = require('../databaseConnector');
const errorCreator = require('../../objects/error/errorCreator');

const walletSchema = new mongoose.Schema(dbConnector.createSchema({
  amount: { type: Number, default: 0 },
  isProtected: { type: Boolean, default: false },
}), { collection: 'wallets' });

const Wallet = mongoose.model('Wallet', walletSchema);

const walletFilter = {
  amount: 1,
  lastUpdated: 1,
  ownerId: 1,
  ownerAliasId: 1,
};

/**
 * Get wallets
 * @private
 * @param {Object} params - Parameters
 * @param {string} params.query - Database query
 * @param {Function} params.callback - Callback
 */
function getWallets({ filter, query, callback }) {
  dbConnector.getObjects({
    query,
    filter,
    object: Wallet,
    errorNameContent: 'getWallets',
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({
        data: {
          wallets: data.objects,
        },
      });
    },
  });
}

/**
 * Get wallet.
 * @private
 * @param {Object} params - Parameters.
 * @param {string} params.query - Query to get wallet.
 * @param {Function} params.callback - Callback
 */
function getWallet({ query, callback }) {
  dbConnector.getObject({
    query,
    object: Wallet,
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      } else if (!data.object) {
        callback({ error: new errorCreator.DoesNotExist({ name: `wallet ${query.toString()}` }) });

        return;
      }

      callback({ data: { wallet: data.object } });
    },
  });
}

/**
 * Update wallet
 * @private
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet to update
 * @param {Function} params.callback - Callback
 */
function updateObject({ update, walletId, callback }) {
  dbConnector.updateObject({
    update,
    query: { _id: walletId },
    object: Wallet,
    errorNameContent: 'updateWallet',
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({ data: { wallet: data.object } });
    },
  });
}

/**
 * Get all wallets
 * @param {Object} params - Parameters
 * @param {Function} params.callback - Callback
 */
function getAllWallets({ callback }) {
  getWallets({
    callback,
    errorNameContent: 'getAllWallets',
  });
}

/**
 * Get wallets by teams
 * @param {Object} params - Parameters
 * @param {string} params.teamIds - ID of the teams
 * @param {Function} params.callback - Callback
 */
function getWalletsByTeams({ teamIds, callback }) {
  const query = {
    $or: [
      { ownerId: { $in: teamIds } },
      { teamIds: { $in: teamIds } },
    ],
  };

  getWallets({
    callback,
    query,
  });
}

/**
 * Get wallets that the user has access to.
 * @param {Object} params - Parameters
 * @param {string} params.user - User retrieving wallets.
 * @param {Function} params.callback - Callback
 */
function getWalletsByUser({ full, user, callback }) {
  const query = dbConnector.createUserQuery({ user });
  const filter = !full ? walletFilter : {};

  getWallets({
    query,
    filter,
    callback,
  });
}

/**
 * Create and save wallet.
 * @param {Object} params - Parameters.
 * @param {Object} params.wallet - New wallet.
 * @param {Function} params.callback - Callback.
 * @param {Object} [params.options] - Options.
 */
function createWallet({
  wallet,
  callback,
  options = {},
}) {
  const walletToSave = wallet;

  if (options.setId && walletToSave.objectId) {
    walletToSave._id = walletToSave.objectId; // eslint-disable-line no-underscore-dangle
  }

  dbConnector.saveObject({
    object: new Wallet(walletToSave),
    objectType: 'wallet',
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({ data: { wallet: data.savedObject } });
    },
  });
}

/**
 * Update wallet
 * @param {Object} params - Parameters
 * @param {string} params.walletId - Wallet ID
 * @param {Object} params.wallet - Update wallet
 * @param {number} params.wallet.amount - Amount to increase or decrease with
 * @param {Object} params.options - Options
 * @param {boolean} params.options.shouldDecreaseAmount - Should the amount in the wallet be decreased?
 * @param {boolean} params.options.resetAmount - Should the wallet amount be reset?
 * @param {boolean} params.options.resetOwnerAliasId - Should owner alias ID be removed?
 * @param {Function} params.callback - Callback
 */
function updateWallet({
  walletId,
  wallet,
  callback,
  options = {},
}) {
  const {
    amount,
    ownerAliasId,
    visibility,
    accessLevel,
    isProtected,
  } = wallet;
  const {
    shouldDecreaseAmount,
    resetAmount,
    resetOwnerAliasId,
  } = options;
  const update = { $set: {} };

  if (typeof resetAmount === 'boolean' && resetAmount) {
    update.$set.amount = 0;
  } else if (amount) {
    update.$inc = {};

    if (shouldDecreaseAmount) {
      update.$inc.amount = Math.abs(amount);
    } else {
      update.$inc.amount = -Math.abs(amount);
    }
  }

  if (resetOwnerAliasId) {
    update.$unset = { ownerAliasId: '' };
  } else if (ownerAliasId) {
    update.$set.ownerAliasId = ownerAliasId;
  }

  if (typeof isProtected === 'boolean') { update.$set.isProtected = isProtected; }
  if (visibility) { update.$set.visibility = visibility; }
  if (accessLevel) { update.$set.accessLevel = accessLevel; }

  updateObject({
    update,
    walletId,
    callback,
  });
}

/**
 * Add access to the wallet for users or teams
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet
 * @param {string[]} [params.userIds] - ID of the users
 * @param {string[]} [params.teamIds] - ID of the teams
 * @param {string[]} [params.bannedIds] - ID of the blocked Ids to add
 * @param {string[]} [params.teamAdminIds] - Id of the teams to give admin access to. They will also be added to teamIds.
 * @param {string[]} [params.userAdminIds] - Id of the users to give admin access to. They will also be added to userIds.
 * @param {Function} params.callback - Callback
 */
function addAccess({
  userIds,
  teamIds,
  bannedIds,
  walletId,
  teamAdminIds,
  userAdminIds,
  callback,
}) {
  dbConnector.addObjectAccess({
    userIds,
    teamIds,
    bannedIds,
    teamAdminIds,
    userAdminIds,
    objectId: walletId,
    object: Wallet,
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({ wallet: data.object });
    },
  });
}

/**
 * Remove access to the wallet for users and/or teams
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet
 * @param {string[]} [params.userIds] - ID of the users
 * @param {string[]} [params.teamIds] - ID of the teams
 * @param {string[]} [params.bannedIds] - ID of the blocked Ids to add
 * @param {string[]} [params.teamAdminIds] - Id of the teams to remove admin access from. They will not be removed from teamIds.
 * @param {string[]} [params.userAdminIds] - Id of the users to remove admin access from. They will not be removed from userIds.
 * @param {Function} params.callback - Callback
 */
function removeAccess({
  userIds,
  teamIds,
  bannedIds,
  teamAdminIds,
  userAdminIds,
  walletId,
  callback,
}) {
  dbConnector.removeObjectAccess({
    userIds,
    teamIds,
    bannedIds,
    teamAdminIds,
    userAdminIds,
    callback,
    objectId: walletId,
    object: Wallet,
  });
}

/**
 * Remove wallet
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet to remove
 * @param {Function} params.callback - Callback
 */
function removeWallet({ walletId, callback }) {
  dbConnector.removeObject({
    callback,
    query: { _id: walletId },
    object: Wallet,
  });
}

/**
 * Get wallets by Ids
 * @param {Object} params - Parameters
 * @param {string[]} params.walletIds - Wallet Ids
 * @param {Function} params.callback - Callback
 */
function getWalletsByIds({ walletIds, callback }) {
  getWallets({
    callback,
    query: { _id: { $in: walletIds } },
  });
}

/**
 * Get wallet by Id.
 * @param {Object} params - Parameters.
 * @param {string[]} params.walletId - Wallet Id.
 * @param {Function} params.callback - Callback.
 */
function getWalletById({ walletId, callback }) {
  getWallet({
    callback,
    query: { _id: walletId },
  });
}

exports.createWallet = createWallet;
exports.getAllWallets = getAllWallets;
exports.getWalletsByUser = getWalletsByUser;
exports.removeAccess = removeAccess;
exports.addAccess = addAccess;
exports.removeWallet = removeWallet;
exports.updateWallet = updateWallet;
exports.getWalletsByTeams = getWalletsByTeams;
exports.getWalletsByIds = getWalletsByIds;
exports.getWalletById = getWalletById;
<commit_msg>Error properly prints object.<commit_after>/*
 Copyright 2017 Aleksandar Jankovic

 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';

const mongoose = require('mongoose');
const dbConnector = require('../databaseConnector');
const errorCreator = require('../../objects/error/errorCreator');

const walletSchema = new mongoose.Schema(dbConnector.createSchema({
  amount: { type: Number, default: 0 },
  isProtected: { type: Boolean, default: false },
}), { collection: 'wallets' });

const Wallet = mongoose.model('Wallet', walletSchema);

const walletFilter = {
  amount: 1,
  lastUpdated: 1,
  ownerId: 1,
  ownerAliasId: 1,
};

/**
 * Get wallets
 * @private
 * @param {Object} params - Parameters
 * @param {string} params.query - Database query
 * @param {Function} params.callback - Callback
 */
function getWallets({ filter, query, callback }) {
  dbConnector.getObjects({
    query,
    filter,
    object: Wallet,
    errorNameContent: 'getWallets',
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({
        data: {
          wallets: data.objects,
        },
      });
    },
  });
}

/**
 * Get wallet.
 * @private
 * @param {Object} params - Parameters.
 * @param {string} params.query - Query to get wallet.
 * @param {Function} params.callback - Callback
 */
function getWallet({ query, callback }) {
  dbConnector.getObject({
    query,
    object: Wallet,
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      } else if (!data.object) {
        callback({ error: new errorCreator.DoesNotExist({ name: `wallet ${JSON.stringify(query, null, 4)}` }) });

        return;
      }

      callback({ data: { wallet: data.object } });
    },
  });
}

/**
 * Update wallet
 * @private
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet to update
 * @param {Function} params.callback - Callback
 */
function updateObject({ update, walletId, callback }) {
  dbConnector.updateObject({
    update,
    query: { _id: walletId },
    object: Wallet,
    errorNameContent: 'updateWallet',
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({ data: { wallet: data.object } });
    },
  });
}

/**
 * Get all wallets
 * @param {Object} params - Parameters
 * @param {Function} params.callback - Callback
 */
function getAllWallets({ callback }) {
  getWallets({
    callback,
    errorNameContent: 'getAllWallets',
  });
}

/**
 * Get wallets by teams
 * @param {Object} params - Parameters
 * @param {string} params.teamIds - ID of the teams
 * @param {Function} params.callback - Callback
 */
function getWalletsByTeams({ teamIds, callback }) {
  const query = {
    $or: [
      { ownerId: { $in: teamIds } },
      { teamIds: { $in: teamIds } },
    ],
  };

  getWallets({
    callback,
    query,
  });
}

/**
 * Get wallets that the user has access to.
 * @param {Object} params - Parameters
 * @param {string} params.user - User retrieving wallets.
 * @param {Function} params.callback - Callback
 */
function getWalletsByUser({ full, user, callback }) {
  const query = dbConnector.createUserQuery({ user });
  const filter = !full ? walletFilter : {};

  getWallets({
    query,
    filter,
    callback,
  });
}

/**
 * Create and save wallet.
 * @param {Object} params - Parameters.
 * @param {Object} params.wallet - New wallet.
 * @param {Function} params.callback - Callback.
 * @param {Object} [params.options] - Options.
 */
function createWallet({
  wallet,
  callback,
  options = {},
}) {
  const walletToSave = wallet;

  if (options.setId && walletToSave.objectId) {
    walletToSave._id = walletToSave.objectId; // eslint-disable-line no-underscore-dangle
  }

  dbConnector.saveObject({
    object: new Wallet(walletToSave),
    objectType: 'wallet',
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({ data: { wallet: data.savedObject } });
    },
  });
}

/**
 * Update wallet
 * @param {Object} params - Parameters
 * @param {string} params.walletId - Wallet ID
 * @param {Object} params.wallet - Update wallet
 * @param {number} params.wallet.amount - Amount to increase or decrease with
 * @param {Object} params.options - Options
 * @param {boolean} params.options.shouldDecreaseAmount - Should the amount in the wallet be decreased?
 * @param {boolean} params.options.resetAmount - Should the wallet amount be reset?
 * @param {boolean} params.options.resetOwnerAliasId - Should owner alias ID be removed?
 * @param {Function} params.callback - Callback
 */
function updateWallet({
  walletId,
  wallet,
  callback,
  options = {},
}) {
  const {
    amount,
    ownerAliasId,
    visibility,
    accessLevel,
    isProtected,
  } = wallet;
  const {
    shouldDecreaseAmount,
    resetAmount,
    resetOwnerAliasId,
  } = options;
  const update = { $set: {} };

  if (typeof resetAmount === 'boolean' && resetAmount) {
    update.$set.amount = 0;
  } else if (amount) {
    update.$inc = {};

    if (shouldDecreaseAmount) {
      update.$inc.amount = Math.abs(amount);
    } else {
      update.$inc.amount = -Math.abs(amount);
    }
  }

  if (resetOwnerAliasId) {
    update.$unset = { ownerAliasId: '' };
  } else if (ownerAliasId) {
    update.$set.ownerAliasId = ownerAliasId;
  }

  if (typeof isProtected === 'boolean') { update.$set.isProtected = isProtected; }
  if (visibility) { update.$set.visibility = visibility; }
  if (accessLevel) { update.$set.accessLevel = accessLevel; }

  updateObject({
    update,
    walletId,
    callback,
  });
}

/**
 * Add access to the wallet for users or teams
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet
 * @param {string[]} [params.userIds] - ID of the users
 * @param {string[]} [params.teamIds] - ID of the teams
 * @param {string[]} [params.bannedIds] - ID of the blocked Ids to add
 * @param {string[]} [params.teamAdminIds] - Id of the teams to give admin access to. They will also be added to teamIds.
 * @param {string[]} [params.userAdminIds] - Id of the users to give admin access to. They will also be added to userIds.
 * @param {Function} params.callback - Callback
 */
function addAccess({
  userIds,
  teamIds,
  bannedIds,
  walletId,
  teamAdminIds,
  userAdminIds,
  callback,
}) {
  dbConnector.addObjectAccess({
    userIds,
    teamIds,
    bannedIds,
    teamAdminIds,
    userAdminIds,
    objectId: walletId,
    object: Wallet,
    callback: ({ error, data }) => {
      if (error) {
        callback({ error });

        return;
      }

      callback({ wallet: data.object });
    },
  });
}

/**
 * Remove access to the wallet for users and/or teams
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet
 * @param {string[]} [params.userIds] - ID of the users
 * @param {string[]} [params.teamIds] - ID of the teams
 * @param {string[]} [params.bannedIds] - ID of the blocked Ids to add
 * @param {string[]} [params.teamAdminIds] - Id of the teams to remove admin access from. They will not be removed from teamIds.
 * @param {string[]} [params.userAdminIds] - Id of the users to remove admin access from. They will not be removed from userIds.
 * @param {Function} params.callback - Callback
 */
function removeAccess({
  userIds,
  teamIds,
  bannedIds,
  teamAdminIds,
  userAdminIds,
  walletId,
  callback,
}) {
  dbConnector.removeObjectAccess({
    userIds,
    teamIds,
    bannedIds,
    teamAdminIds,
    userAdminIds,
    callback,
    objectId: walletId,
    object: Wallet,
  });
}

/**
 * Remove wallet
 * @param {Object} params - Parameters
 * @param {string} params.walletId - ID of the wallet to remove
 * @param {Function} params.callback - Callback
 */
function removeWallet({ walletId, callback }) {
  dbConnector.removeObject({
    callback,
    query: { _id: walletId },
    object: Wallet,
  });
}

/**
 * Get wallets by Ids
 * @param {Object} params - Parameters
 * @param {string[]} params.walletIds - Wallet Ids
 * @param {Function} params.callback - Callback
 */
function getWalletsByIds({ walletIds, callback }) {
  getWallets({
    callback,
    query: { _id: { $in: walletIds } },
  });
}

/**
 * Get wallet by Id.
 * @param {Object} params - Parameters.
 * @param {string[]} params.walletId - Wallet Id.
 * @param {Function} params.callback - Callback.
 */
function getWalletById({ walletId, callback }) {
  getWallet({
    callback,
    query: { _id: walletId },
  });
}

exports.createWallet = createWallet;
exports.getAllWallets = getAllWallets;
exports.getWalletsByUser = getWalletsByUser;
exports.removeAccess = removeAccess;
exports.addAccess = addAccess;
exports.removeWallet = removeWallet;
exports.updateWallet = updateWallet;
exports.getWalletsByTeams = getWalletsByTeams;
exports.getWalletsByIds = getWalletsByIds;
exports.getWalletById = getWalletById;
<|endoftext|>


<commit_before>$(document).ready(function(){
  var tf2bp = function(){
    var pageCount = 6; // Pages/backpack
    var itemCount = 50; // Items/page
    var itemImageMask = null;

    var createBox = function(parent, id){
	    if(typeof parent === 'string') {
        parent = $(parent);
      }

      parent.append('

');
    }

    var fillBox = function(box, data) {
      if(typeof box === 'string') {
        box = $(box);
      }

      if(data.quality) {
        box.addClass('bp'+data.quality);
      }

      box.append('');

      if(data.quantity) {
        box.append('' + data.quantity + '');
      }

      if(data.custom_desc) {
        box.append('');
      }

      if(data.custom_name) {
        box.append('');
      }

      if(data.color) {
        box.append('');
      }

      if(data.gift) {
        box.append('Gift');
      }

      if(data.equipped && data.equipped.length) {
        box.append('Equipped');
      }

      box.tfToolTip(function(toolTip) {
        toolTip.addClass('bpinfo');

        if(data.quality) {
          toolTip.addClass('bp'+data.quality);
        }

        if(data.custom_name) {
          toolTip.append('"' + data.custom_name + '"');
          toolTip.append('' + data.name + '');
        }
        else {
          toolTip.append('' + data.name + '');
        }

        if(typeof data.level === 'undefined') {
          toolTip.append('' + data.type + '');
        }
        else {
          toolTip.append('Level ' + data.level + ' ' + data.type + '');
        }

        if(data.custom_desc) {
          toolTip.append('' + data.custom_desc + '');
        }

        for(var a in data.attributes) {
          var att = data.attributes[a];
          toolTip.append('' + att.name + '');
        }

        if(!data.tradable) {
          toolTip.append('(Not Tradable)');
        }

        if(data.quantity) {
          toolTip.append('This is a limited use item. Uses: ' + data.quantity + '');
        }

        if(data.used_by.length) {
          var used_by = toolTip.append('

').find(".bpusedby");
          for(var c in data.used_by) {
              used_by.append('
' + data.used_by[c] + '
');
          }
          for(var c in data.equipped) {
            $('.bp'+data.equipped[c]).addClass('bpactive');
          }
        }
      });
    };

    // If we have a imageMask function, apply it
    var getItemImage = function(str, data) {
      return itemImageMask ? itemImageMask(str, data) : str;
    }

    return {
      loadBP: function(element, id, callback) {
        if(typeof element === 'string') {
          element = $(element);
        }

        var date = new Date();
        var bpuid = date.getMilliseconds();
        element.append('

');

        // Find the backpack element
        var backpack = $('#bp'+bpuid);

        var n = 1;
        for(var x = 1; x <= pageCount; x++) {
          // Create, find and hide each page, so we can make a cool effect when everything is loaded
          var page = backpack.append('

').find('#bppage-' + x).hide();

          // Each page has 50 items named "bpitem-1", "bpitem-2", ...
          for(var y = 0; y < itemCount; y++, n++) {
            createBox(page, ('bpitem-' + n + '-' + bpuid));
          }
        }

        $.steam.tf2items.getItems(id, function(data){
          for(var i in data.items) {
            var itm = data.items[i];
            fillBox(('#bpitem-' + itm.position + '-' + bpuid), itm);
          }

          // Fix the boxes, since we use float CSS prop.
          backpack.find('.bppage').append('

');

          if(data.invalid.length) {
            var n = 1;
            var inv = backpack.append('

').find('.bpinvalid').hide();
            for(var i in data.invalid) {
              var itm = data.invalid[i];
              createBox(inv, ('bpitem-i' + n + '-' + bpuid));
              fillBox(('#bpitem-i' + n + '-' + bpuid), itm);
              n++;
            }
            inv.append('

');
          }

          backpack.siblings().remove();
          backpack.find('.bppage, .bpinvalid').fadeIn('slow');

          callback();
        });
      },
      loadList: function(element, callback) {
        if(typeof element === 'string') {
          element = $(element);
        }

        var date = new Date();
        var bpuid = date.getMilliseconds();
        element.append('

');

        // Find the backpack element
        var backpack = $('#bp'+bpuid);

        $.steam.tf2items.getItemList(function(items){
          var n = 1;
          // Each page has 50 items named "bpitem-1", "bpitem-2", ...
          for(var i in items) {
            var itm = items[i];
            createBox(backpack, ('bpitem-' + n + '-' + bpuid));
            fillBox(('#bpitem-' + n + '-' + bpuid), itm);
            n++;
          }

          // Fix the boxes, since we use float CSS prop.
          backpack.append('

');

          backpack.siblings().remove();
          backpack.find('.bppage, .bpinvalid').fadeIn('slow');

          callback();
        });
      },
      setItemImageMask: function(_itemImageMask){
        itemImageMask = _itemImageMask;
      }
    }
  }();

  $.tf2bp = tf2bp;
});<commit_msg>Max number of pages is now 20<commit_after>$(document).ready(function(){
  var tf2bp = function(){
    var pageCount = 20; // Pages/backpack
    var itemCount = 50; // Items/page
    var itemImageMask = null;

    var createBox = function(parent, id){
	    if(typeof parent === 'string') {
        parent = $(parent);
      }

      parent.append('

');
    }

    var fillBox = function(box, data) {
      if(typeof box === 'string') {
        box = $(box);
      }

      if(data.quality) {
        box.addClass('bp'+data.quality);
      }

      box.append('');

      if(data.quantity) {
        box.append('' + data.quantity + '');
      }

      if(data.custom_desc) {
        box.append('');
      }

      if(data.custom_name) {
        box.append('');
      }

      if(data.color) {
        box.append('');
      }

      if(data.gift) {
        box.append('Gift');
      }

      if(data.equipped && data.equipped.length) {
        box.append('Equipped');
      }

      box.tfToolTip(function(toolTip) {
        toolTip.addClass('bpinfo');

        if(data.quality) {
          toolTip.addClass('bp'+data.quality);
        }

        if(data.custom_name) {
          toolTip.append('"' + data.custom_name + '"');
          toolTip.append('' + data.name + '');
        }
        else {
          toolTip.append('' + data.name + '');
        }

        if(typeof data.level === 'undefined') {
          toolTip.append('' + data.type + '');
        }
        else {
          toolTip.append('Level ' + data.level + ' ' + data.type + '');
        }

        if(data.custom_desc) {
          toolTip.append('' + data.custom_desc + '');
        }

        for(var a in data.attributes) {
          var att = data.attributes[a];
          toolTip.append('' + att.name + '');
        }

        if(!data.tradable) {
          toolTip.append('(Not Tradable)');
        }

        if(data.quantity) {
          toolTip.append('This is a limited use item. Uses: ' + data.quantity + '');
        }

        if(data.used_by.length) {
          var used_by = toolTip.append('

').find(".bpusedby");
          for(var c in data.used_by) {
              used_by.append('
' + data.used_by[c] + '
');
          }
          for(var c in data.equipped) {
            $('.bp'+data.equipped[c]).addClass('bpactive');
          }
        }
      });
    };

    // If we have a imageMask function, apply it
    var getItemImage = function(str, data) {
      return itemImageMask ? itemImageMask(str, data) : str;
    }

    return {
      loadBP: function(element, id, callback) {
        if(typeof element === 'string') {
          element = $(element);
        }

        var date = new Date();
        var bpuid = date.getMilliseconds();
        element.append('

');

        // Find the backpack element
        var backpack = $('#bp'+bpuid);

        var n = 1;
        for(var x = 1; x <= pageCount; x++) {
          // Create, find and hide each page, so we can make a cool effect when everything is loaded
          var page = backpack.append('

').find('#bppage-' + x).hide();

          // Each page has 50 items named "bpitem-1", "bpitem-2", ...
          for(var y = 0; y < itemCount; y++, n++) {
            createBox(page, ('bpitem-' + n + '-' + bpuid));
          }
        }

        $.steam.tf2items.getItems(id, function(data){
          for(var i in data.items) {
            var itm = data.items[i];
            fillBox(('#bpitem-' + itm.position + '-' + bpuid), itm);
          }

          // Fix the boxes, since we use float CSS prop.
          backpack.find('.bppage').append('

');

          if(data.invalid.length) {
            var n = 1;
            var inv = backpack.append('

').find('.bpinvalid').hide();
            for(var i in data.invalid) {
              var itm = data.invalid[i];
              createBox(inv, ('bpitem-i' + n + '-' + bpuid));
              fillBox(('#bpitem-i' + n + '-' + bpuid), itm);
              n++;
            }
            inv.append('

');
          }

          backpack.siblings().remove();
          backpack.find('.bppage, .bpinvalid').fadeIn('slow');

          callback();
        });
      },
      loadList: function(element, callback) {
        if(typeof element === 'string') {
          element = $(element);
        }

        var date = new Date();
        var bpuid = date.getMilliseconds();
        element.append('

');

        // Find the backpack element
        var backpack = $('#bp'+bpuid);

        $.steam.tf2items.getItemList(function(items){
          var n = 1;
          // Each page has 50 items named "bpitem-1", "bpitem-2", ...
          for(var i in items) {
            var itm = items[i];
            createBox(backpack, ('bpitem-' + n + '-' + bpuid));
            fillBox(('#bpitem-' + n + '-' + bpuid), itm);
            n++;
          }

          // Fix the boxes, since we use float CSS prop.
          backpack.append('

');

          backpack.siblings().remove();
          backpack.find('.bppage, .bpinvalid').fadeIn('slow');

          callback();
        });
      },
      setItemImageMask: function(_itemImageMask){
        itemImageMask = _itemImageMask;
      }
    }
  }();

  $.tf2bp = tf2bp;
});<|endoftext|>


<commit_before>//Particle component
//Based on Parcycle by Mr. Speaker, licensed under the MIT,
//Ported by Leo Koppelkamm
//**This is canvas only & won't do anything if the browser doesn't support it!**

Crafty.c("particles", {
	init: function () {
		//nothing to do here...
	},
	particles: function (options) {

		if (!Crafty.support.canvas) return this;

		//If we drew on the main canvas, we'd have to redraw
		//potentially huge sections of the screen every frame
		//So we create a separate canvas, where we only have to redraw
		//the changed particles.
		var c, ctx, relativeX, relativeY, bounding;

		c = document.createElement("canvas");
		c.width = Crafty.viewport.width;
		c.height = Crafty.viewport.height;
		c.style.position = 'absolute';

		Crafty.stage.elem.appendChild(c);

		ctx = c.getContext('2d');

		this._Particles.init(options);

		relativeX = this.x + Crafty.viewport.x;
		relativeY = this.y + Crafty.viewport.y;
		this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

		var oldViewport = {x: Crafty.viewport.x, y:Crafty.viewport.y};

		this.bind('enterframe', function () {
			relativeX = this.x + Crafty.viewport.x;
			relativeY = this.y + Crafty.viewport.y;
			this._Particles.viewportDelta = {x: Crafty.viewport.x - oldViewport.x, y: Crafty.viewport.y - oldViewport.y};

			oldViewport = {x: Crafty.viewport.x, y:Crafty.viewport.y};

			this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

			//Selective clearing
			if (typeof Crafty.DrawManager.boundingRect == 'function') {
				bounding = Crafty.DrawManager.boundingRect(this._Particles.register);
				if (bounding) ctx.clearRect(bounding._x, bounding._y, bounding._w, bounding._h);
			} else {
				ctx.clearRect(0, 0, Crafty.viewport.width, Crafty.viewport.height);
			}

			//This updates all particle colors & positions
			this._Particles.update();

			//This renders the updated particles
			this._Particles.render(ctx);
		});
		return this;
	},
	_Particles: {
		presets: {
			maxParticles: 150,
			particles: [],
			active: true,

			// Properties
			size: 18,
			sizeRandom: 4,
			speed: 1,
			speedRandom: 1.2,
			lifeSpan: 29,
			lifeSpanRandom: 7,
			angle: 65,
			angleRandom: 34,
			startColour: [255, 131, 0, 1],
			startColourRandom: [48, 50, 45, 0],
			endColour: [245, 35, 0, 0],
			endColourRandom: [60, 60, 60, 0],
			sharpness: 20,
			sharpnessRandom: 10,
			spread: 10,
			particleCount: 0,
			elapsedFrames: 0,
			duration: -1,
			emissionRate: 0,
			emitCounter: 0,
			particleIndex: 0,
			fastMode: false,
			gravity:{x: 0, y: 0.1}
		},

		init: function (options) {
			this.position = this.vectorHelpers.create(0, 0);
			if (typeof options == 'undefined') var options = {};

			//Create current config by mergin given options and presets.
			for (key in this.presets) {
				if (typeof options[key] != 'undefined') this[key] = options[key];
				else this[key] = this.presets[key];
			}

			this.emissionRate = this.maxParticles / this.lifeSpan;
			this.positionRandom = this.vectorHelpers.create(this.spread, this.spread);
		},

		addParticle: function () {
			if (this.particleCount == this.maxParticles) {
				return false;
			}

			// Take the next particle out of the particle pool we have created and initialize it
			var particle = new this.particle(this.vectorHelpers);
			this.initParticle(particle);
			this.particles[this.particleCount] = particle;
			// Increment the particle count
			this.particleCount++;

			return true;
		},

		initParticle: function (particle) {
			var RANDM1TO1 = function () {
				return Math.random() * 2 - 1;
			};

			particle.position.x = this.position.x + this.positionRandom.x * RANDM1TO1();
			particle.position.y = this.position.y + this.positionRandom.y * RANDM1TO1();

			var newAngle = (this.angle + this.angleRandom * RANDM1TO1()) * (Math.PI / 180); // convert to radians
			var vector = this.vectorHelpers.create(Math.sin(newAngle), -Math.cos(newAngle)); // Could move to lookup for speed
			var vectorSpeed = this.speed + this.speedRandom * RANDM1TO1();
			particle.direction = this.vectorHelpers.multiply(vector, vectorSpeed);

			particle.size = this.size + this.sizeRandom * RANDM1TO1();
			particle.size = particle.size < 0 ? 0 : ~~particle.size;
			particle.timeToLive = this.lifeSpan + this.lifeSpanRandom * RANDM1TO1();

			particle.sharpness = this.sharpness + this.sharpnessRandom * RANDM1TO1();
			particle.sharpness = particle.sharpness > 100 ? 100 : particle.sharpness < 0 ? 0 : particle.sharpness;
			// internal circle gradient size - affects the sharpness of the radial gradient
			particle.sizeSmall = ~~ ((particle.size / 200) * particle.sharpness); //(size/2/100)
			var start = [
				this.startColour[0] + this.startColourRandom[0] * RANDM1TO1(),
				this.startColour[1] + this.startColourRandom[1] * RANDM1TO1(),
				this.startColour[2] + this.startColourRandom[2] * RANDM1TO1(),
				this.startColour[3] + this.startColourRandom[3] * RANDM1TO1()
				];

			var end = [
				this.endColour[0] + this.endColourRandom[0] * RANDM1TO1(),
				this.endColour[1] + this.endColourRandom[1] * RANDM1TO1(),
				this.endColour[2] + this.endColourRandom[2] * RANDM1TO1(),
				this.endColour[3] + this.endColourRandom[3] * RANDM1TO1()
				];

			particle.colour = start;
			particle.deltaColour[0] = (end[0] - start[0]) / particle.timeToLive;
			particle.deltaColour[1] = (end[1] - start[1]) / particle.timeToLive;
			particle.deltaColour[2] = (end[2] - start[2]) / particle.timeToLive;
			particle.deltaColour[3] = (end[3] - start[3]) / particle.timeToLive;
		},

		update: function () {
			if (this.active && this.emissionRate > 0) {
				var rate = 1 / this.emissionRate;
				this.emitCounter++;
				while (this.particleCount < this.maxParticles && this.emitCounter > rate) {
					this.addParticle();
					this.emitCounter -= rate;
				}
				this.elapsedFrames++;
				if (this.duration != -1 && this.duration < this.elapsedFrames) {
					this.stop();
				}
			}

			this.particleIndex = 0;
			this.register = [];
			while (this.particleIndex < this.particleCount) {

				var currentParticle = this.particles[this.particleIndex];

				// If the current particle is alive then update it
				if (currentParticle.timeToLive > 0) {

					// Calculate the new direction based on gravity
					currentParticle.direction = this.vectorHelpers.add(currentParticle.direction, this.gravity);
					currentParticle.position = this.vectorHelpers.add(currentParticle.position, currentParticle.direction);
					currentParticle.position = this.vectorHelpers.add(currentParticle.position, this.viewportDelta);
					currentParticle.timeToLive--;

					// Update colours
					var r = currentParticle.colour[0] += currentParticle.deltaColour[0];
					var g = currentParticle.colour[1] += currentParticle.deltaColour[1];
					var b = currentParticle.colour[2] += currentParticle.deltaColour[2];
					var a = currentParticle.colour[3] += currentParticle.deltaColour[3];

					// Calculate the rgba string to draw.
					var draw = [];
					draw.push("rgba(" + (r > 255 ? 255 : r < 0 ? 0 : ~~r));
					draw.push(g > 255 ? 255 : g < 0 ? 0 : ~~g);
					draw.push(b > 255 ? 255 : b < 0 ? 0 : ~~b);
					draw.push((a > 1 ? 1 : a < 0 ? 0 : a.toFixed(2)) + ")");
					currentParticle.drawColour = draw.join(",");
					draw[3] = "0)";
					currentParticle.drawColourEnd = draw.join(",");
					this.particleIndex++;
				} else {
					// Replace particle with the last active
					if (this.particleIndex != this.particleCount - 1) {
						this.particles[this.particleIndex] = this.particles[this.particleCount - 1];
					}
					this.particleCount--;
				}
				var rect = {};
				rect._x = ~~currentParticle.position.x;
				rect._y = ~~currentParticle.position.y;
				rect._w = currentParticle.size;
				rect._h = currentParticle.size;

				this.register.push(rect);
			}
		},

		stop: function () {
			this.active = false;
			this.elapsedFrames = 0;
			this.emitCounter = 0;
		},

		render: function (context) {

			for (var i = 0, j = this.particleCount; i < j; i++) {
				var particle = this.particles[i];
				var size = particle.size;
				var halfSize = size >> 1;

				if (particle.position.x + size < 0
					|| particle.position.y + size < 0
					|| particle.position.x - size > Crafty.viewport.width
					|| particle.position.y - size > Crafty.viewport.height) {
					//Particle is outside
					continue;
				}
				var x = ~~particle.position.x;
				var y = ~~particle.position.y;

				if (this.fastMode) {
					context.fillStyle = particle.drawColour;
				} else {
					var radgrad = context.createRadialGradient(x + halfSize, y + halfSize, particle.sizeSmall, x + halfSize, y + halfSize, halfSize);
					radgrad.addColorStop(0, particle.drawColour);
					//0.9 to avoid visible boxing
					radgrad.addColorStop(0.9, particle.drawColourEnd);
					context.fillStyle = radgrad;
				}
				context.fillRect(x, y, size, size);
			}
		},
		particle: function (vectorHelpers) {
			this.position = vectorHelpers.create(0, 0);
			this.direction = vectorHelpers.create(0, 0);
			this.size = 0;
			this.sizeSmall = 0;
			this.timeToLive = 0;
			this.colour = [];
			this.drawColour = "";
			this.deltaColour = [];
			this.sharpness = 0;
		},
		vectorHelpers: {
			create: function (x, y) {
				return {
					"x": x,
					"y": y
				};
			},
			multiply: function (vector, scaleFactor) {
				vector.x *= scaleFactor;
				vector.y *= scaleFactor;
				return vector;
			},
			add: function (vector1, vector2) {
				vector1.x += vector2.x;
				vector1.y += vector2.y;
				return vector1;
			}
		}
	}
});<commit_msg>Particles: *add some cmts, *jitter function etc.<commit_after>//Particle component
//Based on Parcycle by Mr. Speaker, licensed under the MIT,
//Ported by Leo Koppelkamm
//**This is canvas only & won't do anything if the browser doesn't support it!**

Crafty.c("particles", {
	init: function () {
		//nothing to do here...
	},
	particles: function (options) {

		if (!Crafty.support.canvas) return this;

		//If we drew on the main canvas, we'd have to redraw
		//potentially huge sections of the screen every frame
		//So we create a separate canvas, where we only have to redraw
		//the changed particles.
		var c, ctx, relativeX, relativeY, bounding;

		c = document.createElement("canvas");
		c.width = Crafty.viewport.width;
		c.height = Crafty.viewport.height;
		c.style.position = 'absolute';

		Crafty.stage.elem.appendChild(c);

		ctx = c.getContext('2d');

		this._Particles.init(options);

		relativeX = this.x + Crafty.viewport.x;
		relativeY = this.y + Crafty.viewport.y;
		this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

		var oldViewport = {x: Crafty.viewport.x, y:Crafty.viewport.y};

		this.bind('enterframe', function () {
			relativeX = this.x + Crafty.viewport.x;
			relativeY = this.y + Crafty.viewport.y;
			this._Particles.viewportDelta = {x: Crafty.viewport.x - oldViewport.x, y: Crafty.viewport.y - oldViewport.y};

			oldViewport = {x: Crafty.viewport.x, y:Crafty.viewport.y};

			this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

			//Selective clearing
			if (typeof Crafty.DrawManager.boundingRect == 'function') {
				bounding = Crafty.DrawManager.boundingRect(this._Particles.register);
				if (bounding) ctx.clearRect(bounding._x, bounding._y, bounding._w, bounding._h);
			} else {
				ctx.clearRect(0, 0, Crafty.viewport.width, Crafty.viewport.height);
			}

			//This updates all particle colors & positions
			this._Particles.update();

			//This renders the updated particles
			this._Particles.render(ctx);
		});
		return this;
	},
	_Particles: {
		presets: {
			maxParticles: 150,
			size: 18,
			sizeRandom: 4,
			speed: 1,
			speedRandom: 1.2,
			// Lifespan in frames
			lifeSpan: 29,
			lifeSpanRandom: 7,
			// Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
			angle: 65,
			angleRandom: 34,
			startColour: [255, 131, 0, 1],
			startColourRandom: [48, 50, 45, 0],
			endColour: [245, 35, 0, 0],
			endColourRandom: [60, 60, 60, 0],
			// Only applies when fastMode is off, specifies how sharp the gradients are drawn
			sharpness: 20,
			sharpnessRandom: 10,
			// Random spread from origin
			spread: 10,
			// How many frames should this last
			duration: -1,
			// Will draw squares instead of circle gradients
			fastMode: false,
			gravity:{x: 0, y: 0.1},
			// sensible values are 0-3
			jitter: 0,

			//Don't modify the following
			particles: [],
			active: true,
			particleCount: 0,
			elapsedFrames: 0,
			emissionRate: 0,
			emitCounter: 0,
			particleIndex: 0
		},

		init: function (options) {
			this.position = this.vectorHelpers.create(0, 0);
			if (typeof options == 'undefined') var options = {};

			//Create current config by mergin given options and presets.
			for (key in this.presets) {
				if (typeof options[key] != 'undefined') this[key] = options[key];
				else this[key] = this.presets[key];
			}

			this.emissionRate = this.maxParticles / this.lifeSpan;
			this.positionRandom = this.vectorHelpers.create(this.spread, this.spread);
		},

		addParticle: function () {
			if (this.particleCount == this.maxParticles) {
				return false;
			}

			// Take the next particle out of the particle pool we have created and initialize it
			var particle = new this.particle(this.vectorHelpers);
			this.initParticle(particle);
			this.particles[this.particleCount] = particle;
			// Increment the particle count
			this.particleCount++;

			return true;
		},
		RANDM1TO1: function() {
			return Math.random() * 2 - 1;
		},
		initParticle: function (particle) {
			particle.position.x = this.position.x + this.positionRandom.x * this.RANDM1TO1();
			particle.position.y = this.position.y + this.positionRandom.y * this.RANDM1TO1();

			var newAngle = (this.angle + this.angleRandom * this.RANDM1TO1()) * (Math.PI / 180); // convert to radians
			var vector = this.vectorHelpers.create(Math.sin(newAngle), -Math.cos(newAngle)); // Could move to lookup for speed
			var vectorSpeed = this.speed + this.speedRandom * this.RANDM1TO1();
			particle.direction = this.vectorHelpers.multiply(vector, vectorSpeed);

			particle.size = this.size + this.sizeRandom * this.RANDM1TO1();
			particle.size = particle.size < 0 ? 0 : ~~particle.size;
			particle.timeToLive = this.lifeSpan + this.lifeSpanRandom * this.RANDM1TO1();

			particle.sharpness = this.sharpness + this.sharpnessRandom * this.RANDM1TO1();
			particle.sharpness = particle.sharpness > 100 ? 100 : particle.sharpness < 0 ? 0 : particle.sharpness;
			// internal circle gradient size - affects the sharpness of the radial gradient
			particle.sizeSmall = ~~ ((particle.size / 200) * particle.sharpness); //(size/2/100)
			var start = [
				this.startColour[0] + this.startColourRandom[0] * this.RANDM1TO1(),
				this.startColour[1] + this.startColourRandom[1] * this.RANDM1TO1(),
				this.startColour[2] + this.startColourRandom[2] * this.RANDM1TO1(),
				this.startColour[3] + this.startColourRandom[3] * this.RANDM1TO1()
				];

			var end = [
				this.endColour[0] + this.endColourRandom[0] * this.RANDM1TO1(),
				this.endColour[1] + this.endColourRandom[1] * this.RANDM1TO1(),
				this.endColour[2] + this.endColourRandom[2] * this.RANDM1TO1(),
				this.endColour[3] + this.endColourRandom[3] * this.RANDM1TO1()
				];

			particle.colour = start;
			particle.deltaColour[0] = (end[0] - start[0]) / particle.timeToLive;
			particle.deltaColour[1] = (end[1] - start[1]) / particle.timeToLive;
			particle.deltaColour[2] = (end[2] - start[2]) / particle.timeToLive;
			particle.deltaColour[3] = (end[3] - start[3]) / particle.timeToLive;
		},
		update: function () {
			if (this.active && this.emissionRate > 0) {
				var rate = 1 / this.emissionRate;
				this.emitCounter++;
				while (this.particleCount < this.maxParticles && this.emitCounter > rate) {
					this.addParticle();
					this.emitCounter -= rate;
				}
				this.elapsedFrames++;
				if (this.duration != -1 && this.duration < this.elapsedFrames) {
					this.stop();
				}
			}

			this.particleIndex = 0;
			this.register = [];
			var draw;
			while (this.particleIndex < this.particleCount) {

				var currentParticle = this.particles[this.particleIndex];

				// If the current particle is alive then update it
				if (currentParticle.timeToLive > 0) {

					// Calculate the new direction based on gravity
					currentParticle.direction = this.vectorHelpers.add(currentParticle.direction, this.gravity);
					currentParticle.position = this.vectorHelpers.add(currentParticle.position, currentParticle.direction);
					currentParticle.position = this.vectorHelpers.add(currentParticle.position, this.viewportDelta);
					if (this.jitter) {
						currentParticle.position.x += this.jitter * this.RANDM1TO1();
						currentParticle.position.y += this.jitter * this.RANDM1TO1();
					}
					currentParticle.timeToLive--;

					// Update colours
					var r = currentParticle.colour[0] += currentParticle.deltaColour[0];
					var g = currentParticle.colour[1] += currentParticle.deltaColour[1];
					var b = currentParticle.colour[2] += currentParticle.deltaColour[2];
					var a = currentParticle.colour[3] += currentParticle.deltaColour[3];

					// Calculate the rgba string to draw.
					draw = [];
					draw.push("rgba(" + (r > 255 ? 255 : r < 0 ? 0 : ~~r));
					draw.push(g > 255 ? 255 : g < 0 ? 0 : ~~g);
					draw.push(b > 255 ? 255 : b < 0 ? 0 : ~~b);
					draw.push((a > 1 ? 1 : a < 0 ? 0 : a.toFixed(2)) + ")");
					currentParticle.drawColour = draw.join(",");

					if (!this.fastMode) {
						draw[3] = "0)";
						currentParticle.drawColourEnd = draw.join(",");
					}

					this.particleIndex++;
				} else {
					// Replace particle with the last active
					if (this.particleIndex != this.particleCount - 1) {
						this.particles[this.particleIndex] = this.particles[this.particleCount - 1];
					}
					this.particleCount--;
				}
				var rect = {};
				rect._x = ~~currentParticle.position.x;
				rect._y = ~~currentParticle.position.y;
				rect._w = currentParticle.size;
				rect._h = currentParticle.size;

				this.register.push(rect);
			}
		},

		stop: function () {
			this.active = false;
			this.elapsedFrames = 0;
			this.emitCounter = 0;
		},

		render: function (context) {

			for (var i = 0, j = this.particleCount; i < j; i++) {
				var particle = this.particles[i];
				var size = particle.size;
				var halfSize = size >> 1;

				if (particle.position.x + size < 0
					|| particle.position.y + size < 0
					|| particle.position.x - size > Crafty.viewport.width
					|| particle.position.y - size > Crafty.viewport.height) {
					//Particle is outside
					continue;
				}
				var x = ~~particle.position.x;
				var y = ~~particle.position.y;

				if (this.fastMode) {
					context.fillStyle = particle.drawColour;
				} else {
					var radgrad = context.createRadialGradient(x + halfSize, y + halfSize, particle.sizeSmall, x + halfSize, y + halfSize, halfSize);
					radgrad.addColorStop(0, particle.drawColour);
					//0.9 to avoid visible boxing
					radgrad.addColorStop(0.9, particle.drawColourEnd);
					context.fillStyle = radgrad;
				}
				context.fillRect(x, y, size, size);
			}
		},
		particle: function (vectorHelpers) {
			this.position = vectorHelpers.create(0, 0);
			this.direction = vectorHelpers.create(0, 0);
			this.size = 0;
			this.sizeSmall = 0;
			this.timeToLive = 0;
			this.colour = [];
			this.drawColour = "";
			this.deltaColour = [];
			this.sharpness = 0;
		},
		vectorHelpers: {
			create: function (x, y) {
				return {
					"x": x,
					"y": y
				};
			},
			multiply: function (vector, scaleFactor) {
				vector.x *= scaleFactor;
				vector.y *= scaleFactor;
				return vector;
			},
			add: function (vector1, vector2) {
				vector1.x += vector2.x;
				vector1.y += vector2.y;
				return vector1;
			}
		}
	}
});<|endoftext|>


<commit_before>/*
 * L.CRS.Earth is the base class for all CRS representing Earth.
 */

L.CRS.Earth = L.extend({}, L.CRS, {
	wrapLng: [-180, 180],

	R: 6378137,

	// distane between two geographic points using Harvesine formula
	distance: function (latlng1, latlng2) {
		var rad = Math.PI / 180,
		    lat1 = latlng1.lat * rad,
		    lat2 = latlng2.lat * rad,
		    sin1 = Math.sin((lat2 - lat1) / 2),
		    sin2 = Math.sin((latlng2.lng - latlng1.lng) * rad / 2);

		var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);

		return this.R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
	}
});
<commit_msg>use simpler distance formula (precision is good enough)<commit_after>/*
 * L.CRS.Earth is the base class for all CRS representing Earth.
 */

L.CRS.Earth = L.extend({}, L.CRS, {
	wrapLng: [-180, 180],

	R: 6378137,

	// distane between two geographical points using spherical law of cosines approximation
	distance: function (latlng1, latlng2) {
		var rad = Math.PI / 180,
		    lat1 = latlng1.lat * rad,
		    lat2 = latlng2.lat * rad;

		return this.R * Math.acos(Math.sin(lat1) * Math.sin(lat2) +
				Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad));
	}
});
<|endoftext|>


<commit_before>var rescue = require('rescue')
var abend = require('abend')
var cadence = require('cadence')
var Chunk = require('prolific.chunk')

function Queue (stream, stderr) {
    this._buffers = []
    this._chunks = []
    this._chunkNumber = 1
    this._previousChecksum = 'aaaaaaaa'
    this._stream = stream
    this._terminated = false
    this._closed = false
    this._writing = false
    this._stderr = stderr
    this._chunks.push(new Chunk(0, new Buffer(''), 1))
}

Queue.prototype.write = function (buffer) {
    this._buffers.push(buffer)
    if (!this._writing) {
        this._writing = true
        this.flush(abend)
    }
}

Queue.prototype._chunkEntries = function () {
    var buffers = this._buffers
    if (buffers.length == 0) {
        return
    }
    this._buffers = []

    var buffer = Buffer.concat(buffers)
    this._chunks.push(new Chunk(this._chunkNumber++, buffer, buffer.length))
}

Queue.prototype._checkTerminated = function () {
    if (this._closed) {
        throw new Error('bigeasy.prolific.queue#closed')
    }
}

Queue.prototype.flush = cadence(function (async) {
    async([function () {
        var loop = async(function () {
            this._checkTerminated()
            if (this._chunks.length == 0) {
                this._chunkEntries()
                if (this._chunks.length == 0) {
                    this._writing = false
                    return [ loop.break ]
                }
            }
            var chunk = this._chunks[0]
            async(function () {
                this._checkTerminated()
                this._stream.write(chunk.header(this._previousChecksum), async())
            }, function () {
                this._checkTerminated()
    // TODO Wait for a response, let's get for reals here.
                this._stream.write(chunk.buffer, async())
            }, function () {
                this._checkTerminated()
                this._previousChecksum = chunk.checksum
                this._chunks.shift()
            })
        })()
    }, rescue(/^bigeasy.prolific.queue#closed$/, function () {
        this.exit(async())
    })])
})

Queue.prototype.close = function () {
    if (!this._closed) {
        this._closed = true
        this._stream.end()
    }
}

Queue.prototype.exit = function (callback) {
    this._writing = false

    this.close()

    this._chunkEntries()

    if (this._chunks.length == 0) {
        return
    }

    if (!this._terminated) {
        this._terminated = true
        this._chunks.unshift(new Chunk(0, new Buffer(''), this._chunks[0].number))
        this._previousChecksum = 'aaaaaaaa'
    }

    var buffers = []
    while (this._chunks.length) {
        var chunk = this._chunks.shift()
        buffers.push(chunk.header(this._previousChecksum), chunk.buffer)
        this._previousChecksum = chunk.checksum
    }

    this._stderr.write(Buffer.concat(buffers), callback)
}

module.exports = Queue
<commit_msg>No more callback to `Queue.exit`.<commit_after>var rescue = require('rescue')
var abend = require('abend')
var cadence = require('cadence')
var Chunk = require('prolific.chunk')

function Queue (stream, stderr) {
    this._buffers = []
    this._chunks = []
    this._chunkNumber = 1
    this._previousChecksum = 'aaaaaaaa'
    this._stream = stream
    this._terminated = false
    this._closed = false
    this._writing = false
    this._stderr = stderr
    this._chunks.push(new Chunk(0, new Buffer(''), 1))
}

Queue.prototype.write = function (buffer) {
    this._buffers.push(buffer)
    if (!this._writing) {
        this._writing = true
        this.flush(abend)
    }
}

Queue.prototype._chunkEntries = function () {
    var buffers = this._buffers
    if (buffers.length == 0) {
        return
    }
    this._buffers = []

    var buffer = Buffer.concat(buffers)
    this._chunks.push(new Chunk(this._chunkNumber++, buffer, buffer.length))
}

Queue.prototype._checkTerminated = function () {
    if (this._closed) {
        throw new Error('bigeasy.prolific.queue#closed')
    }
}

Queue.prototype.flush = cadence(function (async) {
    async([function () {
        var loop = async(function () {
            this._checkTerminated()
            if (this._chunks.length == 0) {
                this._chunkEntries()
                if (this._chunks.length == 0) {
                    this._writing = false
                    return [ loop.break ]
                }
            }
            var chunk = this._chunks[0]
            async(function () {
                this._checkTerminated()
                this._stream.write(chunk.header(this._previousChecksum), async())
            }, function () {
                this._checkTerminated()
    // TODO Wait for a response, let's get for reals here.
                this._stream.write(chunk.buffer, async())
            }, function () {
                this._checkTerminated()
                this._previousChecksum = chunk.checksum
                this._chunks.shift()
            })
        })()
    }, rescue(/^bigeasy.prolific.queue#closed$/, function () {
        this.exit(async())
    })])
})

Queue.prototype.close = function () {
    if (!this._closed) {
        this._closed = true
        this._stream.end()
    }
}

Queue.prototype.exit = function () {
    this._writing = false

    this.close()

    this._chunkEntries()

    if (this._chunks.length == 0) {
        return
    }

    if (!this._terminated) {
        this._terminated = true
        this._chunks.unshift(new Chunk(0, new Buffer(''), this._chunks[0].number))
        this._previousChecksum = 'aaaaaaaa'
    }

    var buffers = []
    while (this._chunks.length) {
        var chunk = this._chunks.shift()
        buffers.push(chunk.header(this._previousChecksum), chunk.buffer)
        this._previousChecksum = chunk.checksum
    }

    this._stderr.write(Buffer.concat(buffers))
}

module.exports = Queue
<|endoftext|>


<commit_before>//const Discord = require('discord.js');

module.exports = (client) => {
  client.log('Log', `Entrou como ${client.user.tag}!`);
  client.user.setPresence({ game: { name: 'Digite !ajuda', type: 0 } });
  message.channel.send('O BoT está Online!');

  /*let channel = client.channels.get('372892589252411393');
  //ou via nome
  //channel = client.channels.find('name', 'Musica');

  channel.join()
  .then(connection => console.log('Conectado'))
  .catch(console.error);*/

  /*voiceChannel.join()
  .then(connection => console.log('Conectado!'))
  .catch(console.error);*/

};
<commit_msg><commit_after>//const Discord = require('discord.js');

module.exports = (client) => {
  client.log('Log', `Entrou como ${client.user.tag}!`);
  client.user.setPresence({ game: { name: 'Digite !ajuda', type: 0 } });
  client.channel.send('O BoT está Online!');

  /*let channel = client.channels.get('372892589252411393');
  //ou via nome
  //channel = client.channels.find('name', 'Musica');

  channel.join()
  .then(connection => console.log('Conectado'))
  .catch(console.error);*/

  /*voiceChannel.join()
  .then(connection => console.log('Conectado!'))
  .catch(console.error);*/

};
<|endoftext|>


<commit_before>var Crafty = require('./core.js'),
    document = window.document;

/**@
 * #Particles
 * @category Graphics
 * Based on Parcycle by Mr. Speaker, licensed under the MIT, Ported by Leo Koppelkamm
 * **This is canvas only & won't do anything if the browser doesn't support it!**
 * To see how this works take a look in https://github.com/craftyjs/Crafty/blob/master/src/particles.js
 */
Crafty.c("Particles", {
    init: function () {
        //We need to clone it
        this._Particles = Crafty.clone(this._Particles);
    },

    /**@
     * #.particles
     * @comp Particles
     * @sign public this .particles(Object options)
     * @param options - Map of options that specify the behavior and look of the particles.
     *
     * @example
     * ~~~
     * var options = {
     *	maxParticles: 150,
     *	size: 18,
     *	sizeRandom: 4,
     *	speed: 1,
     *	speedRandom: 1.2,
     *	// Lifespan in frames
     *	lifeSpan: 29,
     *	lifeSpanRandom: 7,
     *	// Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
     *	angle: 65,
     *	angleRandom: 34,
     *	startColour: [255, 131, 0, 1],
     *	startColourRandom: [48, 50, 45, 0],
     *	endColour: [245, 35, 0, 0],
     *	endColourRandom: [60, 60, 60, 0],
     *	// Only applies when fastMode is off, specifies how sharp the gradients are drawn
     *	sharpness: 20,
     *	sharpnessRandom: 10,
     *	// Random spread from origin
     *	spread: 10,
     *	// How many frames should this last
     *	duration: -1,
     *	// Will draw squares instead of circle gradients
     *	fastMode: false,
     *	gravity: { x: 0, y: 0.1 },
     *	// sensible values are 0-3
     *	jitter: 0
     * }
     *
     * Crafty.e("2D,Canvas,Particles").particles(options);
     * ~~~
     */
    particles: function (options) {

        if (!Crafty.support.canvas || Crafty.deactivateParticles) return this;

        //If we drew on the main canvas, we'd have to redraw
        //potentially huge sections of the screen every frame
        //So we create a separate canvas, where we only have to redraw
        //the changed particles.
        var c, ctx, relativeX, relativeY, bounding;

        c = document.createElement("canvas");
        c.width = Crafty.viewport.width;
        c.height = Crafty.viewport.height;
        c.style.position = 'absolute';
        c.style.left = "0px";
        c.style.top = "0px";

        Crafty.stage.elem.appendChild(c);

        ctx = c.getContext('2d');

        this._Particles.init(options);

        // Clean up the DOM when this component is removed
        this.bind('Remove', function () {
            Crafty.stage.elem.removeChild(c);
        }).bind("RemoveComponent", function (id) {
            if (id === "particles")
                Crafty.stage.elem.removeChild(c);
        });

        relativeX = this.x + Crafty.viewport.x;
        relativeY = this.y + Crafty.viewport.y;
        this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

        var oldViewport = {
            x: Crafty.viewport.x,
            y: Crafty.viewport.y
        };

        this.bind('EnterFrame', function () {
            relativeX = this.x + Crafty.viewport.x;
            relativeY = this.y + Crafty.viewport.y;
            this._Particles.viewportDelta = {
                x: Crafty.viewport.x - oldViewport.x,
                y: Crafty.viewport.y - oldViewport.y
            };

            oldViewport = {
                x: Crafty.viewport.x,
                y: Crafty.viewport.y
            };

            this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

            //Selective clearing
            if (typeof Crafty.DrawManager.boundingRect == 'function') {
                bounding = Crafty.DrawManager.boundingRect(this._Particles.register);
                if (bounding) ctx.clearRect(bounding._x, bounding._y, bounding._w, bounding._h);
            } else {
                ctx.clearRect(0, 0, Crafty.viewport.width, Crafty.viewport.height);
            }

            //This updates all particle colors & positions
            this._Particles.update();

            //This renders the updated particles
            this._Particles.render(ctx);
        });
        return this;
    },
    _Particles: {
        presets: {
            maxParticles: 150,
            size: 18,
            sizeRandom: 4,
            speed: 1,
            speedRandom: 1.2,
            // Lifespan in frames
            lifeSpan: 29,
            lifeSpanRandom: 7,
            // Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
            angle: 65,
            angleRandom: 34,
            startColour: [255, 131, 0, 1],
            startColourRandom: [48, 50, 45, 0],
            endColour: [245, 35, 0, 0],
            endColourRandom: [60, 60, 60, 0],
            // Only applies when fastMode is off, specifies how sharp the gradients are drawn
            sharpness: 20,
            sharpnessRandom: 10,
            // Random spread from origin
            spread: 10,
            // How many frames should this last
            duration: -1,
            // Will draw squares instead of circle gradients
            fastMode: false,
            gravity: {
                x: 0,
                y: 0.1
            },
            // sensible values are 0-3
            jitter: 0,

            //Don't modify the following
            particles: [],
            active: true,
            particleCount: 0,
            elapsedFrames: 0,
            emissionRate: 0,
            emitCounter: 0,
            particleIndex: 0
        },

        init: function (options) {
            this.position = this.vectorHelpers.create(0, 0);
            if (typeof options == 'undefined') options = {};

            //Create current config by merging given options and presets.
            for (var key in this.presets) {
                if (typeof options[key] != 'undefined') this[key] = options[key];
                else this[key] = this.presets[key];
            }

            this.emissionRate = this.maxParticles / this.lifeSpan;
            this.positionRandom = this.vectorHelpers.create(this.spread, this.spread);
        },

        addParticle: function () {
            if (this.particleCount == this.maxParticles) {
                return false;
            }

            // Take the next particle out of the particle pool we have created and initialize it
            var particle = new this.particle(this.vectorHelpers);
            this.initParticle(particle);
            this.particles[this.particleCount] = particle;
            // Increment the particle count
            this.particleCount++;

            return true;
        },
        RANDM1TO1: function () {
            return Math.random() * 2 - 1;
        },
        initParticle: function (particle) {
            particle.position.x = this.position.x + this.positionRandom.x * this.RANDM1TO1();
            particle.position.y = this.position.y + this.positionRandom.y * this.RANDM1TO1();

            var newAngle = (this.angle + this.angleRandom * this.RANDM1TO1()) * (Math.PI / 180); // convert to radians
            var vector = this.vectorHelpers.create(Math.sin(newAngle), -Math.cos(newAngle)); // Could move to lookup for speed
            var vectorSpeed = this.speed + this.speedRandom * this.RANDM1TO1();
            particle.direction = this.vectorHelpers.multiply(vector, vectorSpeed);

            particle.size = this.size + this.sizeRandom * this.RANDM1TO1();
            particle.size = particle.size < 0 ? 0 : ~~particle.size;
            particle.timeToLive = this.lifeSpan + this.lifeSpanRandom * this.RANDM1TO1();

            particle.sharpness = this.sharpness + this.sharpnessRandom * this.RANDM1TO1();
            particle.sharpness = particle.sharpness > 100 ? 100 : particle.sharpness < 0 ? 0 : particle.sharpness;
            // internal circle gradient size - affects the sharpness of the radial gradient
            particle.sizeSmall = ~~ ((particle.size / 200) * particle.sharpness); //(size/2/100)
            var start = [
                this.startColour[0] + this.startColourRandom[0] * this.RANDM1TO1(),
                this.startColour[1] + this.startColourRandom[1] * this.RANDM1TO1(),
                this.startColour[2] + this.startColourRandom[2] * this.RANDM1TO1(),
                this.startColour[3] + this.startColourRandom[3] * this.RANDM1TO1()
            ];

            var end = [
                this.endColour[0] + this.endColourRandom[0] * this.RANDM1TO1(),
                this.endColour[1] + this.endColourRandom[1] * this.RANDM1TO1(),
                this.endColour[2] + this.endColourRandom[2] * this.RANDM1TO1(),
                this.endColour[3] + this.endColourRandom[3] * this.RANDM1TO1()
            ];

            particle.colour = start;
            particle.deltaColour[0] = (end[0] - start[0]) / particle.timeToLive;
            particle.deltaColour[1] = (end[1] - start[1]) / particle.timeToLive;
            particle.deltaColour[2] = (end[2] - start[2]) / particle.timeToLive;
            particle.deltaColour[3] = (end[3] - start[3]) / particle.timeToLive;
        },
        update: function () {
            if (this.active && this.emissionRate > 0) {
                var rate = 1 / this.emissionRate;
                this.emitCounter++;
                while (this.particleCount < this.maxParticles && this.emitCounter > rate) {
                    this.addParticle();
                    this.emitCounter -= rate;
                }
                this.elapsedFrames++;
                if (this.duration != -1 && this.duration < this.elapsedFrames) {
                    this.stop();
                }
            }

            this.particleIndex = 0;
            this.register = [];
            var draw;
            while (this.particleIndex < this.particleCount) {

                var currentParticle = this.particles[this.particleIndex];

                // If the current particle is alive then update it
                if (currentParticle.timeToLive > 0) {

                    // Calculate the new direction based on gravity
                    currentParticle.direction = this.vectorHelpers.add(currentParticle.direction, this.gravity);
                    currentParticle.position = this.vectorHelpers.add(currentParticle.position, currentParticle.direction);
                    currentParticle.position = this.vectorHelpers.add(currentParticle.position, this.viewportDelta);
                    if (this.jitter) {
                        currentParticle.position.x += this.jitter * this.RANDM1TO1();
                        currentParticle.position.y += this.jitter * this.RANDM1TO1();
                    }
                    currentParticle.timeToLive--;

                    // Update colours
                    var r = currentParticle.colour[0] += currentParticle.deltaColour[0];
                    var g = currentParticle.colour[1] += currentParticle.deltaColour[1];
                    var b = currentParticle.colour[2] += currentParticle.deltaColour[2];
                    var a = currentParticle.colour[3] += currentParticle.deltaColour[3];

                    // Calculate the rgba string to draw.
                    draw = [];
                    draw.push("rgba(" + (r > 255 ? 255 : r < 0 ? 0 : ~~r));
                    draw.push(g > 255 ? 255 : g < 0 ? 0 : ~~g);
                    draw.push(b > 255 ? 255 : b < 0 ? 0 : ~~b);
                    draw.push((a > 1 ? 1 : a < 0 ? 0 : a.toFixed(2)) + ")");
                    currentParticle.drawColour = draw.join(",");

                    if (!this.fastMode) {
                        draw[3] = "0)";
                        currentParticle.drawColourEnd = draw.join(",");
                    }

                    this.particleIndex++;
                } else {
                    // Replace particle with the last active
                    if (this.particleIndex != this.particleCount - 1) {
                        this.particles[this.particleIndex] = this.particles[this.particleCount - 1];
                    }
                    this.particleCount--;
                }
                var rect = {};
                rect._x = ~~currentParticle.position.x;
                rect._y = ~~currentParticle.position.y;
                rect._w = currentParticle.size;
                rect._h = currentParticle.size;

                this.register.push(rect);
            }
        },

        stop: function () {
            this.active = false;
            this.elapsedFrames = 0;
            this.emitCounter = 0;
        },

        render: function (context) {

            for (var i = 0, j = this.particleCount; i < j; i++) {
                var particle = this.particles[i];
                var size = particle.size;
                var halfSize = size >> 1;

                if (particle.position.x + size < 0 || particle.position.y + size < 0 || particle.position.x - size > Crafty.viewport.width || particle.position.y - size > Crafty.viewport.height) {
                    //Particle is outside
                    continue;
                }
                var x = ~~particle.position.x;
                var y = ~~particle.position.y;

                if (this.fastMode) {
                    context.fillStyle = particle.drawColour;
                } else {
                    var radgrad = context.createRadialGradient(x + halfSize, y + halfSize, particle.sizeSmall, x + halfSize, y + halfSize, halfSize);
                    radgrad.addColorStop(0, particle.drawColour);
                    //0.9 to avoid visible boxing
                    radgrad.addColorStop(0.9, particle.drawColourEnd);
                    context.fillStyle = radgrad;
                }
                context.fillRect(x, y, size, size);
            }
        },
        particle: function (vectorHelpers) {
            this.position = vectorHelpers.create(0, 0);
            this.direction = vectorHelpers.create(0, 0);
            this.size = 0;
            this.sizeSmall = 0;
            this.timeToLive = 0;
            this.colour = [];
            this.drawColour = "";
            this.deltaColour = [];
            this.sharpness = 0;
        },
        vectorHelpers: {
            create: function (x, y) {
                return {
                    "x": x,
                    "y": y
                };
            },
            multiply: function (vector, scaleFactor) {
                vector.x *= scaleFactor;
                vector.y *= scaleFactor;
                return vector;
            },
            add: function (vector1, vector2) {
                vector1.x += vector2.x;
                vector1.y += vector2.y;
                return vector1;
            }
        }
    }
});<commit_msg>Trigger event when particle animation finishes<commit_after>var Crafty = require('./core.js'),
    document = window.document;

/**@
 * #Particles
 * @category Graphics
 * @trigger ParticleEnd - when the particle animation has finished
 * Based on Parcycle by Mr. Speaker, licensed under the MIT, Ported by Leo Koppelkamm
 * **This is canvas only & won't do anything if the browser doesn't support it!**
 * To see how this works take a look in https://github.com/craftyjs/Crafty/blob/master/src/particles.js
 */
Crafty.c("Particles", {
    init: function () {
        //We need to clone it
        this._Particles = Crafty.clone(this._Particles);
        this._Particles.parentEntity = this;
    },

    /**@
     * #.particles
     * @comp Particles
     * @sign public this .particles(Object options)
     * @param options - Map of options that specify the behavior and look of the particles.
     *
     * @example
     * ~~~
     * var options = {
     *	maxParticles: 150,
     *	size: 18,
     *	sizeRandom: 4,
     *	speed: 1,
     *	speedRandom: 1.2,
     *	// Lifespan in frames
     *	lifeSpan: 29,
     *	lifeSpanRandom: 7,
     *	// Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
     *	angle: 65,
     *	angleRandom: 34,
     *	startColour: [255, 131, 0, 1],
     *	startColourRandom: [48, 50, 45, 0],
     *	endColour: [245, 35, 0, 0],
     *	endColourRandom: [60, 60, 60, 0],
     *	// Only applies when fastMode is off, specifies how sharp the gradients are drawn
     *	sharpness: 20,
     *	sharpnessRandom: 10,
     *	// Random spread from origin
     *	spread: 10,
     *	// How many frames should this last
     *	duration: -1,
     *	// Will draw squares instead of circle gradients
     *	fastMode: false,
     *	gravity: { x: 0, y: 0.1 },
     *	// sensible values are 0-3
     *	jitter: 0
     * }
     *
     * Crafty.e("2D,Canvas,Particles").particles(options);
     * ~~~
     */
    particles: function (options) {

        if (!Crafty.support.canvas || Crafty.deactivateParticles) return this;

        //If we drew on the main canvas, we'd have to redraw
        //potentially huge sections of the screen every frame
        //So we create a separate canvas, where we only have to redraw
        //the changed particles.
        var c, ctx, relativeX, relativeY, bounding;

        c = document.createElement("canvas");
        c.width = Crafty.viewport.width;
        c.height = Crafty.viewport.height;
        c.style.position = 'absolute';
        c.style.left = "0px";
        c.style.top = "0px";

        Crafty.stage.elem.appendChild(c);

        ctx = c.getContext('2d');

        this._Particles.init(options);

        // Clean up the DOM when this component is removed
        this.bind('Remove', function () {
            Crafty.stage.elem.removeChild(c);
        }).bind("RemoveComponent", function (id) {
            if (id === "particles")
                Crafty.stage.elem.removeChild(c);
        });

        relativeX = this.x + Crafty.viewport.x;
        relativeY = this.y + Crafty.viewport.y;
        this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

        var oldViewport = {
            x: Crafty.viewport.x,
            y: Crafty.viewport.y
        };

        this.bind('EnterFrame', function () {
            relativeX = this.x + Crafty.viewport.x;
            relativeY = this.y + Crafty.viewport.y;
            this._Particles.viewportDelta = {
                x: Crafty.viewport.x - oldViewport.x,
                y: Crafty.viewport.y - oldViewport.y
            };

            oldViewport = {
                x: Crafty.viewport.x,
                y: Crafty.viewport.y
            };

            this._Particles.position = this._Particles.vectorHelpers.create(relativeX, relativeY);

            //Selective clearing
            if (typeof Crafty.DrawManager.boundingRect == 'function') {
                bounding = Crafty.DrawManager.boundingRect(this._Particles.register);
                if (bounding) ctx.clearRect(bounding._x, bounding._y, bounding._w, bounding._h);
            } else {
                ctx.clearRect(0, 0, Crafty.viewport.width, Crafty.viewport.height);
            }

            //This updates all particle colors & positions
            this._Particles.update();

            //This renders the updated particles
            this._Particles.render(ctx);
        });
        return this;
    },
    _Particles: {
        presets: {
            maxParticles: 150,
            size: 18,
            sizeRandom: 4,
            speed: 1,
            speedRandom: 1.2,
            // Lifespan in frames
            lifeSpan: 29,
            lifeSpanRandom: 7,
            // Angle is calculated clockwise: 12pm is 0deg, 3pm is 90deg etc.
            angle: 65,
            angleRandom: 34,
            startColour: [255, 131, 0, 1],
            startColourRandom: [48, 50, 45, 0],
            endColour: [245, 35, 0, 0],
            endColourRandom: [60, 60, 60, 0],
            // Only applies when fastMode is off, specifies how sharp the gradients are drawn
            sharpness: 20,
            sharpnessRandom: 10,
            // Random spread from origin
            spread: 10,
            // How many frames should this last
            duration: -1,
            // Will draw squares instead of circle gradients
            fastMode: false,
            gravity: {
                x: 0,
                y: 0.1
            },
            // sensible values are 0-3
            jitter: 0,

            //Don't modify the following
            particles: [],
            active: true,
            particleCount: 0,
            elapsedFrames: 0,
            emissionRate: 0,
            emitCounter: 0,
            particleIndex: 0
        },

        init: function (options) {
            this.position = this.vectorHelpers.create(0, 0);
            if (typeof options == 'undefined') options = {};

            //Create current config by merging given options and presets.
            for (var key in this.presets) {
                if (typeof options[key] != 'undefined') this[key] = options[key];
                else this[key] = this.presets[key];
            }

            this.emissionRate = this.maxParticles / this.lifeSpan;
            this.positionRandom = this.vectorHelpers.create(this.spread, this.spread);
        },

        addParticle: function () {
            if (this.particleCount == this.maxParticles) {
                return false;
            }

            // Take the next particle out of the particle pool we have created and initialize it
            var particle = new this.particle(this.vectorHelpers);
            this.initParticle(particle);
            this.particles[this.particleCount] = particle;
            // Increment the particle count
            this.particleCount++;

            return true;
        },
        RANDM1TO1: function () {
            return Math.random() * 2 - 1;
        },
        initParticle: function (particle) {
            particle.position.x = this.position.x + this.positionRandom.x * this.RANDM1TO1();
            particle.position.y = this.position.y + this.positionRandom.y * this.RANDM1TO1();

            var newAngle = (this.angle + this.angleRandom * this.RANDM1TO1()) * (Math.PI / 180); // convert to radians
            var vector = this.vectorHelpers.create(Math.sin(newAngle), -Math.cos(newAngle)); // Could move to lookup for speed
            var vectorSpeed = this.speed + this.speedRandom * this.RANDM1TO1();
            particle.direction = this.vectorHelpers.multiply(vector, vectorSpeed);

            particle.size = this.size + this.sizeRandom * this.RANDM1TO1();
            particle.size = particle.size < 0 ? 0 : ~~particle.size;
            particle.timeToLive = this.lifeSpan + this.lifeSpanRandom * this.RANDM1TO1();

            particle.sharpness = this.sharpness + this.sharpnessRandom * this.RANDM1TO1();
            particle.sharpness = particle.sharpness > 100 ? 100 : particle.sharpness < 0 ? 0 : particle.sharpness;
            // internal circle gradient size - affects the sharpness of the radial gradient
            particle.sizeSmall = ~~ ((particle.size / 200) * particle.sharpness); //(size/2/100)
            var start = [
                this.startColour[0] + this.startColourRandom[0] * this.RANDM1TO1(),
                this.startColour[1] + this.startColourRandom[1] * this.RANDM1TO1(),
                this.startColour[2] + this.startColourRandom[2] * this.RANDM1TO1(),
                this.startColour[3] + this.startColourRandom[3] * this.RANDM1TO1()
            ];

            var end = [
                this.endColour[0] + this.endColourRandom[0] * this.RANDM1TO1(),
                this.endColour[1] + this.endColourRandom[1] * this.RANDM1TO1(),
                this.endColour[2] + this.endColourRandom[2] * this.RANDM1TO1(),
                this.endColour[3] + this.endColourRandom[3] * this.RANDM1TO1()
            ];

            particle.colour = start;
            particle.deltaColour[0] = (end[0] - start[0]) / particle.timeToLive;
            particle.deltaColour[1] = (end[1] - start[1]) / particle.timeToLive;
            particle.deltaColour[2] = (end[2] - start[2]) / particle.timeToLive;
            particle.deltaColour[3] = (end[3] - start[3]) / particle.timeToLive;
        },
        update: function () {
            if (this.active && this.emissionRate > 0) {
                var rate = 1 / this.emissionRate;
                this.emitCounter++;
                while (this.particleCount < this.maxParticles && this.emitCounter > rate) {
                    this.addParticle();
                    this.emitCounter -= rate;
                }
                this.elapsedFrames++;
                if (this.duration != -1 && this.duration < this.elapsedFrames) {
                    this.stop();
                }
            }

            this.particleIndex = 0;
            this.register = [];
            var draw;
            while (this.particleIndex < this.particleCount) {

                var currentParticle = this.particles[this.particleIndex];

                // If the current particle is alive then update it
                if (currentParticle.timeToLive > 0) {

                    // Calculate the new direction based on gravity
                    currentParticle.direction = this.vectorHelpers.add(currentParticle.direction, this.gravity);
                    currentParticle.position = this.vectorHelpers.add(currentParticle.position, currentParticle.direction);
                    currentParticle.position = this.vectorHelpers.add(currentParticle.position, this.viewportDelta);
                    if (this.jitter) {
                        currentParticle.position.x += this.jitter * this.RANDM1TO1();
                        currentParticle.position.y += this.jitter * this.RANDM1TO1();
                    }
                    currentParticle.timeToLive--;

                    // Update colours
                    var r = currentParticle.colour[0] += currentParticle.deltaColour[0];
                    var g = currentParticle.colour[1] += currentParticle.deltaColour[1];
                    var b = currentParticle.colour[2] += currentParticle.deltaColour[2];
                    var a = currentParticle.colour[3] += currentParticle.deltaColour[3];

                    // Calculate the rgba string to draw.
                    draw = [];
                    draw.push("rgba(" + (r > 255 ? 255 : r < 0 ? 0 : ~~r));
                    draw.push(g > 255 ? 255 : g < 0 ? 0 : ~~g);
                    draw.push(b > 255 ? 255 : b < 0 ? 0 : ~~b);
                    draw.push((a > 1 ? 1 : a < 0 ? 0 : a.toFixed(2)) + ")");
                    currentParticle.drawColour = draw.join(",");

                    if (!this.fastMode) {
                        draw[3] = "0)";
                        currentParticle.drawColourEnd = draw.join(",");
                    }

                    this.particleIndex++;
                } else {
                    // Replace particle with the last active
                    if (this.particleIndex != this.particleCount - 1) {
                        this.particles[this.particleIndex] = this.particles[this.particleCount - 1];
                    }
                    this.particleCount--;
                }
                var rect = {};
                rect._x = ~~currentParticle.position.x;
                rect._y = ~~currentParticle.position.y;
                rect._w = currentParticle.size;
                rect._h = currentParticle.size;

                this.register.push(rect);
            }
        },

        stop: function () {
            this.active = false;
            this.elapsedFrames = 0;
            this.emitCounter = 0;
            this.parentEntity.trigger("ParticleEnd");
        },

        render: function (context) {

            for (var i = 0, j = this.particleCount; i < j; i++) {
                var particle = this.particles[i];
                var size = particle.size;
                var halfSize = size >> 1;

                if (particle.position.x + size < 0 || particle.position.y + size < 0 || particle.position.x - size > Crafty.viewport.width || particle.position.y - size > Crafty.viewport.height) {
                    //Particle is outside
                    continue;
                }
                var x = ~~particle.position.x;
                var y = ~~particle.position.y;

                if (this.fastMode) {
                    context.fillStyle = particle.drawColour;
                } else {
                    var radgrad = context.createRadialGradient(x + halfSize, y + halfSize, particle.sizeSmall, x + halfSize, y + halfSize, halfSize);
                    radgrad.addColorStop(0, particle.drawColour);
                    //0.9 to avoid visible boxing
                    radgrad.addColorStop(0.9, particle.drawColourEnd);
                    context.fillStyle = radgrad;
                }
                context.fillRect(x, y, size, size);
            }
        },
        particle: function (vectorHelpers) {
            this.position = vectorHelpers.create(0, 0);
            this.direction = vectorHelpers.create(0, 0);
            this.size = 0;
            this.sizeSmall = 0;
            this.timeToLive = 0;
            this.colour = [];
            this.drawColour = "";
            this.deltaColour = [];
            this.sharpness = 0;
        },
        vectorHelpers: {
            create: function (x, y) {
                return {
                    "x": x,
                    "y": y
                };
            },
            multiply: function (vector, scaleFactor) {
                vector.x *= scaleFactor;
                vector.y *= scaleFactor;
                return vector;
            },
            add: function (vector1, vector2) {
                vector1.x += vector2.x;
                vector1.y += vector2.y;
                return vector1;
            }
        }
    }
});
<|endoftext|>


<commit_before>define([
    "jquery",
    "pat-parser",
    "pat-registry",
    "pat-utils",
    "pat-inject"
], function($, Parser, registry, utils, inject) {
    var parser = new Parser("modal");

    parser.add_argument("class");

    var modal = {
        name: "modal",
        jquery_plugin: true,
        // div's are turned into modals
        // links and forms inject modals
        trigger: "div.pat-modal, a.pat-modal, form.pat-modal",
        init: function($el, opts) {
            this.$el = $el;
            return $el.each(function() {
                var $el = $(this),
                    cfg = parser.parse($el, opts);

                if ($el.is("div"))
                    modal._init_div1($el, cfg);
                else
                    modal._init_inject1($el, cfg);
            });
        },

        _init_inject1: function($el, cfg) {
            var opts = {
                target: "#pat-modal",
                "class": "pat-modal" + (cfg["class"] ? " " + cfg["class"] : "")
            };
            // if $el is already inside a modal, do not detach #pat-modal,
            // because this would unnecessarily close the modal itself
            if (!$el.closest("#pat-modal")) {
                $("#pat-modal").detach();
            }
            inject.init($el, opts);
        },

        _init_div1: function($el) {
            var $header = $("
"),
                activeElement = document.activeElement;

            $("<button type='button' class='close-panel'>Close</button>").appendTo($header);

            // We cannot handle text nodes here
            $el.children(":last, :not(:first)")
                .wrapAll("
");
            $(".panel-content", $el).before($header);
            $el.children(":first:not(.header)").prependTo($header);

            // Restore focus in case the active element was a child of $el and
            // the focus was lost during the wrapping.
            activeElement.focus();

            // event handlers remove modal - first arg to bind is ``this``
            $(document).on("click.pat-modal", ".close-panel",
                           modal.destroy.bind($el, $el));
            // remove on ESC
            $(document).on("keyup.pat-modal",
                           modal.destroy.bind($el, $el));

            modal.setPosition();
        },

        setPosition: function() {
            if ((this.$el === undefined) || (this.$el.length === 0)) {
                return;
            }

            var $oldClone = $("#pat-modal-clone");
            if ($oldClone.length > 0) {
                $oldClone.remove();
            }

            var $clone = this.$el.clone();

            $clone
                .attr("id", "pat-modal-clone")
                .css({
                    "visibility": "hidden",
                    "position": "absolute",
                    "height": ""
                }).appendTo("body");

            // wait for browser to update DOM
            setTimeout($.proxy(modal.measure, this), 0);
        },

        measure: function() {
            var $clone = $("#pat-modal-clone");
            if ($clone.length === 0) {
                return;
            }

            var maxHeight = $(window).innerHeight() - $clone.outerHeight(true) +
                            $clone.outerHeight(),
                height = $clone.outerHeight();

            $clone.remove();

            if (maxHeight - height < 0) {
                this.$el.addClass("max-height").css("height", maxHeight);
            } else {
                this.$el.removeClass("max-height").css("height", "");
            }

            var top = ($(window).innerHeight() - this.$el.outerHeight(true)) / 2;
            this.$el.css("top", top);
        },

        destroy: function($el, ev) {
            if (ev && ev.type === "keyup" && ev.which !== 27)
                return;
            $(document).off(".pat-modal");
            $el.remove();
        }
    };

    $(window).on("resize.pat-modal-position", $.proxy(modal.setPosition, modal));
    $(window).on("pat-inject-content-loaded.pat-modal-position", "#pat-modal",
        $.proxy(modal.setPosition, modal));
    $(document).on("patterns-injected.pat-modal-position", "#pat-modal,div.pat-modal",
        $.proxy(modal.setPosition, modal));

    registry.register(modal);
    return modal;
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<commit_msg>Enfore a one-to-one mapping between modal objs and DOM elements.<commit_after>define([
    "jquery",
    "pat-parser",
    "pat-registry",
    "pat-utils",
    "pat-inject"
], function($, Parser, registry, utils, inject) {
    var parser = new Parser("modal");

    parser.add_argument("class");

    var modal = {
        name: "modal",
        jquery_plugin: true,
        // div's are turned into modals
        // links and forms inject modals
        trigger: "div.pat-modal, a.pat-modal, form.pat-modal",
        init: function($el, opts) {
            if ($el.length > 1) {
                // We enforce a one-to-one mapping between modal objects and
                // DOM elements, so here we recurse and instantiate a new modal
                // for each $el
                $el.each(function() {
                    $(this).modal();
                });
            } else if ($el.length === 1) {
                var cfg = parser.parse($el, opts);
                this.$el = $el;
                if ($el.is("div"))
                    modal._init_div1($el, cfg);
                else
                    modal._init_inject1($el, cfg);
            }
        },

        _init_inject1: function($el, cfg) {
            var opts = {
                target: "#pat-modal",
                "class": "pat-modal" + (cfg["class"] ? " " + cfg["class"] : "")
            };
            // if $el is already inside a modal, do not detach #pat-modal,
            // because this would unnecessarily close the modal itself
            if (!$el.closest("#pat-modal")) {
                $("#pat-modal").detach();
            }
            inject.init($el, opts);
        },

        _init_div1: function($el) {
            var $header = $("
"),
                activeElement = document.activeElement;

            $("<button type='button' class='close-panel'>Close</button>").appendTo($header);

            // We cannot handle text nodes here
            $el.children(":last, :not(:first)")
                .wrapAll("
");
            $(".panel-content", $el).before($header);
            $el.children(":first:not(.header)").prependTo($header);

            // Restore focus in case the active element was a child of $el and
            // the focus was lost during the wrapping.
            activeElement.focus();

            // event handlers remove modal - first arg to bind is ``this``
            $(document).on("click.pat-modal", ".close-panel",
                           modal.destroy.bind($el, $el));
            // remove on ESC
            $(document).on("keyup.pat-modal",
                           modal.destroy.bind($el, $el));

            modal.setPosition();
        },

        setPosition: function() {
            if ((this.$el === undefined) || (this.$el.length === 0)) {
                return;
            }

            var $oldClone = $("#pat-modal-clone");
            if ($oldClone.length > 0) {
                $oldClone.remove();
            }

            var $clone = this.$el.clone();

            $clone
                .attr("id", "pat-modal-clone")
                .css({
                    "visibility": "hidden",
                    "position": "absolute",
                    "height": ""
                }).appendTo("body");

            // wait for browser to update DOM
            setTimeout($.proxy(modal.measure, this), 0);
        },

        measure: function() {
            var $clone = $("#pat-modal-clone");
            if ($clone.length === 0) {
                return;
            }

            var maxHeight = $(window).innerHeight() - $clone.outerHeight(true) +
                            $clone.outerHeight(),
                height = $clone.outerHeight();

            $clone.remove();

            if (maxHeight - height < 0) {
                this.$el.addClass("max-height").css("height", maxHeight);
            } else {
                this.$el.removeClass("max-height").css("height", "");
            }

            var top = ($(window).innerHeight() - this.$el.outerHeight(true)) / 2;
            this.$el.css("top", top);
        },

        destroy: function($el, ev) {
            if (ev && ev.type === "keyup" && ev.which !== 27)
                return;
            $(document).off(".pat-modal");
            $el.remove();
        }
    };

    $(window).on("resize.pat-modal-position", $.proxy(modal.setPosition, modal));
    $(window).on("pat-inject-content-loaded.pat-modal-position", "#pat-modal",
        $.proxy(modal.setPosition, modal));
    $(document).on("patterns-injected.pat-modal-position", "#pat-modal,div.pat-modal",
        $.proxy(modal.setPosition, modal));

    registry.register(modal);
    return modal;
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<|endoftext|>