<commit_before>/**
 * This module adds GDPR consentManagement support to prebid.js.  It interacts with
 * supported CMPs (Consent Management Platforms) to grab the user's consent information
 * and make it available for any GDPR supported adapters to read/pass this information to
 * their system.
 */
import * as utils from 'src/utils';
import { config } from 'src/config';
import { gdprDataHandler } from 'src/adaptermanager';
import includes from 'core-js/library/fn/array/includes';
import strIncludes from 'core-js/library/fn/string/includes';

const DEFAULT_CMP = 'iab';
const DEFAULT_CONSENT_TIMEOUT = 10000;
const DEFAULT_ALLOW_AUCTION_WO_CONSENT = true;

export let userCMP;
export let consentTimeout;
export let allowAuction;
export let staticConsentData;

let consentData;

// add new CMPs here, with their dedicated lookup function
const cmpCallMap = {
  'iab': lookupIabConsent,
  'static': lookupStaticConsentData
};

/**
 * This function reads the consent string from the config to obtain the consent information of the user.
 * @param {function(string)} cmpSuccess acts as a success callback when the value is read from config; pass along consentObject (string) from CMP
 * @param {function(string)} cmpError acts as an error callback while interacting with the config string; pass along an error message (string)
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 */
function lookupStaticConsentData(cmpSuccess, cmpError, hookConfig) {
  cmpSuccess(staticConsentData, hookConfig);
}

/**
 * This function handles interacting with an IAB compliant CMP to obtain the consent information of the user.
 * Given the async nature of the CMP's API, we pass in acting success/error callback functions to exit this function
 * based on the appropriate result.
 * @param {function(string)} cmpSuccess acts as a success callback when CMP returns a value; pass along consentObject (string) from CMP
 * @param {function(string)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string)
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 */
function lookupIabConsent(cmpSuccess, cmpError, hookConfig) {
  function handleCmpResponseCallbacks() {
    const cmpResponse = {};

    function afterEach() {
      if (cmpResponse.getConsentData && cmpResponse.getVendorConsents) {
        cmpSuccess(cmpResponse, hookConfig);
      }
    }

    return {
      consentDataCallback: function(consentResponse) {
        cmpResponse.getConsentData = consentResponse;
        afterEach();
      },
      vendorConsentsCallback: function(consentResponse) {
        cmpResponse.getVendorConsents = consentResponse;
        afterEach();
      }
    }
  }

  let callbackHandler = handleCmpResponseCallbacks();
  let cmpCallbacks = {};
  let cmpFunction;

  // to collect the consent information from the user, we perform two calls to the CMP in parallel:
  // first to collect the user's consent choices represented in an encoded string (via getConsentData)
  // second to collect the user's full unparsed consent information (via getVendorConsents)

  // the following code also determines where the CMP is located and uses the proper workflow to communicate with it:
  // check to see if CMP is found on the same window level as prebid and call it directly if so
  // check to see if prebid is in a safeframe (with CMP support)
  // else assume prebid may be inside an iframe and use the IAB CMP locator code to see if CMP's located in a higher parent window. this works in cross domain iframes
  // if the CMP is not found, the iframe function will call the cmpError exit callback to abort the rest of the CMP workflow
  try {
    cmpFunction = window.__cmp || utils.getWindowTop().__cmp;
  } catch (e) {}

  if (utils.isFn(cmpFunction)) {
    cmpFunction('getConsentData', null, callbackHandler.consentDataCallback);
    cmpFunction('getVendorConsents', null, callbackHandler.vendorConsentsCallback);
  } else if (inASafeFrame() && typeof window.$sf.ext.cmp === 'function') {
    callCmpWhileInSafeFrame('getConsentData', callbackHandler.consentDataCallback);
    callCmpWhileInSafeFrame('getVendorConsents', callbackHandler.vendorConsentsCallback);
  } else {
    // find the CMP frame
    let f = window;
    let cmpFrame;
    while (!cmpFrame) {
      try {
        if (f.frames['__cmpLocator']) cmpFrame = f;
      } catch (e) {}
      if (f === window.top) break;
      f = f.parent;
    }

    if (!cmpFrame) {
      return cmpError('CMP not found.', hookConfig);
    }

    callCmpWhileInIframe('getConsentData', cmpFrame, callbackHandler.consentDataCallback);
    callCmpWhileInIframe('getVendorConsents', cmpFrame, callbackHandler.vendorConsentsCallback);
  }

  function inASafeFrame() {
    return !!(window.$sf && window.$sf.ext);
  }

  function callCmpWhileInSafeFrame(commandName, callback) {
    function sfCallback(msgName, data) {
      if (msgName === 'cmpReturn') {
        let responseObj = (commandName === 'getConsentData') ? data.vendorConsentData : data.vendorConsents;
        callback(responseObj);
      }
    }

    // find sizes from adUnits object
    let adUnits = hookConfig.adUnits;
    let width = 1;
    let height = 1;
    if (Array.isArray(adUnits) && adUnits.length > 0) {
      let sizes = utils.getAdUnitSizes(adUnits[0]);
      width = sizes[0][0];
      height = sizes[0][1];
    }

    window.$sf.ext.register(width, height, sfCallback);
    window.$sf.ext.cmp(commandName);
  }

  function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) {
    //TODO: improve lookup by first walking the parent tree, to see if a friendly __cmp exists before checking for the __cmpLocator
    /* Setup up a __cmp function to do the postMessage and stash the callback.
      This function behaves (from the caller's perspective identicially to the in-frame __cmp call */
    window.__cmp = function(cmd, arg, callback) {
      let callId = Math.random() + '';
      let msg = {__cmpCall: {
        command: cmd,
        parameter: arg,
        callId: callId
      }};
      cmpCallbacks[callId] = callback;
      cmpFrame.postMessage(msg, '*');
    }

    /** when we get the return message, call the stashed callback */
    window.addEventListener('message', readPostMessageResponse, false);

    // call CMP
    window.__cmp(commandName, null, cmpIframeCallback);

    function readPostMessageResponse(event) {
      let json = (typeof event.data === 'string' && strIncludes(event.data, 'cmpReturn')) ? JSON.parse(event.data) : event.data;
      if (json.__cmpReturn && json.__cmpReturn.callId) {
        let i = json.__cmpReturn;
        // TODO - clean up this logic (move listeners?); we have duplicate messages responses because 2 eventlisteners are active from the 2 cmp requests running in parallel
        if (typeof cmpCallbacks[i.callId] !== 'undefined') {
          cmpCallbacks[i.callId](i.returnValue, i.success);
          delete cmpCallbacks[i.callId];
        }
      }
    }

    function removePostMessageListener() {
      window.removeEventListener('message', readPostMessageResponse, false);
    }

    function cmpIframeCallback(consentObject) {
      removePostMessageListener();
      moduleCallback(consentObject);
    }
  }
}

/**
 * If consentManagement module is enabled (ie included in setConfig), this hook function will attempt to fetch the
 * user's encoded consent string from the supported CMP.  Once obtained, the module will store this
 * data as part of a gdprConsent object which gets transferred to adaptermanager's gdprDataHandler object.
 * This information is later added into the bidRequest object for any supported adapters to read/pass along to their system.
 * @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
 * @param {function} fn required; The next function in the chain, used by hook.js
 */
export function requestBidsHook(reqBidsConfigObj, fn) {
  // preserves all module related variables for the current auction instance (used primiarily for concurrent auctions)
  const hookConfig = {
    context: this,
    args: arguments,
    nextFn: fn,
    adUnits: reqBidsConfigObj.adUnits || $$PREBID_GLOBAL$$.adUnits,
    bidsBackHandler: reqBidsConfigObj.bidsBackHandler,
    haveExited: false,
    timer: null
  };

  // in case we already have consent (eg during bid refresh)
  if (consentData) {
    return exitModule(null, hookConfig);
  }

  if (!includes(Object.keys(cmpCallMap), userCMP)) {
    utils.logWarn(`CMP framework (${userCMP}) is not a supported framework.  Aborting consentManagement module and resuming auction.`);
    return hookConfig.nextFn.apply(hookConfig.context, hookConfig.args);
  }

  cmpCallMap[userCMP].call(this, processCmpData, cmpFailed, hookConfig);

  // only let this code run if module is still active (ie if the callbacks used by CMPs haven't already finished)
  if (!hookConfig.haveExited) {
    if (consentTimeout === 0) {
      processCmpData(undefined, hookConfig);
    } else {
      hookConfig.timer = setTimeout(cmpTimedOut.bind(null, hookConfig), consentTimeout);
    }
  }
}

/**
 * This function checks the consent data provided by CMP to ensure it's in an expected state.
 * If it's bad, we exit the module depending on config settings.
 * If it's good, then we store the value and exits the module.
 * @param {object} consentObject required; object returned by CMP that contains user's consent choices
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 */
function processCmpData(consentObject, hookConfig) {
  let gdprApplies = consentObject && consentObject.getConsentData && consentObject.getConsentData.gdprApplies;
  if (
    (typeof gdprApplies !== 'boolean') ||
    (gdprApplies === true &&
      !(utils.isStr(consentObject.getConsentData.consentData) &&
        utils.isPlainObject(consentObject.getVendorConsents) &&
        Object.keys(consentObject.getVendorConsents).length > 1
      )
    )
  ) {
    cmpFailed(`CMP returned unexpected value during lookup process.`, hookConfig, consentObject);
  } else {
    clearTimeout(hookConfig.timer);
    storeConsentData(consentObject);

    exitModule(null, hookConfig);
  }
}

/**
 * General timeout callback when interacting with CMP takes too long.
 */
function cmpTimedOut(hookConfig) {
  cmpFailed('CMP workflow exceeded timeout threshold.', hookConfig);
}

/**
 * This function contains the controlled steps to perform when there's a problem with CMP.
 * @param {string} errMsg required; should be a short descriptive message for why the failure/issue happened.
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 * @param {object} extraArgs contains additional data that's passed along in the error/warning messages for easier debugging
*/
function cmpFailed(errMsg, hookConfig, extraArgs) {
  clearTimeout(hookConfig.timer);

  // still set the consentData to undefined when there is a problem as per config options
  if (allowAuction) {
    storeConsentData(undefined);
  }
  exitModule(errMsg, hookConfig, extraArgs);
}

/**
 * Stores CMP data locally in module and then invokes gdprDataHandler.setConsentData() to make information available in adaptermanger.js for later in the auction
 * @param {object} cmpConsentObject required; an object representing user's consent choices (can be undefined in certain use-cases for this function only)
 */
function storeConsentData(cmpConsentObject) {
  consentData = {
    consentString: (cmpConsentObject) ? cmpConsentObject.getConsentData.consentData : undefined,
    vendorData: (cmpConsentObject) ? cmpConsentObject.getVendorConsents : undefined,
    gdprApplies: (cmpConsentObject) ? cmpConsentObject.getConsentData.gdprApplies : undefined
  };
  gdprDataHandler.setConsentData(consentData);
}

/**
 * This function handles the exit logic for the module.
 * There are several paths in the module's logic to call this function and we only allow 1 of the 3 potential exits to happen before suppressing others.
 *
 * We prevent multiple exits to avoid conflicting messages in the console depending on certain scenarios.
 * One scenario could be auction was canceled due to timeout with CMP being reached.
 * While the timeout is the accepted exit and runs first, the CMP's callback still tries to process the user's data (which normally leads to a good exit).
 * In this case, the good exit will be suppressed since we already decided to cancel the auction.
 *
 * Three exit paths are:
 * 1. good exit where auction runs (CMP data is processed normally).
 * 2. bad exit but auction still continues (warning message is logged, CMP data is undefined and still passed along).
 * 3. bad exit with auction canceled (error message is logged).
 * @param {string} errMsg optional; only to be used when there was a 'bad' exit.  String is a descriptive message for the failure/issue encountered.
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 * @param {object} extraArgs contains additional data that's passed along in the error/warning messages for easier debugging
 */
function exitModule(errMsg, hookConfig, extraArgs) {
  if (hookConfig.haveExited === false) {
    hookConfig.haveExited = true;

    let context = hookConfig.context;
    let args = hookConfig.args;
    let nextFn = hookConfig.nextFn;

    if (errMsg) {
      if (allowAuction) {
        utils.logWarn(errMsg + ' Resuming auction without consent data as per consentManagement config.', extraArgs);
        nextFn.apply(context, args);
      } else {
        utils.logError(errMsg + ' Canceling auction as per consentManagement config.', extraArgs);
        if (typeof hookConfig.bidsBackHandler === 'function') {
          hookConfig.bidsBackHandler();
        } else {
          utils.logError('Error executing bidsBackHandler');
        }
      }
    } else {
      nextFn.apply(context, args);
    }
  }
}

/**
 * Simply resets the module's consentData variable back to undefined, mainly for testing purposes
 */
export function resetConsentData() {
  consentData = undefined;
  gdprDataHandler.setConsentData(null);
}

/**
 * A configuration function that initializes some module variables, as well as add a hook into the requestBids function
 * @param {object} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean)
 */
export function setConfig(config) {
  if (utils.isStr(config.cmpApi)) {
    userCMP = config.cmpApi;
  } else {
    userCMP = DEFAULT_CMP;
    utils.logInfo(`consentManagement config did not specify cmp.  Using system default setting (${DEFAULT_CMP}).`);
  }

  if (utils.isNumber(config.timeout)) {
    consentTimeout = config.timeout;
  } else {
    consentTimeout = DEFAULT_CONSENT_TIMEOUT;
    utils.logInfo(`consentManagement config did not specify timeout.  Using system default setting (${DEFAULT_CONSENT_TIMEOUT}).`);
  }

  if (typeof config.allowAuctionWithoutConsent === 'boolean') {
    allowAuction = config.allowAuctionWithoutConsent;
  } else {
    allowAuction = DEFAULT_ALLOW_AUCTION_WO_CONSENT;
    utils.logInfo(`consentManagement config did not specify allowAuctionWithoutConsent.  Using system default setting (${DEFAULT_ALLOW_AUCTION_WO_CONSENT}).`);
  }

  utils.logInfo('consentManagement module has been activated...');

  if (userCMP === 'static') {
    if (utils.isPlainObject(config.consentData)) {
      staticConsentData = config.consentData;
      consentTimeout = 0;
    } else {
      utils.logError(`consentManagement config with cmpApi: 'static' did not specify consentData. No consents will be available to adapters.`);
    }
  }
  $$PREBID_GLOBAL$$.requestBids.addHook(requestBidsHook, 50);
}
config.getConfig('consentManagement', config => setConfig(config.consentManagement));
<commit_msg>added retry logic, in case the cmp fails<commit_after>/**
 * This module adds GDPR consentManagement support to prebid.js.  It interacts with
 * supported CMPs (Consent Management Platforms) to grab the user's consent information
 * and make it available for any GDPR supported adapters to read/pass this information to
 * their system.
 */
import * as utils from 'src/utils';
import { config } from 'src/config';
import { gdprDataHandler } from 'src/adaptermanager';
import includes from 'core-js/library/fn/array/includes';
import strIncludes from 'core-js/library/fn/string/includes';

const DEFAULT_CMP = 'iab';
const DEFAULT_CONSENT_TIMEOUT = 10000;
const DEFAULT_ALLOW_AUCTION_WO_CONSENT = true;

export let userCMP;
export let consentTimeout;
export let allowAuction;
export let staticConsentData;

let consentData;

// add new CMPs here, with their dedicated lookup function
const cmpCallMap = {
  'iab': lookupIabConsent,
  'static': lookupStaticConsentData
};

/**
 * This function reads the consent string from the config to obtain the consent information of the user.
 * @param {function(string)} cmpSuccess acts as a success callback when the value is read from config; pass along consentObject (string) from CMP
 * @param {function(string)} cmpError acts as an error callback while interacting with the config string; pass along an error message (string)
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 */
function lookupStaticConsentData(cmpSuccess, cmpError, hookConfig) {
  cmpSuccess(staticConsentData, hookConfig);
}

/**
 * This function handles interacting with an IAB compliant CMP to obtain the consent information of the user.
 * Given the async nature of the CMP's API, we pass in acting success/error callback functions to exit this function
 * based on the appropriate result.
 * @param {function(string)} cmpSuccess acts as a success callback when CMP returns a value; pass along consentObject (string) from CMP
 * @param {function(string)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string)
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 */
function lookupIabConsent(cmpSuccess, cmpError, hookConfig) {
  function handleCmpResponseCallbacks() {
    const cmpResponse = {};

    function afterEach() {
      utils.logInfo(`CMP framework completecheck: `,!!(cmpResponse.getConsentData && cmpResponse.getVendorConsents));
      if (cmpResponse.getConsentData && cmpResponse.getVendorConsents) {
        cmpSuccess(cmpResponse, hookConfig);
      }
    }

    return {
      consentDataCallback: function(consentResponse) {
        utils.logInfo(`CMP framework received consentCallback: `,consentResponse);
        cmpResponse.getConsentData = consentResponse;
        afterEach();
      },
      vendorConsentsCallback: function(consentResponse) {
        utils.logInfo(`CMP framework received vendorCallback: `,consentResponse);
        cmpResponse.getVendorConsents = consentResponse;
        afterEach();
      },
      hasConsentData:function(){
        return !!cmpResponse.getConsentData;
      },
      hasVendorConsentData:function(){
        return !!cmpResponse.getVendorConsents;
      }
    }
  }

  let callbackHandler = handleCmpResponseCallbacks();
  let cmpCallbacks = {};
  let cmpFunction;

  // to collect the consent information from the user, we perform two calls to the CMP in parallel:
  // first to collect the user's consent choices represented in an encoded string (via getConsentData)
  // second to collect the user's full unparsed consent information (via getVendorConsents)

  // the following code also determines where the CMP is located and uses the proper workflow to communicate with it:
  // check to see if CMP is found on the same window level as prebid and call it directly if so
  // check to see if prebid is in a safeframe (with CMP support)
  // else assume prebid may be inside an iframe and use the IAB CMP locator code to see if CMP's located in a higher parent window. this works in cross domain iframes
  // if the CMP is not found, the iframe function will call the cmpError exit callback to abort the rest of the CMP workflow
  try {
    cmpFunction = window.__cmp || utils.getWindowTop().__cmp;
  } catch (e) {}

  if (utils.isFn(cmpFunction)) {
    let tryDelegate = function(){
      let didCall = false;
      if(!callbackHandler.hasConsentData()) {
        cmpFunction('getConsentData', null, callbackHandler.consentDataCallback);
        didCall = true;
      }
      if(!callbackHandler.hasVendorConsentData()) {
        cmpFunction('getVendorConsents', null, callbackHandler.vendorConsentsCallback);
        didCall = true;
      }
      utils.logInfo(`CMP framework calling directly for consent data didCalls: `+didCall +' hookConfig.exit: '+hookConfig.haveExited);
      if(didCall && !hookConfig.haveExited){
        setTimeout(tryDelegate,40);
      }
    }
    tryDelegate();
  } else if (inASafeFrame() && typeof window.$sf.ext.cmp === 'function') {
    callCmpWhileInSafeFrame('getConsentData', callbackHandler.consentDataCallback);
    callCmpWhileInSafeFrame('getVendorConsents', callbackHandler.vendorConsentsCallback);
  } else {
    // find the CMP frame
    let f = window;
    let cmpFrame;
    while (!cmpFrame) {
      try {
        if (f.frames['__cmpLocator']) cmpFrame = f;
      } catch (e) {}
      if (f === window.top) break;
      f = f.parent;
    }

    if (!cmpFrame) {
      return cmpError('CMP not found.', hookConfig);
    }

    callCmpWhileInIframe('getConsentData', cmpFrame, callbackHandler.consentDataCallback);
    callCmpWhileInIframe('getVendorConsents', cmpFrame, callbackHandler.vendorConsentsCallback);
  }

  function inASafeFrame() {
    return !!(window.$sf && window.$sf.ext);
  }

  function callCmpWhileInSafeFrame(commandName, callback) {
    function sfCallback(msgName, data) {
      if (msgName === 'cmpReturn') {
        let responseObj = (commandName === 'getConsentData') ? data.vendorConsentData : data.vendorConsents;
        callback(responseObj);
      }
    }

    // find sizes from adUnits object
    let adUnits = hookConfig.adUnits;
    let width = 1;
    let height = 1;
    if (Array.isArray(adUnits) && adUnits.length > 0) {
      let sizes = utils.getAdUnitSizes(adUnits[0]);
      width = sizes[0][0];
      height = sizes[0][1];
    }

    window.$sf.ext.register(width, height, sfCallback);
    window.$sf.ext.cmp(commandName);
  }

  function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) {
    //TODO: improve lookup by first walking the parent tree, to see if a friendly __cmp exists before checking for the __cmpLocator
    /* Setup up a __cmp function to do the postMessage and stash the callback.
      This function behaves (from the caller's perspective identicially to the in-frame __cmp call */
    window.__cmp = function(cmd, arg, callback) {
      let callId = Math.random() + '';
      let msg = {__cmpCall: {
        command: cmd,
        parameter: arg,
        callId: callId
      }};
      cmpCallbacks[callId] = callback;
      cmpFrame.postMessage(msg, '*');
    }

    /** when we get the return message, call the stashed callback */
    window.addEventListener('message', readPostMessageResponse, false);

    // call CMP
    window.__cmp(commandName, null, cmpIframeCallback);

    function readPostMessageResponse(event) {
      let json = (typeof event.data === 'string' && strIncludes(event.data, 'cmpReturn')) ? JSON.parse(event.data) : event.data;
      if (json.__cmpReturn && json.__cmpReturn.callId) {
        let i = json.__cmpReturn;
        // TODO - clean up this logic (move listeners?); we have duplicate messages responses because 2 eventlisteners are active from the 2 cmp requests running in parallel
        if (typeof cmpCallbacks[i.callId] !== 'undefined') {
          cmpCallbacks[i.callId](i.returnValue, i.success);
          delete cmpCallbacks[i.callId];
        }
      }
    }

    function removePostMessageListener() {
      window.removeEventListener('message', readPostMessageResponse, false);
    }

    function cmpIframeCallback(consentObject) {
      removePostMessageListener();
      moduleCallback(consentObject);
    }
  }
}

/**
 * If consentManagement module is enabled (ie included in setConfig), this hook function will attempt to fetch the
 * user's encoded consent string from the supported CMP.  Once obtained, the module will store this
 * data as part of a gdprConsent object which gets transferred to adaptermanager's gdprDataHandler object.
 * This information is later added into the bidRequest object for any supported adapters to read/pass along to their system.
 * @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
 * @param {function} fn required; The next function in the chain, used by hook.js
 */
export function requestBidsHook(reqBidsConfigObj, fn) {
  // preserves all module related variables for the current auction instance (used primiarily for concurrent auctions)
  const hookConfig = {
    context: this,
    args: arguments,
    nextFn: fn,
    adUnits: reqBidsConfigObj.adUnits || $$PREBID_GLOBAL$$.adUnits,
    bidsBackHandler: reqBidsConfigObj.bidsBackHandler,
    haveExited: false,
    timer: null
  };

  // in case we already have consent (eg during bid refresh)
  if (consentData) {
    return exitModule(null, hookConfig);
  }

  if (!includes(Object.keys(cmpCallMap), userCMP)) {
    utils.logWarn(`CMP framework (${userCMP}) is not a supported framework.  Aborting consentManagement module and resuming auction.`);
    return hookConfig.nextFn.apply(hookConfig.context, hookConfig.args);
  }

  cmpCallMap[userCMP].call(this, processCmpData, cmpFailed, hookConfig);

  // only let this code run if module is still active (ie if the callbacks used by CMPs haven't already finished)
  if (!hookConfig.haveExited) {
    if (consentTimeout === 0) {
      processCmpData(undefined, hookConfig);
    } else {
      hookConfig.timer = setTimeout(cmpTimedOut.bind(null, hookConfig), consentTimeout);
    }
  }
}

/**
 * This function checks the consent data provided by CMP to ensure it's in an expected state.
 * If it's bad, we exit the module depending on config settings.
 * If it's good, then we store the value and exits the module.
 * @param {object} consentObject required; object returned by CMP that contains user's consent choices
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 */
function processCmpData(consentObject, hookConfig) {
  let gdprApplies = consentObject && consentObject.getConsentData && consentObject.getConsentData.gdprApplies;
  if (
    (typeof gdprApplies !== 'boolean') ||
    (gdprApplies === true &&
      !(utils.isStr(consentObject.getConsentData.consentData) &&
        utils.isPlainObject(consentObject.getVendorConsents) &&
        Object.keys(consentObject.getVendorConsents).length > 1
      )
    )
  ) {
    cmpFailed(`CMP returned unexpected value during lookup process.`, hookConfig, consentObject);
  } else {
    clearTimeout(hookConfig.timer);
    storeConsentData(consentObject);

    exitModule(null, hookConfig);
  }
}

/**
 * General timeout callback when interacting with CMP takes too long.
 */
function cmpTimedOut(hookConfig) {
  cmpFailed('CMP workflow exceeded timeout threshold.', hookConfig);
}

/**
 * This function contains the controlled steps to perform when there's a problem with CMP.
 * @param {string} errMsg required; should be a short descriptive message for why the failure/issue happened.
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 * @param {object} extraArgs contains additional data that's passed along in the error/warning messages for easier debugging
*/
function cmpFailed(errMsg, hookConfig, extraArgs) {
  clearTimeout(hookConfig.timer);

  // still set the consentData to undefined when there is a problem as per config options
  if (allowAuction) {
    storeConsentData(undefined);
  }
  exitModule(errMsg, hookConfig, extraArgs);
}

/**
 * Stores CMP data locally in module and then invokes gdprDataHandler.setConsentData() to make information available in adaptermanger.js for later in the auction
 * @param {object} cmpConsentObject required; an object representing user's consent choices (can be undefined in certain use-cases for this function only)
 */
function storeConsentData(cmpConsentObject) {
  consentData = {
    consentString: (cmpConsentObject) ? cmpConsentObject.getConsentData.consentData : undefined,
    vendorData: (cmpConsentObject) ? cmpConsentObject.getVendorConsents : undefined,
    gdprApplies: (cmpConsentObject) ? cmpConsentObject.getConsentData.gdprApplies : undefined
  };
  gdprDataHandler.setConsentData(consentData);
}

/**
 * This function handles the exit logic for the module.
 * There are several paths in the module's logic to call this function and we only allow 1 of the 3 potential exits to happen before suppressing others.
 *
 * We prevent multiple exits to avoid conflicting messages in the console depending on certain scenarios.
 * One scenario could be auction was canceled due to timeout with CMP being reached.
 * While the timeout is the accepted exit and runs first, the CMP's callback still tries to process the user's data (which normally leads to a good exit).
 * In this case, the good exit will be suppressed since we already decided to cancel the auction.
 *
 * Three exit paths are:
 * 1. good exit where auction runs (CMP data is processed normally).
 * 2. bad exit but auction still continues (warning message is logged, CMP data is undefined and still passed along).
 * 3. bad exit with auction canceled (error message is logged).
 * @param {string} errMsg optional; only to be used when there was a 'bad' exit.  String is a descriptive message for the failure/issue encountered.
 * @param {object} hookConfig contains module related variables (see comment in requestBidsHook function)
 * @param {object} extraArgs contains additional data that's passed along in the error/warning messages for easier debugging
 */
function exitModule(errMsg, hookConfig, extraArgs) {
  if (hookConfig.haveExited === false) {
    hookConfig.haveExited = true;

    let context = hookConfig.context;
    let args = hookConfig.args;
    let nextFn = hookConfig.nextFn;

    if (errMsg) {
      if (allowAuction) {
        utils.logWarn(errMsg + ' Resuming auction without consent data as per consentManagement config.', extraArgs);
        nextFn.apply(context, args);
      } else {
        utils.logError(errMsg + ' Canceling auction as per consentManagement config.', extraArgs);
        if (typeof hookConfig.bidsBackHandler === 'function') {
          hookConfig.bidsBackHandler();
        } else {
          utils.logError('Error executing bidsBackHandler');
        }
      }
    } else {
      nextFn.apply(context, args);
    }
  }
}

/**
 * Simply resets the module's consentData variable back to undefined, mainly for testing purposes
 */
export function resetConsentData() {
  consentData = undefined;
  gdprDataHandler.setConsentData(null);
}

/**
 * A configuration function that initializes some module variables, as well as add a hook into the requestBids function
 * @param {object} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean)
 */
export function setConfig(config) {
  if (utils.isStr(config.cmpApi)) {
    userCMP = config.cmpApi;
  } else {
    userCMP = DEFAULT_CMP;
    utils.logInfo(`consentManagement config did not specify cmp.  Using system default setting (${DEFAULT_CMP}).`);
  }

  if (utils.isNumber(config.timeout)) {
    consentTimeout = config.timeout;
  } else {
    consentTimeout = DEFAULT_CONSENT_TIMEOUT;
    utils.logInfo(`consentManagement config did not specify timeout.  Using system default setting (${DEFAULT_CONSENT_TIMEOUT}).`);
  }

  if (typeof config.allowAuctionWithoutConsent === 'boolean') {
    allowAuction = config.allowAuctionWithoutConsent;
  } else {
    allowAuction = DEFAULT_ALLOW_AUCTION_WO_CONSENT;
    utils.logInfo(`consentManagement config did not specify allowAuctionWithoutConsent.  Using system default setting (${DEFAULT_ALLOW_AUCTION_WO_CONSENT}).`);
  }

  utils.logInfo('consentManagement module has been activated...');

  if (userCMP === 'static') {
    if (utils.isPlainObject(config.consentData)) {
      staticConsentData = config.consentData;
      consentTimeout = 0;
    } else {
      utils.logError(`consentManagement config with cmpApi: 'static' did not specify consentData. No consents will be available to adapters.`);
    }
  }
  $$PREBID_GLOBAL$$.requestBids.addHook(requestBidsHook, 50);
}
config.getConfig('consentManagement', config => setConfig(config.consentManagement));
<|endoftext|>


<commit_before>/*
EB_Template version: 1.46
https://github.com/Amarok24/EB-template
EB_Template is released under The Unlicense,
see LICENSE.md or http://unlicense.org/ for more information.
*/

var EB_Template = (function() {

  "use strict";

  const _ONEPAGELAYOUT = false; // set this to either "true" or "false"
  const _DESKTOPBREAKPOINT = 481; // minimal screen width for desktop layout
  var _DOMQUERY = {};

  var _monsterTemplateType = {
    jv30_general: false, // true in both cases: fullpage + combined view
    jv30_combined: false  // true only in combinded view
  };
  var _timeoutIDwindowResize = null;
  var _isMobileScreen = false;
  var _winScrollBy = null; // used by scrollToObject
  var _iframeParent = null; // used in jv30

  function startTemplate() {
    _DOMQUERY = {
      container : document.querySelector(".containerIA"),
      tabContents : document.querySelectorAll(".tabContent"),
      navButtons : document.querySelectorAll("#navigation button"),
      sitemapButtons : document.querySelectorAll("#sitemap button")
    }
    detectMonsterTemplateType();
    onWindowResize(); // decides if mobile view should be used
    addEvents();
    initAllTabs();
  }

  function removeClassAll(nList, className) {
    var nodeList = nList;
    for (var i = 0; i < nodeList.length; i++) {
      nodeList[i].classList.remove(className);
    }
  }

  function iframeParentResize() {
    // this function handles iframe height in JV30
    /*
        var MUXmethod = window.MUX;
        if ((MUXmethod != null) && (MUXmethod.callResize != null)) {
          try {
            MUXmethod.callResize();
            console.info("iframe resized, MUX method");
          } catch (er) {
            console.error("EB_template: callResize error", er);
          }
        }
    */
    if (_iframeParent != null) {
      _iframeParent.style.height = _DOMQUERY.container.offsetHeight + 40 + "px";
      console.info("iframe resized, own method");
    }
  }

  function scrollToObject(elementObject, yCorrection) {
    var yCorrection = yCorrection || 0;
    try {
      elementObject.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'nearest'});
      /*
      if (yCorrection != 0) {
        if (_monsterTemplateType.jv30_combined) {
          window.setTimeout( function() {_winScrollBy.scrollBy(0, yCorrection);}, 50);
          // TODO: bug in Chrome + Edge in combined view!
        } else {
          window.setTimeout( function() {_winScrollBy(0, yCorrection);}, 50); // dirty hack because scrollIntoView is asynchronous, *TODO*
        }
      }
      */
    } catch (er) {
      console.error("scrollToObject error", er);
    }
  }

  function navButtonClick(buttonIndex, ev /* default true (MouseEvent click) */ ) {
    var i;
    var tabContents = _DOMQUERY.tabContents;
    var clickOrigin = ev ? ev.target.parentElement.id : null;
    var scrollCorrection = _monsterTemplateType.jv30_combined ? -78 : 0; // JobViewHeader height is 72px

    if (!_ONEPAGELAYOUT) {
      for (i = 0; i < tabContents.length; i++) {
        tabContents[i].style.display = "none";
      }
      tabContents[buttonIndex].style.display = "block";
      removeClassAll(_DOMQUERY.navButtons, "active");
      _DOMQUERY.navButtons[buttonIndex].classList.add("active");
    }

    // first we need to resize iframe and THEN we can scroll, else wrong behaviour can be expected
    if (_monsterTemplateType.jv30_general) {
      window.setTimeout(iframeParentResize, 200); // wait a little bit for content to settle
    }

    if (_ONEPAGELAYOUT || _isMobileScreen || (clickOrigin == "sitemap")) {
      window.setTimeout( function() {scrollToObject(tabContents[buttonIndex], scrollCorrection);}, 120);
    }
  }

  function onWindowResize() {
    function windowResizeAction() {
      var mainContainer = _DOMQUERY.container;
      var availWidth = document.body.clientWidth;

      mainContainer.classList.remove("mobile");
      if (availWidth < _DESKTOPBREAKPOINT) {
        _isMobileScreen = true;
        mainContainer.classList.add("mobile");
      } else {
        _isMobileScreen = false;
      }
      console.log("EB_Template _isMobileScreen:", _isMobileScreen);
    }

    if (_timeoutIDwindowResize) {
      window.clearTimeout(_timeoutIDwindowResize);
    }
    _timeoutIDwindowResize = window.setTimeout(windowResizeAction, 50);
  }

/*
  function onParentContentScroll() {
    // combined view feature to detect reduced banner
    if (_JobViewHeader.classList.contains("is-reduced")) {
      _iframeParent.style.marginTop = "72px";
    } else {
      _iframeParent.style.marginTop = "0";
    }
  }
*/

  function initStartingTab() {
    // switches to some tab directly if URL parameter "tab" found, or just switches to 1st tab
    var params = {}, // pairs of  key - value (name - value)
        locationSearch = "",
        i;

    if (_monsterTemplateType.jv30_general) {
      try {
        locationSearch = window.parent.location.search;
      }
      catch(er) {
        console.error("EB_Template initStartingTab: locationSearch error, details following");
        console.log(er);
      }
    } else {
      locationSearch = window.location.search;
    }

    if (locationSearch) {
      var a_parts = locationSearch.substring(1).split('&');
      // "a_parts" will be eg. ["tab=2", "bla=text", "val="]
      for (i = 0; i < a_parts.length; i++) {
        var nv = a_parts[i].split('=');
        console.log("EB_Template initStartingTab:", nv);
        if (!nv[0]) {
          continue;
        } // skip cases like "=25" with no 'key'
        params[nv[0]] = nv[1] || true; // 'true' if no value was provided
      }
    }

    if (params.tab) {
      try {
        navButtonClick(params.tab);
        //document.querySelectorAll(_QUERYNAVIGATION)[params.tab].click();
      } catch (er) {
        console.error("EB_Template initStartingTab: navigation click error, details following");
        console.log(er);
        navButtonClick(0);
      }
    } else {
      navButtonClick(0);
    }
  } // end initStartingTab

  function addEvents() {
    var i;
    var navItems = _DOMQUERY.navButtons;
    var navSitemapItems = _DOMQUERY.sitemapButtons;

    for (i = 0; i < navItems.length; i++) {
      navItems[i].addEventListener("click", navButtonClick.bind(null, i));
      navSitemapItems[i].addEventListener("click", navButtonClick.bind(null, i));
    }
    window.addEventListener("resize", onWindowResize);
  }

  function detectMonsterTemplateType() {
    var jobId = document.getElementsByTagName("body")[0].getAttribute("data-job-id");

    function insideOfIframe() { // test if this html document is in iframe
      try { return window.self !== window.top }
      catch (e) { return true; } // fallback for bad browsers, assumes true
    }

    _monsterTemplateType.jv30_general = insideOfIframe();
    _monsterTemplateType.jv30_combined = window.parent.document.getElementById("ContentScrollable") ? true : false;

    console.group("EB_Template detectMonsterTemplateType");
    console.log("_monsterTemplateType:", _monsterTemplateType);
    if (jobId) { console.log("jobId = ", jobId); }
    console.groupEnd();
  }

  function initAllTabs() {
    var i;
    var tabContents = _DOMQUERY.tabContents;

    if (_monsterTemplateType.jv30_combined) {
      _winScrollBy =  window.parent.document.getElementById("ContentScrollable"); // not possible to use directly .scrollBy here, TypeError: 'scrollBy' called on an object that does not implement interface Element.
      _iframeParent = window.parent.document.getElementById("JobPreviewSandbox");
      //_JobViewHeader = window.parent.document.getElementById("JobViewHeader");
    } else if (_monsterTemplateType.jv30_general) {
      _winScrollBy = window.parent.scrollBy;
      _iframeParent = window.parent.document.getElementById("JobPreviewSandbox");
    } else {
      _winScrollBy =  window.scrollBy;
    }

    if (!_ONEPAGELAYOUT) {
      for (i = 0; i < tabContents.length; i++) {
        tabContents[i].style.display = "none";
      }
      initStartingTab();
    }
  }

  document.addEventListener("DOMContentLoaded", startTemplate);

  window.addEventListener("load", function () {
    window.setTimeout(iframeParentResize, 1000);
  });

  return {
    /*publicProperty: "test",
    publicMethod: function() {},*/
    navButtonClick: navButtonClick,
    _monsterTemplateType: _monsterTemplateType // info: public properties won't get updated during runtime
  };

})(); // end EB_Template
<commit_msg>Add files via upload<commit_after>/*
EB_Template version: 1.47
https://github.com/Amarok24/EB-template
EB_Template is released under The Unlicense,
see LICENSE.md or http://unlicense.org/ for more information.
*/

var EB_Template = (function() {

  "use strict";

  const _ONEPAGELAYOUT = false; // set this to either "true" or "false"
  const _DESKTOPBREAKPOINT = 481; // minimal screen width for desktop layout
  var _DOMQUERY = {};

  var _monsterTemplateType = {
    jv30_general: false, // true in both cases: fullpage + combined view
    jv30_combined: false  // true only in combinded view
  };
  var _timeoutIDwindowResize = null;
  var _isMobileScreen = false;
  var _winScrollBy = null; // used by scrollToObject
  var _iframeParent = null; // used in jv30

  function startTemplate() {
    _DOMQUERY = {
      container : document.querySelector(".containerIA"),
      tabContents : document.querySelectorAll(".tabContent"),
      navButtons : document.querySelectorAll("#navigation button"),
      sitemapButtons : document.querySelectorAll("#sitemap button")
    }
    detectMonsterTemplateType();
    onWindowResize(); // decides if mobile view should be used
    addEvents();
    initAllTabs();
  }

  function removeClassAll(nList, className) {
    var nodeList = nList;
    for (var i = 0; i < nodeList.length; i++) {
      nodeList[i].classList.remove(className);
    }
  }

  function iframeParentResize() {
    // this function handles iframe height in JV30
    /*
        var MUXmethod = window.MUX;
        if ((MUXmethod != null) && (MUXmethod.callResize != null)) {
          try {
            MUXmethod.callResize();
            console.info("iframe resized, MUX method");
          } catch (er) {
            console.error("EB_template: callResize error", er);
          }
        }
    */
    if (_iframeParent != null) {
      _iframeParent.style.height = _DOMQUERY.container.offsetHeight + 40 + "px";
      console.info("iframe resized, own method");
    }
  }

  function scrollToObject(elementObject, yCorrection) {
    var yCorrection = yCorrection || 0;
    try {
      elementObject.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'nearest'});
      /*
      if (yCorrection != 0) {
        if (_monsterTemplateType.jv30_combined) {
          window.setTimeout( function() {_winScrollBy.scrollBy(0, yCorrection);}, 50);
          // TODO: bug in Chrome + Edge in combined view!
        } else {
          window.setTimeout( function() {_winScrollBy(0, yCorrection);}, 50); // dirty hack because scrollIntoView is asynchronous, *TODO*
        }
      }
      */
    } catch (er) {
      console.error("scrollToObject error", er);
    }
  }

  function navButtonClick(buttonIndex, ev /* default true (MouseEvent click) */ ) {
    var i;
    var tabContents = _DOMQUERY.tabContents;
    var clickOrigin = ev ? ev.target.parentElement.id : null;
    var scrollCorrection = _monsterTemplateType.jv30_combined ? -78 : 0; // JobViewHeader height is 72px

    if (!_ONEPAGELAYOUT) {
      for (i = 0; i < tabContents.length; i++) {
        tabContents[i].style.display = "none";
        _DOMQUERY.container.classList.remove("tab" + i);
      }
      tabContents[buttonIndex].style.display = "block";
      removeClassAll(_DOMQUERY.navButtons, "active");
      _DOMQUERY.navButtons[buttonIndex].classList.add("active");
    }

    _DOMQUERY.container.classList.add("tab" + buttonIndex);

    // first we need to resize iframe and THEN we can scroll, else wrong behaviour can be expected
    if (_monsterTemplateType.jv30_general) {
      window.setTimeout(iframeParentResize, 200); // wait a little bit for content to settle
    }

    if (_ONEPAGELAYOUT || _isMobileScreen || (clickOrigin == "sitemap")) {
      window.setTimeout( function() {scrollToObject(tabContents[buttonIndex], scrollCorrection);}, 120);
    }
  }

  function onWindowResize() {
    function windowResizeAction() {
      var mainContainer = _DOMQUERY.container;
      var availWidth = document.body.clientWidth;

      mainContainer.classList.remove("mobile");
      if (availWidth < _DESKTOPBREAKPOINT) {
        _isMobileScreen = true;
        mainContainer.classList.add("mobile");
      } else {
        _isMobileScreen = false;
      }
      console.log("EB_Template _isMobileScreen:", _isMobileScreen);
    }

    if (_timeoutIDwindowResize) {
      window.clearTimeout(_timeoutIDwindowResize);
    }
    _timeoutIDwindowResize = window.setTimeout(windowResizeAction, 50);
  }

/*
  function onParentContentScroll() {
    // combined view feature to detect reduced banner
    if (_JobViewHeader.classList.contains("is-reduced")) {
      _iframeParent.style.marginTop = "72px";
    } else {
      _iframeParent.style.marginTop = "0";
    }
  }
*/

  function initStartingTab() {
    // switches to some tab directly if URL parameter "tab" found, or just switches to 1st tab
    var params = {}, // pairs of  key - value (name - value)
        locationSearch = "",
        i;

    if (_monsterTemplateType.jv30_general) {
      try {
        locationSearch = window.parent.location.search;
      }
      catch(er) {
        console.error("EB_Template initStartingTab: locationSearch error, details following");
        console.log(er);
      }
    } else {
      locationSearch = window.location.search;
    }

    if (locationSearch) {
      var a_parts = locationSearch.substring(1).split('&');
      // "a_parts" will be eg. ["tab=2", "bla=text", "val="]
      for (i = 0; i < a_parts.length; i++) {
        var nv = a_parts[i].split('=');
        console.log("EB_Template initStartingTab:", nv);
        if (!nv[0]) {
          continue;
        } // skip cases like "=25" with no 'key'
        params[nv[0]] = nv[1] || true; // 'true' if no value was provided
      }
    }

    if (params.tab) {
      try {
        navButtonClick(params.tab);
        //document.querySelectorAll(_QUERYNAVIGATION)[params.tab].click();
      } catch (er) {
        console.error("EB_Template initStartingTab: navigation click error, details following");
        console.log(er);
        navButtonClick(0);
      }
    } else {
      navButtonClick(0);
    }
  } // end initStartingTab

  function addEvents() {
    var i;
    var navItems = _DOMQUERY.navButtons;
    var navSitemapItems = _DOMQUERY.sitemapButtons;

    for (i = 0; i < navItems.length; i++) {
      navItems[i].addEventListener("click", navButtonClick.bind(null, i));
      navSitemapItems[i].addEventListener("click", navButtonClick.bind(null, i));
    }
    window.addEventListener("resize", onWindowResize);
  }

  function detectMonsterTemplateType() {
    var jobId = document.getElementsByTagName("body")[0].getAttribute("data-job-id");

    function insideOfIframe() { // test if this html document is in iframe
      try { return window.self !== window.top }
      catch (e) { return true; } // fallback for bad browsers, assumes true
    }

    _monsterTemplateType.jv30_general = insideOfIframe();
    _monsterTemplateType.jv30_combined = window.parent.document.getElementById("ContentScrollable") ? true : false;

    console.group("EB_Template detectMonsterTemplateType");
    console.log("_monsterTemplateType:", _monsterTemplateType);
    if (jobId) { console.log("jobId = ", jobId); }
    console.groupEnd();
  }

  function initAllTabs() {
    var i;
    var tabContents = _DOMQUERY.tabContents;

    if (_monsterTemplateType.jv30_combined) {
      _winScrollBy =  window.parent.document.getElementById("ContentScrollable"); // not possible to use directly .scrollBy here, TypeError: 'scrollBy' called on an object that does not implement interface Element.
      _iframeParent = window.parent.document.getElementById("JobPreviewSandbox");
      //_JobViewHeader = window.parent.document.getElementById("JobViewHeader");
    } else if (_monsterTemplateType.jv30_general) {
      _winScrollBy = window.parent.scrollBy;
      _iframeParent = window.parent.document.getElementById("JobPreviewSandbox");
    } else {
      _winScrollBy =  window.scrollBy;
    }

    if (!_ONEPAGELAYOUT) {
      for (i = 0; i < tabContents.length; i++) {
        tabContents[i].style.display = "none";
      }
      initStartingTab();
    }
  }

  document.addEventListener("DOMContentLoaded", startTemplate);

  window.addEventListener("load", function () {
    window.setTimeout(iframeParentResize, 1000);
  });

  return {
    /*publicProperty: "test",
    publicMethod: function() {},*/
    navButtonClick: navButtonClick,
    _monsterTemplateType: _monsterTemplateType // info: public properties won't get updated during runtime
  };

})(); // end EB_Template
<|endoftext|>


<commit_before>'use strict';

Darwinator.Player = function(game, x, y, cursors, health, strength, agility, intellect) {
  var anims = [['walk-left', [8,9,10,11], 10, true], ['walk-right', [12,13,14,15], 10, true],
               ['walk-up', [0,1,2,3], 10, true], ['walk-down', [4,5,6,7], 10, true]];
  Darwinator.Entity.call(this, game, x, y, 'player', anims, health, strength, agility, intellect);
  this.cursors = cursors;
  this.scale.setTo(0.25,0.25);
  this.anchor.setTo(0.5, 0.5);
  this.initKeys(game);
  this.maxSpeed = 100;

  /*
      Notes until later:
      each body can setCircle, Rectangle or Polygon.
      Check the phaser.js on line ~41837
  */
  this.body.setRectangle(12, 4, 2, 12);
};
Darwinator.Player.prototype = Object.create(Darwinator.Entity.prototype);

Darwinator.Player.prototype.update = function() {
  this.body.velocity.setTo(0,0);
  var dir = [0,0];
  var moving = false;
  this.animations.stop();
  this.body.frame = 4;

  if (this.cursors.left.isDown || this.leftKey.isDown) {
    this.body.velocity.x = -this.maxSpeed;
    this.animations.play('walk-left');
    moving = true;
  } else if (this.cursors.right.isDown || this.rightKey.isDown) {
    this.body.velocity.x = this.maxSpeed;
    this.animations.play('walk-right');
    moving = true;
  }
  if (this.cursors.up.isDown || this.upKey.isDown) {
    this.body.velocity.y = -this.maxSpeed;
    this.animations.play('walk-up');
    moving = true;
  } else if (this.cursors.down.isDown || this.downKey.isDown) {
    this.body.velocity.y = this.maxSpeed;
    this.animations.play('walk-down');
    moving = true;
  }

  if(this.sprintKey.isDown && this.currBreath > 1 && moving) {
    this.body.velocity.multiply(2,2);
    this.currBreath--;
  } else if (this.currBreath < this.stamina) {
    this.currBreath += 0.2;
  }

  if (this.health <= 0) {
    //TODO Set to this.kill();
    this.health = 100;
  }
};

  Darwinator.Player.prototype.initKeys = function(game) {
    this.upKey = game.input.keyboard.addKey(Phaser.Keyboard.W);
    this.leftKey = game.input.keyboard.addKey(Phaser.Keyboard.A);
    this.downKey = game.input.keyboard.addKey(Phaser.Keyboard.S);
    this.rightKey = game.input.keyboard.addKey(Phaser.Keyboard.D);
    this.sprintKey = game.input.keyboard.addKey(Phaser.Keyboard.SHIFT);
  };
<commit_msg>Fixed so the player sprite can't go outside top world bound<commit_after>'use strict';

Darwinator.Player = function(game, x, y, cursors, health, strength, agility, intellect) {
  var anims = [['walk-left', [8,9,10,11], 10, true], ['walk-right', [12,13,14,15], 10, true],
               ['walk-up', [0,1,2,3], 10, true], ['walk-down', [4,5,6,7], 10, true]];
  Darwinator.Entity.call(this, game, x, y, 'player', anims, health, strength, agility, intellect);
  this.cursors = cursors;
  this.scale.setTo(0.25,0.25);
  this.anchor.setTo(0.5, 0.5);
  this.initKeys(game);
  this.maxSpeed = 100;

  /*
      Notes until later:
      each body can setCircle, Rectangle or Polygon.
      Check the phaser.js on line ~41837
  */
  this.body.setRectangle(12, 4, 2, 12);
};
Darwinator.Player.prototype = Object.create(Darwinator.Entity.prototype);

Darwinator.Player.prototype.update = function() {
  this.body.velocity.setTo(0,0);
  var dir = [0,0];
  var moving = false;
  this.animations.stop();
  this.body.frame = 4;

  if (this.cursors.left.isDown || this.leftKey.isDown) {
    this.body.velocity.x = -this.maxSpeed;
    this.animations.play('walk-left');
    moving = true;
  } else if (this.cursors.right.isDown || this.rightKey.isDown) {
    this.body.velocity.x = this.maxSpeed;
    this.animations.play('walk-right');
    moving = true;
  }
  if (this.cursors.up.isDown || this.upKey.isDown) {
    if (this.topLeft.y <= 0){
      this.body.velocity.y = 0;
    } else {
      this.body.velocity.y = -this.maxSpeed;
    }
    this.animations.play('walk-up');
    moving = true;
  } else if (this.cursors.down.isDown || this.downKey.isDown) {
    this.body.velocity.y = this.maxSpeed;
    this.animations.play('walk-down');
    moving = true;
  }

  if(this.sprintKey.isDown && this.currBreath > 1 && moving) {
    this.body.velocity.multiply(2,2);
    this.currBreath--;
  } else if (this.currBreath < this.stamina) {
    this.currBreath += 0.2;
  }

  if (this.health <= 0) {
    //TODO Set to this.kill();
    this.health = 100;
  }
};

  Darwinator.Player.prototype.initKeys = function(game) {
    this.upKey = game.input.keyboard.addKey(Phaser.Keyboard.W);
    this.leftKey = game.input.keyboard.addKey(Phaser.Keyboard.A);
    this.downKey = game.input.keyboard.addKey(Phaser.Keyboard.S);
    this.rightKey = game.input.keyboard.addKey(Phaser.Keyboard.D);
    this.sprintKey = game.input.keyboard.addKey(Phaser.Keyboard.SHIFT);
  };
<|endoftext|>


<commit_before>var path = require('path');
var async = require('async'); // NB: async is used here in the benchmarking code, in case co or
                              // asyncawait won't run on the version of node being benchmarked.
var _ = require('lodash');
var memwatch = require('memwatch');
var rewire = require('rewire');

// Functions available for benchmarking.
var functions = {
    countFiles: 'countFiles',
    fibonacci: 'fibonacci',
    largest: 'largest'

};

// Variants available for benchmarking.
var variants = {
    async: 'async',
    asyncawait: 'asyncawait',
    asyncx: 'asyncx',
    bluebird: 'bluebird',
    callbacks: 'callbacks',
    co: 'co',
    synchronous: 'synchronous'
};

// ================================================================================
// Benchmark configuration - adjust to suit.

var SELECTED_FUNCTION = functions.largest;

var SELECTED_VARIANT = variants.asyncawait;

var SAMPLES_PER_RUN = 100;   // How many times the function will be called per run.

var RUNS_PER_BENCHMARK = 10;  // How many runs make up the whole benchmark.

var CONCURRENCY_FACTOR = 10;  // Max number of concurrent invocations of the function.

// Some additional switches
var JUST_CHECK_THE_FUNCTION = false;            // If true, just call the function once and display its results.
var USE_SAME_SYMBOL_FOR_ALL_SAMPLES = true;     // If true, all samples will use the same symbol ('.'). Otherwise, concurrent samples will use distinct symbols.
var USE_MOCK_FS = false;                         // If true, uses a mocked 'fs' module returning fixed in-memory results.
var OUTPUT_GC_STATS = true;                     // If true, indicate GC pauses and statistics, and indicate possible memory leaks.
var OUTPUT_SAMPLES_PER_SEC_SUMMARY = false;     // If true, print all samples/sec numbers at the end, to export for anaysis (eg for charting).

// ================================================================================

// Set up memory diagnostics
var fullGCs = 0;
var incrGCs = 0;
var leaked = 0;
if (OUTPUT_GC_STATS) {
    memwatch.on('leak', function(info) {
        leaked += info.growth;
        process.stdout.write(' [LEAK+' + info.growth +'] ');
    });
    memwatch.on('stats', function(stats) {
        fullGCs = stats.num_full_gc;
        incrGCs = stats.num_inc_gc;
        process.stdout.write(' [GC] ');
    });
}

// Run the benchmark (or just check the function).
if (JUST_CHECK_THE_FUNCTION) {
    var name = SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
    var sample = createSampleFunction();
    console.log("========== CHECKING '" + name + "': ==========");
    sample(function(err, result) {
        console.log(err || result);
        if (OUTPUT_GC_STATS) {
            console.log("========== GCs: " + fullGCs + 'full/' + incrGCs + "incr ==========");
            console.log("========== Leaked: " + leaked + " ==========");
        }
    });
} else {
    benchmark();
}

function benchmark() {
    var name = SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
    var sample = createSampleFunction();
    var allSamplesPerSec = [];
    console.log('========== PERFORMING ' + RUNS_PER_BENCHMARK + " RUNS ON '" + name + "': ==========\n");
    var times = [];
    async.timesSeries(
        RUNS_PER_BENCHMARK,
        function (n, next) {
            process.stdout.write('RUN ' + (n + 1));
            run(sample, function (err, timing) {
                if (err) {
                    next(err);
                } else {
                    times.push(timing.totalElapsed);
                    allSamplesPerSec.push(SAMPLES_PER_RUN * 1000.0 / timing.totalElapsed);
                    var msg = SAMPLES_PER_RUN
                        + ' samples took '
                        + (timing.totalElapsed / 1000.0)
                        + ' seconds ('
                        + (SAMPLES_PER_RUN * 1000.0 / timing.totalElapsed)
                        + ' samples/sec), average latency per sample: '
                        + timing.perSample
                        + 'ms';
                    if (OUTPUT_GC_STATS) {
                        msg = msg
                            + ', GCs: '
                            + timing.fullGCs
                            + 'full/'
                            + timing.incrGCs
                            + 'incr, leaked: '
                            + timing.leaked;
                    }
                    console.log(msg + '\n');
                    next();
                }
            });
        },
        function (err) {
            if (err) {
                console.log(err);
                process.exit();
            } else {
                totalTime = _.reduce(times, function (sum, time) { return sum + time; });
                var averageTime = totalTime / RUNS_PER_BENCHMARK;
                    var msg = 'Average time: '
                        + (averageTime / 1000.0)
                        + ' seconds ('
                        + (SAMPLES_PER_RUN * 1000.0 / averageTime)
                        + ' samples/sec)';
                console.log('========== ' + msg + ' ==========');
                if (OUTPUT_GC_STATS) {
                    console.log("========== GCs: " + fullGCs + 'full/' + incrGCs + "incr ==========");
                    console.log("========== Leaked: " + leaked + " ==========");
                }
                if (OUTPUT_SAMPLES_PER_SEC_SUMMARY) {
                    console.log("========== Summary of samples/sec for all runs: ==========");
                    console.log(allSamplesPerSec.join(', '));
                }
            }
        });
}

function run(sample, callback) {
    var chars = USE_SAME_SYMBOL_FOR_ALL_SAMPLES ? '.' : './#$@%^&*+!=-?~`|()[]ABCDEFGHIJKLMNOPQRS';
    var start = new Date().getTime();
    var startFullGCs = fullGCs;
    var startIncrGCs = incrGCs;
    var startLeaked = leaked;
    var sumOfTimePerSample = 0.0;
    async.times(
        CONCURRENCY_FACTOR,
        function (m, nextOuter) {
            var char = chars.charAt(m % chars.length);
            async.timesSeries(
                1.0 * SAMPLES_PER_RUN / CONCURRENCY_FACTOR,
                function (n, nextInner) {
                    process.stdout.write(char);
                    var start = new Date().getTime();
                    sample(function() {
                        var end = new Date().getTime();
                        sumOfTimePerSample += (end - start);
                        nextInner();
                    });
                },
                function (err) {
                    nextOuter(err);
                }
            );
        },
        function(err, res) {
            process.stdout.write('\n');
            if (err) { callback(err); return; }
            var perSample = sumOfTimePerSample / SAMPLES_PER_RUN;
            var totalElapsed = new Date().getTime() - start;
            callback(null, {
                perSample: perSample,
                totalElapsed: totalElapsed,
                fullGCs: fullGCs - startFullGCs,
                incrGCs: incrGCs - startIncrGCs,
                leaked: leaked - startLeaked
            });
        }
    );
};

function createSampleFunction() {
    var moduleId = './' + SELECTED_FUNCTION + '/' + SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
    var selectedFunction = rewire(moduleId);
    if (USE_MOCK_FS) selectedFunction.__set__('fs', require('./mockfs'));
    switch (SELECTED_FUNCTION) {
        case functions.countFiles:
            var dirToCheck = path.join(__dirname, '.');
            var sample = function (callback) {
                selectedFunction(dirToCheck, function (err, result) {
                    setImmediate(callback, err, result);
                });
            };
            break;

        case functions.fibonacci:
            var n = 5;
            var sample = function (callback) {
                selectedFunction(n, function (err, result) {
                    setImmediate(callback, err, result);
                });
            };
            break;

        case functions.largest:
            var dirToCheck = path.join(__dirname, '.');
            var options = { recurse: true, preview: true };
            var sample = function (callback) {
                selectedFunction(dirToCheck, options, function (err, result) {
                    setImmediate(callback, err, result);
                });
            };
            break;

    }
    return sample;
}
<commit_msg>benchmark: work without memwatch<commit_after>var path = require('path');
var async = require('async'); // NB: async is used here in the benchmarking code, in case co or
                              // asyncawait won't run on the version of node being benchmarked.
var _ = require('lodash');
try { var memwatch = require('memwatch'); } catch (ex) { }
var rewire = require('rewire');

// Functions available for benchmarking.
var functions = {
    countFiles: 'countFiles',
    fibonacci: 'fibonacci',
    largest: 'largest'

};

// Variants available for benchmarking.
var variants = {
    async: 'async',
    asyncawait: 'asyncawait',
    asyncx: 'asyncx',
    bluebird: 'bluebird',
    callbacks: 'callbacks',
    co: 'co',
    synchronous: 'synchronous'
};

// ================================================================================
// Benchmark configuration - adjust to suit.

var SELECTED_FUNCTION = functions.largest;

var SELECTED_VARIANT = variants.asyncawait;

var SAMPLES_PER_RUN = 100;   // How many times the function will be called per run.

var RUNS_PER_BENCHMARK = 10;  // How many runs make up the whole benchmark.

var CONCURRENCY_FACTOR = 10;  // Max number of concurrent invocations of the function.

// Some additional switches
var JUST_CHECK_THE_FUNCTION = false;            // If true, just call the function once and display its results.
var USE_SAME_SYMBOL_FOR_ALL_SAMPLES = true;     // If true, all samples will use the same symbol ('.'). Otherwise, concurrent samples will use distinct symbols.
var USE_MOCK_FS = false;                         // If true, uses a mocked 'fs' module returning fixed in-memory results.
var OUTPUT_GC_STATS = true;                     // If true, indicate GC pauses and statistics, and indicate possible memory leaks.
var OUTPUT_SAMPLES_PER_SEC_SUMMARY = false;     // If true, print all samples/sec numbers at the end, to export for anaysis (eg for charting).

// ================================================================================

// Set up memory diagnostics
var fullGCs = 0;
var incrGCs = 0;
var leaked = 0;
if (OUTPUT_GC_STATS && memwatch) {
    memwatch.on('leak', function(info) {
        leaked += info.growth;
        process.stdout.write(' [LEAK+' + info.growth +'] ');
    });
    memwatch.on('stats', function(stats) {
        fullGCs = stats.num_full_gc;
        incrGCs = stats.num_inc_gc;
        process.stdout.write(' [GC] ');
    });
}

// Run the benchmark (or just check the function).
if (JUST_CHECK_THE_FUNCTION) {
    var name = SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
    var sample = createSampleFunction();
    console.log("========== CHECKING '" + name + "': ==========");
    sample(function(err, result) {
        console.log(err || result);
        if (OUTPUT_GC_STATS) {
            console.log("========== GCs: " + fullGCs + 'full/' + incrGCs + "incr ==========");
            console.log("========== Leaked: " + leaked + " ==========");
        }
    });
} else {
    benchmark();
}

function benchmark() {
    var name = SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
    var sample = createSampleFunction();
    var allSamplesPerSec = [];
    console.log('========== PERFORMING ' + RUNS_PER_BENCHMARK + " RUNS ON '" + name + "': ==========\n");
    var times = [];
    async.timesSeries(
        RUNS_PER_BENCHMARK,
        function (n, next) {
            process.stdout.write('RUN ' + (n + 1));
            run(sample, function (err, timing) {
                if (err) {
                    next(err);
                } else {
                    times.push(timing.totalElapsed);
                    allSamplesPerSec.push(SAMPLES_PER_RUN * 1000.0 / timing.totalElapsed);
                    var msg = SAMPLES_PER_RUN
                        + ' samples took '
                        + (timing.totalElapsed / 1000.0)
                        + ' seconds ('
                        + (SAMPLES_PER_RUN * 1000.0 / timing.totalElapsed)
                        + ' samples/sec), average latency per sample: '
                        + timing.perSample
                        + 'ms';
                    if (OUTPUT_GC_STATS) {
                        msg = msg
                            + ', GCs: '
                            + timing.fullGCs
                            + 'full/'
                            + timing.incrGCs
                            + 'incr, leaked: '
                            + timing.leaked;
                    }
                    console.log(msg + '\n');
                    next();
                }
            });
        },
        function (err) {
            if (err) {
                console.log(err);
                process.exit();
            } else {
                totalTime = _.reduce(times, function (sum, time) { return sum + time; });
                var averageTime = totalTime / RUNS_PER_BENCHMARK;
                    var msg = 'Average time: '
                        + (averageTime / 1000.0)
                        + ' seconds ('
                        + (SAMPLES_PER_RUN * 1000.0 / averageTime)
                        + ' samples/sec)';
                console.log('========== ' + msg + ' ==========');
                if (OUTPUT_GC_STATS) {
                    console.log("========== GCs: " + fullGCs + 'full/' + incrGCs + "incr ==========");
                    console.log("========== Leaked: " + leaked + " ==========");
                }
                if (OUTPUT_SAMPLES_PER_SEC_SUMMARY) {
                    console.log("========== Summary of samples/sec for all runs: ==========");
                    console.log(allSamplesPerSec.join(', '));
                }
            }
        });
}

function run(sample, callback) {
    var chars = USE_SAME_SYMBOL_FOR_ALL_SAMPLES ? '.' : './#$@%^&*+!=-?~`|()[]ABCDEFGHIJKLMNOPQRS';
    var start = new Date().getTime();
    var startFullGCs = fullGCs;
    var startIncrGCs = incrGCs;
    var startLeaked = leaked;
    var sumOfTimePerSample = 0.0;
    async.times(
        CONCURRENCY_FACTOR,
        function (m, nextOuter) {
            var char = chars.charAt(m % chars.length);
            async.timesSeries(
                1.0 * SAMPLES_PER_RUN / CONCURRENCY_FACTOR,
                function (n, nextInner) {
                    process.stdout.write(char);
                    var start = new Date().getTime();
                    sample(function() {
                        var end = new Date().getTime();
                        sumOfTimePerSample += (end - start);
                        nextInner();
                    });
                },
                function (err) {
                    nextOuter(err);
                }
            );
        },
        function(err, res) {
            process.stdout.write('\n');
            if (err) { callback(err); return; }
            var perSample = sumOfTimePerSample / SAMPLES_PER_RUN;
            var totalElapsed = new Date().getTime() - start;
            callback(null, {
                perSample: perSample,
                totalElapsed: totalElapsed,
                fullGCs: fullGCs - startFullGCs,
                incrGCs: incrGCs - startIncrGCs,
                leaked: leaked - startLeaked
            });
        }
    );
};

function createSampleFunction() {
    var moduleId = './' + SELECTED_FUNCTION + '/' + SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
    var selectedFunction = rewire(moduleId);
    if (USE_MOCK_FS) selectedFunction.__set__('fs', require('./mockfs'));
    switch (SELECTED_FUNCTION) {
        case functions.countFiles:
            var dirToCheck = path.join(__dirname, '.');
            var sample = function (callback) {
                selectedFunction(dirToCheck, function (err, result) {
                    setImmediate(callback, err, result);
                });
            };
            break;

        case functions.fibonacci:
            var n = 5;
            var sample = function (callback) {
                selectedFunction(n, function (err, result) {
                    setImmediate(callback, err, result);
                });
            };
            break;

        case functions.largest:
            var dirToCheck = path.join(__dirname, '.');
            var options = { recurse: true, preview: true };
            var sample = function (callback) {
                selectedFunction(dirToCheck, options, function (err, result) {
                    setImmediate(callback, err, result);
                });
            };
            break;

    }
    return sample;
}
<|endoftext|>


<commit_before>$(document).ready(function () {
    var t = window.openerp.test_support;

    t.module('check', 'data');
    t.test('check1', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'res.users', {});
        t.expect(ds.create({name: 'foo'}), function (result) {
            ok(false, 'ha ha ha')
        });
    });
});
<commit_msg>[ADD] a bunch of tests of the dataset API (translation of javascript dataset calls to XMLRPC ones)<commit_after>$(document).ready(function () {
    var t = window.openerp.test_support;

    t.module('Dataset shortcuts', 'data');
    t.test('read_index', function (openerp) {
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'some.model');
        ds.ids = [10, 20, 30, 40, 50];
        ds.index = 2;
        t.expect(ds.read_index(['a', 'b', 'c']), function (result) {
            strictEqual(result.method, 'read');
            strictEqual(result.model, 'some.model');

            strictEqual(result.args.length, 3);
            deepEqual(result.args[0], [30]);
            deepEqual(result.args[1], ['a', 'b', 'c']);

            ok(_.isEmpty(result.kwargs));
        });
    });
    t.test('default_get', function (openerp) {
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'some.model', {foo: 'bar'});
        t.expect(ds.default_get(['a', 'b', 'c']), function (result) {
            strictEqual(result.method, 'default_get');
            strictEqual(result.model, 'some.model');

            strictEqual(result.args.length, 2);
            deepEqual(result.args[0], ['a', 'b', 'c']);
            // FIXME: args[1] is context w/ user context, where to get? Hardcode?
            strictEqual(result.args[1].foo, 'bar');

            ok(_.isEmpty(result.kwargs));
        });
    });
    t.test('create', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'some.model');
        t.expect(ds.create({foo: 1, bar: 2}), function (r) {
            strictEqual(r.method, 'create');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], {foo: 1, bar: 2});

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('write', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.write(42, {foo: 1}), function (r) {
            strictEqual(r.method, 'write');

            strictEqual(r.args.length, 3);
            deepEqual(r.args[0], [42]);
            deepEqual(r.args[1], {foo: 1});

            ok(_.isEmpty(r.kwargs));
        });
        // FIXME: can't run multiple sessions in the same test(), fucks everything up
//        t.expect(ds.write(42, {foo: 1}, { context: {lang: 'bob'} }), function (r) {
//            strictEqual(r.args.length, 3);
//            strictEqual(r.args[2].lang, 'bob');
//        });
    });
    t.test('unlink', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.unlink([42]), function (r) {
            strictEqual(r.method, 'unlink');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], [42]);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('call', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.call('frob', ['a', 'b', 42]), function (r) {
            strictEqual(r.method, 'frob');

            strictEqual(r.args.length, 3);
            deepEqual(r.args, ['a', 'b', 42]);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_get', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_get([1, 2], null), function (r) {
            strictEqual(r.method, 'name_get');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], [1, 2]);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search, name', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_search('bob'), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], 'bob');
            // domain
            deepEqual(r.args[1], []);
            strictEqual(r.args[2], 'ilike');
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search, domain & operator', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_search(0, [['foo', '=', 3]], 'someop'), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], '');
            // domain
            deepEqual(r.args[1], [['foo', '=', 3]]);
            strictEqual(r.args[2], 'someop');
            // limit
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('exec_workflow', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.exec_workflow(42, 'foo'), function (r) {
            strictEqual(r['service'], 'object');
            strictEqual(r.method, 'exec_workflow');

            // db, id, password, model, method, id
            strictEqual(r.args.length, 6);
            strictEqual(r.args[4], 'foo');
            strictEqual(r.args[5], 42);
        });
    });
    // TODO: SearchDataSet#read_slice?
    // TODO: non-literal domains and contexts basics
    // TODO: call_and_eval
    // TODO: name_search, non-literal domains

});
<|endoftext|>


<commit_before>/*
	Basic Commands
*/

Settings.addPermissions(['say']);

exports.commands = {
	credits: 'about',
	bot: 'about',
	about: function () {
		this.restrictReply(this.trad('about') + ". " + this.trad('author') + ": " + Settings.package.author.name + ". (" + Settings.package.homepage + ")", 'info');
	},

	suspect: function () {
		if (!this.isRanked('+')) return false;
		this.reply('Little Cup is currently suspecting Drifloon! Check it out here:' + " (http://www.smogon.com/forums/threads/lc-suspect-black-balloon.3575912/)", 'info');
	},

	viability: function () {
		if (!this.isRanked('+')) return false;
		this.reply(this.trad('viability') + " (http://www.smogon.com/forums/threads/lc-viability-rankings-2-0.3547566/page-5#post-6519836)", 'info');
	},

	introductions: function () {
		if (!this.isRanked('+')) return false;
		this.reply(this.trad('introductions') + " (http://www.smogon.com/forums/threads/useful-lc-stuff-introductions-simple-questions-simple-answers.3491009/)", 'info');
	},

	timburr: function () {
		if (!this.isRanked('+')) return false;
		this.reply(this.trad('timburr') + " (http://www.smogon.com/forums/threads/metagame-discussion-thread.3505710/page-55#post-6586231)", 'info');
	},

	git: 'github',
	github: function () {
		if (Settings.package.repository) this.restrictReply(Tools.stripCommands(Settings.package.repository.url), 'info');
	},

	botversion: 'version',
	version: function () {
		this.restrictReply(Tools.stripCommands(Settings.package.version), 'info');
	},

	guide: 'help',
	botguide: 'help',
	help: function () {
		this.restrictReply(this.trad('guide') + ': ' + (Config.botguide || (Settings.package.homepage + "/blob/master/commands/README.md")), 'info');
	},

	bottime: 'time',
	time: function () {
		var f = new Date();
		this.restrictReply("**" + this.trad('time') + ":** __" + f.toString() + "__", 'info');
	},

	uptime: function () {
		var text = '';
		text += '**Uptime:** ';
		var divisors = [52, 7, 24, 60, 60];
		var units = [this.trad('week'), this.trad('day'), this.trad('hour'), this.trad('minute'), this.trad('second')];
		var buffer = [];
		var uptime = ~~(process.uptime());
		do {
			var divisor = divisors.pop();
			var unit = uptime % divisor;
			if (!unit) {
				units.pop();
				uptime = ~~(uptime / divisor);
				continue;
			}
			buffer.push(unit > 1 ? unit + ' ' + units.pop() + 's' : unit + ' ' + units.pop());
			uptime = ~~(uptime / divisor);
		} while (uptime);

		switch (buffer.length) {
		case 5:
			text += buffer[4] + ', ';
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 4:
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 3:
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 2:
			text += buffer[1] + ' ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 1:
			text += buffer[0];
			break;
		}
		this.restrictReply(text, 'info');
	},

	   insult: function (target, room, user) {
		if (!this.isRanked('%')) return false;
        if (this.roomType !== 'chat' || !this.can('insult')) return;
        if (!target) return;

        var arrayA = [
            'the dude who got shit on by Pawniard on ladder and changed his Timburr to Spritzee',
            'guess you couldve gone all the way after critting me 4 times in our dpp ou playoffs game but hey not quite there yet in terms of skill',
            'bet you played more mons than me in the last 2 weeks alone good job "not caring"',
            'if i cared about mons half as much as you do id jerk off to the unbelievable thought of you winning once in your life',
            'i hate it when dumbass bitches like you make retarded moves and choke the whole fucking game away that way',
            'i swear to fucking god how can a single player be this lucky after getting played all the fucking way',
            'no person using a team that shit and standard and fucking horrible in ORAS should ever be gaining any of my respect whatsoever because its disgusting and a waste of time',
            'arent you the guy whose friend got fucking trashbagged by me in what 12 minutes? while he cheered for him',
            'arent you the guy whose friend got fucking trashbagged by me in what 12 minutes? while he cheered for him',
            'something is very wrong with your head to begin with, seeing how you cant handle a timer consisting of 300 seconds in a game designed for children and actually start to hyperventilate'];

        var arrayB = [
            'Maybe you should shut the fuck up instead of calling out other people',
            'Get real',
            'Why are you still posting',
            'You almost ruined our season by just existing',
            'You are a mere slave',
            'I am here to show Smogon how fucking awful you are at everything',
            'You\'re useless for everything everywhere on Smogon',
            'I wish the people i played werent actually worthless',
            'Die in a ditch and dont ever mention my name again',
            'This felt like the special olympics of ORAS'];

        var arrayC = [
            'nut-hugger',
            'dumbass baboon',
            'glorified heap of trash',
            'filth',
            'stinky italian',
            'worthless protoplasm',
            'random grinch',
            'useless sack of shit',
            'no one on a site that means nothing',
            'literal trash'];

        var returnInfo = [arrayA[Math.floor(Math.random() * 10)], arrayB[Math.floor(Math.random() * 10)], arrayC[Math.floor(Math.random() * 10)]];
        this.reply('Oh, its ' + target + ', ' + returnInfo[0] + '. ' + returnInfo[1] + ' you ' + returnInfo[2] + '.');
    },

    	flirt: function (target, room, user) {
		if (!this.isRanked('%')) return false;
        if (this.roomType !== 'chat' || !this.can('flirt')) return;
        if (!target) return;

        var lines = [
       		'you are hotter than the bottom of my laptop.',
       		'if i could rearrange the alphabet, i\'d put my dick in your butt.',
       		'my mom thinks i\'m gay, can you help me prove her wrong?',
       		'there are 8 planets in the universe, but only 7 after I destroy Uranus',
       		'you know why I am like a squirrel? Cause I want to bury my nuts in you.',
       		'I like every bone in your body, especially mine.',
       		'I`m no weatherman, but you can expect a few inches tonight.',
       		'You make me want to calibrate my joystick without the latest drivers',
       		'is that a keg in your pants? Because I’d love to tap that ass'
        	];
        var line = Math.floor(Math.random() * lines.length);
        this.reply('Hey ' + target + ', ' + lines[line]);
    },

	seen: function (arg, by, room, cmd) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		if (arg === toId(Bot.status.nickName)) return this.pmReply(this.trad('bot'));
		if (arg === toId(by)) return this.pmReply(this.trad('self'));
		var dSeen = Settings.userManager.getSeen(arg);
		if (dSeen) {
			text += '**' + (dSeen.name || arg).trim() + '** ' + this.trad('s1') + ' __' + Tools.getTimeAgo(dSeen.time, this.language).trim() + (this.trad('s2') ? ('__ ' + this.trad('s2')) : '__');
			if (dSeen.room) {
				switch (dSeen.action) {
					case 'j':
						text += ', ' + this.trad('j') + ' <<' + dSeen.room + '>>';
						break;
					case 'l':
						text += ', ' + this.trad('l') + ' <<' + dSeen.room + '>>';
						break;
					case 'c':
						text += ', ' + this.trad('c') + ' <<' + dSeen.room + '>>';
						break;
					case 'n':
						text += ', ' + this.trad('n') + ' **' + dSeen.args[0] + '**';
						break;
				}
			}
		} else {
			text += this.trad('n1') + ' ' + arg + ' ' + this.trad('n2');
		}
		this.pmReply(text);
	},

	publicalts: 'alts',
	alts: function (arg) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		var alts = Settings.userManager.getAlts(arg);
		if (alts && alts.length) {
			if (this.can("alts")) {
				var cmds = [];
				var toAdd;
				text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": ";
				for (var i = 0; i < alts.length; i++) {
					toAdd = alts[i] + (i < alts.length - 1 ? ", " : "");
					if ((text + toAdd).length > 300) {
						cmds.push(text);
						text = "";
					}
					text += toAdd;
				}
				if (text.length) cmds.push(text);
				this.pmReply(cmds);
				return;
			} else {
				if (alts.length <= 10) {
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + alts.join(", ");
				} else {
					var fAlts = [];
					for (var i = alts.length - 1; i >= 0 && i > alts.length - 10; i--) {
						fAlts.push(alts[i]);
					}
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + fAlts.join(", ") + ", (" + (alts.length - 10) + this.trad('more') + ")";
				}
			}
		} else {
			text += this.trad('n') + ' ' +  Settings.userManager.getName(arg);
		}
		this.pmReply(text);
	},

	say: function (arg) {
		if (!arg) return;
		if (!this.can('say')) return;
		this.reply(Tools.stripCommands(arg));
	}
};<commit_msg>Add dab command<commit_after>/*
	Basic Commands
*/

Settings.addPermissions(['say']);

exports.commands = {
	credits: 'about',
	bot: 'about',
	about: function () {
		this.restrictReply(this.trad('about') + ". " + this.trad('author') + ": " + Settings.package.author.name + ". (" + Settings.package.homepage + ")", 'info');
	},

	suspect: function () {
		if (!this.isRanked('+')) return false;
		this.reply('Little Cup is currently suspecting Drifloon! Check it out here:' + " (http://www.smogon.com/forums/threads/lc-suspect-black-balloon.3575912/)", 'info');
	},

	viability: function () {
		if (!this.isRanked('+')) return false;
		this.reply(this.trad('viability') + " (http://www.smogon.com/forums/threads/lc-viability-rankings-2-0.3547566/page-5#post-6519836)", 'info');
	},

	introductions: function () {
		if (!this.isRanked('+')) return false;
		this.reply(this.trad('introductions') + " (http://www.smogon.com/forums/threads/useful-lc-stuff-introductions-simple-questions-simple-answers.3491009/)", 'info');
	},

	timburr: function () {
		if (!this.isRanked('+')) return false;
		this.reply(this.trad('timburr') + " (http://www.smogon.com/forums/threads/metagame-discussion-thread.3505710/page-55#post-6586231)", 'info');
	},

	git: 'github',
	github: function () {
		if (Settings.package.repository) this.restrictReply(Tools.stripCommands(Settings.package.repository.url), 'info');
	},

	botversion: 'version',
	version: function () {
		this.restrictReply(Tools.stripCommands(Settings.package.version), 'info');
	},

	guide: 'help',
	botguide: 'help',
	help: function () {
		this.restrictReply(this.trad('guide') + ': ' + (Config.botguide || (Settings.package.homepage + "/blob/master/commands/README.md")), 'info');
	},

	bottime: 'time',
	time: function () {
		var f = new Date();
		this.restrictReply("**" + this.trad('time') + ":** __" + f.toString() + "__", 'info');
	},

	uptime: function () {
		var text = '';
		text += '**Uptime:** ';
		var divisors = [52, 7, 24, 60, 60];
		var units = [this.trad('week'), this.trad('day'), this.trad('hour'), this.trad('minute'), this.trad('second')];
		var buffer = [];
		var uptime = ~~(process.uptime());
		do {
			var divisor = divisors.pop();
			var unit = uptime % divisor;
			if (!unit) {
				units.pop();
				uptime = ~~(uptime / divisor);
				continue;
			}
			buffer.push(unit > 1 ? unit + ' ' + units.pop() + 's' : unit + ' ' + units.pop());
			uptime = ~~(uptime / divisor);
		} while (uptime);

		switch (buffer.length) {
		case 5:
			text += buffer[4] + ', ';
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 4:
			text += buffer[3] + ', ';
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 3:
			text += buffer[2] + ', ' + buffer[1] + ', ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 2:
			text += buffer[1] + ' ' + this.trad('and') + ' ' + buffer[0];
			break;
		case 1:
			text += buffer[0];
			break;
		}
		this.restrictReply(text, 'info');
	},

	insult: function (target, room, user) {
		if (!this.isRanked('%')) return false;
        	if (this.roomType !== 'chat' || !this.can('insult')) return;
        	if (!target) return;

        var arrayA = [
            'the dude who got shit on by Pawniard on ladder and changed his Timburr to Spritzee',
            'guess you couldve gone all the way after critting me 4 times in our dpp ou playoffs game but hey not quite there yet in terms of skill',
            'bet you played more mons than me in the last 2 weeks alone good job "not caring"',
            'if i cared about mons half as much as you do id jerk off to the unbelievable thought of you winning once in your life',
            'i hate it when dumbass bitches like you make retarded moves and choke the whole fucking game away that way',
            'i swear to fucking god how can a single player be this lucky after getting played all the fucking way',
            'no person using a team that shit and standard and fucking horrible in ORAS should ever be gaining any of my respect whatsoever because its disgusting and a waste of time',
            'arent you the guy whose friend got fucking trashbagged by me in what 12 minutes? while he cheered for him',
            'arent you the guy whose friend got fucking trashbagged by me in what 12 minutes? while he cheered for him',
            'something is very wrong with your head to begin with, seeing how you cant handle a timer consisting of 300 seconds in a game designed for children and actually start to hyperventilate'];

        var arrayB = [
            'Maybe you should shut the fuck up instead of calling out other people',
            'Get real',
            'Why are you still posting',
            'You almost ruined our season by just existing',
            'You are a mere slave',
            'I am here to show Smogon how fucking awful you are at everything',
            'You\'re useless for everything everywhere on Smogon',
            'I wish the people i played werent actually worthless',
            'Die in a ditch and dont ever mention my name again',
            'This felt like the special olympics of ORAS'];

        var arrayC = [
            'nut-hugger',
            'dumbass baboon',
            'glorified heap of trash',
            'filth',
            'stinky italian',
            'worthless protoplasm',
            'random grinch',
            'useless sack of shit',
            'no one on a site that means nothing',
            'literal trash'];

        var returnInfo = [arrayA[Math.floor(Math.random() * 10)], arrayB[Math.floor(Math.random() * 10)], arrayC[Math.floor(Math.random() * 10)]];
        this.reply('Oh, its ' + target + ', ' + returnInfo[0] + '. ' + returnInfo[1] + ' you ' + returnInfo[2] + '.');
    },

    flirt: function (target, room, user) {
	if (!this.isRanked('%')) return false;
        if (this.roomType !== 'chat' || !this.can('flirt')) return;
        if (!target) return;

        var lines = [
       		'you are hotter than the bottom of my laptop.',
       		'if i could rearrange the alphabet, i\'d put my dick in your butt.',
       		'my mom thinks i\'m gay, can you help me prove her wrong?',
       		'there are 8 planets in the universe, but only 7 after I destroy Uranus',
       		'you know why I am like a squirrel? Cause I want to bury my nuts in you.',
       		'I like every bone in your body, especially mine.',
       		'I`m no weatherman, but you can expect a few inches tonight.',
       		'You make me want to calibrate my joystick without the latest drivers',
       		'is that a keg in your pants? Because I’d love to tap that ass'
        	];
        var line = Math.floor(Math.random() * lines.length);
        this.reply('Hey ' + target + ', ' + lines[line]);
    },

    	dab: function (arg, by, room, cmd) { // this is for you, tcr
    		if (!this.isRanked('%')) return false;
		var dab = 'dabs';
		if (arg.length > 1) {
			dab += ' on ' + by;
		}
		this.reply('/me ' + dab);
    	},

	seen: function (arg, by, room, cmd) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		if (arg === toId(Bot.status.nickName)) return this.pmReply(this.trad('bot'));
		if (arg === toId(by)) return this.pmReply(this.trad('self'));
		var dSeen = Settings.userManager.getSeen(arg);
		if (dSeen) {
			text += '**' + (dSeen.name || arg).trim() + '** ' + this.trad('s1') + ' __' + Tools.getTimeAgo(dSeen.time, this.language).trim() + (this.trad('s2') ? ('__ ' + this.trad('s2')) : '__');
			if (dSeen.room) {
				switch (dSeen.action) {
					case 'j':
						text += ', ' + this.trad('j') + ' <<' + dSeen.room + '>>';
						break;
					case 'l':
						text += ', ' + this.trad('l') + ' <<' + dSeen.room + '>>';
						break;
					case 'c':
						text += ', ' + this.trad('c') + ' <<' + dSeen.room + '>>';
						break;
					case 'n':
						text += ', ' + this.trad('n') + ' **' + dSeen.args[0] + '**';
						break;
				}
			}
		} else {
			text += this.trad('n1') + ' ' + arg + ' ' + this.trad('n2');
		}
		this.pmReply(text);
	},

	publicalts: 'alts',
	alts: function (arg) {
		var text = '';
		arg = toId(arg);
		if (!arg || arg.length > 18) return this.pmReply(this.trad('inv'));
		var alts = Settings.userManager.getAlts(arg);
		if (alts && alts.length) {
			if (this.can("alts")) {
				var cmds = [];
				var toAdd;
				text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": ";
				for (var i = 0; i < alts.length; i++) {
					toAdd = alts[i] + (i < alts.length - 1 ? ", " : "");
					if ((text + toAdd).length > 300) {
						cmds.push(text);
						text = "";
					}
					text += toAdd;
				}
				if (text.length) cmds.push(text);
				this.pmReply(cmds);
				return;
			} else {
				if (alts.length <= 10) {
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + alts.join(", ");
				} else {
					var fAlts = [];
					for (var i = alts.length - 1; i >= 0 && i > alts.length - 10; i--) {
						fAlts.push(alts[i]);
					}
					text += this.trad('alts') + " " + Settings.userManager.getName(arg) + ": " + fAlts.join(", ") + ", (" + (alts.length - 10) + this.trad('more') + ")";
				}
			}
		} else {
			text += this.trad('n') + ' ' +  Settings.userManager.getName(arg);
		}
		this.pmReply(text);
	},

	say: function (arg) {
		if (!arg) return;
		if (!this.can('say')) return;
		this.reply(Tools.stripCommands(arg));
	}
};
<|endoftext|>