<commit_before>import Module from 'Parser/Core/Module';
import SPELLS from 'common/SPELLS';
import Combatants from 'Parser/Core/Modules/Combatants';
import HealingValue from 'Parser/Core/Modules/HealingValue';

import { ABILITIES_AFFECTED_BY_HEALING_INCREASES } from '../../Constants';
import { HEALS_MASTERY_STACK } from '../../Constants';

const MASTERY_BONUS_FROM_ONE_RATING = 1 / 66666.6666666;
const BASE_MASTERY_PERCENT = 0.048;

class Mastery extends Module {
  static dependencies = {
    combatants: Combatants,
  };

  totalNoMasteryHealing = 0;
  druidSpellNoMasteryHealing = 0;
  masteryTimesHealing = 0;

  /*
   * Keeps track of the attribution of healing done by your hots. Each spellId gets an entry with a 'direct' and 'mastery' field.
   * The 'direct' field just tracks the direct healing from the HoT, should be same as entry in WCL. Includes the benefit from own stack of Mastery.
   * The 'mastery' field tracks which spells were boosted by the presence of this HoT, and by how much.
   * The total healing attributable to the presence of the HoT is the sum of the direct field and all the attributions in the mastery field.
   * While the attribution to any one HoT is correctly counted by simply summing, there are double counts when considering multiple HoTs together.
   */
  hotHealingAttrib = {};

  /*
   * Tracks common Mastery buff IDs, their strength, and the healing attributed to them over an encounter.
   */
  masteryBuffs = {};

  on_initialized() {
    HEALS_MASTERY_STACK.forEach(healId => this.hotHealingAttrib[healId] = { direct: 0, mastery: {} });

    this.masteryBuffs = {
      [SPELLS.ASTRAL_HARMONY.id]: { amount: 4000 },
      [SPELLS.JACINS_RUSE.id]: { amount: 3000 },
    };
    Object.values(this.masteryBuffs).forEach(entry => entry.attributableHealing = 0);
  }

  // TODO handle pre proc mastery buffs? Looks like pre-hots are already handled by the system.

  on_byPlayer_heal(event) {
    const spellId = event.ability.guid;
    const target = this.combatants.getEntity(event);
    const healVal = new HealingValue(event.amount, event.absorbed, event.overheal);

    if (target === null) {
      return;
    }

    if (spellId in this.hotHealingAttrib) { this.hotHealingAttrib[spellId].direct += healVal.effective; }

    if (ABILITIES_AFFECTED_BY_HEALING_INCREASES.includes(spellId)) {
      const hotsOn = target.activeBuffs()
          .map(buffObj => buffObj.ability.guid)
          .filter(buffId => HEALS_MASTERY_STACK.includes(buffId));
      const numHotsOn = hotsOn.length;
      const decomposedHeal = this._decompHeal(healVal, numHotsOn);

      this.totalNoMasteryHealing += decomposedHeal.noMastery;
      this.druidSpellNoMasteryHealing += decomposedHeal.noMastery;
      this.masteryTimesHealing += decomposedHeal.noMastery * numHotsOn;

      hotsOn
          .filter(hotOn => hotOn !== spellId) // don't double count
          .forEach(hotOn => this._tallyMasteryBenefit(hotOn, spellId, decomposedHeal.oneStack));

      Object.entries(this.masteryBuffs)
          .filter(entry => this.combatants.selected.hasBuff(entry[0]))
          .forEach(entry => entry[1].attributableHealing += decomposedHeal.oneRating * entry[1].amount);
    } else {
      this.totalNoMasteryHealing += healVal.effective;
    }
  }

  on_byPlayer_absorbed(event) {
    this.totalNoMasteryHealing += event.amount;
  }

  /* accessors for computed values */

  /*
   * Gets the direct healing attributed to the given resto HoT ID
   */
  getDirectHealing(healId) {
    return this.hotHealingAttrib[healId].direct;
  }

  /*
   * Gets the total mastery healing attributed to the given resto HoT ID
   */
  getMasteryHealing(healId) {
    return Object.values(this.hotHealingAttrib[healId].mastery)
        .reduce((s, v) => s + v, 0);
  }

  /*
   * Gets the total healing attributable to the given resto HoT IDs.
   * Counts both direct and by mastery, and avoids the mastery/direct double count issue between the hots.
   */
  getMultiMasteryHealing(healIds) {
    let total = 0;
    healIds.forEach(healId => {
      total += Object.entries(this.hotHealingAttrib[healId].mastery)
        .filter(entry => !healIds.includes(parseInt(entry[0], 10)))
        .reduce((sum, entry) => sum + entry[1], 0);
      total += this.hotHealingAttrib[healId].direct;
    });
    return total;
  }

  /*
   * Gets detailed direct / mastery healing attribution info from the given resto HoT ID
   */
  getHealingDetails(healId) {
    return this.hotHealingAttrib[healId];
  }

  /*
   * Gets the total mastery healing attributed to the given mastery buff ID
   */
  getBuffBenefit(buffId) {
    return this.masteryBuffs[buffId].attributableHealing;
  }

  /**
   * This is the average number of mastery stacks the player's heals benefitted from, weighted by healing done.
   * Heals and absorbs that don't benefit from mastery are counted as zero mastery stack heals.
   */
  getAverageTotalMasteryStacks() {
    return this.masteryTimesHealing / this.totalNoMasteryHealing;
  }

  /**
   * This is the average number of mastery stacks the player's heals benefitted from, weighted by healing done.
   * Only heals that benefit from mastery are counted.
   */
  getAverageDruidSpellMasteryStacks() {
    return this.masteryTimesHealing / this.druidSpellNoMasteryHealing;
  }

  _tallyMasteryBenefit(hotId, healId, amount) {
    const hotMastery = this.hotHealingAttrib[hotId].mastery;
    if(hotMastery[healId]) {
      hotMastery[healId] += amount;
    } else {
      hotMastery[healId] = amount;
    }
  }

  _decompHeal(healVal, hotCount) {
    const masteryBonus = this._getCurrMasteryBonus();
    const healMasteryMult = 1 + (hotCount * masteryBonus);

    const rawNoMasteryHealing = healVal.raw / healMasteryMult;
    const noMasteryHealing = Math.min(rawNoMasteryHealing, healVal.effective);

    const masteryHealing = healVal.effective - noMasteryHealing;
    const oneStackMasteryHealing = masteryHealing / hotCount;

    // FIXME still using the old way to calculate one rating because it's less obvious how to update it
    const oldNoMasteryHealing = healVal.effective / healMasteryMult;
    const oneRatingMasteryHealing = oldNoMasteryHealing * MASTERY_BONUS_FROM_ONE_RATING * hotCount;

    return {
      noMastery: noMasteryHealing,
      oneStack: oneStackMasteryHealing,
      oneRating: oneRatingMasteryHealing,
    };
  }

  _getCurrMasteryBonus() {
    let currMasteryRating = this.combatants.selected.masteryRating;
    Object.entries(this.masteryBuffs)
        .filter(entry => this.combatants.selected.hasBuff(entry[0]))
        .forEach(entry => currMasteryRating += entry[1].amount);
    return BASE_MASTERY_PERCENT + (currMasteryRating * MASTERY_BONUS_FROM_ONE_RATING);
  }
}

export default Mastery;
<commit_msg>[resto druid] Committed additional not added bits<commit_after>import Module from 'Parser/Core/Module';
import SPELLS from 'common/SPELLS';
import Combatants from 'Parser/Core/Modules/Combatants';
import HealingValue from 'Parser/Core/Modules/HealingValue';

import { ABILITIES_AFFECTED_BY_HEALING_INCREASES } from '../../Constants';
import { HEALS_MASTERY_STACK } from '../../Constants';

const MASTERY_BONUS_FROM_ONE_RATING = 1 / 66666.6666666;
const BASE_MASTERY_PERCENT = 0.048;

class Mastery extends Module {
  static dependencies = {
    combatants: Combatants,
  };

  totalNoMasteryHealing = 0;
  druidSpellNoMasteryHealing = 0;
  masteryTimesHealing = 0;

  /*
   * Keeps track of the attribution of healing done by your hots. Each spellId gets an entry with a 'direct' and 'mastery' field.
   * The 'direct' field just tracks the direct healing from the HoT, should be same as entry in WCL. Includes the benefit from own stack of Mastery.
   * The 'mastery' field tracks which spells were boosted by the presence of this HoT, and by how much.
   * The total healing attributable to the presence of the HoT is the sum of the direct field and all the attributions in the mastery field.
   * While the attribution to any one HoT is correctly counted by simply summing, there are double counts when considering multiple HoTs together.
   */
  hotHealingAttrib = {};

  /*
   * Tracks common Mastery buff IDs, their strength, and the healing attributed to them over an encounter.
   */
  masteryBuffs = {};

  on_initialized() {
    HEALS_MASTERY_STACK.forEach(healId => this.hotHealingAttrib[healId] = { direct: 0, mastery: {} });

    this.masteryBuffs = {
      [SPELLS.ASTRAL_HARMONY.id]: { amount: 4000 },
      [SPELLS.JACINS_RUSE.id]: { amount: 3000 },
    };
    Object.values(this.masteryBuffs).forEach(entry => entry.attributableHealing = 0);
  }

  // TODO handle pre proc mastery buffs? Looks like pre-hots are already handled by the system.

  on_byPlayer_heal(event) {
    const spellId = event.ability.guid;
    const target = this.combatants.getEntity(event);
    const healVal = new HealingValue(event.amount, event.absorbed, event.overheal);

    if (target === null) {
      return;
    }

    if (spellId in this.hotHealingAttrib) { this.hotHealingAttrib[spellId].direct += healVal.effective; }

    if (ABILITIES_AFFECTED_BY_HEALING_INCREASES.includes(spellId)) {
      const hotsOn = target.activeBuffs()
          .map(buffObj => buffObj.ability.guid)
          .filter(buffId => HEALS_MASTERY_STACK.includes(buffId));
      const numHotsOn = hotsOn.length;
      const decomposedHeal = this._decompHeal(healVal, numHotsOn);

      this.totalNoMasteryHealing += decomposedHeal.noMastery;
      this.druidSpellNoMasteryHealing += decomposedHeal.noMastery;
      this.masteryTimesHealing += decomposedHeal.noMastery * decomposedHeal.effectiveStackBenefit;

      hotsOn
          .filter(hotOn => hotOn !== spellId) // don't double count
          .forEach(hotOn => this._tallyMasteryBenefit(hotOn, spellId, decomposedHeal.oneStack));

      Object.entries(this.masteryBuffs)
          .filter(entry => this.combatants.selected.hasBuff(entry[0]))
          .forEach(entry => entry[1].attributableHealing += decomposedHeal.oneRating * entry[1].amount);
    } else {
      this.totalNoMasteryHealing += healVal.effective;
    }
  }

  on_byPlayer_absorbed(event) {
    this.totalNoMasteryHealing += event.amount;
  }

  /* accessors for computed values */

  /*
   * Gets the direct healing attributed to the given resto HoT ID
   */
  getDirectHealing(healId) {
    return this.hotHealingAttrib[healId].direct;
  }

  /*
   * Gets the total mastery healing attributed to the given resto HoT ID
   */
  getMasteryHealing(healId) {
    return Object.values(this.hotHealingAttrib[healId].mastery)
        .reduce((s, v) => s + v, 0);
  }

  /*
   * Gets the total healing attributable to the given resto HoT IDs.
   * Counts both direct and by mastery, and avoids the mastery/direct double count issue between the hots.
   */
  getMultiMasteryHealing(healIds) {
    let total = 0;
    healIds.forEach(healId => {
      total += Object.entries(this.hotHealingAttrib[healId].mastery)
        .filter(entry => !healIds.includes(parseInt(entry[0], 10)))
        .reduce((sum, entry) => sum + entry[1], 0);
      total += this.hotHealingAttrib[healId].direct;
    });
    return total;
  }

  /*
   * Gets detailed direct / mastery healing attribution info from the given resto HoT ID
   */
  getHealingDetails(healId) {
    return this.hotHealingAttrib[healId];
  }

  /*
   * Gets the total mastery healing attributed to the given mastery buff ID
   */
  getBuffBenefit(buffId) {
    return this.masteryBuffs[buffId].attributableHealing;
  }

  /**
   * This is the average number of mastery stacks the player's heals benefitted from, weighted by healing done.
   * Heals and absorbs that don't benefit from mastery are counted as zero mastery stack heals.
   */
  getAverageTotalMasteryStacks() {
    return this.masteryTimesHealing / this.totalNoMasteryHealing;
  }

  /**
   * This is the average number of mastery stacks the player's heals benefitted from, weighted by healing done.
   * Only heals that benefit from mastery are counted.
   */
  getAverageDruidSpellMasteryStacks() {
    return this.masteryTimesHealing / this.druidSpellNoMasteryHealing;
  }

  _tallyMasteryBenefit(hotId, healId, amount) {
    const hotMastery = this.hotHealingAttrib[hotId].mastery;
    if(hotMastery[healId]) {
      hotMastery[healId] += amount;
    } else {
      hotMastery[healId] = amount;
    }
  }

  _decompHeal(healVal, hotCount) {
    const masteryBonus = this._getCurrMasteryBonus();
    const healMasteryMult = 1 + (hotCount * masteryBonus);

    const rawNoMasteryHealing = healVal.raw / healMasteryMult;
    const noMasteryHealing = Math.min(rawNoMasteryHealing, healVal.effective);

    // because Mastery is a bonus on top of the base healing, all overhealing is counted against Mastery
    const effectiveMasteryHealing = healVal.effective - noMasteryHealing;
    // when Mastery bonus is partially but not completely overhealing, the stacks equally share attribution
    const oneStackMasteryHealingEffective = effectiveMasteryHealing / hotCount;

    const oneStackMasteryHealingRaw = rawNoMasteryHealing * masteryBonus;
    // the number of mastery stacks that we actually benefitted from once overheal is considered.
    // if this heal didn't overheal at all, will be the same as hotCount
    const effectiveStackBenefit = effectiveMasteryHealing / oneStackMasteryHealingRaw;

    // FIXME still using the old way to calculate one rating because it's less obvious how to update it
    const oldNoMasteryHealing = healVal.effective / healMasteryMult;
    const oneRatingMasteryHealing = oldNoMasteryHealing * MASTERY_BONUS_FROM_ONE_RATING * hotCount;

    return {
      noMastery: noMasteryHealing,
      oneStack: oneStackMasteryHealingEffective,
      effectiveStackBenefit: effectiveStackBenefit,
      oneRating: oneRatingMasteryHealing,
    };
  }

  _getCurrMasteryBonus() {
    let currMasteryRating = this.combatants.selected.masteryRating;
    Object.entries(this.masteryBuffs)
        .filter(entry => this.combatants.selected.hasBuff(entry[0]))
        .forEach(entry => currMasteryRating += entry[1].amount);
    return BASE_MASTERY_PERCENT + (currMasteryRating * MASTERY_BONUS_FROM_ONE_RATING);
  }
}

export default Mastery;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  const Cc = Components.classes;
  const Ci = Components.interfaces;
  const Pref = Cc['@mozilla.org/preferences;1']
                 .getService(Ci.nsIPrefBranch);
  const kIEPath = "C:\\Program Files\\Internet Explorer\\iexplore.exe";
  const kChromePath = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
  const kPrefPrefix = "extensions.switch-link-external-handler@clear-code.com.";
  const kIEPatternsPref = kPrefPrefix +  "ie.patterns.";
  const kChromePatternsPref = kPrefPrefix + "chrome.patterns.";
  var SwitchLinkExternalHandler = {
    startIE: function startIE(aURL) {
      this.startExternalProcess(kIEPath, aURL);
    },
    startChrome: function startChrome(aURL) {
      this.startExternalProcess(kChromePath, aURL);
    },
    startExternalProcess: function startExternalProcess(aPath, aURL) {
      var process = Cc["@mozilla.org/process/util;1"]
                      .createInstance(Ci.nsIProcess);
      var file = Cc["@mozilla.org/file/local;1"]
                   .createInstance(Ci.nsILocalFile);
      var args = [aURL];
      file.initWithPath(aPath);
      process.init(file);
      process.run(false, args, args.length);
    },
    run: function run(aURL, aEvent) {
      if (aURL.match(this.ChromeMatcher)) {
        this.startChrome(aURL);
        aEvent.preventDefault();
        aEvent.stopPropagation();
      }
      else if (aURL.match(this.IEMatcher)) {
        this.startIE(aURL);
        aEvent.preventDefault();
        aEvent.stopPropagation();
      }
    },
    get IEMatcher() {
      delete this.IEMatcher;
      return this.IEMatcher = this.urlMatcher(kIEPatternsPref);
    },
    get ChromeMatcher() {
      delete this.ChromeMatcher;
      return this.ChromeMatcher = this.urlMatcher(kChromePatternsPref);
    },
    urlMatcher: function urlMatcher(aPatternsPref) {
      var patterns = [];
      Pref.getChildList(aPatternsPref, {}).forEach(function(aPref) {
        try {
          if (Pref.getPrefType(aPref) != Pref.PREF_STRING)
            return;
          let pattern = this.getStringPref(aPref, '');
          pattern = pattern.trim();
          if (!pattern)
            return;

          pattern = pattern.replace(/([$^\|.{}\[\]()+\\])/g, "$1")
                           .replace(/\*/g, ".*")
                           .replace(/\?/g, ".");
          patterns.push(pattern);
        }
        catch(e) {
        }
      }, this);
      if (patterns.length > 0) {
        return new RegExp("^(" + patterns.join("|") + ")", "i");
      }
      else {
        return null;
      }
    },
    getStringPref: function getStringPref(aKey, aDefault) {
      try {
        return Pref.getComplexValue(aKey, Ci.nsISupportsString).data;
      }
      catch(e) {
      }
      return aDefault || '';
    },
    setStringPref: function setStringPref(aKey, aValue) {
      var str = Cc["@mozilla.org/supports-string;1"]
                  .createInstance(Ci.nsISupportsString);
      str.data = aValue;
      try {
        Pref.setComplexValue(aKey, Ci.nsISupportsString, str);
      }
      catch(e) {
      }
    }
  };

  var browser = document.getElementById("messagepane");
  browser.addEventListener("click", function onClick(aEvent) {
    let href = hRefForClickEvent(aEvent);
    SwitchLinkExternalHandler.run(href, aEvent);
  }, true);
  aGlobal.SwitchLinkExternalHandler = SwitchLinkExternalHandler;
})(this);
<commit_msg>Simplify<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  const Cc = Components.classes;
  const Ci = Components.interfaces;
  const Pref = Cc['@mozilla.org/preferences;1']
                 .getService(Ci.nsIPrefBranch);
  const kIEPath = "C:\\Program Files\\Internet Explorer\\iexplore.exe";
  const kChromePath = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
  const kPrefPrefix = "extensions.switch-link-external-handler@clear-code.com.";
  const kIEPatternsPref = kPrefPrefix +  "ie.patterns.";
  const kChromePatternsPref = kPrefPrefix + "chrome.patterns.";
  var SwitchLinkExternalHandler = {
    startIE: function startIE(aURL) {
      this.startExternalProcess(kIEPath, aURL);
    },
    startChrome: function startChrome(aURL) {
      this.startExternalProcess(kChromePath, aURL);
    },
    startExternalProcess: function startExternalProcess(aPath, aURL) {
      var process = Cc["@mozilla.org/process/util;1"]
                      .createInstance(Ci.nsIProcess);
      var file = Cc["@mozilla.org/file/local;1"]
                   .createInstance(Ci.nsILocalFile);
      var args = [aURL];
      file.initWithPath(aPath);
      process.init(file);
      process.run(false, args, args.length);
    },
    onLinkClick: function onLinkClick(aEvent) {
      let href = hRefForClickEvent(aEvent);

      if (href.match(this.ChromeMatcher)) {
        this.startChrome(href);
      }
      else if (href.match(this.IEMatcher)) {
        this.startIE(href);
      }
      else {
        return;
      }
      aEvent.preventDefault();
      aEvent.stopPropagation();
    },
    get IEMatcher() {
      delete this.IEMatcher;
      return this.IEMatcher = this.urlMatcher(kIEPatternsPref);
    },
    get ChromeMatcher() {
      delete this.ChromeMatcher;
      return this.ChromeMatcher = this.urlMatcher(kChromePatternsPref);
    },
    urlMatcher: function urlMatcher(aPatternsPref) {
      var patterns = [];
      Pref.getChildList(aPatternsPref, {}).forEach(function(aPref) {
        try {
          if (Pref.getPrefType(aPref) != Pref.PREF_STRING)
            return;
          let pattern = this.getStringPref(aPref, '');
          pattern = pattern.trim();
          if (!pattern)
            return;

          pattern = pattern.replace(/([$^\|.{}\[\]()+\\])/g, "$1")
                           .replace(/\*/g, ".*")
                           .replace(/\?/g, ".");
          patterns.push(pattern);
        }
        catch(e) {
        }
      }, this);
      if (patterns.length > 0) {
        return new RegExp("^(" + patterns.join("|") + ")", "i");
      }
      else {
        return null;
      }
    },
    getStringPref: function getStringPref(aKey, aDefault) {
      try {
        return Pref.getComplexValue(aKey, Ci.nsISupportsString).data;
      }
      catch(e) {
      }
      return aDefault || '';
    },
    setStringPref: function setStringPref(aKey, aValue) {
      var str = Cc["@mozilla.org/supports-string;1"]
                  .createInstance(Ci.nsISupportsString);
      str.data = aValue;
      try {
        Pref.setComplexValue(aKey, Ci.nsISupportsString, str);
      }
      catch(e) {
      }
    }
  };

  var browser = document.getElementById("messagepane");
  browser.addEventListener("click", function onClick(aEvent) {
    SwitchLinkExternalHandler.onLinkClick(aEvent);
  }, true);
  aGlobal.SwitchLinkExternalHandler = SwitchLinkExternalHandler;
})(this);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  var Ci = Components.interfaces;
  var Cc = Components.classes;

  var tbBug766495 = {
    collectAccounts: function() {
      var accountManager = MailServices.accounts;
      var allServers = accountManager.allServers;
      for (var i = 0,  maxi = allServers.length; i < maxi; ++i) {
        let currentServer = allServers.queryElementAt(i, Ci.nsIMsgIncomingServer);
        if (currentServer.type == "none")
          continue;

        var folderURI = currentServer.rootFolder.server.serverURI + "/" + "Drafts";
      }
    }
  };

  document.addEventListener("DOMContentLoaded", function onDOMContentLoaded(aEvent) {
    document.removeEventListener("DOMContentLoaded", onDOMContentLoaded);
    tbBug766495.collectAccounts();
  });
  aGlobal.tbBug766495 = tbBug766495;
})(this);
<commit_msg>Rename a method<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  var Ci = Components.interfaces;
  var Cc = Components.classes;

  var tbBug766495 = {
    collectDraftFolders: function() {
      var accountManager = MailServices.accounts;
      var allServers = accountManager.allServers;
      for (var i = 0,  maxi = allServers.length; i < maxi; ++i) {
        let currentServer = allServers.queryElementAt(i, Ci.nsIMsgIncomingServer);
        if (currentServer.type == "none")
          continue;

        var folderURI = currentServer.rootFolder.server.serverURI + "/" + "Drafts";
      }
    }
  };

  document.addEventListener("DOMContentLoaded", function onDOMContentLoaded(aEvent) {
    document.removeEventListener("DOMContentLoaded", onDOMContentLoaded);
    tbBug766495.collectDraftFolders();
  });
  aGlobal.tbBug766495 = tbBug766495;
})(this);
<|endoftext|>


<commit_before><commit_msg>devilry_subjectadmin...CreateNewAssignment: Handle submission and pubdates gracefully.<commit_after><|endoftext|>


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

/**
 * @fileoverview Externs definitions for Mocha, 2.5 branch.
 *
 * This file currently only defines the TDD API, and that part should be
 * complete.
 *
 * @externs
 * @see https://mochajs.org/
 */

// Below are the externs for the TDD API: https://mochajs.org/#tdd

/**
 * @param {string} name
 * @param {!Function} cb
 */
var suite = function(name, cb) {};

/**
 * @param {!Function} cb
 */
var setup = function(cb) {};

/**
 * @param {!Function} cb
 */
var teardown = function(cb) {};

/**
 * @param {!Function} cb
 */
var suiteSetup = function(cb) {};

/**
 * @param {!Function} cb
 */
var suiteTeardown = function(cb) {};

/**
 * @param {string} name
 * @param {!Function} cb
 */
var test = function(name, cb) {};

// Below are the externs for the BDD API: https://mochajs.org/#bdd

/**
 * @typedef {function(function(*=): *): (*|IThenable<*>)}
 */
var ActionFunction;

/**
 * @param {string} description
 * @param {function(): void} spec
 */
var describe = function(description, spec) {};

/**
 * @param {string} description
 * @param {function(): void} spec
 */
var context = function(description, spec) {};

/**
 * @param {string} expectation
 * @param {ActionFunction=} assertion
 */
var it = function(expectation, assertion) {};

/**
 * @param {string} expectation
 * @param {ActionFunction=} assertion
 */
var specify = function(expectation, assertion) {};

/**
 * @param {ActionFunction} action
 */
var before = function(action) {};

/**
 * @param {ActionFunction} action
 */
var after = function(action) {};

/**
 * @param {ActionFunction} action
 */
var beforeEach = function(action) {};

/**
 * @param {ActionFunction} action
 */
var afterEach = function(action) {};
<commit_msg>Update the mocha TDD externs to be explicit about the function type.<commit_after>/*
 * Copyright 2016 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Externs definitions for Mocha, 2.5 branch.
 *
 * This file currently only defines the TDD API, and that part should be
 * complete.
 *
 * @externs
 * @see https://mochajs.org/
 */

/**
 * @typedef {function(function(*=): *): (*|IThenable<*>)}
 */
var ActionFunction;

// Below are the externs for the TDD API: https://mochajs.org/#tdd

/**
 * @param {string} description
 * @param {function(): void} spec
 */
var suite = function(description, spec) {};

/**
 * @param {!ActionFunction} action
 */
var setup = function(action) {};

/**
 * @param {!ActionFunction} action
 */
var teardown = function(action) {};

/**
 * @param {!ActionFunction} action
 */
var suiteSetup = function(action) {};

/**
 * @param {!ActionFunction} action
 */
var suiteTeardown = function(action) {};

/**
 * @param {string} expectation
 * @param {!ActionFunction=} assertion
 */
var test = function(expectation, assertion) {};

// Below are the externs for the BDD API: https://mochajs.org/#bdd

/**
 * @param {string} description
 * @param {function(): void} spec
 */
var describe = function(description, spec) {};

/**
 * @param {string} description
 * @param {function(): void} spec
 */
var context = function(description, spec) {};

/**
 * @param {string} expectation
 * @param {!ActionFunction=} assertion
 */
var it = function(expectation, assertion) {};

/**
 * @param {string} expectation
 * @param {!ActionFunction=} assertion
 */
var specify = function(expectation, assertion) {};

/**
 * @param {!ActionFunction} action
 */
var before = function(action) {};

/**
 * @param {!ActionFunction} action
 */
var after = function(action) {};

/**
 * @param {!ActionFunction} action
 */
var beforeEach = function(action) {};

/**
 * @param {!ActionFunction} action
 */
var afterEach = function(action) {};
<|endoftext|>


<commit_before>//include after animation.js

var errorLabelElement = $('#debug-port-error');
var debugContainer = $('#debug-toggle-container');
var stateMachineSpeed = $('#animation-speed-input').val();

//try to load from sessionStorage
var port = sessionStorage['diagnosticsPort'];
$('#debug-port-input').val(port);

var isPolling = false;
if(sessionStorage['isPolling'] == "true") {
	isPolling = true;
	$('#toggle-debug-checkbox').attr("checked", false);
	pollingRefresh();
}

/**
 * Queries the server for diagnostics information.
 * If successful, updates the diagram with `setActiveElements` from `animation.js`
 * If not, clears active elements, and alerts the user.
 */
function refreshElements(){
	$.ajax({
		url: 'http://localhost:' + port + '/' + DIAGNOSTICS_PATH,
		type: 'GET',
		dataType: 'json'
	}).complete(function(response){
		if(response.status == 200 && isPolling){
			setActiveElements((JSON.parse(response.responseText))
				.map(entry => _visualizer.getShapeIdByElementName(entry.location)));
			hideError();
		}
		else{
			clearCurrentActiveElements();
			if(isPolling){
				showError();
			}else{
				hideError();
			}
		}
	});
}

function pollingRefresh(){
	refreshElements();
	if(isPolling){
		setTimeout(pollingRefresh, REFRESH_INTERVAL_IN_MILLISECONDS);
	}
}

//add event listener to the checkbox
$("#toggle-debug-checkbox").on("change", function(){
	if(!this.checked){
		//on
		isPolling = true;
		sessionStorage['isPolling'] = true;
		pollingRefresh();
	}
	else{
		//off
		isPolling = false;
		sessionStorage['isPolling'] = false;
		hideError();
		clearCurrentActiveElements();
	}
});

function showError(){
	errorLabelElement.slideDown();
}

function hideError(){
	errorLabelElement.slideUp();
}

var typingTimer;
var DONE_TYPING_TIMEOUT_IN_MILLISECONDS = 500;

function doneTyping(){
	port = $('#debug-port-input').val();
	sessionStorage['diagnosticsPort'] = port;
}

//add event listeners to the debug input
$('#debug-port-input').on('keyup change input', function(){
	clearTimeout(typingTimer);
	if($('#debug-port-input').val()){
		typingTimer = setTimeout(doneTyping, DONE_TYPING_TIMEOUT_IN_MILLISECONDS);
	}
});

//send animation delay to the server
function sendSMTime(){
	$.ajax({
		url: 'http://localhost:' + port + '/delay',
		type: 'POST',
		dataType: 'json',
		data: {delayTime:stateMachineSpeed},
	    success: function(response) {
        	if(response.status == 200 && isPolling){
				setActiveElements((JSON.parse(response.responseText))
					.map(entry => _visualizer.getShapeIdByElementName(entry.location)));
				hideError();
			}
			else{
				clearCurrentActiveElements();
				if(isPolling){
					showError();
				}else{
					hideError();
				}
			}
    	}
	});
}

//add event listeners to the state machine's speed range
$('#animation-speed-input').on('click', function(){
	stateMachineSpeed = $('#animation-speed-input').val();
	sendSMTime();
});<commit_msg>Change click to change in event listener<commit_after>//include after animation.js

var errorLabelElement = $('#debug-port-error');
var debugContainer = $('#debug-toggle-container');
var stateMachineSpeed = $('#animation-speed-input').val();

//try to load from sessionStorage
var port = sessionStorage['diagnosticsPort'];
$('#debug-port-input').val(port);

var isPolling = false;
if(sessionStorage['isPolling'] == "true") {
	isPolling = true;
	$('#toggle-debug-checkbox').attr("checked", false);
	pollingRefresh();
}

/**
 * Queries the server for diagnostics information.
 * If successful, updates the diagram with `setActiveElements` from `animation.js`
 * If not, clears active elements, and alerts the user.
 */
function refreshElements(){
	$.ajax({
		url: 'http://localhost:' + port + '/' + DIAGNOSTICS_PATH,
		type: 'GET',
		dataType: 'json'
	}).complete(function(response){
		if(response.status == 200 && isPolling){
			setActiveElements((JSON.parse(response.responseText))
				.map(entry => _visualizer.getShapeIdByElementName(entry.location)));
			hideError();
		}
		else{
			clearCurrentActiveElements();
			if(isPolling){
				showError();
			}else{
				hideError();
			}
		}
	});
}

function pollingRefresh(){
	refreshElements();
	if(isPolling){
		setTimeout(pollingRefresh, REFRESH_INTERVAL_IN_MILLISECONDS);
	}
}

//add event listener to the checkbox
$("#toggle-debug-checkbox").on("change", function(){
	if(!this.checked){
		//on
		isPolling = true;
		sessionStorage['isPolling'] = true;
		pollingRefresh();
	}
	else{
		//off
		isPolling = false;
		sessionStorage['isPolling'] = false;
		hideError();
		clearCurrentActiveElements();
	}
});

function showError(){
	errorLabelElement.slideDown();
}

function hideError(){
	errorLabelElement.slideUp();
}

var typingTimer;
var DONE_TYPING_TIMEOUT_IN_MILLISECONDS = 500;

function doneTyping(){
	port = $('#debug-port-input').val();
	sessionStorage['diagnosticsPort'] = port;
}

//add event listeners to the debug input
$('#debug-port-input').on('keyup change input', function(){
	clearTimeout(typingTimer);
	if($('#debug-port-input').val()){
		typingTimer = setTimeout(doneTyping, DONE_TYPING_TIMEOUT_IN_MILLISECONDS);
	}
});

//send animation delay to the server
function sendSMTime(){
	$.ajax({
		url: 'http://localhost:' + port + '/delay',
		type: 'POST',
		dataType: 'json',
		data: {delayTime:stateMachineSpeed},
	    success: function(response) {
        	if(response.status == 200 && isPolling){
				setActiveElements((JSON.parse(response.responseText))
					.map(entry => _visualizer.getShapeIdByElementName(entry.location)));
				hideError();
			}
			else{
				clearCurrentActiveElements();
				if(isPolling){
					showError();
				}else{
					hideError();
				}
			}
    	}
	});
}

//add event listeners to the state machine's speed range
$('#animation-speed-input').on('change', function(){
	stateMachineSpeed = $('#animation-speed-input').val();
	sendSMTime();
});<|endoftext|>


<commit_before>import { render } from '@bolt/twig-renderer';
import { fixture as html } from '@open-wc/testing-helpers';

const { readYamlFileSync } = require('@bolt/build-tools/utils/yaml');
const { join } = require('path');
const schema = readYamlFileSync(join(__dirname, '../text.schema.yml'));
const {
  tag,
  display,
  color,
  align,
  opacity,
  quoted,
  'line-height': lineHeight,
  'letter-spacing': letterSpacing,
  'text-transform': textTransform,
  'font-family': fontFamily,
  'font-size': fontSize,
  'font-style': fontStyle,
  'font-weight': fontWeight,
} = schema.properties;

const timeout = 90000;

describe('<bolt-text> Component', () => {
  let page;

  beforeEach(async () => {
    page = await global.__BROWSER__.newPage();
    await page.goto('http://127.0.0.1:4444/', {
      timeout: 0,
      waitLoad: true,
      waitNetworkIdle: true, // defaults to false
    });
  }, timeout);

  // Basic Usage (Shadow DOM)
  test('Default <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is regular text`;
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // Code text (Shadow DOM)
  test('Code text using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is code text`;
      text.setAttribute('font-family', 'code');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // xxxlarge Headline (Shadow DOM)
  test('xxxlarge headline using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is xxxlarge headline`;
      text.setAttribute('headline', true);
      text.setAttribute('font-size', 'xxxlarge');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // xxxlarge Headline (Light DOM)
  test('xxxlarge headline using <bolt-text> w/o Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is xxxlarge headline`;
      text.setAttribute('headline', true);
      text.setAttribute('font-size', 'xxxlarge');
      document.body.appendChild(text);
      text.useShadow = false;
      text.updated();

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(
      renderedHTML
        .querySelector('.c-bolt-text-v2')
        .classList.contains('c-bolt-text-v2--font-size-xxxlarge'),
    ).toBe(true);

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // All caps xxlarge bold quote (Shadow DOM)
  test('All caps xxlarge bold quote using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is a quote`;
      text.setAttribute('quoted', true);
      text.setAttribute('letter-spacing', 'wide');
      text.setAttribute('text-transform', 'uppercase');
      text.setAttribute('font-size', 'xxlarge');
      text.setAttribute('font-weight', 'bold');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // All caps xxlarge bold quote (Light DOM)
  test('All caps xxlarge bold quote using <bolt-text> w/o Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is a quote`;
      text.setAttribute('quoted', true);
      text.setAttribute('letter-spacing', 'wide');
      text.setAttribute('text-transform', 'uppercase');
      text.setAttribute('font-size', 'xxlarge');
      text.setAttribute('font-weight', 'bold');
      document.body.appendChild(text);
      text.useShadow = false;
      text.updated();

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(
      renderedHTML
        .querySelector('.c-bolt-text-v2')
        .classList.contains(
          'c-bolt-text-v2--quoted',
          'c-bolt-text-v2--letter-spacing-wide',
          'c-bolt-text-v2--text-transform-uppercase',
          'c-bolt-text-v2--font-size-xxlarge',
          'c-bolt-text-v2--font-weight-bold',
        ),
    ).toBe(true);

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // @todo: Bold, wide letter spacing, color, and quoted
});
<commit_msg>test: test for 'is-long' class when headline > 60 chars<commit_after>import { render } from '@bolt/twig-renderer';
import { fixture as html } from '@open-wc/testing-helpers';

const { readYamlFileSync } = require('@bolt/build-tools/utils/yaml');
const { join } = require('path');
const schema = readYamlFileSync(join(__dirname, '../text.schema.yml'));
const {
  tag,
  display,
  color,
  align,
  opacity,
  quoted,
  'line-height': lineHeight,
  'letter-spacing': letterSpacing,
  'text-transform': textTransform,
  'font-family': fontFamily,
  'font-size': fontSize,
  'font-style': fontStyle,
  'font-weight': fontWeight,
} = schema.properties;

const timeout = 90000;

describe('<bolt-text> Component', () => {
  let page;

  beforeEach(async () => {
    page = await global.__BROWSER__.newPage();
    await page.goto('http://127.0.0.1:4444/', {
      timeout: 0,
      waitLoad: true,
      waitNetworkIdle: true, // defaults to false
    });
  }, timeout);

  // Basic Usage (Shadow DOM)
  test('Default <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is regular text`;
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // Code text (Shadow DOM)
  test('Code text using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is code text`;
      text.setAttribute('font-family', 'code');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // xxxlarge Headline (Shadow DOM)
  test('xxxlarge headline using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is xxxlarge headline`;
      text.setAttribute('headline', true);
      text.setAttribute('font-size', 'xxxlarge');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // xxxlarge Headline (Light DOM)
  test('xxxlarge headline using <bolt-text> w/o Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is xxxlarge headline`;
      text.setAttribute('headline', true);
      text.setAttribute('font-size', 'xxxlarge');
      document.body.appendChild(text);
      text.useShadow = false;
      text.updated();

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(
      renderedHTML
        .querySelector('.c-bolt-text-v2')
        .classList.contains('c-bolt-text-v2--font-size-xxxlarge'),
    ).toBe(true);

    expect(
      renderedHTML
        .querySelector('.c-bolt-text-v2')
        .classList.contains('is-long'),
    ).toBe(false);

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // Long xxxlarge Headline (Shadow DOM)
  test('xxxlarge headline using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam in gravida ex.`;
      text.setAttribute('headline', true);
      text.setAttribute('font-size', 'xxxlarge');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // Long xxxlarge Headline (Light DOM)
  test('xxxlarge headline using <bolt-text> w/o Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam in gravida ex.`;
      text.setAttribute('headline', true);
      text.setAttribute('font-size', 'xxxlarge');
      document.body.appendChild(text);
      text.useShadow = false;
      text.updated();

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(
      renderedHTML
        .querySelector('.c-bolt-text-v2')
        .classList.contains('is-long'),
    ).toBe(true);

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // All caps xxlarge bold quote (Shadow DOM)
  test('All caps xxlarge bold quote using <bolt-text> w/ Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is a quote`;
      text.setAttribute('quoted', true);
      text.setAttribute('letter-spacing', 'wide');
      text.setAttribute('text-transform', 'uppercase');
      text.setAttribute('font-size', 'xxlarge');
      text.setAttribute('font-weight', 'bold');
      document.body.appendChild(text);

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // All caps xxlarge bold quote (Light DOM)
  test('All caps xxlarge bold quote using <bolt-text> w/o Shadow DOM renders', async function() {
    const renderedTextHTML = await page.evaluate(() => {
      const text = document.createElement('bolt-text');

      text.textContent = `This is a quote`;
      text.setAttribute('quoted', true);
      text.setAttribute('letter-spacing', 'wide');
      text.setAttribute('text-transform', 'uppercase');
      text.setAttribute('font-size', 'xxlarge');
      text.setAttribute('font-weight', 'bold');
      document.body.appendChild(text);
      text.useShadow = false;
      text.updated();

      return text.outerHTML;
    });

    const renderedHTML = await html(renderedTextHTML);
    const image = await page.screenshot();

    expect(
      renderedHTML
        .querySelector('.c-bolt-text-v2')
        .classList.contains(
          'c-bolt-text-v2--quoted',
          'c-bolt-text-v2--letter-spacing-wide',
          'c-bolt-text-v2--text-transform-uppercase',
          'c-bolt-text-v2--font-size-xxlarge',
          'c-bolt-text-v2--font-weight-bold',
        ),
    ).toBe(true);

    expect(image).toMatchImageSnapshot({
      failureThreshold: '0.01',
      failureThresholdType: 'percent',
    });

    expect(renderedHTML).toMatchSnapshot();
  });

  // @todo: Bold, wide letter spacing, color, and quoted
});
<|endoftext|>


<commit_before>Space.eventSourcing.Aggregate.extend(Donations, `Appeal`, {

  STATES: {
    open: `open`,
    fulfilled: `fulfilled`,
    closed: `closed`
  },

  fields: {
    title: String,
    requiredQuantity: Quantity,
    pledgedQuantity: Quantity,
    organizationId: Guid,
    locationId: Guid,
    pledges: [Donations.Pledge],
    description: String // optional
  },

  commandMap() {
    return {
      'Donations.MakeAppeal': this._makeAppeal,
      'Donations.MakePledge': this._makePledge,
      'Donations.AcceptPledge': this._acceptPledge,
      'Donations.DeclinePledge': this._declinePledge,
      'Donations.FulfillPledge': this._fulfillPledge
    };
  },

  eventMap() {
    return {
      'Donations.AppealMade': this._onAppealMade,
      'Donations.PledgeMade': this._onPledgeMade,
      'Donations.PledgeAccepted': this._onPledgeAccepted,
      'Donations.PledgeDeclined': this._onPledgeDeclined,
      'Donations.PledgeFulfilled': this._onPledgeFulfilled,
      'Donations.AppealFulfilled': this._onAppealFulfilled
    };
  },

  // ============= COMMAND HANDLERS =============

  _makeAppeal(command) {
    this.record(new Donations.AppealMade(this._eventPropsFromCommand(command)));
  },

  _makePledge(command) {
    if (this.hasState(this.STATES.open)) {
      // Pledged quantity is capped at the appeal’s required quantity
      let quantity = command.quantity;
      let newPledgedQuantity = this.pledgedQuantity.add(quantity);
      if (newPledgedQuantity.isMore(this.requiredQuantity)) {
        quantity = quantity.substract(newPledgedQuantity.delta(this.requiredQuantity));
        command.quantity = quantity; // Assign capped quantity
      }
      let pledgedQuantity = this.pledgedQuantity.add(quantity);
      this.record(new Donations.PledgeMade(this._eventPropsFromCommand(command)));
      // Fulfilled when the sum of pledged items equals the required quantity.
      if (pledgedQuantity.equals(this.requiredQuantity)) {
        this.record(new Donations.AppealFulfilled({
          sourceId: this.getId(),
          title: this.title,
          requiredQuantity: this.requiredQuantity,
          organizationId: this.organizationId,
          locationId: this.locationId,
          description: this.description
        }));
      }
    } else {
      throw new Donations.AppealNotOpenForNewPledges();
    }
  },

  _acceptPledge(command) {
    if (this.hasState(this.STATES.open)) {
      this.record(new Donations.PledgeAccepted(_.extend({sourceId: this.getId()}, this._getPledgeById(command.id).toPlainObject())));
    } else {
      throw new Donations.AppealNotOpenToAcceptPledge();
    }

  },

  _declinePledge(command) {
    this.record(new Donations.PledgeDeclined(_.extend({sourceId: this.getId()}, this._getPledgeById(command.id).toPlainObject())));
  },

  _fulfillPledge(command) {
    this.record(new Donations.PledgeFulfilled(_.extend({sourceId: this.getId()}, this._getPledgeById(command.id).toPlainObject())));
  },

  // ============= EVENT HANDLERS =============

  _onAppealMade(event) {
    this._assignFields(event);
    this.pledgedQuantity = new Quantity(0);
    this.pledges = [];
    this._state = this.STATES.open;
  },

  _onPledgeMade(event) {
    this.pledgedQuantity = this.pledgedQuantity.add(event.quantity);
    this.pledges.push(new Donations.Pledge({
      id: event.id,
      donor: event.donor,
      quantity: event.quantity
    }));
  },

  _onAppealFulfilled() {
    this._state = this.STATES.fulfilled;
  },

  _onPledgeAccepted(event) {
    this._getPledgeById(event.id).accept();
  },

  _onPledgeDeclined(event) {
    this._getPledgeById(event.id).decline();
  },

  _onPledgeFulfilled(event) {
    this._getPledgeById(event.id).fulfill();
  },

  // =========== PRIVATE HELPERS ===========

  _getPledgeById(id) {
    let foundPledge = null;
    for (let pledge of this.pledges) {
      if (pledge.getId().equals(id)) {
        foundPledge = pledge;
      }
    }
    if (foundPledge === null) {
      throw new Donations.PledgeNotFoundError(id);
    }
    return foundPledge;
  }

});

Donations.Appeal.registerSnapshotType('Donations.AppealSnapshot');
<commit_msg>Throw errors early<commit_after>Space.eventSourcing.Aggregate.extend(Donations, `Appeal`, {

  STATES: {
    open: `open`,
    fulfilled: `fulfilled`,
    closed: `closed`
  },

  fields: {
    title: String,
    requiredQuantity: Quantity,
    pledgedQuantity: Quantity,
    organizationId: Guid,
    locationId: Guid,
    pledges: [Donations.Pledge],
    description: String // optional
  },

  commandMap() {
    return {
      'Donations.MakeAppeal': this._makeAppeal,
      'Donations.MakePledge': this._makePledge,
      'Donations.AcceptPledge': this._acceptPledge,
      'Donations.DeclinePledge': this._declinePledge,
      'Donations.FulfillPledge': this._fulfillPledge
    };
  },

  eventMap() {
    return {
      'Donations.AppealMade': this._onAppealMade,
      'Donations.PledgeMade': this._onPledgeMade,
      'Donations.PledgeAccepted': this._onPledgeAccepted,
      'Donations.PledgeDeclined': this._onPledgeDeclined,
      'Donations.PledgeFulfilled': this._onPledgeFulfilled,
      'Donations.AppealFulfilled': this._onAppealFulfilled
    };
  },

  // ============= COMMAND HANDLERS =============

  _makeAppeal(command) {
    this.record(new Donations.AppealMade(this._eventPropsFromCommand(command)));
  },

  _makePledge(command) {
    if (!this.hasState(this.STATES.open)) {
      throw new Donations.AppealNotOpenForNewPledges();
    }
    // Pledged quantity is capped at the appeal’s required quantity
    let quantity = command.quantity;
    let newPledgedQuantity = this.pledgedQuantity.add(quantity);
    if (newPledgedQuantity.isMore(this.requiredQuantity)) {
      quantity = quantity.substract(newPledgedQuantity.delta(this.requiredQuantity));
      command.quantity = quantity; // Assign capped quantity
    }
    let pledgedQuantity = this.pledgedQuantity.add(quantity);
    this.record(new Donations.PledgeMade(this._eventPropsFromCommand(command)));
    // Fulfilled when the sum of pledged items equals the required quantity.
    if (pledgedQuantity.equals(this.requiredQuantity)) {
      this.record(new Donations.AppealFulfilled({
        sourceId: this.getId(),
        title: this.title,
        requiredQuantity: this.requiredQuantity,
        organizationId: this.organizationId,
        locationId: this.locationId,
        description: this.description
      }));
    }
  },

  _acceptPledge(command) {
    if (!this.hasState(this.STATES.open)) {
      throw new Donations.AppealNotOpenToAcceptPledge();
    }
    this.record(new Donations.PledgeAccepted(_.extend({ sourceId: this.getId() },
      this._getPledgeById(command.id).toPlainObject()
    )));

  },

  _declinePledge(command) {
    this.record(new Donations.PledgeDeclined(_.extend({ sourceId: this.getId() },
      this._getPledgeById(command.id).toPlainObject()
    )));
  },

  _fulfillPledge(command) {
    this.record(new Donations.PledgeFulfilled(_.extend({ sourceId: this.getId() },
      this._getPledgeById(command.id).toPlainObject()
    )));
  },

  // ============= EVENT HANDLERS =============

  _onAppealMade(event) {
    this._assignFields(event);
    this.pledgedQuantity = new Quantity(0);
    this.pledges = [];
    this._state = this.STATES.open;
  },

  _onPledgeMade(event) {
    this.pledgedQuantity = this.pledgedQuantity.add(event.quantity);
    this.pledges.push(new Donations.Pledge({
      id: event.id,
      donor: event.donor,
      quantity: event.quantity
    }));
  },

  _onAppealFulfilled() {
    this._state = this.STATES.fulfilled;
  },

  _onPledgeAccepted(event) {
    this._getPledgeById(event.id).accept();
  },

  _onPledgeDeclined(event) {
    this._getPledgeById(event.id).decline();
  },

  _onPledgeFulfilled(event) {
    this._getPledgeById(event.id).fulfill();
  },

  // =========== PRIVATE HELPERS ===========

  _getPledgeById(id) {
    let foundPledge = null;
    for (let pledge of this.pledges) {
      if (pledge.getId().equals(id)) {
        foundPledge = pledge;
      }
    }
    if (foundPledge === null) {
      throw new Donations.PledgeNotFoundError(id);
    }
    return foundPledge;
  }

});

Donations.Appeal.registerSnapshotType('Donations.AppealSnapshot');
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import Button from 'fyndiq-component-button'
import { Arrow } from 'fyndiq-icons'

import styles from '../styles.css'

// Note on disabled rules: The component is accessible.
// It might not be built in the perfect way (feel free to redo it)
// but it is accessible. So we can disable the accessibility rules.
/* eslint-disable jsx-a11y/interactive-supports-focus */
/* eslint-disable jsx-a11y/mouse-events-have-key-events */
/* eslint-disable jsx-a11y/click-events-have-key-events */

// FIXME: This function has been duplicated from fyndiq-component-modal
// A better place for this type of function would be in something like fyndiq-utils
const isSameType = (element, component) =>
  React.isValidElement(element) &&
  (element.type === component ||
    (element.type && element.type.displayName === component.name))

class Dropdown extends React.Component {
  static propTypes = {
    opened: PropTypes.bool,
    button: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
      .isRequired,
    children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
    size: PropTypes.string,
    position: PropTypes.oneOf(['bl', 'bc', 'br', 'tl', 'tc', 'tr']),
    className: PropTypes.string,
    wrapperClassName: PropTypes.string,
    margin: PropTypes.number,
    disabled: PropTypes.bool,
    hoverMode: PropTypes.bool,
    noPropagateClickEvent: PropTypes.bool,
    noArrow: PropTypes.bool,
    noWrapperStyle: PropTypes.bool,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
  }

  static defaultProps = {
    opened: false,
    hoverMode: false,
    noArrow: false,
    noWrapperStyle: false,
    size: undefined,
    className: '',
    wrapperClassName: '',
    disabled: false,
    position: 'bl',
    margin: 5,
    noPropagateClickEvent: false,
    onOpen: () => {},
    onClose: () => {},
  }

  constructor(props) {
    super(props)
    this.state = {
      opened: props.opened,
      left: 0,
      top: 0,
    }

    this.onButtonClick = this.onButtonClick.bind(this)
    this.onMouseOver = this.onMouseOver.bind(this)
    this.onMouseOut = this.onMouseOut.bind(this)
    this.handleDocumentClick = this.handleDocumentClick.bind(this)
    this.handleKeypress = this.handleKeypress.bind(this)
    this.closeDropdown = this.closeDropdown.bind(this)
  }

  componentWillMount() {
    document.addEventListener('click', this.handleDocumentClick)
    document.addEventListener('touchend', this.handleDocumentClick)
    document.addEventListener('keypress', this.handleKeypress, true)
  }

  componentDidMount() {
    this.updateDropdownPosition()
  }

  componentDidUpdate(prevProps, prevState) {
    // Call handlers onOpen and onClose if open has changed
    if (prevState.opened !== this.state.opened) {
      if (this.state.opened) {
        this.props.onOpen()
      } else {
        this.props.onClose()
      }
    }
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.handleDocumentClick)
    document.removeEventListener('touchend', this.handleDocumentClick)
    document.removeEventListener('keypress', this.handleKeypress, true)
  }

  onButtonClick() {
    if (this.props.disabled) return

    if (!this.props.hoverMode) {
      this.toggleDropdown()
    } else {
      this.openDropdown()
    }
  }

  onMouseOver() {
    if (this.props.disabled) return

    if (this.props.hoverMode) {
      clearTimeout(this.timeout)
      this.timeout = setTimeout(() => this.openDropdown(), 200)
    }
  }

  onMouseOut() {
    if (this.props.disabled) return

    if (this.props.hoverMode) {
      clearTimeout(this.timeout)
      this.timeout = setTimeout(() => this.closeDropdown(), 100)
    }
  }

  getChildren() {
    // If children prop is a function, pass the close handler
    // to it
    if (typeof this.props.children === 'function') {
      return this.props.children({
        onClose: this.closeDropdown,
      })
    }
    return this.props.children
  }

  updateDropdownPosition() {
    const buttonSize = this.buttonNode.getBoundingClientRect()
    const { position, margin } = this.props
    let left = this.buttonNode.offsetLeft
    let top = this.buttonNode.offsetTop

    // If the dropdown is above the button
    if (position[0] === 't') {
      top -= margin
      // If the dropdown is under the button
    } else {
      top += buttonSize.height + margin
    }

    if (position[1] === 'r') {
      // If the dropdown is on the right
      left += buttonSize.width
    } else if (position[1] === 'c') {
      // If it is in the center
      left += buttonSize.width / 2
    }

    this.setState({ left, top })
  }

  openDropdown() {
    this.updateDropdownPosition()
    this.setState({ opened: true })
  }

  closeDropdown() {
    this.setState({ opened: false })
  }

  toggleDropdown() {
    this.updateDropdownPosition()
    this.setState(state => ({ opened: !state.opened }))
  }

  handleDocumentClick(event) {
    if (this.props.disabled) return

    if (this.wrapperNode && !this.wrapperNode.contains(event.target)) {
      this.closeDropdown()
    }
  }

  handleKeypress(event) {
    if (this.props.disabled) return

    // ESC key
    if (this.state.opened && event.keyCode === 27) {
      this.closeDropdown()
      event.stopImmediatePropagation()
    }
  }

  renderButton() {
    const { button, noArrow, size, position, disabled } = this.props

    // Arrow orientation depends on the position of the dropdown
    const arrowOrientation = position[0] === 't' ? 'up' : 'down'

    if (typeof button === 'string') {
      return (
        <Button
          size={size}
          disabled={disabled}
          onClick={this.onButtonClick}
          buttonRef={e => {
            this.buttonNode = e
          }}
        >
          {button}
          {!noArrow && (
            <Arrow orientation={arrowOrientation} className={styles.arrow} />
          )}
        </Button>
      )
    } else if (isSameType(button, Button)) {
      return React.cloneElement(button, {
        onClick: this.onButtonClick,
        disabled,
        buttonRef: e => {
          this.buttonNode = e
        },
      })
    }

    return (
      <div
        ref={e => {
          this.buttonNode = e
        }}
        onClick={this.onButtonClick}
        role="button"
      >
        {button}
      </div>
    )
  }

  render() {
    const {
      position,
      className,
      wrapperClassName,
      noWrapperStyle,
      noPropagateClickEvent,
    } = this.props

    return (
      <div
        className={`${styles.wrapper} ${className}`}
        ref={e => {
          this.wrapperNode = e
        }}
        onMouseOver={this.onMouseOver}
        onMouseOut={this.onMouseOut}
        onClick={e => noPropagateClickEvent && e.stopPropagation()}
        role="button"
      >
        {this.renderButton()}
        {this.state.opened && (
          <div
            className={`
              ${styles.dropdownWrapper}
              ${!noWrapperStyle ? styles.dropdownDefault : ''}
              ${styles[`position-${position}`]}
              ${wrapperClassName}
            `}
            style={{
              left: this.state.left,
              top: this.state.top,
            }}
          >
            {this.getChildren()}
          </div>
        )}
      </div>
    )
  }
}

export default Dropdown
<commit_msg>:bug: Fix server-side rendering for Dropdown<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import Button from 'fyndiq-component-button'
import { Arrow } from 'fyndiq-icons'

import styles from '../styles.css'

// Note on disabled rules: The component is accessible.
// It might not be built in the perfect way (feel free to redo it)
// but it is accessible. So we can disable the accessibility rules.
/* eslint-disable jsx-a11y/interactive-supports-focus */
/* eslint-disable jsx-a11y/mouse-events-have-key-events */
/* eslint-disable jsx-a11y/click-events-have-key-events */

// FIXME: This function has been duplicated from fyndiq-component-modal
// A better place for this type of function would be in something like fyndiq-utils
const isSameType = (element, component) =>
  React.isValidElement(element) &&
  (element.type === component ||
    (element.type && element.type.displayName === component.name))

class Dropdown extends React.Component {
  static propTypes = {
    opened: PropTypes.bool,
    button: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
      .isRequired,
    children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
    size: PropTypes.string,
    position: PropTypes.oneOf(['bl', 'bc', 'br', 'tl', 'tc', 'tr']),
    className: PropTypes.string,
    wrapperClassName: PropTypes.string,
    margin: PropTypes.number,
    disabled: PropTypes.bool,
    hoverMode: PropTypes.bool,
    noPropagateClickEvent: PropTypes.bool,
    noArrow: PropTypes.bool,
    noWrapperStyle: PropTypes.bool,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
  }

  static defaultProps = {
    opened: false,
    hoverMode: false,
    noArrow: false,
    noWrapperStyle: false,
    size: undefined,
    className: '',
    wrapperClassName: '',
    disabled: false,
    position: 'bl',
    margin: 5,
    noPropagateClickEvent: false,
    onOpen: () => {},
    onClose: () => {},
  }

  constructor(props) {
    super(props)
    this.state = {
      opened: props.opened,
      left: 0,
      top: 0,
    }

    this.onButtonClick = this.onButtonClick.bind(this)
    this.onMouseOver = this.onMouseOver.bind(this)
    this.onMouseOut = this.onMouseOut.bind(this)
    this.handleDocumentClick = this.handleDocumentClick.bind(this)
    this.handleKeypress = this.handleKeypress.bind(this)
    this.closeDropdown = this.closeDropdown.bind(this)
  }

  componentWillMount() {
    // The guard is here in order to handle server-side rendering
    if (global.document && document.addEventListener) {
      document.addEventListener('click', this.handleDocumentClick)
      document.addEventListener('touchend', this.handleDocumentClick)
      document.addEventListener('keypress', this.handleKeypress, true)
    }
  }

  componentDidMount() {
    this.updateDropdownPosition()
  }

  componentDidUpdate(prevProps, prevState) {
    // Call handlers onOpen and onClose if open has changed
    if (prevState.opened !== this.state.opened) {
      if (this.state.opened) {
        this.props.onOpen()
      } else {
        this.props.onClose()
      }
    }
  }

  componentWillUnmount() {
    if (global.document && document.addEventListener) {
      document.removeEventListener('click', this.handleDocumentClick)
      document.removeEventListener('touchend', this.handleDocumentClick)
      document.removeEventListener('keypress', this.handleKeypress, true)
    }
  }

  onButtonClick() {
    if (this.props.disabled) return

    if (!this.props.hoverMode) {
      this.toggleDropdown()
    } else {
      this.openDropdown()
    }
  }

  onMouseOver() {
    if (this.props.disabled) return

    if (this.props.hoverMode) {
      clearTimeout(this.timeout)
      this.timeout = setTimeout(() => this.openDropdown(), 200)
    }
  }

  onMouseOut() {
    if (this.props.disabled) return

    if (this.props.hoverMode) {
      clearTimeout(this.timeout)
      this.timeout = setTimeout(() => this.closeDropdown(), 100)
    }
  }

  getChildren() {
    // If children prop is a function, pass the close handler
    // to it
    if (typeof this.props.children === 'function') {
      return this.props.children({
        onClose: this.closeDropdown,
      })
    }
    return this.props.children
  }

  updateDropdownPosition() {
    const buttonSize = this.buttonNode.getBoundingClientRect()
    const { position, margin } = this.props
    let left = this.buttonNode.offsetLeft
    let top = this.buttonNode.offsetTop

    // If the dropdown is above the button
    if (position[0] === 't') {
      top -= margin
      // If the dropdown is under the button
    } else {
      top += buttonSize.height + margin
    }

    if (position[1] === 'r') {
      // If the dropdown is on the right
      left += buttonSize.width
    } else if (position[1] === 'c') {
      // If it is in the center
      left += buttonSize.width / 2
    }

    this.setState({ left, top })
  }

  openDropdown() {
    this.updateDropdownPosition()
    this.setState({ opened: true })
  }

  closeDropdown() {
    this.setState({ opened: false })
  }

  toggleDropdown() {
    this.updateDropdownPosition()
    this.setState(state => ({ opened: !state.opened }))
  }

  handleDocumentClick(event) {
    if (this.props.disabled) return

    if (this.wrapperNode && !this.wrapperNode.contains(event.target)) {
      this.closeDropdown()
    }
  }

  handleKeypress(event) {
    if (this.props.disabled) return

    // ESC key
    if (this.state.opened && event.keyCode === 27) {
      this.closeDropdown()
      event.stopImmediatePropagation()
    }
  }

  renderButton() {
    const { button, noArrow, size, position, disabled } = this.props

    // Arrow orientation depends on the position of the dropdown
    const arrowOrientation = position[0] === 't' ? 'up' : 'down'

    if (typeof button === 'string') {
      return (
        <Button
          size={size}
          disabled={disabled}
          onClick={this.onButtonClick}
          buttonRef={e => {
            this.buttonNode = e
          }}
        >
          {button}
          {!noArrow && (
            <Arrow orientation={arrowOrientation} className={styles.arrow} />
          )}
        </Button>
      )
    } else if (isSameType(button, Button)) {
      return React.cloneElement(button, {
        onClick: this.onButtonClick,
        disabled,
        buttonRef: e => {
          this.buttonNode = e
        },
      })
    }

    return (
      <div
        ref={e => {
          this.buttonNode = e
        }}
        onClick={this.onButtonClick}
        role="button"
      >
        {button}
      </div>
    )
  }

  render() {
    const {
      position,
      className,
      wrapperClassName,
      noWrapperStyle,
      noPropagateClickEvent,
    } = this.props

    return (
      <div
        className={`${styles.wrapper} ${className}`}
        ref={e => {
          this.wrapperNode = e
        }}
        onMouseOver={this.onMouseOver}
        onMouseOut={this.onMouseOut}
        onClick={e => noPropagateClickEvent && e.stopPropagation()}
        role="button"
      >
        {this.renderButton()}
        {this.state.opened && (
          <div
            className={`
              ${styles.dropdownWrapper}
              ${!noWrapperStyle ? styles.dropdownDefault : ''}
              ${styles[`position-${position}`]}
              ${wrapperClassName}
            `}
            style={{
              left: this.state.left,
              top: this.state.top,
            }}
          >
            {this.getChildren()}
          </div>
        )}
      </div>
    )
  }
}

export default Dropdown
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50, browser: true */
/*global window, CSSShapesEditor */

/*
    Register CSSShapesEditor as a provider for editing shape properties.
    A provider is just a wrapper over custom editors to provide a common interface.

    All providers MUST implement at a minimum this interface:
    {
        // turns on editor on specified target HTMLElement.
        // picks-up necessary setup args from model
        setup: function (target, model) {},

        // update the editor state given the provided model
        update: function (model) {},

        // turn off the editor and remove any scaffolding
        remove: function () {},

        // sets a callback to be called with the new value
        onValueChange: function (callback) {}
    }
*/
(function () {

    "use strict";

    if (!window._LD_CSS_EDITOR || typeof window._LD_CSS_EDITOR.registerProvider !== "function") {
        throw new Error("Missing LiveEditorRemoteDriver from Brackets");
    }

    if (!window.CSSShapesEditor) {
        throw new Error("Missing CSSShapesEditor");
    }

    var _onKeypress;

    function Provider() {}

    Provider.prototype.setup = function (target, model) {
        var scope = this,
            options = {};

        switch (model.property) {
        case "shape-inside":
        case "-webkit-shape-inside":
            options.defaultRefBox = "content-box";
            break;
        case "clip-path":
        case "-webkit-clip-path":
            options.defaultRefBox = "border-box";
            break;
        default:
            options.defaultRefBox = "margin-box";
        }

        // declared here to intentionally encapsulate scope
        // exposed outside, but still private to the module, for use by removeEventHandler
        _onKeypress = function (e) {
            // T key toggles rotate/scale editor just for polygon editor
            if (e.keyCode === 116 && scope.inst.type === "polygon") {
                scope.inst.toggleFreeTransform();
            }
        };

        scope.inst = new CSSShapesEditor(target, model.value, options);
        scope.inst.on("shapechange", function () {
            if (scope.callback) {
                scope.callback.call(scope.inst, scope.inst.getCSSValue());
            }
        });

        document.addEventListener("keypress", _onKeypress);
    };

    Provider.prototype.update = function (model) {
        if (!model || typeof model !== "object") {
            throw new TypeError("Invalid input model. Expected object, got " + model);
        }

        if (!model.value || typeof model.value !== "string") {
            throw new TypeError("Invalid update value. Expected string, got " + model.value);
        }

        this.inst.update(model.value);
    };

    Provider.prototype.onValueChange = function (fn) {
        if (typeof fn !== "function") {
            throw new TypeError("Invalid callback. Expected function, got " + fn);
        }

        this.callback = fn;
    };

    Provider.prototype.remove = function () {
        this.inst.remove();
        this.inst = null;
        this.callback = undefined;
        document.removeEventListener("keypress", _onKeypress);
    };

    var properties = ["shape-inside", "-webkit-shape-inside", "shape-outside", "-webkit-shape-outside", "clip-path", "-webkit-clip-path"];

    properties.forEach(function (property) {

        // expose an editor only when the property is supported by the browser
        if (document.body.style[property] !== undefined) {
            window._LD_CSS_EDITOR.registerProvider(property, Provider);
        } else {
            console.warn(property + " is not supported by this browser.\n Perhaps it is not enabled. See html.adobe.com/webplatform/enable");
        }
    });

}());
<commit_msg>toggle rotate/transform editor with Ctrl+T; turn off with Esc<commit_after>/*
 * Copyright (c) 2013 Adobe Systems Incorporated.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50, browser: true */
/*global window, CSSShapesEditor */

/*
    Register CSSShapesEditor as a provider for editing shape properties.
    A provider is just a wrapper over custom editors to provide a common interface.

    All providers MUST implement at a minimum this interface:
    {
        // turns on editor on specified target HTMLElement.
        // picks-up necessary setup args from model
        setup: function (target, model) {},

        // update the editor state given the provided model
        update: function (model) {},

        // turn off the editor and remove any scaffolding
        remove: function () {},

        // sets a callback to be called with the new value
        onValueChange: function (callback) {}
    }
*/
(function () {

    "use strict";

    if (!window._LD_CSS_EDITOR || typeof window._LD_CSS_EDITOR.registerProvider !== "function") {
        throw new Error("Missing LiveEditorRemoteDriver from Brackets");
    }

    if (!window.CSSShapesEditor) {
        throw new Error("Missing CSSShapesEditor");
    }

    var _onKeydown;

    function Provider() {}

    Provider.prototype.setup = function (target, model) {
        var scope = this,
            options = {};

        switch (model.property) {
        case "shape-inside":
        case "-webkit-shape-inside":
            options.defaultRefBox = "content-box";
            break;
        case "clip-path":
        case "-webkit-clip-path":
            options.defaultRefBox = "border-box";
            break;
        default:
            options.defaultRefBox = "margin-box";
        }

        /*
          @private
          Handle keydown events when a polygon editor is active.
          Declared globally within module so it can be removed by Provider.remove()
          Defined here so it can access scope.inst

          Ctrl+T toggles the free transform editor (scale/rotate)
          Esc key turns off free transform editor; quietly ignored if editor was never turned on.

          @param {Event} e keydown event
        */
        _onKeydown = function (e) {
            // only handle cases for polygon editors
            if (scope.inst.type !== "polygon") {
                return;
            }
            // Ctrl+T toggles rotate/scale editor
            if (e.ctrlKey && e.keyIdentifier === "U+0054") {
                scope.inst.toggleFreeTransform();
            }

            // escape key turns off rotate/scale editor
            if (e.keyCode === 27) {
                scope.inst.turnOffFreeTransform();
            }
        };

        scope.inst = new CSSShapesEditor(target, model.value, options);
        scope.inst.on("shapechange", function () {
            if (scope.callback) {
                scope.callback.call(scope.inst, scope.inst.getCSSValue());
            }
        });

        document.addEventListener("keydown", _onKeydown);
    };

    Provider.prototype.update = function (model) {
        if (!model || typeof model !== "object") {
            throw new TypeError("Invalid input model. Expected object, got " + model);
        }

        if (!model.value || typeof model.value !== "string") {
            throw new TypeError("Invalid update value. Expected string, got " + model.value);
        }

        this.inst.update(model.value);
    };

    Provider.prototype.onValueChange = function (fn) {
        if (typeof fn !== "function") {
            throw new TypeError("Invalid callback. Expected function, got " + fn);
        }

        this.callback = fn;
    };

    Provider.prototype.remove = function () {
        this.inst.remove();
        this.inst = null;
        this.callback = undefined;
        document.removeEventListener("keydown", _onKeydown);
    };

    var properties = ["shape-inside", "-webkit-shape-inside", "shape-outside", "-webkit-shape-outside", "clip-path", "-webkit-clip-path"];

    properties.forEach(function (property) {

        // expose an editor only when the property is supported by the browser
        if (document.body.style[property] !== undefined) {
            window._LD_CSS_EDITOR.registerProvider(property, Provider);
        } else {
            console.warn(property + " is not supported by this browser.\n Perhaps it is not enabled. See html.adobe.com/webplatform/enable");
        }
    });

}());
<|endoftext|>


<commit_before>import { expect }                               from 'chai';
import React                                    from 'react';
import { isBarrierSupported, barriersToString } from '../barriers';

describe('Barriers', () => {
    describe('isBarrierSupported', () => {
        it('should return false when barrier is not in CONTRACT_SHADES', () => {
            expect(isBarrierSupported('SomeThinMadeUp')).to.eql(false);
        });
        it('should return true when barrier is in CONTRACT_SHADES', () => {
            expect(isBarrierSupported('CALL')).to.eql(true);
        });
    });

    describe('barriersToString', () => {
        it('should convert non-zero barriers which do not have +/- to string consisting of them without +/-', () => {
            expect(barriersToString(false, [10, 15])).to.deep.eql(['10','15']);
        });
        it('should convert values with +/- to string consisting of them with +/-', () => {
            expect(barriersToString(false, [0, 15])).to.deep.eql(['0','15']);
        });
        it('should convert barriers which do not have +/- to string consisting of them without +/-', () => {
            expect(barriersToString(false, [+11, 15])).to.deep.eql(['11','15']);
        });
        it('should convert barriers which do not have +/- to string consisting of them with +/- while is_relative is true', () => {
            expect(barriersToString(true, [+11, +15])).to.deep.eql(['+11','+15']);
        });
    });
});<commit_msg>fix test descriptions<commit_after>import { expect }                               from 'chai';
import React                                    from 'react';
import { isBarrierSupported, barriersToString } from '../barriers';

describe('Barriers', () => {
    describe('isBarrierSupported', () => {
        it('should return false when barrier is not in CONTRACT_SHADES', () => {
            expect(isBarrierSupported('SomeThinMadeUp')).to.eql(false);
        });
        it('should return true when barrier is in CONTRACT_SHADES', () => {
            expect(isBarrierSupported('CALL')).to.eql(true);
        });
    });

    describe('barriersToString', () => {
        it('should convert non-zero barriers which do not have +/- to string consisting of them without +/- while is_relative is false', () => {
            expect(barriersToString(false, [10, 15])).to.deep.eql(['10','15']);
        });
        it('should convert values without +/- and zero to string consisting of them without +/- while is_relative is false', () => {
            expect(barriersToString(false, [0, 15])).to.deep.eql(['0','15']);
        });
        it('should convert barriers which have +/- to string consisting of them without +/- while is_relative is false', () => {
            expect(barriersToString(false, [+11, 15])).to.deep.eql(['11','15']);
        });
        it('should convert barriers which have +/- to string consisting of them with +/- while is_relative is true', () => {
            expect(barriersToString(true, [+11, +15])).to.deep.eql(['+11','+15']);
        });
    });
});<|endoftext|>


<commit_before>const {
  addFields,
  addArguments,
  createTypeFetcher,
} = require('olymp-graphql/server');
const { get, set, lowerFirst } = require('lodash');

export default {
  // aggregation
  name: 'reference',
  description: 'Marks a type as a relative.',
  resolveStatic: {
    enter(node, directive, { resolvers, parent, ancestors, ast }) {
      const isList = node.type.kind === 'ListType';
      const type = isList ? node.type.type : node.type;

      const argProperty =
        directive.arguments.length &&
        directive.arguments.find(x => x.name.value === 'property');
      const argRelationType =
        directive.arguments.length &&
        directive.arguments.find(x => x.name.value === 'type');
      const relationType = get(
        argRelationType,
        'value.value',
        isList ? '1-n' : '1-1'
      );

      const leftType = ancestors[ancestors.length - 1].name.value;
      const leftTable = ancestors[
        ancestors.length - 1
      ].name.value.toLowerCase();
      const leftField = node.name.value;
      const leftNode = parent;
      const rightType = type.name.value;
      const rightTable = type.name.value.toLowerCase();
      const rightField =
        get(argProperty, 'value.value') || lowerFirst(leftTable);
      const rightNode = createTypeFetcher(
        node => get(node, 'type.name.value') === rightType
      )(ast);

      // one-to-one / one-to-many
      // Only add xxxId field to store/access the id
      if (relationType === '1-1') {
        addFields(ast, leftNode, `${leftField}Id: String`);
        set(resolvers, `${leftType}.${leftField}`, (source, args, { monk }) =>
          monk.collection(rightTable).findOne({
            id: source[`${leftField}Id`],
          })
        );
      } else if (relationType === '1-n') {
        // Add list accessor and xxxIds field since dealing with many
        // addInputTypes(rightTable, ast);
        /* addArguments(
          ast,
          node,
          `query: ${rightType}Query, sort: ${rightType}Sort, limit: Int, skip: Int`
        );*/
        addFields(ast, leftNode, `${leftField}Id: String`);
        set(resolvers, `${leftType}.${leftField}`, (source, args, { monk }) =>
          monk.collection(rightTable).findOne({
            id: source[`${leftField}Id`],
          })
        );
        addFields(ast, leftNode, `${rightField}: [${leftType}]`);
        set(resolvers, `${rightType}.${rightField}`, (source, args, { monk }) =>
          monk.collection(leftTable).find({ [`${leftField}Id`]: source.id })
        );
      } else if (relationType === 'e1-n') {
        // Add list accessor and xxxIds field since dealing with many
        // addInputTypes(rightTable, ast);
        /* addArguments(
          ast,
          node,
          `query: ${rightType}Query, sort: ${rightType}Sort, limit: Int, skip: Int`
        );*/
        addFields(ast, leftNode, `${leftField}Id: String`);
        set(resolvers, `${leftType}.${leftField}`, (source, args, { monk }) =>
          monk.collection(rightTable).findOne({
            id: source[`${leftField}Id`],
          })
        );
        addFields(ast, leftNode, `${rightField}: [${leftType}]`);
        set(resolvers, `${rightType}.${rightField}`, (source, args, { monk }) =>
          monk.collection(leftTable).find({ [`${leftField}Id`]: source.id })
        );
      }

      /* if (rightField) {
        const rightNode = ast.definitions.find(
          x => x.name && x.name.value === node.type.name.value
        );

        if (relationType === '1-n') {
          addFields(ast, rightNode, `${rightField}: [${leftType}]`);
          set(resolvers, `${rightType}.${rightField}`, (source, args, { monk }) =>
            monk.collection(leftTable).find({})
          );
        } else if (relationType === '1-1') {
          addFields(ast, rightNode, `${rightField}: ${leftType}`);
          set(resolvers, `${rightType}.${rightField}`, (source, args, { monk }) =>
            monk.collection(leftTable).findOne({})
          );
        }
      }*/
    },
  },
};
<commit_msg>clean<commit_after><|endoftext|>


<commit_before>module.exports = require('./../inferno/dist/inferno-component.node');
module.exports.default = module.exports;<commit_msg>removed relative hint in inferno-component<commit_after>module.exports = require('inferno/dist/inferno-component.node');
module.exports.default = module.exports;<|endoftext|>