<commit_before>const joi = require('joi');
const colorField = joi.string().regex(/^#|(#)\d{6}$/)
  .meta({regexHuman: "6 character long hex color string, eg: ``'#000000'``"});

const reverseDnsField = joi.string().regex(/^[a-zA-Z][a-zA-Z0-9\_\.]+$/)
  .meta({regexHuman: "Reverse DNS notation unique name for your app. For example, host.exp.exponent, where exp.host is our domain and Exponent is our app."});

const iosBundleIdentifier = joi.string().regex(/^[a-zA-Z][a-zA-Z0-9\-\.]+$/)
  .meta({regexHuman: "iOS bundle identifier notation unique name for your app. For example, host.exp.exponent, where exp.host is our domain and Exponent is our app."});

// Update the PNG_FIELDS exported value when you use this
const pngField = joi.string().uri();

module.exports = joi.object().keys({
  name: joi.string().required().description(`
    The name of your app as it appears both within Exponent and on your home screen as a standalone app.
  `),
  description: joi.string().description(`
    A short description of what your app is and why it is great.
  `),
  slug: joi.string().regex(/^[a-zA-Z0-9\-]+$/).required().description(`
    The friendly url name for publishing. eg: \`\`exp.host/@your-username/slug\`\`.
  `),
  sdkVersion: joi.string().regex(/^(\d+\.\d+\.\d+)|(UNVERSIONED)$/).required().description(`
    The Exponent sdkVersion to run the project on. This should line up with the version specified in your package.json.
  `),
  version: joi.string().description(`
    Your app version, use whatever versioning scheme that you like.
  `),
  orientation: joi.any().valid('default', 'portrait', 'landscape').description(`
    Lock your app to a specific orientation with \`\`portrait\`\` or \`\`landscape\`\`. Defaults to no lock.
  `),
  primaryColor: colorField.description(`
    On Android, this will determine the color of your app in the multitasker. Currently this is not used on iOS, but it may be used for other purposes in the future.
  `),
  iconUrl: pngField.description(`
    A url that points to your app's icon image. We recommend that you use a 512x512 png file with transparency. This icon will appear on the home screen and within the Exponent app.
  `),
  notification: joi.object().keys({
    iconUrl: pngField.description(
      `Url that points to the icon to use for push notifications. 48x48 png grayscale with transparency.`
    ),
    color: colorField.description(
      `Tint color for the push notification image when it appears in the notification tray.`
    ),
    androidMode: joi.any().valid('default', 'collapse').description(
      "Show each push notification individually (``default``) or collapse into one (``collapse``)."
    ),
    androidCollapsedTitle: joi.string().description(
      "If ``androidMode`` is set to ``collapse``, this title is used for the collapsed notification message. eg: ``'#{unread_notifications} new interactions'``."
    ),
  }).description(`
    Configuration for remote (push) notifications.
  `),
  loading: joi.object().keys({
    iconUrl: pngField.uri().description(
      `Url that points to the icon to display while starting up the app. Image size and aspect ratio are up to you. Must be a .png.`
    ),
    exponentIconColor: joi.any().valid('white', 'blue').description(
      "If no icon is provided, we will show the Exponent logo. You can choose between ``white`` and ``blue``."
    ),
    exponentIconGrayscale: joi.number().min(0).max(1).description(
      "Similar to ``exponentIconColor`` but instead indicate if it should be grayscale (``1``) or not (``0``)."
    ),
    backgroundImageUrl: pngField.uri().description(
      "Url that points to an image to fill the background of the loading screen. Image size and aspect ratio are up to you. Must be a .png."
    ),
    backgroundColor: colorField.description(
      "Color to fill the loading screen background"
    ),
    hideExponentText: joi.boolean().description(
      "By default, Exponent shows some text at the bottom of the loading screen. Set this to ``true`` to disable."
    ),
  }).description(`
    Configuration for the loading screen that users see when opening your app, while fetching & caching bundle and assets.
  `),
  appKey: joi.string().description(
    "By default, Exponent looks for the application registered with the AppRegistry as ``main``. If you would like to change this, you can specify the name in this property."
  ),
  androidStatusBarColor: colorField,
  androidHideExponentNotificationInShellApp: joi.boolean().description(
    "By default, Exponent adds a notification to your app with refresh button and debug info. Set this to ``true`` to disable."
  ),
  scheme: joi.string().alphanum().description(
    "Url scheme to link into your app. For example, if we set this to ``'rnplay'``, then rnplay:// urls would open your app when tapped."
  ).meta({standaloneOnly: true}),

  entryPoint: joi.string().description(`
    The relative path to your main JavaScript file.
  `),

  extra: joi.object().description(`
    Any extra fields you want to pass to your experience.
  `),
  rnCliPath: joi.string(),
  packagerOpts: joi.object(),
  ignoreNodeModulesValidation: joi.boolean(),
  nodeModulesPath: joi.string(),

  ios: joi.object().keys({
    bundleIdentifier: iosBundleIdentifier.description(
      "The bundle identifier for your iOS standalone app. You make it up, but it needs to be unique on the App Store. See `this StackOverflow question <http://stackoverflow.com/questions/11347470/what-does-bundle-identifier-mean-in-the-ios-project>`_."
    ),
    config: joi.object().keys({
      fabric: joi.object().keys({
        apiKey: joi.string().alphanum().description('Your Fabric API key'),
        buildSecret: joi.string().alphanum().description('Your Fabric build secret'),
      }).description(
        "`Twitter Fabric <https://get.fabric.io/>`_ keys to hook up Crashlytics and other services."
      ),
      googleSignIn: joi.object().keys({
        reservedClientId: joi.string().description(
          'The reserved client id url scheme. Can be found in `GoogeService-Info.plist`.'
        ),
      }).description(
        "`Google Sign-In iOS SDK <https://developers.google.com/identity/sign-in/ios/start-integrating>`_ keys for your standalone app."
      ),
    }),
  }).description('iOS standalone app specific configuration').meta({standaloneOnly: true}),
  android: joi.object().keys({
    package: reverseDnsField.description(
      "The package name for your Android standalone app. You make it up, but it needs to be unique on the Play Store. See `this StackOverflow question <http://stackoverflow.com/questions/6273892/android-package-name-convention>`_."
    ),
    versionCode: joi.number().description(
      "Version number required by Google Play. Increment by one for each release. https://developer.android.com/studio/publish/versioning.html."
    ),
    config: joi.object().keys({
      fabric: joi.object().keys({
        apiKey: joi.string().alphanum().description('Your Fabric API key'),
        buildSecret: joi.string().alphanum().description('Your Fabric build secret'),
      }).description(
        "`Twitter Fabric <https://get.fabric.io/>`_ keys to hook up Crashlytics and other services."
      ),
      googleMaps: joi.object().keys({
        apiKey: joi.string().alphanum().description('Your Google Maps Android SDK API key'),
      }).description(
        "`Google Maps Android SDK <https://developers.google.com/maps/documentation/android-api/signup>`_ key for your standalone app."
      ),
      googleSignIn: joi.object().keys({
        apiKey: joi.string().alphanum().description('The Android API key. Can be found in the credentials section of the developer console or in `google-services.json`.'),
        certificateHash: joi.string().alphanum().description(
          'The SHA-1 hash of the signing certificate used to build the apk without any separator `:`. Can be found in `google-services.json`. https://developers.google.com/android/guides/client-auth'
        ),
      }).description(
        "`Google Sign-In Android SDK <https://developers.google.com/identity/sign-in/android/start-integrating>`_ keys for your standalone app."
      ),
    }),
  }).description('Android standalone app specific configuration').meta({standaloneOnly: true}),
});

module.exports.PNG_FIELDS = ['iconUrl', 'notification.iconUrl', 'loading.iconUrl', 'loading.backgroundImageUrl'];
<commit_msg>Enforce that versionCode is an int<commit_after>const joi = require('joi');
const colorField = joi.string().regex(/^#|(#)\d{6}$/)
  .meta({regexHuman: "6 character long hex color string, eg: ``'#000000'``"});

const reverseDnsField = joi.string().regex(/^[a-zA-Z][a-zA-Z0-9\_\.]+$/)
  .meta({regexHuman: "Reverse DNS notation unique name for your app. For example, host.exp.exponent, where exp.host is our domain and Exponent is our app."});

const iosBundleIdentifier = joi.string().regex(/^[a-zA-Z][a-zA-Z0-9\-\.]+$/)
  .meta({regexHuman: "iOS bundle identifier notation unique name for your app. For example, host.exp.exponent, where exp.host is our domain and Exponent is our app."});

// Update the PNG_FIELDS exported value when you use this
const pngField = joi.string().uri();

module.exports = joi.object().keys({
  name: joi.string().required().description(`
    The name of your app as it appears both within Exponent and on your home screen as a standalone app.
  `),
  description: joi.string().description(`
    A short description of what your app is and why it is great.
  `),
  slug: joi.string().regex(/^[a-zA-Z0-9\-]+$/).required().description(`
    The friendly url name for publishing. eg: \`\`exp.host/@your-username/slug\`\`.
  `),
  sdkVersion: joi.string().regex(/^(\d+\.\d+\.\d+)|(UNVERSIONED)$/).required().description(`
    The Exponent sdkVersion to run the project on. This should line up with the version specified in your package.json.
  `),
  version: joi.string().description(`
    Your app version, use whatever versioning scheme that you like.
  `),
  orientation: joi.any().valid('default', 'portrait', 'landscape').description(`
    Lock your app to a specific orientation with \`\`portrait\`\` or \`\`landscape\`\`. Defaults to no lock.
  `),
  primaryColor: colorField.description(`
    On Android, this will determine the color of your app in the multitasker. Currently this is not used on iOS, but it may be used for other purposes in the future.
  `),
  iconUrl: pngField.description(`
    A url that points to your app's icon image. We recommend that you use a 512x512 png file with transparency. This icon will appear on the home screen and within the Exponent app.
  `),
  notification: joi.object().keys({
    iconUrl: pngField.description(
      `Url that points to the icon to use for push notifications. 48x48 png grayscale with transparency.`
    ),
    color: colorField.description(
      `Tint color for the push notification image when it appears in the notification tray.`
    ),
    androidMode: joi.any().valid('default', 'collapse').description(
      "Show each push notification individually (``default``) or collapse into one (``collapse``)."
    ),
    androidCollapsedTitle: joi.string().description(
      "If ``androidMode`` is set to ``collapse``, this title is used for the collapsed notification message. eg: ``'#{unread_notifications} new interactions'``."
    ),
  }).description(`
    Configuration for remote (push) notifications.
  `),
  loading: joi.object().keys({
    iconUrl: pngField.uri().description(
      `Url that points to the icon to display while starting up the app. Image size and aspect ratio are up to you. Must be a .png.`
    ),
    exponentIconColor: joi.any().valid('white', 'blue').description(
      "If no icon is provided, we will show the Exponent logo. You can choose between ``white`` and ``blue``."
    ),
    exponentIconGrayscale: joi.number().min(0).max(1).description(
      "Similar to ``exponentIconColor`` but instead indicate if it should be grayscale (``1``) or not (``0``)."
    ),
    backgroundImageUrl: pngField.uri().description(
      "Url that points to an image to fill the background of the loading screen. Image size and aspect ratio are up to you. Must be a .png."
    ),
    backgroundColor: colorField.description(
      "Color to fill the loading screen background"
    ),
    hideExponentText: joi.boolean().description(
      "By default, Exponent shows some text at the bottom of the loading screen. Set this to ``true`` to disable."
    ),
  }).description(`
    Configuration for the loading screen that users see when opening your app, while fetching & caching bundle and assets.
  `),
  appKey: joi.string().description(
    "By default, Exponent looks for the application registered with the AppRegistry as ``main``. If you would like to change this, you can specify the name in this property."
  ),
  androidStatusBarColor: colorField,
  androidHideExponentNotificationInShellApp: joi.boolean().description(
    "By default, Exponent adds a notification to your app with refresh button and debug info. Set this to ``true`` to disable."
  ),
  scheme: joi.string().alphanum().description(
    "Url scheme to link into your app. For example, if we set this to ``'rnplay'``, then rnplay:// urls would open your app when tapped."
  ).meta({standaloneOnly: true}),

  entryPoint: joi.string().description(`
    The relative path to your main JavaScript file.
  `),

  extra: joi.object().description(`
    Any extra fields you want to pass to your experience.
  `),
  rnCliPath: joi.string(),
  packagerOpts: joi.object(),
  ignoreNodeModulesValidation: joi.boolean(),
  nodeModulesPath: joi.string(),

  ios: joi.object().keys({
    bundleIdentifier: iosBundleIdentifier.description(
      "The bundle identifier for your iOS standalone app. You make it up, but it needs to be unique on the App Store. See `this StackOverflow question <http://stackoverflow.com/questions/11347470/what-does-bundle-identifier-mean-in-the-ios-project>`_."
    ),
    config: joi.object().keys({
      fabric: joi.object().keys({
        apiKey: joi.string().alphanum().description('Your Fabric API key'),
        buildSecret: joi.string().alphanum().description('Your Fabric build secret'),
      }).description(
        "`Twitter Fabric <https://get.fabric.io/>`_ keys to hook up Crashlytics and other services."
      ),
      googleSignIn: joi.object().keys({
        reservedClientId: joi.string().description(
          'The reserved client id url scheme. Can be found in `GoogeService-Info.plist`.'
        ),
      }).description(
        "`Google Sign-In iOS SDK <https://developers.google.com/identity/sign-in/ios/start-integrating>`_ keys for your standalone app."
      ),
    }),
  }).description('iOS standalone app specific configuration').meta({standaloneOnly: true}),
  android: joi.object().keys({
    package: reverseDnsField.description(
      "The package name for your Android standalone app. You make it up, but it needs to be unique on the Play Store. See `this StackOverflow question <http://stackoverflow.com/questions/6273892/android-package-name-convention>`_."
    ),
    versionCode: joi.number().integer().description(
      "Version number required by Google Play. Increment by one for each release. https://developer.android.com/studio/publish/versioning.html."
    ),
    config: joi.object().keys({
      fabric: joi.object().keys({
        apiKey: joi.string().alphanum().description('Your Fabric API key'),
        buildSecret: joi.string().alphanum().description('Your Fabric build secret'),
      }).description(
        "`Twitter Fabric <https://get.fabric.io/>`_ keys to hook up Crashlytics and other services."
      ),
      googleMaps: joi.object().keys({
        apiKey: joi.string().alphanum().description('Your Google Maps Android SDK API key'),
      }).description(
        "`Google Maps Android SDK <https://developers.google.com/maps/documentation/android-api/signup>`_ key for your standalone app."
      ),
      googleSignIn: joi.object().keys({
        apiKey: joi.string().alphanum().description('The Android API key. Can be found in the credentials section of the developer console or in `google-services.json`.'),
        certificateHash: joi.string().alphanum().description(
          'The SHA-1 hash of the signing certificate used to build the apk without any separator `:`. Can be found in `google-services.json`. https://developers.google.com/android/guides/client-auth'
        ),
      }).description(
        "`Google Sign-In Android SDK <https://developers.google.com/identity/sign-in/android/start-integrating>`_ keys for your standalone app."
      ),
    }),
  }).description('Android standalone app specific configuration').meta({standaloneOnly: true}),
});

module.exports.PNG_FIELDS = ['iconUrl', 'notification.iconUrl', 'loading.iconUrl', 'loading.backgroundImageUrl'];
<|endoftext|>


<commit_before>import CoreCombatLogParser from 'Parser/Core/CombatLogParser';
import DamageDone from 'Parser/Core/Modules/DamageDone';

import EyeBeamNormalizer from './Normalizers/EyeBeam';
import Channeling from './Modules/Core/Channeling';
import GlobalCooldown from './Modules/Core/GlobalCooldown';

import AlwaysBeCasting from './Modules/Features/AlwaysBeCasting';
import Abilities from './Modules/Abilities';
import CooldownThroughputTracker from './Modules/Features/CooldownThroughputTracker';

import Checklist from './Modules/Features/Checklist';

import Momentum from './Modules/Spells/Momentum';
import Nemesis from './Modules/Spells/Nemesis';

//Resources
import FuryDetails from './Modules/ResourceTracker/FuryDetails';
import FuryTracker from './Modules/ResourceTracker/FuryTracker';

//Items
import DelusionsOfGrandeur from './Modules/Items/DelusionsOfGrandeur';
import RaddonsCascadingEyes from './Modules/Items/RaddonsCascadingEyes';
import MoargBionicStabilizers from './Modules/Items/MoargBionicStabilizers';
import SoulOfTheSlayer from '../Shared/Modules/Items/SoulOfTheSlayer';
import AngerOfTheHalfGiants from './Modules/Items/AngerOfTheHalfGiants';
import Tier21_2set from './Modules/Items/Tier21_2set';
import Tier21_4set from './Modules/Items/Tier21_4set';

//Traits
import RelicTraits from './Modules/Traits/RelicTraits';
import ChaosVision from './Modules/Traits/ChaosVision';
import SharpenedGlaives from './Modules/Traits/SharpenedGlaives';
import DemonRage from './Modules/Traits/DemonRage';
import UnleashedDemons from './Modules/Traits/UnleashedDemons';
import FeastOnTheSouls from './Modules/Traits/FeastOnTheSouls';

class CombatLogParser extends CoreCombatLogParser {
  static specModules = {
    // Core Statistics
    damageDone: [DamageDone, { showStatistic: true }],
    channeling: Channeling,
    globalCooldown: GlobalCooldown,

    //Normalizer
    eyeBeamNormalizer: EyeBeamNormalizer,

    // Features
    alwaysBeCasting: AlwaysBeCasting,
    abilities: Abilities,
    cooldownThroughputTracker: CooldownThroughputTracker,
    checklist: Checklist,

    // Spells
    momentum: Momentum,
    nemesis: Nemesis,

    //Resources
    furyTracker: FuryTracker,
    furyDetails: FuryDetails,

    //Items
    delusionsOfGrandeur: DelusionsOfGrandeur,
    raddonsCascadingEyes: RaddonsCascadingEyes,
    moargBionicStabilizers: MoargBionicStabilizers,
    soulOfTheSlayer: SoulOfTheSlayer,
    angerOfTheHalfGiants: AngerOfTheHalfGiants,
    tier21_2set: Tier21_2set,
    tier21_4set: Tier21_4set,

    //Traits
    relicTraits: RelicTraits,
    chaosVision: ChaosVision,
    sharpenedGlaives: SharpenedGlaives,
    demonRage: DemonRage,
    unleashedDemons: UnleashedDemons,
    feastOnTheSouls: FeastOnTheSouls,
  };
}

export default CombatLogParser;
<commit_msg>updated checklist module import<commit_after>import CoreCombatLogParser from 'Parser/Core/CombatLogParser';
import DamageDone from 'Parser/Core/Modules/DamageDone';

import EyeBeamNormalizer from './Normalizers/EyeBeam';
import Channeling from './Modules/Core/Channeling';
import GlobalCooldown from './Modules/Core/GlobalCooldown';

import AlwaysBeCasting from './Modules/Features/AlwaysBeCasting';
import Abilities from './Modules/Abilities';
import CooldownThroughputTracker from './Modules/Features/CooldownThroughputTracker';

import Checklist from './Modules/Features/Checklist/Module';

import Momentum from './Modules/Spells/Momentum';
import Nemesis from './Modules/Spells/Nemesis';

//Resources
import FuryDetails from './Modules/ResourceTracker/FuryDetails';
import FuryTracker from './Modules/ResourceTracker/FuryTracker';

//Items
import DelusionsOfGrandeur from './Modules/Items/DelusionsOfGrandeur';
import RaddonsCascadingEyes from './Modules/Items/RaddonsCascadingEyes';
import MoargBionicStabilizers from './Modules/Items/MoargBionicStabilizers';
import SoulOfTheSlayer from '../Shared/Modules/Items/SoulOfTheSlayer';
import AngerOfTheHalfGiants from './Modules/Items/AngerOfTheHalfGiants';
import Tier21_2set from './Modules/Items/Tier21_2set';
import Tier21_4set from './Modules/Items/Tier21_4set';

//Traits
import RelicTraits from './Modules/Traits/RelicTraits';
import ChaosVision from './Modules/Traits/ChaosVision';
import SharpenedGlaives from './Modules/Traits/SharpenedGlaives';
import DemonRage from './Modules/Traits/DemonRage';
import UnleashedDemons from './Modules/Traits/UnleashedDemons';
import FeastOnTheSouls from './Modules/Traits/FeastOnTheSouls';

class CombatLogParser extends CoreCombatLogParser {
  static specModules = {
    // Core Statistics
    damageDone: [DamageDone, { showStatistic: true }],
    channeling: Channeling,
    globalCooldown: GlobalCooldown,

    //Normalizer
    eyeBeamNormalizer: EyeBeamNormalizer,

    // Features
    alwaysBeCasting: AlwaysBeCasting,
    abilities: Abilities,
    cooldownThroughputTracker: CooldownThroughputTracker,
    checklist: Checklist,

    // Spells
    momentum: Momentum,
    nemesis: Nemesis,

    //Resources
    furyTracker: FuryTracker,
    furyDetails: FuryDetails,

    //Items
    delusionsOfGrandeur: DelusionsOfGrandeur,
    raddonsCascadingEyes: RaddonsCascadingEyes,
    moargBionicStabilizers: MoargBionicStabilizers,
    soulOfTheSlayer: SoulOfTheSlayer,
    angerOfTheHalfGiants: AngerOfTheHalfGiants,
    tier21_2set: Tier21_2set,
    tier21_4set: Tier21_4set,

    //Traits
    relicTraits: RelicTraits,
    chaosVision: ChaosVision,
    sharpenedGlaives: SharpenedGlaives,
    demonRage: DemonRage,
    unleashedDemons: UnleashedDemons,
    feastOnTheSouls: FeastOnTheSouls,
  };
}

export default CombatLogParser;
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// 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.

'use strict';

var EventEmitter = require('events').EventEmitter;
var util = require('util');

var v1 = require('./index');
module.exports = TChannelHandler;

function TChannelHandler(channel, options) {
    if (!(this instanceof TChannelHandler)) {
        return new TChannelHandler(channel, options);
    }
    EventEmitter.call(this);
    var self = this;
    self.channel = channel;
    self.writeFrame = options.writeFrame;
    // TODO: may be better suited to pull out an operation collection
    // abstraction and then encapsulate through that rather than this
    // run/complete approach
    self.runInOp = options.runInOp;
    self.completeOutOp = options.completeOutOp;
    self.remoteName = null; // filled in by identify message
    self.lastSentFrameId = 0;
}

util.inherits(TChannelHandler, EventEmitter);

TChannelHandler.prototype.nextFrameId = function nextFrameId() {
    var self = this;
    self.lastSentFrameId = (self.lastSentFrameId + 1) % 0xffffffff;
    return self.lastSentFrameId;
};

TChannelHandler.prototype.handleFrame = function handleFrame(frame) {
    var self = this;
    switch (frame.header.type) {
        case v1.Types.reqCompleteMessage:
            return self.handleCallRequest(frame);
        case v1.Types.resCompleteMessage:
            return self.handleCallResponse(frame);
        case v1.Types.resError:
            return self.handleError(frame);
        default:
            self.logger.error('unhandled frame type', {
                type: frame.header.type
            });
    }
};

TChannelHandler.prototype.handleInitRequest = function handleInitRequest(reqFrame) {
    var self = this;
    if (self.remoteName !== null) {
        self.emit('error', new Error('duplicate init request')); // TODO typed error
        return;
    }
    var hostPort = reqFrame.arg2.toString();
    self.remoteName = hostPort;
    self.emit('identify.in', hostPort);
    self.sendInitResponse(reqFrame);
};

TChannelHandler.prototype.handleInitResponse = function handleInitResponse(resFrame) {
    var self = this;
    if (self.remoteName !== null) {
        self.emit('error', new Error('duplicate init response')); // TODO typed error
        return;
    }
    var remote = String(resFrame.arg2);
    self.remoteName = remote;
    self.emit('identify.out', remote);
};

TChannelHandler.prototype.handleCallRequest = function handleCallRequest(reqFrame) {
    var self = this;
    var id = reqFrame.header.id;
    var name = reqFrame.arg1.toString();

    if (name === 'TChannel identify') {
        self.handleInitRequest(reqFrame);
        return;
    }

    if (self.remoteName === null) {
        self.emit('error', new Error('call request before init request')); // TODO typed error
        return;
    }

    var handler = self.channel.getEndpointHandler(name);
    self.runInOp(handler, {
        id: id,
        arg1: reqFrame.arg1,
        arg2: reqFrame.arg2,
        arg3: reqFrame.arg3,
    }, function sendResponseFrame(err, res1, res2, callback) {
        self.sendResponseFrame(reqFrame, err, res1, res2, callback);
    });
};

TChannelHandler.prototype.handleCallResponse = function handleCallResponse(resFrame) {
    var self = this;

    if (String(resFrame.arg1) === 'TChannel identify') {
        self.handleInitResponse(resFrame);
        return;
    }

    if (self.remoteName === null) {
        self.emit('error', new Error('call response before init response')); // TODO typed error
        return;
    }

    var id = resFrame.header.id;
    var arg2 = resFrame.arg2;
    var arg3 = resFrame.arg3;
    self.completeOutOp(null, id, arg2, arg3);
};

TChannelHandler.prototype.handleError = function handleError(errFrame) {
    var self = this;
    var id = errFrame.header.id;
    var message = errFrame.arg1;
    var err = new Error(message);
    self.completeOutOp(err, id, null, null);
};

TChannelHandler.prototype.sendInitRequest = function sendInitRequest() {
    var self = this;
    var reqFrame = new v1.Frame();
    var id = self.nextFrameId();
    reqFrame.header.id = id;
    reqFrame.header.seq = 0;
    reqFrame.set('TChannel identify', self.channel.hostPort, null);
    reqFrame.header.type = v1.Types.reqCompleteMessage;
    self.writeFrame(reqFrame);
};

TChannelHandler.prototype.sendInitResponse = function sendInitResponse(reqFrame) {
    var self = this;
    var id = reqFrame.header.id;
    var arg1 = reqFrame.arg1;
    var resFrame = new v1.Frame();
    resFrame.header.id = id;
    resFrame.header.seq = 0;
    resFrame.set(arg1, self.channel.hostPort, null);
    resFrame.header.type = v1.Types.resCompleteMessage;
    self.writeFrame(resFrame);
};

/* jshint maxparams:5 */
TChannelHandler.prototype.sendRequestFrame = function sendRequestFrame(options, arg1, arg2, arg3, callback) {
    var self = this;
    var id = self.nextFrameId();
    var reqFrame = new v1.Frame();
    reqFrame.header.id = id;
    reqFrame.header.seq = 0;
    reqFrame.set(arg1, arg2, arg3);
    reqFrame.header.type = v1.Types.reqCompleteMessage;
    self.writeFrame(reqFrame, callback);
    return id;
};

TChannelHandler.prototype.sendResponseFrame = function sendResponseFrame(reqFrame, err, res1, res2, callback) {
    var self = this;
    var resFrame = new v1.Frame();
    var id = reqFrame.header.id;
    var name = reqFrame.arg1.toString();
    resFrame.header.id = id;
    resFrame.header.seq = 0;
    if (err) {
        // TODO should the error response contain a head ?
        // Is there any value in sending meta data along with
        // the error.
        resFrame.set(isError(err) ? err.message : err, null, null);
        resFrame.header.type = v1.Types.resError;
    } else {
        resFrame.set(name, res1, res2);
        resFrame.header.type = v1.Types.resCompleteMessage;
    }
    self.writeFrame(resFrame, callback);
};
/* jshint maxparams:4 */

function isError(obj) {
    return typeof obj === 'object' && (
        Object.prototype.toString.call(obj) === '[object Error]' ||
        obj instanceof Error);
}
<commit_msg>v1/handler: drop old TODO remarks wrt typde errors<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// 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.

'use strict';

var EventEmitter = require('events').EventEmitter;
var util = require('util');

var v1 = require('./index');
module.exports = TChannelHandler;

function TChannelHandler(channel, options) {
    if (!(this instanceof TChannelHandler)) {
        return new TChannelHandler(channel, options);
    }
    EventEmitter.call(this);
    var self = this;
    self.channel = channel;
    self.writeFrame = options.writeFrame;
    // TODO: may be better suited to pull out an operation collection
    // abstraction and then encapsulate through that rather than this
    // run/complete approach
    self.runInOp = options.runInOp;
    self.completeOutOp = options.completeOutOp;
    self.remoteName = null; // filled in by identify message
    self.lastSentFrameId = 0;
}

util.inherits(TChannelHandler, EventEmitter);

TChannelHandler.prototype.nextFrameId = function nextFrameId() {
    var self = this;
    self.lastSentFrameId = (self.lastSentFrameId + 1) % 0xffffffff;
    return self.lastSentFrameId;
};

TChannelHandler.prototype.handleFrame = function handleFrame(frame) {
    var self = this;
    switch (frame.header.type) {
        case v1.Types.reqCompleteMessage:
            return self.handleCallRequest(frame);
        case v1.Types.resCompleteMessage:
            return self.handleCallResponse(frame);
        case v1.Types.resError:
            return self.handleError(frame);
        default:
            self.logger.error('unhandled frame type', {
                type: frame.header.type
            });
    }
};

TChannelHandler.prototype.handleInitRequest = function handleInitRequest(reqFrame) {
    var self = this;
    if (self.remoteName !== null) {
        self.emit('error', new Error('duplicate init request'));
        return;
    }
    var hostPort = reqFrame.arg2.toString();
    self.remoteName = hostPort;
    self.emit('identify.in', hostPort);
    self.sendInitResponse(reqFrame);
};

TChannelHandler.prototype.handleInitResponse = function handleInitResponse(resFrame) {
    var self = this;
    if (self.remoteName !== null) {
        self.emit('error', new Error('duplicate init response'));
        return;
    }
    var remote = String(resFrame.arg2);
    self.remoteName = remote;
    self.emit('identify.out', remote);
};

TChannelHandler.prototype.handleCallRequest = function handleCallRequest(reqFrame) {
    var self = this;
    var id = reqFrame.header.id;
    var name = reqFrame.arg1.toString();

    if (name === 'TChannel identify') {
        self.handleInitRequest(reqFrame);
        return;
    }

    if (self.remoteName === null) {
        self.emit('error', new Error('call request before init request'));
        return;
    }

    var handler = self.channel.getEndpointHandler(name);
    self.runInOp(handler, {
        id: id,
        arg1: reqFrame.arg1,
        arg2: reqFrame.arg2,
        arg3: reqFrame.arg3,
    }, function sendResponseFrame(err, res1, res2, callback) {
        self.sendResponseFrame(reqFrame, err, res1, res2, callback);
    });
};

TChannelHandler.prototype.handleCallResponse = function handleCallResponse(resFrame) {
    var self = this;

    if (String(resFrame.arg1) === 'TChannel identify') {
        self.handleInitResponse(resFrame);
        return;
    }

    if (self.remoteName === null) {
        self.emit('error', new Error('call response before init response'));
        return;
    }

    var id = resFrame.header.id;
    var arg2 = resFrame.arg2;
    var arg3 = resFrame.arg3;
    self.completeOutOp(null, id, arg2, arg3);
};

TChannelHandler.prototype.handleError = function handleError(errFrame) {
    var self = this;
    var id = errFrame.header.id;
    var message = errFrame.arg1;
    var err = new Error(message);
    self.completeOutOp(err, id, null, null);
};

TChannelHandler.prototype.sendInitRequest = function sendInitRequest() {
    var self = this;
    var reqFrame = new v1.Frame();
    var id = self.nextFrameId();
    reqFrame.header.id = id;
    reqFrame.header.seq = 0;
    reqFrame.set('TChannel identify', self.channel.hostPort, null);
    reqFrame.header.type = v1.Types.reqCompleteMessage;
    self.writeFrame(reqFrame);
};

TChannelHandler.prototype.sendInitResponse = function sendInitResponse(reqFrame) {
    var self = this;
    var id = reqFrame.header.id;
    var arg1 = reqFrame.arg1;
    var resFrame = new v1.Frame();
    resFrame.header.id = id;
    resFrame.header.seq = 0;
    resFrame.set(arg1, self.channel.hostPort, null);
    resFrame.header.type = v1.Types.resCompleteMessage;
    self.writeFrame(resFrame);
};

/* jshint maxparams:5 */
TChannelHandler.prototype.sendRequestFrame = function sendRequestFrame(options, arg1, arg2, arg3, callback) {
    var self = this;
    var id = self.nextFrameId();
    var reqFrame = new v1.Frame();
    reqFrame.header.id = id;
    reqFrame.header.seq = 0;
    reqFrame.set(arg1, arg2, arg3);
    reqFrame.header.type = v1.Types.reqCompleteMessage;
    self.writeFrame(reqFrame, callback);
    return id;
};

TChannelHandler.prototype.sendResponseFrame = function sendResponseFrame(reqFrame, err, res1, res2, callback) {
    var self = this;
    var resFrame = new v1.Frame();
    var id = reqFrame.header.id;
    var name = reqFrame.arg1.toString();
    resFrame.header.id = id;
    resFrame.header.seq = 0;
    if (err) {
        // TODO should the error response contain a head ?
        // Is there any value in sending meta data along with
        // the error.
        resFrame.set(isError(err) ? err.message : err, null, null);
        resFrame.header.type = v1.Types.resError;
    } else {
        resFrame.set(name, res1, res2);
        resFrame.header.type = v1.Types.resCompleteMessage;
    }
    self.writeFrame(resFrame, callback);
};
/* jshint maxparams:4 */

function isError(obj) {
    return typeof obj === 'object' && (
        Object.prototype.toString.call(obj) === '[object Error]' ||
        obj instanceof Error);
}
<|endoftext|>


<commit_before>var root = this,
    jQuery = root.jQuery,
    Backbone = root.Backbone,
    _ = root._;

var VIE = root.VIE = function(config) {
    this.config = (config) ? config : {};
    this.services = {};
    this.entities = new this.Collection();

    this.Entity.prototype.entities = this.entities;
    this.entities.vie = this;
    this.Entity.prototype.entityCollection = this.Collection;
    this.Entity.prototype.vie = this;

    this.defaultProxyUrl = (this.config.defaultProxyUrl) ? this.config.defaultProxyUrl : "../utils/proxy/proxy.php";

    this.Namespaces.prototype.vie = this;
    this.namespaces = new this.Namespaces(
        (this.config.defaultNamespace) ? this.config.defaultNamespace : "http://ontology.vie.js/"
    );

    this.Type.prototype.vie = this;
    this.Types.prototype.vie = this;
    this.Attribute.prototype.vie = this;
    this.Attributes.prototype.vie = this;
    this.types = new this.Types();
    this.types.add("Thing");

    if (this.config.classic !== false) {
        // Load Classic API as well
        this.RDFa = new this.ClassicRDFa(this);
        this.RDFaEntities = new this.ClassicRDFaEntities(this);
        this.EntityManager = new this.ClassicEntityManager(this);

        this.cleanup = function() {
            this.entities.reset();
        };
    }
};

VIE.prototype.use = function(service, name) {
  if (!name) {
    name = service.name;
  }
  service.vie = this;
  service.name = name;
  if (service.init) {
      service.init();
  }
  this.services[name] = service;
};

VIE.prototype.service = function(name) {
  if (!this.services[name]) {
    throw "Undefined service " + name;
  }
  return this.services[name];
};

VIE.prototype.getServicesArray = function() {
  var res = [];
  _.each(this.services, function(service, i){res.push(service);});
  return res;
};

// Declaring the ..able classes
// Loadable
VIE.prototype.load = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Loadable(options);
};

// Savable
VIE.prototype.save = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Savable(options);
};

// Removable
VIE.prototype.remove = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Removable(options);
};

// Analyzable
VIE.prototype.analyze = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Analyzable(options);
};

// Findable
VIE.prototype.find = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Findable(options);
};

if(typeof exports === 'object') {
    // Running under Node.js or other CommonJS environment
    exports.VIE = VIE;

    if (!jQuery) {
        jQuery = require('jquery');
    }
    if (!Backbone) {
        Backbone = require('backbone');
    }
    if (!_) {
        _ = require('underscore')._;
    }
}
<commit_msg>first API sketch to load schemas<commit_after>var root = this,
    jQuery = root.jQuery,
    Backbone = root.Backbone,
    _ = root._;

var VIE = root.VIE = function(config) {
    this.config = (config) ? config : {};
    this.services = {};
    this.entities = new this.Collection();

    this.Entity.prototype.entities = this.entities;
    this.entities.vie = this;
    this.Entity.prototype.entityCollection = this.Collection;
    this.Entity.prototype.vie = this;

    this.defaultProxyUrl = (this.config.defaultProxyUrl) ? this.config.defaultProxyUrl : "../utils/proxy/proxy.php";

    this.Namespaces.prototype.vie = this;
    this.namespaces = new this.Namespaces(
        (this.config.defaultNamespace) ? this.config.defaultNamespace : "http://ontology.vie.js/"
    );

    this.Type.prototype.vie = this;
    this.Types.prototype.vie = this;
    this.Attribute.prototype.vie = this;
    this.Attributes.prototype.vie = this;
    this.types = new this.Types();
    this.types.add("Thing");

    if (this.config.classic !== false) {
        // Load Classic API as well
        this.RDFa = new this.ClassicRDFa(this);
        this.RDFaEntities = new this.ClassicRDFaEntities(this);
        this.EntityManager = new this.ClassicEntityManager(this);

        this.cleanup = function() {
            this.entities.reset();
        };
    }
};

VIE.prototype.use = function(service, name) {
  if (!name) {
    name = service.name;
  }
  service.vie = this;
  service.name = name;
  if (service.init) {
      service.init();
  }
  this.services[name] = service;
};

VIE.prototype.service = function(name) {
  if (!this.services[name]) {
    throw "Undefined service " + name;
  }
  return this.services[name];
};

VIE.prototype.getServicesArray = function() {
  var res = [];
  _.each(this.services, function(service, i){res.push(service);});
  return res;
};

// Declaring the ..able classes
// Loadable
VIE.prototype.load = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Loadable(options);
};

// Savable
VIE.prototype.save = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Savable(options);
};

// Removable
VIE.prototype.remove = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Removable(options);
};

// Analyzable
VIE.prototype.analyze = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Analyzable(options);
};

// Findable
VIE.prototype.find = function(options) {
  if (!options) { options = {}; }
  options.vie = this;
  return new this.Findable(options);
};

// bootstrap VIE with a type and attribute ontology/schema
VIE.prototype.loadSchema = function(id) {
    if (!id) {
        //TODO: load default schema
    } else {
        //TODO: try to load the given schema if available
    }
};

if(typeof exports === 'object') {
    // Running under Node.js or other CommonJS environment
    exports.VIE = VIE;

    if (!jQuery) {
        jQuery = require('jquery');
    }
    if (!Backbone) {
        Backbone = require('backbone');
    }
    if (!_) {
        _ = require('underscore')._;
    }
}
<|endoftext|>


<commit_before>import CoreCombatLogParser from 'Parser/Core/CombatLogParser';
import LowHealthHealing from 'Parser/Core/Modules/Features/LowHealthHealing';
import HealingDone from 'Parser/Core/Modules/HealingDone';

import WildGrowthNormalizer from './Normalizers/WildGrowth';
import ClearcastingNormalizer from './Normalizers/ClearcastingNormalizer';
import HotApplicationNormalizer from './Normalizers/HotApplicationNormalizer';
import TreeOfLifeNormalizer from './Normalizers/TreeOfLifeNormalizer';

import Checklist from './Modules/Features/Checklist';

import Mastery from './Modules/Core/Mastery';
import Rejuvenation from './Modules/Core/Rejuvenation';

import HotTracker from './Modules/Core/HotTracking/HotTracker';
import RejuvenationAttributor from './Modules/Core/HotTracking/RejuvenationAttributor';
import RegrowthAttributor from './Modules/Core/HotTracking/RegrowthAttributor';

import AlwaysBeCasting from './Modules/Features/AlwaysBeCasting';
import AverageHots from './Modules/Features/AverageHots';
import Abilities from './Modules/Abilities';
import CooldownThroughputTracker from './Modules/Features/CooldownThroughputTracker';
import WildGrowth from './Modules/Features/WildGrowth';
import Lifebloom from './Modules/Features/Lifebloom';
import Efflorescence from './Modules/Features/Efflorescence';
import Clearcasting from './Modules/Features/Clearcasting';
import Innervate from './Modules/Features/Innervate';
import Ironbark from './Modules/Features/Ironbark';
import NaturesEssence from './Modules/Features/NaturesEssence';
import ManaUsage from './Modules/Features/ManaUsage';

import CenarionWard from './Modules/Talents/CenarionWard';
import Cultivation from './Modules/Talents/Cultivation';
import Flourish from './Modules/Talents/Flourish';
import SpringBlossoms from './Modules/Talents/SpringBlossoms';
import SoulOfTheForest from './Modules/Talents/SoulOfTheForest';
import TreeOfLife from './Modules/Talents/TreeOfLife';
import Photosynthesis from './Modules/Talents/Photosynthesis';

import FungalEssence from './Modules/Items/AzeriteTraits/FungalEssence';
import AutumnLeaves from './Modules/Items/AzeriteTraits/AutumnLeaves';
import GroveTending from './Modules/Items/AzeriteTraits/GroveTending';
import LaserMatrix from './Modules/Items/AzeriteTraits/LaserMatrixRestoDruid';
import WakingDream from './Modules/Items/AzeriteTraits/WakingDream';

import StatWeights from './Modules/Features/StatWeights';

import { ABILITIES_AFFECTED_BY_HEALING_INCREASES } from './Constants';

class CombatLogParser extends CoreCombatLogParser {
  static abilitiesAffectedByHealingIncreases = ABILITIES_AFFECTED_BY_HEALING_INCREASES;

  static specModules = {
    // Normalizers
    wildGrowthNormalizer: WildGrowthNormalizer,
    clearcastingNormalizer: ClearcastingNormalizer,
    hotApplicationNormalizer: HotApplicationNormalizer, // this needs to be loaded after potaNormalizer, as potaNormalizer can sometimes unfix the events if loaded before...
    treeOfLifeNormalizer: TreeOfLifeNormalizer,

    // Core
    healingDone: [HealingDone, { showStatistic: true }],
    rejuvenation: Rejuvenation,
    mastery: Mastery,

    // Checklist
    checklist: Checklist,

    // Hot Tracking
    hotTracker: HotTracker,
    rejuvenationAttributor: RejuvenationAttributor,
    regrowthAttributor: RegrowthAttributor,

    // Features
    lowHealthHealing: LowHealthHealing,
    alwaysBeCasting: AlwaysBeCasting,
    averageHots: AverageHots,
    cooldownThroughputTracker: CooldownThroughputTracker,
    abilities: Abilities,
    wildGrowth: WildGrowth,
    lifebloom: Lifebloom,
    efflorescence: Efflorescence,
    clearcasting: Clearcasting,
    treeOfLife: TreeOfLife,
    photosynthesis: Photosynthesis,
    flourish: Flourish,
    innervate: Innervate,
    soulOfTheForest: SoulOfTheForest,
    springBlossoms: SpringBlossoms,
    cultivation: Cultivation,
    cenarionWard: CenarionWard,
    ironbark: Ironbark,
    naturesEssence: NaturesEssence,
    ManaUsage: ManaUsage,
    // Items:

    // Azerite traits
    fungalEssence: FungalEssence,
    autumnLeaves: AutumnLeaves,
    groveTending: GroveTending,
    laserMatrix: LaserMatrix,
    wakingDream: WakingDream,

    statWeights: StatWeights,
  };
}

export default CombatLogParser;
<commit_msg>Fix module name<commit_after>import CoreCombatLogParser from 'Parser/Core/CombatLogParser';
import LowHealthHealing from 'Parser/Core/Modules/Features/LowHealthHealing';
import HealingDone from 'Parser/Core/Modules/HealingDone';

import WildGrowthNormalizer from './Normalizers/WildGrowth';
import ClearcastingNormalizer from './Normalizers/ClearcastingNormalizer';
import HotApplicationNormalizer from './Normalizers/HotApplicationNormalizer';
import TreeOfLifeNormalizer from './Normalizers/TreeOfLifeNormalizer';

import Checklist from './Modules/Features/Checklist';

import Mastery from './Modules/Core/Mastery';
import Rejuvenation from './Modules/Core/Rejuvenation';

import HotTracker from './Modules/Core/HotTracking/HotTracker';
import RejuvenationAttributor from './Modules/Core/HotTracking/RejuvenationAttributor';
import RegrowthAttributor from './Modules/Core/HotTracking/RegrowthAttributor';

import AlwaysBeCasting from './Modules/Features/AlwaysBeCasting';
import AverageHots from './Modules/Features/AverageHots';
import Abilities from './Modules/Abilities';
import CooldownThroughputTracker from './Modules/Features/CooldownThroughputTracker';
import WildGrowth from './Modules/Features/WildGrowth';
import Lifebloom from './Modules/Features/Lifebloom';
import Efflorescence from './Modules/Features/Efflorescence';
import Clearcasting from './Modules/Features/Clearcasting';
import Innervate from './Modules/Features/Innervate';
import Ironbark from './Modules/Features/Ironbark';
import NaturesEssence from './Modules/Features/NaturesEssence';
import ManaUsage from './Modules/Features/ManaUsage';

import CenarionWard from './Modules/Talents/CenarionWard';
import Cultivation from './Modules/Talents/Cultivation';
import Flourish from './Modules/Talents/Flourish';
import SpringBlossoms from './Modules/Talents/SpringBlossoms';
import SoulOfTheForest from './Modules/Talents/SoulOfTheForest';
import TreeOfLife from './Modules/Talents/TreeOfLife';
import Photosynthesis from './Modules/Talents/Photosynthesis';

import FungalEssence from './Modules/Items/AzeriteTraits/FungalEssence';
import AutumnLeaves from './Modules/Items/AzeriteTraits/AutumnLeaves';
import GroveTending from './Modules/Items/AzeriteTraits/GroveTending';
import LaserMatrix from './Modules/Items/AzeriteTraits/LaserMatrixRestoDruid';
import WakingDream from './Modules/Items/AzeriteTraits/WakingDream';

import StatWeights from './Modules/Features/StatWeights';

import { ABILITIES_AFFECTED_BY_HEALING_INCREASES } from './Constants';

class CombatLogParser extends CoreCombatLogParser {
  static abilitiesAffectedByHealingIncreases = ABILITIES_AFFECTED_BY_HEALING_INCREASES;

  static specModules = {
    // Normalizers
    wildGrowthNormalizer: WildGrowthNormalizer,
    clearcastingNormalizer: ClearcastingNormalizer,
    hotApplicationNormalizer: HotApplicationNormalizer, // this needs to be loaded after potaNormalizer, as potaNormalizer can sometimes unfix the events if loaded before...
    treeOfLifeNormalizer: TreeOfLifeNormalizer,

    // Core
    healingDone: [HealingDone, { showStatistic: true }],
    rejuvenation: Rejuvenation,
    mastery: Mastery,

    // Checklist
    checklist: Checklist,

    // Hot Tracking
    hotTracker: HotTracker,
    rejuvenationAttributor: RejuvenationAttributor,
    regrowthAttributor: RegrowthAttributor,

    // Features
    lowHealthHealing: LowHealthHealing,
    alwaysBeCasting: AlwaysBeCasting,
    averageHots: AverageHots,
    cooldownThroughputTracker: CooldownThroughputTracker,
    abilities: Abilities,
    wildGrowth: WildGrowth,
    lifebloom: Lifebloom,
    efflorescence: Efflorescence,
    clearcasting: Clearcasting,
    treeOfLife: TreeOfLife,
    photosynthesis: Photosynthesis,
    flourish: Flourish,
    innervate: Innervate,
    soulOfTheForest: SoulOfTheForest,
    springBlossoms: SpringBlossoms,
    cultivation: Cultivation,
    cenarionWard: CenarionWard,
    ironbark: Ironbark,
    naturesEssence: NaturesEssence,
    manaUsage: ManaUsage,
    // Items:

    // Azerite traits
    fungalEssence: FungalEssence,
    autumnLeaves: AutumnLeaves,
    groveTending: GroveTending,
    laserMatrix: LaserMatrix,
    wakingDream: WakingDream,

    statWeights: StatWeights,
  };
}

export default CombatLogParser;
<|endoftext|>


<commit_before>// MouseInput.js v3.5.2

//---------------------------------------------------------------------------
// ★MouseEventクラス マウス入力に関する情報の保持とイベント処理を扱う
//---------------------------------------------------------------------------
// パズル共通 マウス入力部
// MouseEventクラスを定義
pzpr.classmgr.makeCommon({
//---------------------------------------------------------
MouseEvent:{
	initialize : function(){
		this.cursor = this.puzzle.cursor;

		this.enableMouse = true;	// マウス入力は有効か

		this.mouseCell = null;		// 入力されたセル等のID
		this.firstCell = null;		// mousedownされた時のセルのID(連黒分断禁用)

		this.inputPoint = new this.klass.RawAddress();	// 入力イベントが発生したborder座標 ※端数あり
		this.firstPoint = new this.klass.RawAddress();	// mousedownされた時のborder座標 ※端数あり
		this.prevPos    = new this.klass.Address();		// 前回のマウス入力イベントのborder座標

		this.btn = '';				// 押されているボタン
		this.inputData = null;		// 入力中のデータ番号(実装依存)

		this.bordermode = false;	// 境界線を入力中かどうか

		this.mousestart = false;	// mousedown/touchstartイベントかどうか
		this.mousemove = false;		// mousemove/touchmoveイベントかどうか
		this.mouseend = false;		// mouseup/touchendイベントかどうか

		this.mousereset();
	},

	RBShadeCell : false,	// 連黒分断禁のパズル

	use      : false,	// 黒マスの入力方法選択
	bgcolor  : false,	// 背景色の入力を可能にする
	redline  : false,	// 線の繋がりチェックを可能にする
	redblk   : false,	// 黒マスつながりチェックを可能にする (連黒分断禁も)

	//---------------------------------------------------------------------------
	// mv.mousereset() マウス入力に関する情報を初期化する
	//---------------------------------------------------------------------------
	mousereset : function(){
		var cell0 = this.mouseCell;

		this.mouseCell = // 下の行へ続く
		this.firstCell = this.board.emptycell;

		this.firstPoint.reset();
		this.prevPos.reset();

		this.btn = '';
		this.inputData = null;

		this.bordermode = false;

		this.mousestart = false;
		this.mousemove  = false;
		this.mouseend   = false;

		if(this.puzzle.execConfig('dispmove') && !!cell0 && !cell0.isnull){ cell0.draw();}
	},

	//---------------------------------------------------------------------------
	// mv.e_mousedown() Canvas上でマウスのボタンを押した際のイベント共通処理
	// mv.e_mouseup()   Canvas上でマウスのボタンを放した際のイベント共通処理
	// mv.e_mousemove() Canvas上でマウスを動かした際のイベント共通処理
	//---------------------------------------------------------------------------
	//イベントハンドラから呼び出される
	// この3つのマウスイベントはCanvasから呼び出される(mvをbindしている)
	e_mousedown : function(e){
		if(!this.enableMouse){ return true;}

		this.setMouseButton(e);			/* どのボタンが押されたか取得 (mousedown時のみ) */
		var addrtarget = this.getBoardAddress(e);
		this.moveTo(addrtarget.bx, addrtarget.by);

		e.stopPropagation();
		e.preventDefault();
	},
	e_mouseup   : function(e){
		if(!this.enableMouse){ return true;}

		this.inputEnd();

		e.stopPropagation();
		e.preventDefault();
	},
	e_mousemove : function(e){
		if(!this.enableMouse){ return true;}

		if(typeof e.which==='undefined' || e.which!==0){
			var addrtarget = this.getBoardAddress(e);
			this.lineTo(addrtarget.bx, addrtarget.by);
		}
		else{ this.mousereset();}

		e.stopPropagation();
		e.preventDefault();
	},

	//---------------------------------------------------------------------------
	// mv.setMouseButton()  イベントが起こったボタンを設定する
	// mv.getBoardAddress() イベントが起こったcanvas内の座標を取得する
	//---------------------------------------------------------------------------
	setMouseButton : function(e){
		this.btn = pzpr.util.getMouseButton(e);

		// SHIFTキー/Commandキーを押している時は左右ボタン反転
		var kc = this.puzzle.key;
		kc.checkmodifiers(e);
		if((kc.isSHIFT || kc.isMETA)!==this.puzzle.getConfig('lrcheck')){
			if     (this.btn==='left'){ this.btn = 'right';}
			else if(this.btn==='right'){ this.btn = 'left';}
		}
	},
	getBoardAddress : function(e){
		var puzzle = this.puzzle, pc = puzzle.painter;
		var pix = {px:NaN,py:NaN};
		var g = pc.context;
		if(!g){ return pix;}
		if(!pzpr.env.API.touchevent || pzpr.env.OS.iOS){
			if(!isNaN(e.offsetX)){ pix = {px:e.offsetX, py:e.offsetY};}
			else                 { pix = {px:e.layerX, py:e.layerY};}  // Firefox 39以前, iOSはこちら
		}
		else{
			var pagePos = pzpr.util.getPagePos(e), rect = pzpr.util.getRect(pc.context.child);
			pix = {px:(pagePos.px-rect.left), py:(pagePos.py-rect.top)};
		}
		return {bx:(pix.px-pc.x0)/pc.bw, by:(pix.py-pc.y0)/pc.bh};
	},

	//---------------------------------------------------------------------------
	// mv.moveTo()   Canvas上にマウスの位置を設定する
	// mv.lineTo()   Canvas上でマウスを動かす
	// mv.inputEnd() Canvas上のマウス入力処理を終了する
	// mv.inputPath() Canvas上でひとつながりになる線を入力する
	//---------------------------------------------------------------------------
	moveTo : function(bx,by){
		this.inputPoint.init(bx,by);
		this.mouseevent(0);
	},
	lineTo : function(bx,by){
		/* 前回の位置からの差分を順番に入力していきます */
		var dx = (bx-this.inputPoint.bx), dy = (by-this.inputPoint.by);
		var distance = (((dx>=0?dx:-dx)+(dy>=0?dy:-dy))*2+0.9)|0; /* 0.5くらいずつ動かす */
		var mx = dx/distance, my = dy/distance;
		for(var i=0;i<distance-1;i++){
			this.inputPoint.move(mx,my);
			this.mouseevent(1);
		}
		this.inputPoint.init(bx,by);
		this.mouseevent(1);
	},
	inputEnd : function(){
		this.mouseevent(2);
		this.mousereset();
	},
	inputPath : function(){
		var args = Array.prototype.slice.call(arguments);
		this.mousereset();
		this.btn = (typeof args[0]==='string' ? args.shift() : 'left');
		this.moveTo(args[0], args[1]);
		for(var i=2;i<args.length-1;i+=2){ /* 奇数個の最後の一つは切り捨て */
			this.lineTo(args[i], args[i+1]);
		}
		this.inputEnd();
	},

	//---------------------------------------------------------------------------
	// mv.mouseevent() マウスイベント処理
	//---------------------------------------------------------------------------
	mouseevent : function(step){
		this.cancelEvent = false;
		this.mousestart = (step===0);
		this.mousemove  = (step===1);
		this.mouseend   = (step===2);

		var puzzle = this.puzzle;
		puzzle.emit('mouse');
		if(!this.cancelEvent && (this.btn==='left' || this.btn==='right')){
			if(this.mousestart){
				puzzle.opemgr.newOperation();
				puzzle.board.errclear();
			}
			else{ puzzle.opemgr.newChain();}

			if(!this.mousestart || !this.dispRed()){
				this.mouseinput();		/* 各パズルのルーチンへ */
			}
		}
	},

	//---------------------------------------------------------------------------
	// mv.dispRed()   赤く表示する際などのイベント処理
	//---------------------------------------------------------------------------
	dispRed : function(){
		var puzzle = this.puzzle, isZ = puzzle.key.isZ;
		var flagline = puzzle.validConfig('redline') && !!(puzzle.getConfig('redline') ^isZ);
		var flagblk  = puzzle.validConfig('redblk') && !!(puzzle.getConfig('redblk') ^isZ);

		if     (flagline){ this.dispRedLine();}
		else if(flagblk) { this.dispRedBlk();}
		return (flagline || flagblk);
	},

	//---------------------------------------------------------------------------
	// mv.mouseinput() マウスイベント処理。各パズルのファイルでオーバーライドされる。
	//---------------------------------------------------------------------------
	//オーバーライド用
	mouseinput : function(){ },

	//---------------------------------------------------------------------------
	// mv.notInputted()   盤面への入力が行われたかどうか判定する
	//---------------------------------------------------------------------------
	notInputted : function(){ return !this.puzzle.opemgr.changeflag;},

	//---------------------------------------------------------------------------
	// mv.getcell()    入力された位置がどのセルに該当するかを返す
	// mv.getcell_excell()  入力された位置がどのセル/EXCELLに該当するかを返す
	// mv.getcross()   入力された位置がどの交差点に該当するかを返す
	// mv.getborder()  入力された位置がどの境界線・Lineに該当するかを返す(クリック用)
	// mv.getpos()    入力された位置が仮想セル上でどこの(X*2,Y*2)に該当するかを返す。
	//                外枠の左上が(0,0)で右下は(bd.cols*2,bd.rows*2)。rcは0～0.5のパラメータ。
	// mv.isBorderMode() 境界線入力モードかどうか判定する
	//---------------------------------------------------------------------------
	getcell : function(){
		return this.getpos(0).getc();
	},
	getcell_excell : function(){
		var pos = this.getpos(0), excell = pos.getex();
		return (!excell.isnull ? excell : pos.getc());
	},
	getcross : function(){
		return this.getpos(0.5).getx();
	},

	getpos : function(spc){
		var addr=this.inputPoint, m1=2*spc, m2=2*(1-spc);
		// 符号反転の影響なく計算したいので、+4して-4する
		var bx=addr.bx+4, by=addr.by+4, dx=bx%2, dy=by%2;
		bx = (bx&~1) + (+(dx>=m1)) + (+(dx>=m2)) - 4;
		by = (by&~1) + (+(dy>=m1)) + (+(dy>=m2)) - 4;
		return (new this.klass.Address(bx,by));
	},

	getborder : function(spc){
		var addr = this.inputPoint;
		var bx = (addr.bx&~1)+1, by = (addr.by&~1)+1;
		var dx = addr.bx+1-bx, dy = addr.by+1-by;

		// 真ん中のあたりはどこにも該当しないようにする
		var bd = this.board;
		if(bd.linegraph.isLineCross){
			if(!bd.borderAsLine){
				var m1=2*spc, m2=2*(1-spc);
				if((dx<m1||m2<dx) && (dy<m1||m2<dy)){ return bd.emptyborder;}
			}
			else{
				var m1=2*(0.5-spc), m2=2*(0.5+spc);
				if(m1<dx && dx<m2 && m1<dy && dy<m2){ return bd.emptyborder;}
			}
		}

		if(dx<2-dy){	//左上
			if(dx>dy){ return bd.getb(bx  ,by-1);}	//左上＆右上 -> 上
			else     { return bd.getb(bx-1,by  );}	//左上＆左下 -> 左
		}
		else{	//右下
			if(dx>dy){ return bd.getb(bx+1,by  );}	//右下＆右上 -> 右
			else     { return bd.getb(bx,  by+1);}	//右下＆左下 -> 下
		}
		return bd.emptyborder;
	},

	isBorderMode : function(){
		if(this.mousestart){
			this.bordermode = !this.getpos(0.25).oncell();
		}
		return this.bordermode;
	},

	//---------------------------------------------------------------------------
	// mv.setcursor() TargetCursorの場所を移動する
	//---------------------------------------------------------------------------
	setcursor : function(pos){
		var pos0 = this.cursor.getaddr();
		this.cursor.setaddr(pos);
		pos0.draw();
		pos.draw();
	}
}
});
<commit_msg>Mouse: Touch Eventsでmoveイベントがキャンセルされる不具合を修正<commit_after>// MouseInput.js v3.5.2

//---------------------------------------------------------------------------
// ★MouseEventクラス マウス入力に関する情報の保持とイベント処理を扱う
//---------------------------------------------------------------------------
// パズル共通 マウス入力部
// MouseEventクラスを定義
pzpr.classmgr.makeCommon({
//---------------------------------------------------------
MouseEvent:{
	initialize : function(){
		this.cursor = this.puzzle.cursor;

		this.enableMouse = true;	// マウス入力は有効か

		this.mouseCell = null;		// 入力されたセル等のID
		this.firstCell = null;		// mousedownされた時のセルのID(連黒分断禁用)

		this.inputPoint = new this.klass.RawAddress();	// 入力イベントが発生したborder座標 ※端数あり
		this.firstPoint = new this.klass.RawAddress();	// mousedownされた時のborder座標 ※端数あり
		this.prevPos    = new this.klass.Address();		// 前回のマウス入力イベントのborder座標

		this.btn = '';				// 押されているボタン
		this.inputData = null;		// 入力中のデータ番号(実装依存)

		this.bordermode = false;	// 境界線を入力中かどうか

		this.mousestart = false;	// mousedown/touchstartイベントかどうか
		this.mousemove = false;		// mousemove/touchmoveイベントかどうか
		this.mouseend = false;		// mouseup/touchendイベントかどうか

		this.mousereset();
	},

	RBShadeCell : false,	// 連黒分断禁のパズル

	use      : false,	// 黒マスの入力方法選択
	bgcolor  : false,	// 背景色の入力を可能にする
	redline  : false,	// 線の繋がりチェックを可能にする
	redblk   : false,	// 黒マスつながりチェックを可能にする (連黒分断禁も)

	//---------------------------------------------------------------------------
	// mv.mousereset() マウス入力に関する情報を初期化する
	//---------------------------------------------------------------------------
	mousereset : function(){
		var cell0 = this.mouseCell;

		this.mouseCell = // 下の行へ続く
		this.firstCell = this.board.emptycell;

		this.firstPoint.reset();
		this.prevPos.reset();

		this.btn = '';
		this.inputData = null;

		this.bordermode = false;

		this.mousestart = false;
		this.mousemove  = false;
		this.mouseend   = false;

		if(this.puzzle.execConfig('dispmove') && !!cell0 && !cell0.isnull){ cell0.draw();}
	},

	//---------------------------------------------------------------------------
	// mv.e_mousedown() Canvas上でマウスのボタンを押した際のイベント共通処理
	// mv.e_mouseup()   Canvas上でマウスのボタンを放した際のイベント共通処理
	// mv.e_mousemove() Canvas上でマウスを動かした際のイベント共通処理
	//---------------------------------------------------------------------------
	//イベントハンドラから呼び出される
	// この3つのマウスイベントはCanvasから呼び出される(mvをbindしている)
	e_mousedown : function(e){
		if(!this.enableMouse){ return true;}

		this.setMouseButton(e);			/* どのボタンが押されたか取得 (mousedown時のみ) */
		var addrtarget = this.getBoardAddress(e);
		this.moveTo(addrtarget.bx, addrtarget.by);

		e.stopPropagation();
		e.preventDefault();
	},
	e_mouseup   : function(e){
		if(!this.enableMouse){ return true;}

		this.inputEnd();

		e.stopPropagation();
		e.preventDefault();
	},
	e_mousemove : function(e){
		if(!this.enableMouse){ return true;}

		if(typeof e.touches!=='undefined' || typeof e.which==='undefined' || e.which!==0){
			var addrtarget = this.getBoardAddress(e);
			this.lineTo(addrtarget.bx, addrtarget.by);
		}
		else{ this.mousereset();}

		e.stopPropagation();
		e.preventDefault();
	},

	//---------------------------------------------------------------------------
	// mv.setMouseButton()  イベントが起こったボタンを設定する
	// mv.getBoardAddress() イベントが起こったcanvas内の座標を取得する
	//---------------------------------------------------------------------------
	setMouseButton : function(e){
		this.btn = pzpr.util.getMouseButton(e);

		// SHIFTキー/Commandキーを押している時は左右ボタン反転
		var kc = this.puzzle.key;
		kc.checkmodifiers(e);
		if((kc.isSHIFT || kc.isMETA)!==this.puzzle.getConfig('lrcheck')){
			if     (this.btn==='left'){ this.btn = 'right';}
			else if(this.btn==='right'){ this.btn = 'left';}
		}
	},
	getBoardAddress : function(e){
		var puzzle = this.puzzle, pc = puzzle.painter;
		var pix = {px:NaN,py:NaN};
		var g = pc.context;
		if(!g){ return pix;}
		if(!pzpr.env.API.touchevent || pzpr.env.OS.iOS){
			if(!isNaN(e.offsetX)){ pix = {px:e.offsetX, py:e.offsetY};}
			else                 { pix = {px:e.layerX, py:e.layerY};}  // Firefox 39以前, iOSはこちら
		}
		else{
			var pagePos = pzpr.util.getPagePos(e), rect = pzpr.util.getRect(pc.context.child);
			pix = {px:(pagePos.px-rect.left), py:(pagePos.py-rect.top)};
		}
		return {bx:(pix.px-pc.x0)/pc.bw, by:(pix.py-pc.y0)/pc.bh};
	},

	//---------------------------------------------------------------------------
	// mv.moveTo()   Canvas上にマウスの位置を設定する
	// mv.lineTo()   Canvas上でマウスを動かす
	// mv.inputEnd() Canvas上のマウス入力処理を終了する
	// mv.inputPath() Canvas上でひとつながりになる線を入力する
	//---------------------------------------------------------------------------
	moveTo : function(bx,by){
		this.inputPoint.init(bx,by);
		this.mouseevent(0);
	},
	lineTo : function(bx,by){
		/* 前回の位置からの差分を順番に入力していきます */
		var dx = (bx-this.inputPoint.bx), dy = (by-this.inputPoint.by);
		var distance = (((dx>=0?dx:-dx)+(dy>=0?dy:-dy))*2+0.9)|0; /* 0.5くらいずつ動かす */
		var mx = dx/distance, my = dy/distance;
		for(var i=0;i<distance-1;i++){
			this.inputPoint.move(mx,my);
			this.mouseevent(1);
		}
		this.inputPoint.init(bx,by);
		this.mouseevent(1);
	},
	inputEnd : function(){
		this.mouseevent(2);
		this.mousereset();
	},
	inputPath : function(){
		var args = Array.prototype.slice.call(arguments);
		this.mousereset();
		this.btn = (typeof args[0]==='string' ? args.shift() : 'left');
		this.moveTo(args[0], args[1]);
		for(var i=2;i<args.length-1;i+=2){ /* 奇数個の最後の一つは切り捨て */
			this.lineTo(args[i], args[i+1]);
		}
		this.inputEnd();
	},

	//---------------------------------------------------------------------------
	// mv.mouseevent() マウスイベント処理
	//---------------------------------------------------------------------------
	mouseevent : function(step){
		this.cancelEvent = false;
		this.mousestart = (step===0);
		this.mousemove  = (step===1);
		this.mouseend   = (step===2);

		var puzzle = this.puzzle;
		puzzle.emit('mouse');
		if(!this.cancelEvent && (this.btn==='left' || this.btn==='right')){
			if(this.mousestart){
				puzzle.opemgr.newOperation();
				puzzle.board.errclear();
			}
			else{ puzzle.opemgr.newChain();}

			if(!this.mousestart || !this.dispRed()){
				this.mouseinput();		/* 各パズルのルーチンへ */
			}
		}
	},

	//---------------------------------------------------------------------------
	// mv.dispRed()   赤く表示する際などのイベント処理
	//---------------------------------------------------------------------------
	dispRed : function(){
		var puzzle = this.puzzle, isZ = puzzle.key.isZ;
		var flagline = puzzle.validConfig('redline') && !!(puzzle.getConfig('redline') ^isZ);
		var flagblk  = puzzle.validConfig('redblk') && !!(puzzle.getConfig('redblk') ^isZ);

		if     (flagline){ this.dispRedLine();}
		else if(flagblk) { this.dispRedBlk();}
		return (flagline || flagblk);
	},

	//---------------------------------------------------------------------------
	// mv.mouseinput() マウスイベント処理。各パズルのファイルでオーバーライドされる。
	//---------------------------------------------------------------------------
	//オーバーライド用
	mouseinput : function(){ },

	//---------------------------------------------------------------------------
	// mv.notInputted()   盤面への入力が行われたかどうか判定する
	//---------------------------------------------------------------------------
	notInputted : function(){ return !this.puzzle.opemgr.changeflag;},

	//---------------------------------------------------------------------------
	// mv.getcell()    入力された位置がどのセルに該当するかを返す
	// mv.getcell_excell()  入力された位置がどのセル/EXCELLに該当するかを返す
	// mv.getcross()   入力された位置がどの交差点に該当するかを返す
	// mv.getborder()  入力された位置がどの境界線・Lineに該当するかを返す(クリック用)
	// mv.getpos()    入力された位置が仮想セル上でどこの(X*2,Y*2)に該当するかを返す。
	//                外枠の左上が(0,0)で右下は(bd.cols*2,bd.rows*2)。rcは0～0.5のパラメータ。
	// mv.isBorderMode() 境界線入力モードかどうか判定する
	//---------------------------------------------------------------------------
	getcell : function(){
		return this.getpos(0).getc();
	},
	getcell_excell : function(){
		var pos = this.getpos(0), excell = pos.getex();
		return (!excell.isnull ? excell : pos.getc());
	},
	getcross : function(){
		return this.getpos(0.5).getx();
	},

	getpos : function(spc){
		var addr=this.inputPoint, m1=2*spc, m2=2*(1-spc);
		// 符号反転の影響なく計算したいので、+4して-4する
		var bx=addr.bx+4, by=addr.by+4, dx=bx%2, dy=by%2;
		bx = (bx&~1) + (+(dx>=m1)) + (+(dx>=m2)) - 4;
		by = (by&~1) + (+(dy>=m1)) + (+(dy>=m2)) - 4;
		return (new this.klass.Address(bx,by));
	},

	getborder : function(spc){
		var addr = this.inputPoint;
		var bx = (addr.bx&~1)+1, by = (addr.by&~1)+1;
		var dx = addr.bx+1-bx, dy = addr.by+1-by;

		// 真ん中のあたりはどこにも該当しないようにする
		var bd = this.board;
		if(bd.linegraph.isLineCross){
			if(!bd.borderAsLine){
				var m1=2*spc, m2=2*(1-spc);
				if((dx<m1||m2<dx) && (dy<m1||m2<dy)){ return bd.emptyborder;}
			}
			else{
				var m1=2*(0.5-spc), m2=2*(0.5+spc);
				if(m1<dx && dx<m2 && m1<dy && dy<m2){ return bd.emptyborder;}
			}
		}

		if(dx<2-dy){	//左上
			if(dx>dy){ return bd.getb(bx  ,by-1);}	//左上＆右上 -> 上
			else     { return bd.getb(bx-1,by  );}	//左上＆左下 -> 左
		}
		else{	//右下
			if(dx>dy){ return bd.getb(bx+1,by  );}	//右下＆右上 -> 右
			else     { return bd.getb(bx,  by+1);}	//右下＆左下 -> 下
		}
		return bd.emptyborder;
	},

	isBorderMode : function(){
		if(this.mousestart){
			this.bordermode = !this.getpos(0.25).oncell();
		}
		return this.bordermode;
	},

	//---------------------------------------------------------------------------
	// mv.setcursor() TargetCursorの場所を移動する
	//---------------------------------------------------------------------------
	setcursor : function(pos){
		var pos0 = this.cursor.getaddr();
		this.cursor.setaddr(pos);
		pos0.draw();
		pos.draw();
	}
}
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { List, Map } from 'immutable';
import initFetch from 'iso-fetch-stream';

import { isSchema, getIdField } from './lib/schema';
import store from './store';
import logger, { logError, logWarn } from './lib/log';
import getQueryString from './lib/query_string';

/* basic state tree design for api.js
* {
*   $api: {
*     [url]: {
*       [resource]: {
*         models: {
*           [id]: {
*             [key]: any,
*             _fetched?: boolean
*           }
*         },
*         requests: {
*           [url]: {
*             pending?: boolean,
*             error?: Object,
*             data?: List | Map
*           }
*         }
*       }
*     }
*   }
* }
*/

// TODO: use some more specific method from store's undo implementation
const isUndo = () => store.getState( '_reset' );

function createActions( props ) {

  const fetchOptions = props.credentials ? { credentials: props.credentials } : {};
  if ( props.auth || props.authorization ) fetchOptions.headers = { Authorization: props.auth || props.authorization };
  const fetch = initFetch( fetchOptions );

  return Object.keys( props ).reduce(( memo, key ) => {

    if ( isSchema( props[ key ] ) ) {

      const schema       = props[ key ].schema || props[ key ];
      const getData      = props[ key ].getData || ( data => data );
      const idField      = getIdField( schema );
      const resourceType = schema.title;
      const resourcePath = `/${resourceType}`;
      const url          = `${props.url}${resourcePath}`;

      const getState      = key => store.getState([ '$api', props.url, resourceType ].concat( key || [] ).map( item => String( item ) ) );
      const setState      = ( val, key ) => store.setState([ '$api', props.url, resourceType ].concat( key || [] ).map( item => String( item ) ), val );
      const setStateQuiet = ( val, key ) => store.setStateQuiet([ '$api', props.url, resourceType ].concat( key || [] ), val );
      const deleteState   = path => setState( null, path );

      const getRequests = path => getState([ 'requests' ].concat( path || [] ) );
      const getRequestsData = path => getRequests([ path, 'data' ]);
      const setRequests = ( data, path ) => setState( data, [ 'requests' ].concat( path || [] ) );
      const setRequestsData = ( path, data ) => setState( data, [ 'requests', path, 'data' ] );

      const getCollection = path => {
        const collection = getRequestsData( path );
        if ( collection ) {
          const models = getModels();
          return collection.reduce(( memo, id ) => {
            const model = models.get( id );
            if ( model ) return memo.push( model );
            return memo;
          }, List());
        } else {
          return null;
        }
      };

      const setCollection = ( data = List(), path = '/' ) => {
        const state = getState();
        const nextState = state.withMutations( map => {
          const dict = data.reduce(( memo, item ) => ({ ...memo, [item[idField]]: item }), {});
          map.set( 'models', getModels().mergeDeep( dict ) );
          map.setIn([ 'requests', path, 'data' ], List( Object.keys( dict ) ) );
        });
        setState( nextState );
      };

      const clearCollection = ( path = '/' ) => setRequestsData( path, null );
      const clearAll = () => {
        setRequests( Map() );
        setModels( Map() );
      };

      const getModels  = () => getState( 'models' ) || Map();
      const setModels  = data => setState( data, 'models' );
      const getModel   = id => getState([ 'models', id ]);
      const setModel   = ( id, data ) => setState( data, [ 'models', id ] );
      const getPending = path => getState([ 'requests', path, 'pending' ]);
      const setPending = ( path, data ) => setStateQuiet( data, [ 'requests', path, 'pending' ]);
      const getError   = path => getState([ 'requests', path, 'error' ]);
      const setError   = ( path, error, options = {} ) => {
        const method = options.quiet ? setStateQuiet : setState;
        method( error, [ 'requests', path, 'error' ] );
      };

      const setSearchResults = path => data => setCollection( data, path );
      const getSearchResults = path => getCollection( path );

      const removeFromCollections = ( map, id ) =>
        [ ...map.get( 'requests' ).entries() ]
          .reduce(( undo, [path, request] ) => {
            const collection = request.get( 'data' );
            if ( List.isList( collection ) ) {
              const modelIdx = collection.findIndex( modelId => modelId === id );
              if ( ~modelIdx ) {
                const nextCollection = collection.delete( modelIdx );
                map.setIn([ 'requests', path, 'data' ], nextCollection );
                undo.push(() => setCollection( nextCollection.insert( modelIdx, id ), path ));
              }
            }
            return undo;
          }, []);

      const deleteModel = id => {
        const undo = [];
        const state = getState();
        setState( state.withMutations( map => {
          const model = getModel( id );
          if ( model ) {
            map.set( 'models', getModels().delete( id ) );
            undo.push(() => setModels( getModels().set( id, model ) ));
            undo.push( ...removeFromCollections( map, id ) );
          }
        }));
        return undo;
      };

      const updateModel = ( id, vals ) => {
        const undo = [];
        const model = getModel( id );
        if ( model ) {
          setModel( id, model.mergeDeep( vals ) );
          undo.push(() => setModels( getModels().set( id, model ) ) );
        }
        return undo;
      };

      const shouldFetch = path => !isUndo() && !getPending( path ) && !getError( path );

      const api = ['get', 'post', 'put', 'delete' ].reduce(( memo, method ) => ({
        ...memo,
        [method]: ( path, ...args ) => {
          setPending( path, true );
          return fetch[ method ]( `${url}${path === '/' ? '' : path}`, ...args )
            .then(
              response => {
                const data = getData( response );
                setPending( path, false );
                setError( path, null, { quiet: true } );
                return data;
              },
              err => {
                setPending( path, false );
                setError( path, err );
                return Promise.reject( err );
              }
            );
        }
      }), {});

      /**
       * api calls
       */
      const fetchAll = ( path = '/' ) => {
        if ( shouldFetch( path ) ) {
          api.get( path ).then( data => setCollection( data, path ) );
        }
      };

      const fetchOne = id => {
        const path = `/${id}`;
        if ( shouldFetch( path ) ) {
          api.get( path ).then( data => setModel( data[ idField ], { ...data, _fetched: true }));
        }
      };

      const createOne = data => api.post( '/', data ).then( data => {
        fetchAll();
        return data;
      });

      const updateOne = ( id, data ) => api.put( `/${id}`, data );

      const deleteOne = id => api.delete( `/${id}` );

      const search = path => {
        if ( shouldFetch( path ) ) {
          return api.get( path ).then( setSearchResults( path ) );
        }
        // TODO: store promise as pending value so it can be used on repeat
        // calls
        return Promise.resolve( 'pending' );
      };

      /**/

      const $clear = id => () => setModel( id, null );
      const $reset = id => () => fetchOne( id );

      const optimisticDelete = id => {
        const undoDelete = deleteModel( id );
        if ( undoDelete.length ) {
          return deleteOne( id ).catch( err => {
            logError( `Failed to delete ${id}`, err );
            undoDelete.forEach( undo => undo() );
            return Promise.reject( err );
          });
        } else {
          return Promise.reject( new Error( 404 ) );
        }
      };

      const $delete = id => ( options = {} ) =>
        options.optimistic === false || !getModel( id )
          ? deleteOne( id ).then(() => deleteModel( id ) )
          : optimisticDelete( id );

      const optimisticUpdate = ( id, vals, options = {} ) => {
        const placeholder = typeof options.optimistic === 'function'
          ? options.optimistic({ id, ...vals })
          : vals;
        const undoUpdate = updateModel( id, placeholder );
        if ( undoUpdate.length ) {
          return updateOne( id, vals ).catch( err => {
            logError( `Failed to update ${id} with vals`, vals, err );
            undoUpdate.forEach( undo => undo() );
            return Promise.reject( err );
          });
        } else {
          return Promise.reject( new Error( 404 ) );
        }
      };

      const $update = id => ( vals, options = {} ) =>
        options.optimistic === false || !getModel( id )
          ? updateOne( id, vals ).then( data => updateModel( id, data ) )
          : optimisticUpdate( id, vals, options );

      const getPlaceholder = ( path = null, dataType = List ) => {
        const placeholder = dataType();
        placeholder.$isPending = true;
        placeholder.$error = path ? getError( path ) : null;
        return placeholder;
      };

      const addRestMethods = model => {
        const id = model.get( idField );
        model.$delete = $delete( id );
        model.$update = $update( id );
        model.$clear  = $clear( id );
        model.$reset  = $reset( id );
        return model;
      };

      const main = params => {
        const path = '/' + ( params ? `?${getQueryString( params )}` : '' );
        const collection = getCollection( path );
        if ( !collection ) {
          fetchAll( path );
          return getPlaceholder( path );
        } else {
          return collection.map( addRestMethods );
        }
      };

      main.$get = id => {
        const model = getModel( id );
        if ( !model || !model.get( '_fetched' ) ) {
          const path = `/${id}`;
          fetchOne( id );
          const placeholder = getPlaceholder( path, Map );
          placeholder.$delete = $delete( id );
          placeholder.$update = $update( id );
          return placeholder;
        } else {
          return addRestMethods( model );
        }
      };

      // remove the cache for the resource collection
      main.$clear = () => clearCollection();
      main.$clearAll = clearAll;
      main.$reset = fetchAll;

      const optimisticCreate = ( data, options = {} ) => {
        if ( typeof options.optimistic === 'function' ) {
          const nextState = getState().withMutations( map => options.optimistic( map, data ) );
          if ( nextState ) setState( nextState );
          return createOne( data );
        } else {
          logWarn( `Optimistic creates must receive a function that updates the state with the optimistic data. The create will proceed pessimistically.`);
          return createOne( data );
        }
      };

      main.$create = ( data, options = {} ) =>
        !options.optimistic
          ? createOne( data )
          : optimisticCreate( data, options );

      // call signature: search({ queryOrWhatever: 'foo', otherParam: 0, anotherParam: 30 })
      // - sort so that we can cache consistently
      main.$search = ({ route = '', ...args }) => {
        const queryString = getQueryString( args );
        return search( route + '?' + queryString );
      };

      main.$search.results = ({ route = '', ...args }) => {
        const queryString = getQueryString( args );
        const path = `${route}?${queryString}`;
        const resultsCached = getSearchResults( path );
        if ( resultsCached ) {
          return addRestMethods( resultsCached );
        } else {
          const placeholder = List();
          placeholder.$isPending = !!getPending( path );
          placeholder.$error = getError( path );
          return placeholder;
        }
      };

      // hang standard api methods off of .api so devs can construct
      // non-standard paths
      main.api = [ 'delete', '$get', 'get', 'post', 'put', 'stream' ].reduce(( memo, method ) => ({
        ...memo,
        [method]: ( path, ...args ) => {
          if ( method === '$get' ) {
            const cache = getState([ 'requests', path, 'data' ]);
            if ( cache ) {
              return List.isList( cache )
                ? getCollection( path ).map( addRestMethods )
                : cache;
            } else {
              if ( shouldFetch( path ) ) {
                api.get( path, ...args ).then( data => {
                  if ( Array.isArray( data ) ) {
                    setCollection( data, path );
                  } else {
                    setState( data, [ 'requests', path, 'data' ] );
                  }
                  return data;
                });
              }
              return getPlaceholder( path );
            }
          } else {
            return api[ method ]( path, ...args );
          }
        }
      }), {});

      main._schema       = schema;
      main._resourceType = resourceType;
      main.getState      = getState;

      memo[ key ] = main;
      if ( typeof window !== 'undefined' ) {
        window.jetset = window.jetset || {};
        window.jetset[ key ] = main;
      }
    }
    return memo;
  }, {} );
}

export default class Api extends React.Component {

  static propTypes = {
    url: PropTypes.string.isRequired,
    // see https://github.com/github/fetch#sending-cookies for reference
    credentials: PropTypes.oneOf( [ 'same-origin', 'include' ] ),
    token: PropTypes.string
  }

  subscriptions = []

  constructor( props ) {
    super( props );
    this.api = createActions( props );
    this.state = { cache: null };
  }

  componentWillMount() {
    this.subscriptions = Object.keys( this.api ).map( key => {
      const resource = this.api[ key ]._resourceType;
      return store.subscribeTo([ '$api', this.props.url, resource ], state => {
        logger(`\uD83C\uDF00 <Api> is re-rendering based on state changes on branch: %c${this.props.url + ' ‣ ' + resource}`, 'color: #5B4532' );
        this.setState({ cache: state });
      });
    });
  }

  componentWillUnmount() {
    this.subscriptions.forEach( subscription => store.unsubscribe( subscription ) );
  }

  render() {
    const { children, ...props } = this.props;
    return children && typeof children.type === 'function'
      ? React.cloneElement( children, { ...props, ...this.api } )
      : children;
  }
}
<commit_msg>return full response to $search<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { List, Map } from 'immutable';
import initFetch from 'iso-fetch-stream';

import { isSchema, getIdField } from './lib/schema';
import store from './store';
import logger, { logError, logWarn } from './lib/log';
import getQueryString from './lib/query_string';

/* basic state tree design for api.js
* {
*   $api: {
*     [url]: {
*       [resource]: {
*         models: {
*           [id]: {
*             [key]: any,
*             _fetched?: boolean
*           }
*         },
*         requests: {
*           [url]: {
*             pending?: boolean,
*             error?: Object,
*             data?: List | Map
*           }
*         }
*       }
*     }
*   }
* }
*/

// TODO: use some more specific method from store's undo implementation
const isUndo = () => store.getState( '_reset' );

function createActions( props ) {

  const fetchOptions = props.credentials ? { credentials: props.credentials } : {};
  if ( props.auth || props.authorization ) fetchOptions.headers = { Authorization: props.auth || props.authorization };
  const fetch = initFetch( fetchOptions );

  return Object.keys( props ).reduce(( memo, key ) => {

    if ( isSchema( props[ key ] ) ) {

      const schema       = props[ key ].schema || props[ key ];
      const getData      = props[ key ].getData || ( data => data );
      const idField      = getIdField( schema );
      const resourceType = schema.title;
      const resourcePath = `/${resourceType}`;
      const url          = `${props.url}${resourcePath}`;

      const getState      = key => store.getState([ '$api', props.url, resourceType ].concat( key || [] ).map( item => String( item ) ) );
      const setState      = ( val, key ) => store.setState([ '$api', props.url, resourceType ].concat( key || [] ).map( item => String( item ) ), val );
      const setStateQuiet = ( val, key ) => store.setStateQuiet([ '$api', props.url, resourceType ].concat( key || [] ), val );
      const deleteState   = path => setState( null, path );

      const getRequests = path => getState([ 'requests' ].concat( path || [] ) );
      const getRequestsData = path => getRequests([ path, 'data' ]);
      const setRequests = ( data, path ) => setState( data, [ 'requests' ].concat( path || [] ) );
      const setRequestsData = ( path, data ) => setState( data, [ 'requests', path, 'data' ] );

      const getCollection = path => {
        const collection = getRequestsData( path );
        if ( collection ) {
          const models = getModels();
          return collection.reduce(( memo, id ) => {
            const model = models.get( id );
            if ( model ) return memo.push( model );
            return memo;
          }, List());
        } else {
          return null;
        }
      };

      const setCollection = ( data = List(), path = '/' ) => {
        const state = getState();
        const nextState = state.withMutations( map => {
          const dict = data.reduce(( memo, item ) => ({ ...memo, [item[idField]]: item }), {});
          map.set( 'models', getModels().mergeDeep( dict ) );
          map.setIn([ 'requests', path, 'data' ], List( Object.keys( dict ) ) );
        });
        setState( nextState );
      };

      const clearCollection = ( path = '/' ) => setRequestsData( path, null );
      const clearAll = () => {
        setRequests( Map() );
        setModels( Map() );
      };

      const getModels  = () => getState( 'models' ) || Map();
      const setModels  = data => setState( data, 'models' );
      const getModel   = id => getState([ 'models', id ]);
      const setModel   = ( id, data ) => setState( data, [ 'models', id ] );
      const getPending = path => getState([ 'requests', path, 'pending' ]);
      const setPending = ( path, data ) => setStateQuiet( data, [ 'requests', path, 'pending' ]);
      const getError   = path => getState([ 'requests', path, 'error' ]);
      const setError   = ( path, error, options = {} ) => {
        const method = options.quiet ? setStateQuiet : setState;
        method( error, [ 'requests', path, 'error' ] );
      };

      const setSearchResults = path => data => setCollection( data, path );
      const getSearchResults = path => getCollection( path );

      const removeFromCollections = ( map, id ) =>
        [ ...map.get( 'requests' ).entries() ]
          .reduce(( undo, [path, request] ) => {
            const collection = request.get( 'data' );
            if ( List.isList( collection ) ) {
              const modelIdx = collection.findIndex( modelId => modelId === id );
              if ( ~modelIdx ) {
                const nextCollection = collection.delete( modelIdx );
                map.setIn([ 'requests', path, 'data' ], nextCollection );
                undo.push(() => setCollection( nextCollection.insert( modelIdx, id ), path ));
              }
            }
            return undo;
          }, []);

      const deleteModel = id => {
        const undo = [];
        const state = getState();
        setState( state.withMutations( map => {
          const model = getModel( id );
          if ( model ) {
            map.set( 'models', getModels().delete( id ) );
            undo.push(() => setModels( getModels().set( id, model ) ));
            undo.push( ...removeFromCollections( map, id ) );
          }
        }));
        return undo;
      };

      const updateModel = ( id, vals ) => {
        const undo = [];
        const model = getModel( id );
        if ( model ) {
          setModel( id, model.mergeDeep( vals ) );
          undo.push(() => setModels( getModels().set( id, model ) ) );
        }
        return undo;
      };

      const shouldFetch = path => !isUndo() && !getPending( path ) && !getError( path );

      const api = ['get', 'post', 'put', 'delete' ].reduce(( memo, method ) => ({
        ...memo,
        [method]: ( path, ...args ) => {
          setPending( path, true );
          return fetch[ method ]( `${url}${path === '/' ? '' : path}`, ...args )
            .then(
              response => {
                const data = getData( response );
                setPending( path, false );
                setError( path, null, { quiet: true } );
                return data;
              },
              err => {
                setPending( path, false );
                setError( path, err );
                return Promise.reject( err );
              }
            );
        }
      }), {});

      /**
       * api calls
       */
      const fetchAll = ( path = '/' ) => {
        if ( shouldFetch( path ) ) {
          api.get( path ).then( data => setCollection( data, path ) );
        }
      };

      const fetchOne = id => {
        const path = `/${id}`;
        if ( shouldFetch( path ) ) {
          api.get( path ).then( data => setModel( data[ idField ], { ...data, _fetched: true }));
        }
      };

      const createOne = data => api.post( '/', data ).then( data => {
        fetchAll();
        return data;
      });

      const updateOne = ( id, data ) => api.put( `/${id}`, data );

      const deleteOne = id => api.delete( `/${id}` );

      const search = route => {
        if ( shouldFetch( route ) ) {
          return api.get( route ).then( data => {
            setSearchResults( route, data );
            return data;
          });
        }
        // TODO: store promise as pending value so it can be used on repeat
        // calls
        return Promise.resolve( 'pending' );
      };

      /**/

      const $clear = id => () => setModel( id, null );
      const $reset = id => () => fetchOne( id );

      const optimisticDelete = id => {
        const undoDelete = deleteModel( id );
        if ( undoDelete.length ) {
          return deleteOne( id ).catch( err => {
            logError( `Failed to delete ${id}`, err );
            undoDelete.forEach( undo => undo() );
            return Promise.reject( err );
          });
        } else {
          return Promise.reject( new Error( 404 ) );
        }
      };

      const $delete = id => ( options = {} ) =>
        options.optimistic === false || !getModel( id )
          ? deleteOne( id ).then(() => deleteModel( id ) )
          : optimisticDelete( id );

      const optimisticUpdate = ( id, vals, options = {} ) => {
        const placeholder = typeof options.optimistic === 'function'
          ? options.optimistic({ id, ...vals })
          : vals;
        const undoUpdate = updateModel( id, placeholder );
        if ( undoUpdate.length ) {
          return updateOne( id, vals ).catch( err => {
            logError( `Failed to update ${id} with vals`, vals, err );
            undoUpdate.forEach( undo => undo() );
            return Promise.reject( err );
          });
        } else {
          return Promise.reject( new Error( 404 ) );
        }
      };

      const $update = id => ( vals, options = {} ) =>
        options.optimistic === false || !getModel( id )
          ? updateOne( id, vals ).then( data => updateModel( id, data ) )
          : optimisticUpdate( id, vals, options );

      const getPlaceholder = ( path = null, dataType = List ) => {
        const placeholder = dataType();
        placeholder.$isPending = true;
        placeholder.$error = path ? getError( path ) : null;
        return placeholder;
      };

      const addRestMethods = model => {
        const id = model.get( idField );
        model.$delete = $delete( id );
        model.$update = $update( id );
        model.$clear  = $clear( id );
        model.$reset  = $reset( id );
        return model;
      };

      const main = params => {
        const path = '/' + ( params ? `?${getQueryString( params )}` : '' );
        const collection = getCollection( path );
        if ( !collection ) {
          fetchAll( path );
          return getPlaceholder( path );
        } else {
          return collection.map( addRestMethods );
        }
      };

      main.$get = id => {
        const model = getModel( id );
        if ( !model || !model.get( '_fetched' ) ) {
          const path = `/${id}`;
          fetchOne( id );
          const placeholder = getPlaceholder( path, Map );
          placeholder.$delete = $delete( id );
          placeholder.$update = $update( id );
          return placeholder;
        } else {
          return addRestMethods( model );
        }
      };

      // remove the cache for the resource collection
      main.$clear = () => clearCollection();
      main.$clearAll = clearAll;
      main.$reset = fetchAll;

      const optimisticCreate = ( data, options = {} ) => {
        if ( typeof options.optimistic === 'function' ) {
          const nextState = getState().withMutations( map => options.optimistic( map, data ) );
          if ( nextState ) setState( nextState );
          return createOne( data );
        } else {
          logWarn( `Optimistic creates must receive a function that updates the state with the optimistic data. The create will proceed pessimistically.`);
          return createOne( data );
        }
      };

      main.$create = ( data, options = {} ) =>
        !options.optimistic
          ? createOne( data )
          : optimisticCreate( data, options );

      // call signature: search({ queryOrWhatever: 'foo', otherParam: 0, anotherParam: 30 })
      // - sort so that we can cache consistently
      main.$search = ({ route = '', ...args }) => {
        const queryString = getQueryString( args );
        return search( route + '?' + queryString );
      };

      main.$search.results = ({ route = '', ...args }) => {
        const queryString = getQueryString( args );
        const path = `${route}?${queryString}`;
        const resultsCached = getSearchResults( path );
        if ( resultsCached ) {
          return addRestMethods( resultsCached );
        } else {
          const placeholder = List();
          placeholder.$isPending = !!getPending( path );
          placeholder.$error = getError( path );
          return placeholder;
        }
      };

      // hang standard api methods off of .api so devs can construct
      // non-standard paths
      main.api = [ 'delete', '$get', 'get', 'post', 'put', 'stream' ].reduce(( memo, method ) => ({
        ...memo,
        [method]: ( path, ...args ) => {
          if ( method === '$get' ) {
            const cache = getState([ 'requests', path, 'data' ]);
            if ( cache ) {
              return List.isList( cache )
                ? getCollection( path ).map( addRestMethods )
                : cache;
            } else {
              if ( shouldFetch( path ) ) {
                api.get( path, ...args ).then( data => {
                  if ( Array.isArray( data ) ) {
                    setCollection( data, path );
                  } else {
                    setState( data, [ 'requests', path, 'data' ] );
                  }
                  return data;
                });
              }
              return getPlaceholder( path );
            }
          } else {
            return api[ method ]( path, ...args );
          }
        }
      }), {});

      main._schema       = schema;
      main._resourceType = resourceType;
      main.getState      = getState;

      memo[ key ] = main;
      if ( typeof window !== 'undefined' ) {
        window.jetset = window.jetset || {};
        window.jetset[ key ] = main;
      }
    }
    return memo;
  }, {} );
}

export default class Api extends React.Component {

  static propTypes = {
    url: PropTypes.string.isRequired,
    // see https://github.com/github/fetch#sending-cookies for reference
    credentials: PropTypes.oneOf( [ 'same-origin', 'include' ] ),
    token: PropTypes.string
  }

  subscriptions = []

  constructor( props ) {
    super( props );
    this.api = createActions( props );
    this.state = { cache: null };
  }

  componentWillMount() {
    this.subscriptions = Object.keys( this.api ).map( key => {
      const resource = this.api[ key ]._resourceType;
      return store.subscribeTo([ '$api', this.props.url, resource ], state => {
        logger(`\uD83C\uDF00 <Api> is re-rendering based on state changes on branch: %c${this.props.url + ' ‣ ' + resource}`, 'color: #5B4532' );
        this.setState({ cache: state });
      });
    });
  }

  componentWillUnmount() {
    this.subscriptions.forEach( subscription => store.unsubscribe( subscription ) );
  }

  render() {
    const { children, ...props } = this.props;
    return children && typeof children.type === 'function'
      ? React.cloneElement( children, { ...props, ...this.api } )
      : children;
  }
}
<|endoftext|>