text:
import { expect } from 'chai';
import { BeakerXDataGrid } from "@beakerx/tableDisplay/dataGrid/BeakerXDataGrid";
import HighlighterFactory from "@beakerx/tableDisplay/dataGrid/highlighter/HighlighterFactory";
import DataGridColumn from "@beakerx/tableDisplay/dataGrid/column/DataGridColumn";
import highlighterStateMock from "../mock/highlighterStateMock";
import modelStateMock from "../mock/modelStateMock";
import columnOptionsMock from "../mock/columnOptionsMock";
import HeatmapHighlighter from "@beakerx/tableDisplay/dataGrid/highlighter/HeatmapHighlighter";
import createStore from "@beakerx/tableDisplay/dataGrid/store/BeakerXDataStore";

describe('HighlighterFactory', () => {
  const dataStore = createStore(modelStateMock);
  const dataGrid = new BeakerXDataGrid({}, dataStore);
  const column = new DataGridColumn(
    columnOptionsMock,
    dataGrid,
    dataGrid.columnManager
  );

  it('should return HeatmapHighlighter', () => {
    expect(HighlighterFactory.getHighlighter(highlighterStateMock, column))
      .to.be.an.instanceof(HeatmapHighlighter);
  });

  it('should return undefined', () => {
    expect(HighlighterFactory.getHighlighter({}, column))
      .to.equal(undefined);
  });
});


text:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/btn_comments_pressed"
          android:state_pressed="true" />
    <item android:drawable="@drawable/btn_comments_pressed"
          android:state_focused="true" />
    <item android:drawable="@drawable/btn_comments" />
</selector>

redpajama_set_name: RedPajamaGithub


text:
package org.apache.polygene.library.sql.generator.implementation.grammar.factories.pgsql;

import org.apache.polygene.library.sql.generator.grammar.common.datatypes.pgsql.Text;
import org.apache.polygene.library.sql.generator.grammar.factories.pgsql.PgSQLDataTypeFactory;
import org.apache.polygene.library.sql.generator.implementation.grammar.common.datatypes.pgsql.TextImpl;
import org.apache.polygene.library.sql.generator.implementation.grammar.factories.DefaultDataTypeFactory;
import org.apache.polygene.library.sql.generator.implementation.transformation.spi.SQLProcessorAggregator;
import org.apache.polygene.library.sql.generator.vendor.PostgreSQLVendor;

/**
 * @author Stanislav Muhametsin
 */
public class PgSQLDataTypeFactoryImpl extends DefaultDataTypeFactory
    implements PgSQLDataTypeFactory
{

    public PgSQLDataTypeFactoryImpl( PostgreSQLVendor vendor, SQLProcessorAggregator processor )
    {
        super( vendor, processor );
    }

    public Text text()
    {
        return TextImpl.INSTANCE;
    }
}


text:
package com.initishbhatt.loaders;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.View;

import com.initishbhatt.loaders.R;
import com.initishbhatt.loaders.util.Helper;

/**
 * @author nitishbhatt
 */

public class Loader extends View {

    private int animationDuration = 500;
    private int firstShadowColor = 0;
    private int secondShadowColor = 0;
    private Paint firstShadowPaint;
    private Paint secondShadowPaint;
    private boolean isShadowColorSet = false;
    private boolean shouldAnimate = true;
    private int selectedPosition = 1;
    private long logTime = 0;
    private int calculatedHeight = 0;
    private int calculatedWidth = 0;
    private int defaultColor = ContextCompat.getColor(getContext(), R.color.loader_default);
    private int selectedColor = ContextCompat.getColor(getContext(), R.color.loader_selected);
    private int radius = 30;
    private boolean showRunningShadow = true;

    public boolean isShouldAnimate() {
        return shouldAnimate;
    }

    public void setShouldAnimate(boolean shouldAnimate) {
        this.shouldAnimate = shouldAnimate;
    }

    public int getSelectedPosition() {
        return selectedPosition;
    }

    public void setSelectedPosition(int selectedPosition) {
        this.selectedPosition = selectedPosition;
    }

    public int getCalculatedHeight() {
        return calculatedHeight;
    }

    public void setCalculatedHeight(int calculatedHeight) {
        this.calculatedHeight = calculatedHeight;
    }

    public int getCalculatedWidth() {
        return calculatedWidth;
    }

    public void setCalculatedWidth(int calculatedWidth) {
        this.calculatedWidth = calculatedWidth;
    }

    public long getLogTime() {
        return logTime;
    }

    public void setLogTime(long logTime) {
        this.logTime = logTime;
    }

    public Paint getDefaultCirclePaint() {
        return defaultCirclePaint;
    }

    public Paint getSelectedCirclePaint() {
        return selectedCirclePaint;
    }

    private Paint defaultCirclePaint;
    private Paint selectedCirclePaint;

    public Paint getFirstShadowPaint() {
        return firstShadowPaint;
    }

    public Paint getSecondShadowPaint() {
        return secondShadowPaint;
    }

    public int getAnimationDuration() {
        return animationDuration;
    }

    public void setAnimationDuration(int animationDuration) {
        this.animationDuration = animationDuration;
    }

    public int getDefaultColor() {
        return defaultColor;
    }

    public void setDefaultColor(int defaultColor) {
        this.defaultColor = ContextCompat.getColor(getContext(), defaultColor);
        if (defaultCirclePaint != null) {
            defaultCirclePaint.setColor(this.defaultColor);
        }
    }

    public int getSelectedColor() {
        return selectedColor;
    }

    public void setSelectedColor(int selectedColor) {
        this.selectedColor = ContextCompat.getColor(getContext(), selectedColor);
        if (selectedCirclePaint != null) {
            selectedCirclePaint.setColor(this.selectedColor);
            initShadowPaint();
        }
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public boolean isShowRunningShadow() {
        return showRunningShadow;
    }

    public void setShowRunningShadow(boolean showRunningShadow) {
        this.showRunningShadow = showRunningShadow;
    }

    public int getFirstShadowColor() {
        return firstShadowColor;
    }

    public void setFirstShadowColor(int firstShadowColor) {
        this.firstShadowColor = firstShadowColor;
        initShadowPaint();
    }

    public int getSecondShadowColor() {
        return secondShadowColor;
    }

    public void setSecondShadowColor(int secondShadowColor) {
        this.secondShadowColor = secondShadowColor;
        initShadowPaint();
    }

    public Loader(Context context) {
        super(context);
    }

    public Loader(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public Loader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void initAttributes(AttributeSet attributeSet) {

        TypedArray typedArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.Loader, 0, 0);

        this.defaultColor = typedArray.getColor(R.styleable.Loader_default_color,
                ContextCompat.getColor(getContext(), R.color.loader_default));
        this.selectedColor = typedArray.getColor(R.styleable.Loader_selected_color,
                ContextCompat.getColor(getContext(), R.color.loader_selected));

        this.radius = typedArray.getDimensionPixelSize(R.styleable.Loader_circle_radius, 30);

        this.animationDuration = typedArray.getInt(R.styleable.Loader_anim_dur, 500);

        this.showRunningShadow = typedArray.getBoolean(R.styleable.Loader_show_running_shadow, true);

        this.firstShadowColor = typedArray.getColor(R.styleable.Loader_first_shadow_color, 0);
        this.secondShadowColor = typedArray.getColor(R.styleable.Loader_second_shadow_color, 0);

        typedArray.recycle();
    }

    public void initSelectedCirclePaint() {
        selectedCirclePaint = new Paint();
        selectedCirclePaint.setAntiAlias(true);
        selectedCirclePaint.setStyle(Paint.Style.FILL);
        selectedCirclePaint.setColor(selectedColor);
    }

    public void initDefaultCirclePaint() {
        defaultCirclePaint = new Paint();
        defaultCirclePaint.setAntiAlias(true);
        defaultCirclePaint.setStyle(Paint.Style.FILL);
        defaultCirclePaint.setColor(defaultColor);
    }

    public void initShadowPaint() {
        if (showRunningShadow) {
            if (!isShadowColorSet) {
                firstShadowColor = Helper.adjustAlpha(selectedColor, 0.7f);
                secondShadowColor = Helper.adjustAlpha(selectedColor, 0.5f);
                isShadowColorSet = true;
            }

            initFirstShadowPaint();

            initSecondShadowPaint();
        }
    }

    private void initSecondShadowPaint() {
        secondShadowPaint = new Paint();
        secondShadowPaint.setAntiAlias(true);
        secondShadowPaint.setStyle(Paint.Style.FILL);
        secondShadowPaint.setColor(secondShadowColor);
    }

    private void initFirstShadowPaint() {
        firstShadowPaint = new Paint();
        firstShadowPaint.setAntiAlias(true);
        firstShadowPaint.setStyle(Paint.Style.FILL);
        firstShadowPaint.setColor(firstShadowColor);
    }

    public void startAnimation() {
        setShouldAnimate(true);
        invalidate();
    }

    public void stopAnimation() {
        setShouldAnimate(false);
        invalidate();
    }

}


text:
package v1.post.command

import akka.actor.Props
import akka.util.Timeout
import pl.why.common.PersistentEntity.GetState
import pl.why.common._
import v1.post.AddPostInput
import v1.post.command.PostEntity.Command._

import scala.concurrent.duration._

object PostManager {
  val Name = "post-manager"

  case class FindPostById(id: String)

  case class AddPost(key: String, input: AddPostInput)

  case class AnnouncePost(key: String, title: String)

  case class PinPost(key: String, title: String)

  case class UnpinPost(key: String, title: String)

  case class AddRelated(id: String, relatedPostId: String)

  case class AddComment(id: String)

  val TitleNotUniqueError = ErrorMessage("post.title.nonunique", Some("The post title supplied for a create is not unique"))

  def props: Props = Props[PostManager]

}

class PostManager extends Aggregate[PostData, PostEntity] {

  import PostManager._
  import akka.pattern.ask
  import context.dispatcher

  override def entityProps: Props = PostEntity.props

  override def receive: Receive = {
    case FindPostById(id) =>
      forwardCommand(id, GetState(id))

    case AddPost(key, input) =>
      implicit val timeout = Timeout(5.seconds)

      val id = getPostId(key, input.title)

      val stateFut = (entityShardRegion ? GetState(id)).mapTo[ServiceResult[PostData]]
      val caller = sender()
      stateFut onComplete {
        case util.Success(FullResult(_)) =>
          caller ! Failure(FailureType.Validation, TitleNotUniqueError)

        case util.Success(EmptyResult) =>

          val body = input.body.map(c => BodyComponentData(c.component, c.parameters.toMap))
          val fo = PostData(id, key, input.author, input.title, body, input.coverUrl, input.metaTitle, input.metaDescription,
            input.metaKeywords, tags = input.tags, timeToRead = calculateTieToRead(body))
          entityShardRegion.tell(CreatePost(fo), caller)

        case _ =>
          caller ! Failure(FailureType.Service, ServiceResult.UnexpectedFailure)
      }

    case AnnouncePost(key, title) =>
      val postId = getPostId(key, title)
      forwardCommand(postId, PublishPost(postId))

    case arp: AddRelatedPost =>
      forwardCommand(arp.id, arp)

    case PinPost(key, title) =>
      val postId = getPostId(key, title)
      forwardCommand(postId, TogglePinned(postId))

    case UnpinPost(key, title) =>
      val postId = getPostId(key, title)
      forwardCommand(postId, TogglePinned(postId))

    case AddRelated(postId, relatedPostId) =>
      forwardCommand(postId, AddRelatedPost(postId, relatedPostId))

    case AddComment(postId) =>
      forwardCommand(postId, IncrementCommentCount(postId))
  }

  private def calculateTieToRead(body: Seq[BodyComponentData]): String = {
    val minutes = body.filter(bc => bc.parameters.values.toList.contains("text"))
      .flatMap(bc => bc.parameters.filter(p => p._1.eq("text")))
      .map(p => p._2.split("\\W+").length).sum
    if (minutes <= 1) {
      "1 Minuta"
    } else if (minutes > 10) {
      "10+ Minut"
    } else {
      minutes + " Minuty"
    }
  }

  private def getPostId(key: String, title: String): String = {
    Math.abs((key + "-" + title).hashCode).toString
  }
}


text:
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {
	margin:0;
	padding:0;
	border:0;
	outline:0;
	font-size:100%;
	vertical-align:baseline;
	background:transparent
}
body {
	line-height:1
}
ol,ul {
	list-style:none
}
blockquote,q {
	quotes:none
}
blockquote:before,blockquote:after,q:before,q:after {
	content:'';
	content:none
}
:focus {
	outline:0
}
ins {
	text-decoration:none
}
del {
	text-decoration:line-through
}
table {
	border-collapse:collapse;
	border-spacing:0
}
.ui-helper-hidden {
	display:none
}
.ui-helper-hidden-accessible {
	position:absolute;
	left:-99999999px
}
.ui-helper-reset {
	margin:0;
	padding:0;
	border:0;
	outline:0;
	line-height:1.3;
	text-decoration:none;
	font-size:100%;
	list-style:none
}
.ui-helper-clearfix:after {
	content:".";
	display:block;
	height:0;
	clear:both;
	visibility:hidden
}
.ui-helper-clearfix {
	display:inline-block
}
* html .ui-helper-clearfix {
	height:1%
}
.ui-helper-clearfix {
	display:block
}
.ui-helper-zfix {
	width:100%;
	height:100%;
	top:0;
	left:0;
	position:absolute;
	opacity:0;
	-webkit-filter:Alpha(Opacity=0);
	filter:Alpha(Opacity=0)
}
.ui-state-disabled {
	cursor:default!important
}
.ui-icon {
	display:block;
	text-indent:-99999px;
	overflow:hidden;
	background-repeat:no-repeat
}
.ui-widget-overlay {
	position:absolute;
	top:0;
	left:0;
	width:100%;
	height:100%
}
.ui-widget {
	font-family:Lucida Grande,Lucida Sans,Arial,sans-serif;
	font-size:1.1em
}
.ui-widget .ui-widget {
	font-size:1em
}
.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button {
	font-family:Lucida Grande,Lucida Sans,Arial,sans-serif;
	font-size:1em
}
.ui-widget-content {
	border:1px solid #a6c9e2;
	background:#fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x;
	color:#222
}
.ui-widget-content a {
	color:#222
}
.ui-widget-header {
	border:1px solid #4297d7;
	background:#5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x;
	color:#fff;
	font-weight:bold
}
.ui-widget-header a {
	color:#fff
}
.ui-state-default,.ui-widget-content .ui-state-default {
	border:1px solid #c5dbec;
	background:#dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x;
	font-weight:bold;
	color:#2e6e9e;
	outline:0
}
.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited {
	color:#2e6e9e;
	text-decoration:none;
	outline:0
}
.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus {
	border:1px solid #79b7e7;
	background:#d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x;
	font-weight:bold;
	color:#1d5987;
	outline:0
}
.ui-state-hover a,.ui-state-hover a:hover {
	color:#1d5987;
	text-decoration:none;
	outline:0
}
.ui-state-active,.ui-widget-content .ui-state-active {
	border:1px solid #79b7e7;
	background:#f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x;
	font-weight:bold;
	color:#e17009;
	outline:0
}
.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited {
	color:#e17009;
	outline:0;
	text-decoration:none
}
.ui-state-highlight,.ui-widget-content .ui-state-highlight {
	border:1px solid #fad42e;
	background:#fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x;
	color:#363636
}
.ui-state-highlight a,.ui-widget-content .ui-state-highlight a {
	color:#363636
}
.ui-state-error,.ui-widget-content .ui-state-error {
	border:1px solid #cd0a0a;
	background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
	color:#cd0a0a
}
.ui-state-error a,.ui-widget-content .ui-state-error a {
	color:#cd0a0a
}
.ui-state-error-text,.ui-widget-content .ui-state-error-text {
	color:#cd0a0a
}
.ui-state-disabled,.ui-widget-content .ui-state-disabled {
	opacity:.35;
	-webkit-filter:Alpha(Opacity=35);
	filter:Alpha(Opacity=35);
	background-image:none
}
.ui-priority-primary,.ui-widget-content .ui-priority-primary {
	font-weight:bold
}
.ui-priority-secondary,.ui-widget-content .ui-priority-secondary {
	opacity:.7;
	-webkit-filter:Alpha(Opacity=70);
	filter:Alpha(Opacity=70);
	font-weight:normal
}
.ui-icon {
	width:16px;
	height:16px;
	background-image:url(images/ui-icons_469bdd_256x240.png)
}
.ui-widget-content .ui-icon {
	background-image:url(images/ui-icons_469bdd_256x240.png)
}
.ui-widget-header .ui-icon {
	background-image:url(images/ui-icons_d8e7f3_256x240.png)
}
.ui-state-default .ui-icon {
	background-image:url(images/ui-icons_6da8d5_256x240.png)
}
.ui-state-hover .ui-icon,.ui-state-focus .ui-icon {
	background-image:url(images/ui-icons_217bc0_256x240.png)
}
.ui-state-active .ui-icon {
	background-image:url(images/ui-icons_f9bd01_256x240.png)
}
.ui-state-highlight .ui-icon {
	background-image:url(images/ui-icons_2e83ff_256x240.png)
}
.ui-state-error .ui-icon,.ui-state-error-text .ui-icon {
	background-image:url(images/ui-icons_cd0a0a_256x240.png)
}
.ui-icon-carat-1-n {
	background-position:0 0
}
.ui-icon-carat-1-ne {
	background-position:-16px 0
}
.ui-icon-carat-1-e {
	background-position:-32px 0
}
.ui-icon-carat-1-se {
	background-position:-48px 0
}
.ui-icon-carat-1-s {
	background-position:-64px 0
}
.ui-icon-carat-1-sw {
	background-position:-80px 0
}
.ui-icon-carat-1-w {
	background-position:-96px 0
}
.ui-icon-carat-1-nw {
	background-position:-112px 0
}
.ui-icon-carat-2-n-s {
	background-position:-128px 0
}
.ui-icon-carat-2-e-w {
	background-position:-144px 0
}
.ui-icon-triangle-1-n {
	background-position:0 -16px
}
.ui-icon-triangle-1-ne {
	background-position:-16px -16px
}
.ui-icon-triangle-1-e {
	background-position:-32px -16px
}
.ui-icon-triangle-1-se {
	background-position:-48px -16px
}
.ui-icon-triangle-1-s {
	background-position:-64px -16px
}
.ui-icon-triangle-1-sw {
	background-position:-80px -16px
}
.ui-icon-triangle-1-w {
	background-position:-96px -16px
}
.ui-icon-triangle-1-nw {
	background-position:-112px -16px
}
.ui-icon-triangle-2-n-s {
	background-position:-128px -16px
}
.ui-icon-triangle-2-e-w {
	background-position:-144px -16px
}
.ui-icon-arrow-1-n {
	background-position:0 -32px
}
.ui-icon-arrow-1-ne {
	background-position:-16px -32px
}
.ui-icon-arrow-1-e {
	background-position:-32px -32px
}
.ui-icon-arrow-1-se {
	background-position:-48px -32px
}
.ui-icon-arrow-1-s {
	background-position:-64px -32px
}
.ui-icon-arrow-1-sw {
	background-position:-80px -32px
}
.ui-icon-arrow-1-w {
	background-position:-96px -32px
}
.ui-icon-arrow-1-nw {
	background-position:-112px -32px
}
.ui-icon-arrow-2-n-s {
	background-position:-128px -32px
}
.ui-icon-arrow-2-ne-sw {
	background-position:-144px -32px
}
.ui-icon-arrow-2-e-w {
	background-position:-160px -32px
}
.ui-icon-arrow-2-se-nw {
	background-position:-176px -32px
}
.ui-icon-arrowstop-1-n {
	background-position:-192px -32px
}
.ui-icon-arrowstop-1-e {
	background-position:-208px -32px
}
.ui-icon-arrowstop-1-s {
	background-position:-224px -32px
}
.ui-icon-arrowstop-1-w {
	background-position:-240px -32px
}
.ui-icon-arrowthick-1-n {
	background-position:0 -48px
}
.ui-icon-arrowthick-1-ne {
	background-position:-16px -48px
}
.ui-icon-arrowthick-1-e {
	background-position:-32px -48px
}
.ui-icon-arrowthick-1-se {
	background-position:-48px -48px
}
.ui-icon-arrowthick-1-s {
	background-position:-64px -48px
}
.ui-icon-arrowthick-1-sw {
	background-position:-80px -48px
}
.ui-icon-arrowthick-1-w {
	background-position:-96px -48px
}
.ui-icon-arrowthick-1-nw {
	background-position:-112px -48px
}
.ui-icon-arrowthick-2-n-s {
	background-position:-128px -48px
}
.ui-icon-arrowthick-2-ne-sw {
	background-position:-144px -48px
}
.ui-icon-arrowthick-2-e-w {
	background-position:-160px -48px
}
.ui-icon-arrowthick-2-se-nw {
	background-position:-176px -48px
}
.ui-icon-arrowthickstop-1-n {
	background-position:-192px -48px
}
.ui-icon-arrowthickstop-1-e {
	background-position:-208px -48px
}
.ui-icon-arrowthickstop-1-s {
	background-position:-224px -48px
}
.ui-icon-arrowthickstop-1-w {
	background-position:-240px -48px
}
.ui-icon-arrowreturnthick-1-w {
	background-position:0 -64px
}
.ui-icon-arrowreturnthick-1-n {
	background-position:-16px -64px
}
.ui-icon-arrowreturnthick-1-e {
	background-position:-32px -64px
}
.ui-icon-arrowreturnthick-1-s {
	background-position:-48px -64px
}
.ui-icon-arrowreturn-1-w {
	background-position:-64px -64px
}
.ui-icon-arrowreturn-1-n {
	background-position:-80px -64px
}
.ui-icon-arrowreturn-1-e {
	background-position:-96px -64px
}
.ui-icon-arrowreturn-1-s {
	background-position:-112px -64px
}
.ui-icon-arrowrefresh-1-w {
	background-position:-128px -64px
}
.ui-icon-arrowrefresh-1-n {
	background-position:-144px -64px
}
.ui-icon-arrowrefresh-1-e {
	background-position:-160px -64px
}
.ui-icon-arrowrefresh-1-s {
	background-position:-176px -64px
}
.ui-icon-arrow-4 {
	background-position:0 -80px
}
.ui-icon-arrow-4-diag {
	background-position:-16px -80px
}
.ui-icon-extlink {
	background-position:-32px -80px
}
.ui-icon-newwin {
	background-position:-48px -80px
}
.ui-icon-refresh {
	background-position:-64px -80px
}
.ui-icon-shuffle {
	background-position:-80px -80px
}
.ui-icon-transfer-e-w {
	background-position:-96px -80px
}
.ui-icon-transferthick-e-w {
	background-position:-112px -80px
}
.ui-icon-folder-collapsed {
	background-position:0 -96px
}
.ui-icon-folder-open {
	background-position:-16px -96px
}
.ui-icon-document {
	background-position:-32px -96px
}
.ui-icon-document-b {
	background-position:-48px -96px
}
.ui-icon-note {
	background-position:-64px -96px
}
.ui-icon-mail-closed {
	background-position:-80px -96px
}
.ui-icon-mail-open {
	background-position:-96px -96px
}
.ui-icon-suitcase {
	background-position:-112px -96px
}
.ui-icon-comment {
	background-position:-128px -96px
}
.ui-icon-person {
	background-position:-144px -96px
}
.ui-icon-print {
	background-position:-160px -96px
}
.ui-icon-trash {
	background-position:-176px -96px
}
.ui-icon-locked {
	background-position:-192px -96px
}
.ui-icon-unlocked {
	background-position:-208px -96px
}
.ui-icon-bookmark {
	background-position:-224px -96px
}
.ui-icon-tag {
	background-position:-240px -96px
}
.ui-icon-home {
	background-position:0 -112px
}
.ui-icon-flag {
	background-position:-16px -112px
}
.ui-icon-calendar {
	background-position:-32px -112px
}
.ui-icon-cart {
	background-position:-48px -112px
}
.ui-icon-pencil {
	background-position:-64px -112px
}
.ui-icon-clock {
	background-position:-80px -112px
}
.ui-icon-disk {
	background-position:-96px -112px
}
.ui-icon-calculator {
	background-position:-112px -112px
}
.ui-icon-zoomin {
	background-position:-128px -112px
}
.ui-icon-zoomout {
	background-position:-144px -112px
}
.ui-icon-search {
	background-position:-160px -112px
}
.ui-icon-wrench {
	background-position:-176px -112px
}
.ui-icon-gear {
	background-position:-192px -112px
}
.ui-icon-heart {
	background-position:-208px -112px
}
.ui-icon-star {
	background-position:-224px -112px
}
.ui-icon-link {
	background-position:-240px -112px
}
.ui-icon-cancel {
	background-position:0 -128px
}
.ui-icon-plus {
	background-position:-16px -128px
}
.ui-icon-plusthick {
	background-position:-32px -128px
}
.ui-icon-minus {
	background-position:-48px -128px
}
.ui-icon-minusthick {
	background-position:-64px -128px
}
.ui-icon-close {
	background-position:-80px -128px
}
.ui-icon-closethick {
	background-position:-96px -128px
}
.ui-icon-key {
	background-position:-112px -128px
}
.ui-icon-lightbulb {
	background-position:-128px -128px
}
.ui-icon-scissors {
	background-position:-144px -128px
}
.ui-icon-clipboard {
	background-position:-160px -128px
}
.ui-icon-copy {
	background-position:-176px -128px
}
.ui-icon-contact {
	background-position:-192px -128px
}
.ui-icon-image {
	background-position:-208px -128px
}
.ui-icon-video {
	background-position:-224px -128px
}
.ui-icon-script {
	background-position:-240px -128px
}
.ui-icon-alert {
	background-position:0 -144px
}
.ui-icon-info {
	background-position:-16px -144px
}
.ui-icon-notice {
	background-position:-32px -144px
}
.ui-icon-help {
	background-position:-48px -144px
}
.ui-icon-check {
	background-position:-64px -144px
}
.ui-icon-bullet {
	background-position:-80px -144px
}
.ui-icon-radio-off {
	background-position:-96px -144px
}
.ui-icon-radio-on {
	background-position:-112px -144px
}
.ui-icon-pin-w {
	background-position:-128px -144px
}
.ui-icon-pin-s {
	background-position:-144px -144px
}
.ui-icon-play {
	background-position:0 -160px
}
.ui-icon-pause {
	background-position:-16px -160px
}
.ui-icon-seek-next {
	background-position:-32px -160px
}
.ui-icon-seek-prev {
	background-position:-48px -160px
}
.ui-icon-seek-end {
	background-position:-64px -160px
}
.ui-icon-seek-first {
	background-position:-80px -160px
}
.ui-icon-stop {
	background-position:-96px -160px
}
.ui-icon-eject {
	background-position:-112px -160px
}
.ui-icon-volume-off {
	background-position:-128px -160px
}
.ui-icon-volume-on {
	background-position:-144px -160px
}
.ui-icon-power {
	background-position:0 -176px
}
.ui-icon-signal-diag {
	background-position:-16px -176px
}
.ui-icon-signal {
	background-position:-32px -176px
}
.ui-icon-battery-0 {
	background-position:-48px -176px
}
.ui-icon-battery-1 {
	background-position:-64px -176px
}
.ui-icon-battery-2 {
	background-position:-80px -176px
}
.ui-icon-battery-3 {
	background-position:-96px -176px
}
.ui-icon-circle-plus {
	background-position:0 -192px
}
.ui-icon-circle-minus {
	background-position:-16px -192px
}
.ui-icon-circle-close {
	background-position:-32px -192px
}
.ui-icon-circle-triangle-e {
	background-position:-48px -192px
}
.ui-icon-circle-triangle-s {
	background-position:-64px -192px
}
.ui-icon-circle-triangle-w {
	background-position:-80px -192px
}
.ui-icon-circle-triangle-n {
	background-position:-96px -192px
}
.ui-icon-circle-arrow-e {
	background-position:-112px -192px
}
.ui-icon-circle-arrow-s {
	background-position:-128px -192px
}
.ui-icon-circle-arrow-w {
	background-position:-144px -192px
}
.ui-icon-circle-arrow-n {
	background-position:-160px -192px
}
.ui-icon-circle-zoomin {
	background-position:-176px -192px
}
.ui-icon-circle-zoomout {
	background-position:-192px -192px
}
.ui-icon-circle-check {
	background-position:-208px -192px
}
.ui-icon-circlesmall-plus {
	background-position:0 -208px
}
.ui-icon-circlesmall-minus {
	background-position:-16px -208px
}
.ui-icon-circlesmall-close {
	background-position:-32px -208px
}
.ui-icon-squaresmall-plus {
	background-position:-48px -208px
}
.ui-icon-squaresmall-minus {
	background-position:-64px -208px
}
.ui-icon-squaresmall-close {
	background-position:-80px -208px
}
.ui-icon-grip-dotted-vertical {
	background-position:0 -224px
}
.ui-icon-grip-dotted-horizontal {
	background-position:-16px -224px
}
.ui-icon-grip-solid-vertical {
	background-position:-32px -224px
}
.ui-icon-grip-solid-horizontal {
	background-position:-48px -224px
}
.ui-icon-gripsmall-diagonal-se {
	background-position:-64px -224px
}
.ui-icon-grip-diagonal-se {
	background-position:-80px -224px
}
.ui-corner-tl {
	-moz-border-radius-topleft:5px;
	-webkit-border-top-left-radius:5px
}
.ui-corner-tr {
	-moz-border-radius-topright:5px;
	-webkit-border-top-right-radius:5px
}
.ui-corner-bl {
	-moz-border-radius-bottomleft:5px;
	-webkit-border-bottom-left-radius:5px
}
.ui-corner-br {
	-moz-border-radius-bottomright:5px;
	-webkit-border-bottom-right-radius:5px
}
.ui-corner-top {
	-moz-border-radius-topleft:5px;
	-webkit-border-top-left-radius:5px;
	-moz-border-radius-topright:5px;
	-webkit-border-top-right-radius:5px
}
.ui-corner-bottom {
	-moz-border-radius-bottomleft:5px;
	-webkit-border-bottom-left-radius:5px;
	-moz-border-radius-bottomright:5px;
	-webkit-border-bottom-right-radius:5px
}
.ui-corner-right {
	-moz-border-radius-topright:5px;
	-webkit-border-top-right-radius:5px;
	-moz-border-radius-bottomright:5px;
	-webkit-border-bottom-right-radius:5px
}
.ui-corner-left {
	-moz-border-radius-topleft:5px;
	-webkit-border-top-left-radius:5px;
	-moz-border-radius-bottomleft:5px;
	-webkit-border-bottom-left-radius:5px
}
.ui-corner-all {
	-moz-border-radius:5px;
	-webkit-border-radius:5px
}
.ui-widget-overlay {
	background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
	opacity:.30;
	-webkit-filter:Alpha(Opacity=30);
	filter:Alpha(Opacity=30)
}
.ui-widget-shadow {
	margin:-8px 0 0 -8px;
	padding:8px;
	background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
	opacity:.30;
	-webkit-filter:Alpha(Opacity=30);
	filter:Alpha(Opacity=30);
	-moz-border-radius:8px;
	-webkit-border-radius:8px
}
.ui-accordion .ui-accordion-header {
	cursor:pointer;
	position:relative;
	margin-top:1px;
	zoom:1
}
.ui-accordion .ui-accordion-li-fix {
	display:inline
}
.ui-accordion .ui-accordion-header-active {
	border-bottom:0!important
}
.ui-accordion .ui-accordion-header a {
	display:block;
	font-size:1em;
	padding:.5em .5em .5em 2.2em
}
.ui-accordion .ui-accordion-header .ui-icon {
	position:absolute;
	left:.5em;
	top:50%;
	margin-top:-8px
}
.ui-accordion .ui-accordion-content {
	padding:1em 2.2em;
	border-top:0;
	margin-top:-2px;
	position:relative;
	top:1px;
	margin-bottom:2px;
	overflow:auto;
	display:none
}
.ui-accordion .ui-accordion-content-active {
	display:block
}
.ui-datepicker {
	width:17em;
	padding:.2em .2em 0
}
.ui-datepicker .ui-datepicker-header {
	position:relative;
	padding:.2em 0
}
.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next {
	position:absolute;
	top:2px;
	width:1.8em;
	height:1.8em
}
.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover {
	top:1px
}
.ui-datepicker .ui-datepicker-prev {
	left:2px
}
.ui-datepicker .ui-datepicker-next {
	right:2px
}
.ui-datepicker .ui-datepicker-prev-hover {
	left:1px
}
.ui-datepicker .ui-datepicker-next-hover {
	right:1px
}
.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span {
	display:block;
	position:absolute;
	left:50%;
	margin-left:-8px;
	top:50%;
	margin-top:-8px
}
.ui-datepicker .ui-datepicker-title {
	margin:0 2.3em;
	line-height:1.8em;
	text-align:center
}
.ui-datepicker .ui-datepicker-title select {
	float:left;
	font-size:1em;
	margin:1px 0
}
.ui-datepicker select.ui-datepicker-month-year {
	width:100%
}
.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year {
	width:49%
}
.ui-datepicker .ui-datepicker-title select.ui-datepicker-year {
	float:right
}
.ui-datepicker table {
	width:100%;
	font-size:.9em;
	border-collapse:collapse;
	margin:0 0 .4em
}
.ui-datepicker th {
	padding:.7em .3em;
	text-align:center;
	font-weight:bold;
	border:0
}
.ui-datepicker td {
	border:0;
	padding:1px
}
.ui-datepicker td span,.ui-datepicker td a {
	display:block;
	padding:.2em;
	text-align:right;
	text-decoration:none
}
.ui-datepicker .ui-datepicker-buttonpane {
	background-image:none;
	margin:.7em 0 0 0;
	padding:0 .2em;
	border-left:0;
	border-right:0;
	border-bottom:0
}
.ui-datepicker .ui-datepicker-buttonpane button {
	float:right;
	margin:.5em .2em .4em;
	cursor:pointer;
	padding:.2em .6em .3em .6em;
	width:auto;
	overflow:visible
}
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
	float:left
}
.ui-datepicker.ui-datepicker-multi {
	width:auto
}
.ui-datepicker-multi .ui-datepicker-group {
	float:left
}
.ui-datepicker-multi .ui-datepicker-group table {
	width:95%;
	margin:0 auto .4em
}
.ui-datepicker-multi-2 .ui-datepicker-group {
	width:50%
}
.ui-datepicker-multi-3 .ui-datepicker-group {
	width:33.3%
}
.ui-datepicker-multi-4 .ui-datepicker-group {
	width:25%
}
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header {
	border-left-width:0
}
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
	border-left-width:0
}
.ui-datepicker-multi .ui-datepicker-buttonpane {
	clear:left
}
.ui-datepicker-row-break {
	clear:both;
	width:100%
}
.ui-datepicker-rtl {
	direction:rtl
}
.ui-datepicker-rtl .ui-datepicker-prev {
	right:2px;
	left:auto
}
.ui-datepicker-rtl .ui-datepicker-next {
	left:2px;
	right:auto
}
.ui-datepicker-rtl .ui-datepicker-prev:hover {
	right:1px;
	left:auto
}
.ui-datepicker-rtl .ui-datepicker-next:hover {
	left:1px;
	right:auto
}
.ui-datepicker-rtl .ui-datepicker-buttonpane {
	clear:right
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
	float:left
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current {
	float:right
}
.ui-datepicker-rtl .ui-datepicker-group {
	float:right
}
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header {
	border-right-width:0;
	border-left-width:1px
}
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
	border-right-width:0;
	border-left-width:1px
}
.ui-datepicker-cover {
	display:none;
	display:block;
	position:absolute;
	z-index:-1;
	-webkit-filter:mask();
	filter:mask();
	top:-4px;
	left:-4px;
	width:200px;
	height:200px
}
.ui-dialog {
	position:relative;
	padding:.2em;
	width:300px
}
.ui-dialog .ui-dialog-titlebar {
	padding:.5em .3em .3em 1em;
	position:relative
}
.ui-dialog .ui-dialog-title {
	float:left;
	margin:.1em 0 .2em
}
.ui-dialog .ui-dialog-titlebar-close {
	position:absolute;
	right:.3em;
	top:50%;
	width:19px;
	margin:-10px 0 0 0;
	padding:1px;
	height:18px
}
.ui-dialog .ui-dialog-titlebar-close span {
	display:block;
	margin:1px
}
.ui-dialog .ui-dialog-titlebar-close:hover,.ui-dialog .ui-dialog-titlebar-close:focus {
	padding:0
}
.ui-dialog .ui-dialog-content {
	border:0;
	padding:.5em 1em;
	background:0;
	overflow:auto;
	zoom:1
}
.ui-dialog .ui-dialog-buttonpane {
	text-align:left;
	border-width:1px 0 0 0;
	background-image:none;
	margin:.5em 0 0 0;
	padding:.3em 1em .5em .4em
}
.ui-dialog .ui-dialog-buttonpane button {
	float:right;
	margin:.5em .4em .5em 0;
	cursor:pointer;
	padding:.2em .6em .3em .6em;
	line-height:1.4em;
	width:auto;
	overflow:visible
}
.ui-dialog .ui-resizable-se {
	width:14px;
	height:14px;
	right:3px;
	bottom:3px
}
.ui-draggable .ui-dialog-titlebar {
	cursor:move
}
.ui-progressbar {
	height:2em;
	text-align:left
}
.ui-progressbar .ui-progressbar-value {
	margin:-1px;
	height:100%
}
.ui-resizable {
	position:relative
}
.ui-resizable-handle {
	position:absolute;
	font-size:.1px;
	z-index:99999;
	display:block
}
.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle {
	display:none
}
.ui-resizable-n {
	cursor:n-resize;
	height:7px;
	width:100%;
	top:-5px;
	left:0
}
.ui-resizable-s {
	cursor:s-resize;
	height:7px;
	width:100%;
	bottom:-5px;
	left:0
}
.ui-resizable-e {
	cursor:e-resize;
	width:7px;
	right:-5px;
	top:0;
	height:100%
}
.ui-resizable-w {
	cursor:w-resize;
	width:7px;
	left:-5px;
	top:0;
	height:100%
}
.ui-resizable-se {
	cursor:se-resize;
	width:12px;
	height:12px;
	right:1px;
	bottom:1px
}
.ui-resizable-sw {
	cursor:sw-resize;
	width:9px;
	height:9px;
	left:-5px;
	bottom:-5px
}
.ui-resizable-nw {
	cursor:nw-resize;
	width:9px;
	height:9px;
	left:-5px;
	top:-5px
}
.ui-resizable-ne {
	cursor:ne-resize;
	width:9px;
	height:9px;
	right:-5px;
	top:-5px
}
.ui-slider {
	position:relative;
	text-align:left
}
.ui-slider .ui-slider-handle {
	position:absolute;
	z-index:2;
	width:1.2em;
	height:1.2em;
	cursor:default
}
.ui-slider .ui-slider-range {
	position:absolute;
	z-index:1;
	font-size:.7em;
	display:block;
	border:0
}
.ui-slider-horizontal {
	height:.8em
}
.ui-slider-horizontal .ui-slider-handle {
	top:-.3em;
	margin-left:-.6em
}
.ui-slider-horizontal .ui-slider-range {
	top:0;
	height:100%
}
.ui-slider-horizontal .ui-slider-range-min {
	left:0
}
.ui-slider-horizontal .ui-slider-range-max {
	right:0
}
.ui-slider-vertical {
	width:.8em;
	height:100px
}
.ui-slider-vertical .ui-slider-handle {
	left:-.3em;
	margin-left:0;
	margin-bottom:-.6em
}
.ui-slider-vertical .ui-slider-range {
	left:0;
	width:100%
}
.ui-slider-vertical .ui-slider-range-min {
	bottom:0
}
.ui-slider-vertical .ui-slider-range-max {
	top:0
}
.ui-tabs {
	padding:.2em;
	zoom:1
}
.ui-tabs .ui-tabs-nav {
	list-style:none;
	position:relative;
	padding:.2em .2em 0
}
.ui-tabs .ui-tabs-nav li {
	position:relative;
	float:left;
	border-bottom-width:0!important;
	margin:0 .2em -1px 0;
	padding:0
}
.ui-tabs .ui-tabs-nav li a {
	float:left;
	text-decoration:none;
	padding:.5em 1em
}
.ui-tabs .ui-tabs-nav li.ui-tabs-selected {
	padding-bottom:1px;
	border-bottom-width:0
}
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-state-processing a {
	cursor:text
}
.ui-tabs .ui-tabs-nav li a,.ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a {
	cursor:pointer
}
.ui-tabs .ui-tabs-panel {
	padding:1em 1.4em;
	display:block;
	border-width:0;
	background:0
}
.ui-tabs .ui-tabs-hide {
	display:none!important
}
body {
	font-family:Segoe UI,Ubuntu,Arial;
	background-color:#3498db
}
a {
	text-decoration:none;
	color:#3498db
}
a:hover {
	border-bottom:1.5px dotted #3498db
}
footer,section {
	display:block;
	margin:0 12px;
	background-color:#ecf0f1;
	border:thin solid #258cd1;
	border-top:0;
	border-bottom-width:2px;
	padding:1em;
	margin-bottom:1em
}
header h1 {
	font-size:3em;
	font-weight:400;
	margin:0;
	padding:0;
	text-align:center;
	color:#34495e;
	margin:.5em 0 .75em;
	color:white
}
section#content {
	overflow:hidden
}
footer {
	clear:both
}
h1,h2,h3,h4,h5,h6 {
	padding-bottom:.5em
}
ul {
	padding-left:1em
}
li {
	padding-bottom:.5em
}
p {
	padding-bottom:.5em
}
#content {
	padding:0;
	border:0;
	background:0
}
#sounds {
	display:none
}
#drumkit {
	margin:0;
	background:#34495e;
	border:0;
	-webkit-user-select:none
}
#drumkit>ul {
	padding:0;
	display:inline-block;
	vertical-align:top
}
#lights {
	width:69.5%;
	min-width:714px
}
#lights>li {
	padding-bottom:0
}
#lights ul {
	padding:0;
	margin-right:0
}
#tracker {
	margin-bottom:10px
}
#controls {
	min-width:150px;
	width:30%;
	color:#FFF;
	float:right
}
#temposlider {
	margin-top:.5em
}
ul.soundrow li {
	display:inline-block;
	height:15px
}
ul.soundrow li.header {
	width:37%;
	min-width:125px;
	color:#7795b4;
	border-bottom:thin solid #395168;
	margin-right:2%;
	padding:.7em .5%;
	cursor:default;
	-webkit-transition:all .1s ease;
	transition:all .1s ease;
	text-align:right
}
ul.soundrow li.header:hover {
	background:#222f3d
}
ul.soundrow li.pox {
	width:2%;
	min-width:10px;
	background-color:#273747;
	padding:.5%;
	margin-right:.5%;
	text-indent:-99999px;
	-webkit-transition:all .1s ease;
	transition:all .1s ease;
	cursor:pointer
}
#tracker li.pox {
	cursor:default
}
ul.soundrow:not(#tracker) li.pox:hover {
	background:#1abc9c
}
ul.soundrow li.active {
	background-color:#2ecc71
}
ul.soundrow li.col_3,ul.soundrow li.col_7,ul.soundrow li.col_11 {
	margin-right:12px
}
#tracker .pox {
	background:#3d566e
}
#tracker .pox:hover {
	background:#3d566e
}
#tracker li.active {
	background-color:#2ecc71
}
#tracker li {
	background-color:none;
	color:#587ca0
}
#tracker li:hover {
	background:0
}
button {
	width:100%;
	background:#2ecc71;
	color:white;
	border:0;
	padding:10px;
	margin-bottom:5px;
	cursor:pointer
}
button:hover:not([disabled]) {
	background:#27ae60
}
#loadslot,#buttable,#loadtable {
	width:100%;
	margin-left:0
}
#slottable>tr,#slottable,#buttable>tr,#buttable,#loadtable>tr,#loadtable {
	width:100%
}
.slottable-title {
	margin-top:20px;
	display:block
}
#loadtable {
	margin-top:15px
}
#slottable td:not(#p1) {
	width:35%;
	margin-left:2px
}
#slottable td#p1 {
	width:30%
}
#demoslot {
	width:100%
}
#share {
	margin:5px 0 15px;
	background:#3498db;
	font-size:16px;
	padding: 15px 10px
}
button[disabled] {
    background: #2AB364;
}


text:
@class ApptentiveInteractionUsageData;

@interface ApptentiveInteractionInvocation : NSObject <NSCoding, NSCopying>

@property (copy, nonatomic) NSString *interactionID;
@property (assign, nonatomic) NSInteger priority;
@property (strong, nonatomic) NSDictionary *criteria;

+ (ApptentiveInteractionInvocation *)invocationWithJSONDictionary:(NSDictionary *)jsonDictionary;
+ (NSArray *)invocationsWithJSONArray:(NSArray *)jsonArray;

- (BOOL)isValid;

- (BOOL)criteriaAreMet;
- (BOOL)criteriaAreMetForUsageData:(ApptentiveInteractionUsageData *)usageData;

- (NSPredicate *)criteriaPredicate;

@end


text:
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import autobind from 'autobind-decorator';
import fs from 'fs';
import { clipboard, ipcRenderer, remote } from 'electron';
import { parse as urlParse } from 'url';
import HTTPSnippet from 'httpsnippet';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Wrapper from '../components/wrapper';
import WorkspaceEnvironmentsEditModal from '../components/modals/workspace-environments-edit-modal';
import Toast from '../components/toast';
import CookiesModal from '../components/modals/cookies-modal';
import RequestSwitcherModal from '../components/modals/request-switcher-modal';
import SettingsModal, { TAB_INDEX_SHORTCUTS } from '../components/modals/settings-modal';
import {
  COLLAPSE_SIDEBAR_REMS,
  DEFAULT_PANE_HEIGHT,
  DEFAULT_PANE_WIDTH,
  DEFAULT_SIDEBAR_WIDTH,
  MAX_PANE_HEIGHT,
  MAX_PANE_WIDTH,
  MAX_SIDEBAR_REMS,
  MIN_PANE_HEIGHT,
  MIN_PANE_WIDTH,
  MIN_SIDEBAR_REMS,
  PREVIEW_MODE_SOURCE,
} from '../../common/constants';
import * as globalActions from '../redux/modules/global';
import * as db from '../../common/database';
import * as models from '../../models';
import {
  selectActiveCookieJar,
  selectActiveOAuth2Token,
  selectActiveRequest,
  selectActiveRequestMeta,
  selectActiveRequestResponses,
  selectActiveResponse,
  selectActiveWorkspace,
  selectActiveWorkspaceClientCertificates,
  selectActiveWorkspaceMeta,
  selectEntitiesLists,
  selectSidebarChildren,
  selectSyncItems,
  selectUnseenWorkspaces,
  selectWorkspaceRequestsAndRequestGroups,
} from '../redux/selectors';
import RequestCreateModal from '../components/modals/request-create-modal';
import GenerateCodeModal from '../components/modals/generate-code-modal';
import WorkspaceSettingsModal from '../components/modals/workspace-settings-modal';
import RequestSettingsModal from '../components/modals/request-settings-modal';
import RequestRenderErrorModal from '../components/modals/request-render-error-modal';
import * as network from '../../network/network';
import { debounce, getContentDispositionHeader, getDataDirectory } from '../../common/misc';
import * as mime from 'mime-types';
import * as path from 'path';
import * as render from '../../common/render';
import { getKeys } from '../../templating/utils';
import { showAlert, showModal, showPrompt } from '../components/modals/index';
import { exportHarRequest } from '../../common/har';
import { hotKeyRefs } from '../../common/hotkeys';
import { executeHotKey } from '../../common/hotkeys-listener';
import KeydownBinder from '../components/keydown-binder';
import ErrorBoundary from '../components/error-boundary';
import * as plugins from '../../plugins';
import * as templating from '../../templating/index';
import AskModal from '../components/modals/ask-modal';
import { updateMimeType } from '../../models/request';
import MoveRequestGroupModal from '../components/modals/move-request-group-modal';
import * as themes from '../../plugins/misc';
import ExportRequestsModal from '../components/modals/export-requests-modal';
import FileSystemDriver from '../../sync/store/drivers/file-system-driver';
import VCS from '../../sync/vcs';
import SyncMergeModal from '../components/modals/sync-merge-modal';

@autobind
class App extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      showDragOverlay: false,
      draggingSidebar: false,
      draggingPaneHorizontal: false,
      draggingPaneVertical: false,
      sidebarWidth: props.sidebarWidth || DEFAULT_SIDEBAR_WIDTH,
      paneWidth: props.paneWidth || DEFAULT_PANE_WIDTH,
      paneHeight: props.paneHeight || DEFAULT_PANE_HEIGHT,
      isVariableUncovered: props.isVariableUncovered || false,
      vcs: null,
      forceRefreshCounter: 0,
      forceRefreshHeaderCounter: 0,
    };

    this._isMigratingChildren = false;

    this._getRenderContextPromiseCache = {};

    this._savePaneWidth = debounce(paneWidth => this._updateActiveWorkspaceMeta({ paneWidth }));
    this._savePaneHeight = debounce(paneHeight => this._updateActiveWorkspaceMeta({ paneHeight }));
    this._saveSidebarWidth = debounce(sidebarWidth =>
      this._updateActiveWorkspaceMeta({ sidebarWidth }),
    );

    this._globalKeyMap = null;
  }

  _setGlobalKeyMap() {
    this._globalKeyMap = [
      [
        hotKeyRefs.PREFERENCES_SHOW_GENERAL,
        () => {
          showModal(SettingsModal);
        },
      ],
      [
        hotKeyRefs.PREFERENCES_SHOW_KEYBOARD_SHORTCUTS,
        () => {
          showModal(SettingsModal, TAB_INDEX_SHORTCUTS);
        },
      ],
      [
        hotKeyRefs.SHOW_RECENT_REQUESTS,
        () => {
          showModal(RequestSwitcherModal, {
            disableInput: true,
            maxRequests: 10,
            maxWorkspaces: 0,
            selectOnKeyup: true,
            title: 'Recent Requests',
            hideNeverActiveRequests: true,

            // Add an open delay so the dialog won't show for quick presses
            openDelay: 150,
          });
        },
      ],
      [
        hotKeyRefs.WORKSPACE_SHOW_SETTINGS,
        () => {
          const { activeWorkspace } = this.props;
          showModal(WorkspaceSettingsModal, activeWorkspace);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_SETTINGS,
        () => {
          if (this.props.activeRequest) {
            showModal(RequestSettingsModal, {
              request: this.props.activeRequest,
            });
          }
        },
      ],
      [
        hotKeyRefs.REQUEST_QUICK_SWITCH,
        () => {
          showModal(RequestSwitcherModal);
        },
      ],
      [hotKeyRefs.REQUEST_SEND, this._handleSendShortcut],
      [
        hotKeyRefs.ENVIRONMENT_SHOW_EDITOR,
        () => {
          const { activeWorkspace } = this.props;
          showModal(WorkspaceEnvironmentsEditModal, activeWorkspace);
        },
      ],
      [
        hotKeyRefs.SHOW_COOKIES_EDITOR,
        () => {
          const { activeWorkspace } = this.props;
          showModal(CookiesModal, activeWorkspace);
        },
      ],
      [
        hotKeyRefs.REQUEST_QUICK_CREATE,
        async () => {
          const { activeRequest, activeWorkspace } = this.props;
          const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
          const request = await models.request.create({ parentId, name: 'New Request' });
          await this._handleSetActiveRequest(request._id);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_CREATE,
        () => {
          const { activeRequest, activeWorkspace } = this.props;
          const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
          this._requestCreate(parentId);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_DELETE,
        () => {
          const { activeRequest } = this.props;

          if (!activeRequest) {
            return;
          }

          showModal(AskModal, {
            title: 'Delete Request?',
            message: `Really delete ${activeRequest.name}?`,
            onDone: confirmed => {
              if (!confirmed) {
                return;
              }
              models.request.remove(activeRequest);
            },
          });
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_CREATE_FOLDER,
        () => {
          const { activeRequest, activeWorkspace } = this.props;
          const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
          this._requestGroupCreate(parentId);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_GENERATE_CODE_EDITOR,
        async () => {
          showModal(GenerateCodeModal, this.props.activeRequest);
        },
      ],
      [
        hotKeyRefs.REQUEST_SHOW_DUPLICATE,
        async () => {
          await this._requestDuplicate(this.props.activeRequest);
        },
      ],
      [
        hotKeyRefs.REQUEST_TOGGLE_PIN,
        async () => {
          if (!this.props.activeRequest) {
            return;
          }

          const metas = Object.values(this.props.entities.requestMetas).find(
            m => m.parentId === this.props.activeRequest._id,
          );

          await this._handleSetRequestPinned(this.props.activeRequest, !(metas && metas.pinned));
        },
      ],
      [hotKeyRefs.PLUGIN_RELOAD, this._handleReloadPlugins],
      [
        hotKeyRefs.ENVIRONMENT_UNCOVER_VARIABLES,
        async () => {
          await this._updateIsVariableUncovered();
        },
      ],
    ];
  }

  async _handleSendShortcut() {
    const { activeRequest, activeEnvironment } = this.props;
    await this._handleSendRequestWithEnvironment(
      activeRequest ? activeRequest._id : 'n/a',
      activeEnvironment ? activeEnvironment._id : 'n/a',
    );
  }

  _setRequestPaneRef(n) {
    this._requestPane = n;
  }

  _setResponsePaneRef(n) {
    this._responsePane = n;
  }

  _setSidebarRef(n) {
    this._sidebar = n;
  }

  _requestGroupCreate(parentId) {
    showPrompt({
      title: 'New Folder',
      defaultValue: 'My Folder',
      submitName: 'Create',
      label: 'Name',
      selectText: true,
      onComplete: async name => {
        const requestGroup = await models.requestGroup.create({
          parentId,
          name,
        });
        await models.requestGroupMeta.create({
          parentId: requestGroup._id,
          collapsed: false,
        });
      },
    });
  }

  _requestCreate(parentId) {
    showModal(RequestCreateModal, {
      parentId,
      onComplete: request => {
        this._handleSetActiveRequest(request._id);
      },
    });
  }

  static async _requestGroupDuplicate(requestGroup) {
    models.requestGroup.duplicate(requestGroup);
  }

  static async _requestGroupMove(requestGroup) {
    showModal(MoveRequestGroupModal, { requestGroup });
  }

  async _requestDuplicate(request) {
    if (!request) {
      return;
    }

    const newRequest = await models.request.duplicate(request);
    await this._handleSetActiveRequest(newRequest._id);
  }

  async _workspaceDuplicate(callback) {
    const workspace = this.props.activeWorkspace;
    showPrompt({
      title: 'Duplicate Workspace',
      defaultValue: `${workspace.name} (Copy)`,
      submitName: 'Duplicate',
      selectText: true,
      onComplete: async name => {
        const newWorkspace = await db.duplicate(workspace, { name });
        await this.props.handleSetActiveWorkspace(newWorkspace._id);
        callback();
      },
    });
  }

  async _fetchRenderContext() {
    const { activeEnvironment, activeRequest, activeWorkspace } = this.props;
    const environmentId = activeEnvironment ? activeEnvironment._id : null;

    const ancestors = await db.withAncestors(activeRequest || activeWorkspace, [
      models.request.type,
      models.requestGroup.type,
      models.workspace.type,
    ]);

    return render.getRenderContext(activeRequest, environmentId, ancestors);
  }

  async _handleGetRenderContext() {
    const context = await this._fetchRenderContext();
    const keys = getKeys(context);
    return { context, keys };
  }

  /**
   * Heavily optimized render function
   *
   * @param text - template to render
   * @param contextCacheKey - if rendering multiple times in parallel, set this
   * @returns {Promise}
   * @private
   */
  async _handleRenderText(text, contextCacheKey = null) {
    if (!contextCacheKey || !this._getRenderContextPromiseCache[contextCacheKey]) {
      // NOTE: We're caching promises here to avoid race conditions
      this._getRenderContextPromiseCache[contextCacheKey] = this._fetchRenderContext();
    }

    // Set timeout to delete the key eventually
    setTimeout(() => delete this._getRenderContextPromiseCache[contextCacheKey], 5000);

    const context = await this._getRenderContextPromiseCache[contextCacheKey];
    return render.render(text, context);
  }

  _handleGenerateCodeForActiveRequest() {
    App._handleGenerateCode(this.props.activeRequest);
  }

  static _handleGenerateCode(request) {
    showModal(GenerateCodeModal, request);
  }

  async _handleCopyAsCurl(request) {
    const { activeEnvironment } = this.props;
    const environmentId = activeEnvironment ? activeEnvironment._id : 'n/a';
    const har = await exportHarRequest(request._id, environmentId);
    const snippet = new HTTPSnippet(har);
    const cmd = snippet.convert('shell', 'curl');
    clipboard.writeText(cmd);
  }

  static async _updateRequestGroupMetaByParentId(requestGroupId, patch) {
    const requestGroupMeta = await models.requestGroupMeta.getByParentId(requestGroupId);
    if (requestGroupMeta) {
      await models.requestGroupMeta.update(requestGroupMeta, patch);
    } else {
      const newPatch = Object.assign({ parentId: requestGroupId }, patch);
      await models.requestGroupMeta.create(newPatch);
    }
  }

  async _updateActiveWorkspaceMeta(patch) {
    const workspaceId = this.props.activeWorkspace._id;
    const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(workspaceId);
    if (workspaceMeta) {
      return models.workspaceMeta.update(workspaceMeta, patch);
    } else {
      const newPatch = Object.assign({ parentId: workspaceId }, patch);
      return models.workspaceMeta.create(newPatch);
    }
  }

  static async _updateRequestMetaByParentId(requestId, patch) {
    const requestMeta = await models.requestMeta.getByParentId(requestId);
    if (requestMeta) {
      return models.requestMeta.update(requestMeta, patch);
    } else {
      const newPatch = Object.assign({ parentId: requestId }, patch);
      return models.requestMeta.create(newPatch);
    }
  }

  _updateIsVariableUncovered() {
    this.setState({ isVariableUncovered: !this.state.isVariableUncovered });
  }

  _handleSetPaneWidth(paneWidth) {
    this.setState({ paneWidth });
    this._savePaneWidth(paneWidth);
  }

  _handleSetPaneHeight(paneHeight) {
    this.setState({ paneHeight });
    this._savePaneHeight(paneHeight);
  }

  async _handleSetActiveRequest(activeRequestId) {
    await this._updateActiveWorkspaceMeta({ activeRequestId });
    await App._updateRequestMetaByParentId(activeRequestId, { lastActive: Date.now() });
  }

  async _handleSetActiveEnvironment(activeEnvironmentId) {
    await this._updateActiveWorkspaceMeta({ activeEnvironmentId });

    // Give it time to update and re-render
    setTimeout(() => this._wrapper._forceRequestPaneRefresh(), 300);
  }

  _handleSetSidebarWidth(sidebarWidth) {
    this.setState({ sidebarWidth });
    this._saveSidebarWidth(sidebarWidth);
  }

  async _handleSetSidebarHidden(sidebarHidden) {
    await this._updateActiveWorkspaceMeta({ sidebarHidden });
  }

  async _handleSetSidebarFilter(sidebarFilter) {
    await this._updateActiveWorkspaceMeta({ sidebarFilter });
  }

  _handleSetRequestGroupCollapsed(requestGroupId, collapsed) {
    App._updateRequestGroupMetaByParentId(requestGroupId, { collapsed });
  }

  async _handleSetRequestPinned(request, pinned) {
    App._updateRequestMetaByParentId(request._id, { pinned });
  }

  _handleSetResponsePreviewMode(requestId, previewMode) {
    App._updateRequestMetaByParentId(requestId, { previewMode });
  }

  _handleUpdateDownloadPath(requestId, downloadPath) {
    App._updateRequestMetaByParentId(requestId, { downloadPath });
  }

  async _handleSetResponseFilter(requestId, responseFilter) {
    await App._updateRequestMetaByParentId(requestId, { responseFilter });

    clearTimeout(this._responseFilterHistorySaveTimeout);
    this._responseFilterHistorySaveTimeout = setTimeout(async () => {
      const meta = await models.requestMeta.getByParentId(requestId);
      const responseFilterHistory = meta.responseFilterHistory.slice(0, 10);

      // Already in history?
      if (responseFilterHistory.includes(responseFilter)) {
        return;
      }

      // Blank?
      if (!responseFilter) {
        return;
      }

      responseFilterHistory.unshift(responseFilter);
      await App._updateRequestMetaByParentId(requestId, {
        responseFilterHistory,
      });
    }, 2000);
  }

  async _handleUpdateRequestMimeType(mimeType) {
    if (!this.props.activeRequest) {
      console.warn('Tried to update request mime-type when no active request');
      return null;
    }

    const requestMeta = await models.requestMeta.getOrCreateByParentId(
      this.props.activeRequest._id,
    );
    const savedBody = requestMeta.savedRequestBody;

    const saveValue =
      typeof mimeType !== 'string' // Switched to No body
        ? this.props.activeRequest.body
        : {}; // Clear saved value in requestMeta

    await models.requestMeta.update(requestMeta, {
      savedRequestBody: saveValue,
    });

    const newRequest = await updateMimeType(this.props.activeRequest, mimeType, false, savedBody);

    // Force it to update, because other editor components (header editor)
    // needs to change. Need to wait a delay so the next render can finish
    setTimeout(() => {
      this.setState({ forceRefreshHeaderCounter: this.state.forceRefreshHeaderCounter + 1 });
    }, 500);

    return newRequest;
  }

  async _getDownloadLocation() {
    return new Promise(resolve => {
      const options = {
        title: 'Select Download Location',
        buttonLabel: 'Send and Save',
        defaultPath: window.localStorage.getItem('insomnia.sendAndDownloadLocation'),
      };

      remote.dialog.showSaveDialog(options, filename => {
        window.localStorage.setItem('insomnia.sendAndDownloadLocation', filename);
        resolve(filename);
      });
    });
  }

  async _handleSendAndDownloadRequestWithEnvironment(requestId, environmentId, dir) {
    const { settings, handleStartLoading, handleStopLoading } = this.props;

    const request = await models.request.getById(requestId);
    if (!request) {
      return;
    }

    // NOTE: Since request is by far the most popular event, we will throttle
    // it so that we only track it if the request has changed since the last one
    const key = request._id;
    if (this._sendRequestTrackingKey !== key) {
      this._sendRequestTrackingKey = key;
    }

    // Start loading
    handleStartLoading(requestId);

    try {
      const responsePatch = await network.send(requestId, environmentId);
      const headers = responsePatch.headers || [];
      const header = getContentDispositionHeader(headers);
      const nameFromHeader = header ? header.value : null;

      if (
        responsePatch.bodyPath &&
        responsePatch.statusCode >= 200 &&
        responsePatch.statusCode < 300
      ) {
        const extension = mime.extension(responsePatch.contentType) || 'unknown';
        const name =
          nameFromHeader || `${request.name.replace(/\s/g, '-').toLowerCase()}.${extension}`;

        let filename;
        if (dir) {
          filename = path.join(dir, name);
        } else {
          filename = await this._getDownloadLocation();
        }

        const to = fs.createWriteStream(filename);
        const readStream = models.response.getBodyStream(responsePatch);

        if (!readStream) {
          return;
        }

        readStream.pipe(to);

        readStream.on('end', async () => {
          responsePatch.error = `Saved to ${filename}`;
          await models.response.create(responsePatch, settings.maxHistoryResponses);
        });

        readStream.on('error', async err => {
          console.warn('Failed to download request after sending', responsePatch.bodyPath, err);
          await models.response.create(responsePatch, settings.maxHistoryResponses);
        });
      } else {
        // Save the bad responses so failures are shown still
        await models.response.create(responsePatch, settings.maxHistoryResponses);
      }
    } catch (err) {
      showAlert({
        title: 'Unexpected Request Failure',
        message: (

The request failed due to an unhandled error:

{err.message}

        ),
      });
    }

    // Unset active response because we just made a new one
    await App._updateRequestMetaByParentId(requestId, {
      activeResponseId: null,
    });

    // Stop loading
    handleStopLoading(requestId);
  }

  async _handleSendRequestWithEnvironment(requestId, environmentId) {
    const { handleStartLoading, handleStopLoading, settings } = this.props;
    const request = await models.request.getById(requestId);
    if (!request) {
      return;
    }

    // NOTE: Since request is by far the most popular event, we will throttle
    // it so that we only track it if the request has changed since the last noe
    const key = `${request._id}::${request.modified}`;
    if (this._sendRequestTrackingKey !== key) {
      this._sendRequestTrackingKey = key;
    }

    handleStartLoading(requestId);

    try {
      const responsePatch = await network.send(requestId, environmentId);
      await models.response.create(responsePatch, settings.maxHistoryResponses);
    } catch (err) {
      if (err.type === 'render') {
        showModal(RequestRenderErrorModal, { request, error: err });
      } else {
        showAlert({
          title: 'Unexpected Request Failure',
          message: (

The request failed due to an unhandled error:

{err.message}

          ),
        });
      }
    }

    // Unset active response because we just made a new one
    await App._updateRequestMetaByParentId(requestId, {
      activeResponseId: null,
    });

    // Stop loading
    handleStopLoading(requestId);
  }

  async _handleSetActiveResponse(requestId, activeResponse = null) {
    const activeResponseId = activeResponse ? activeResponse._id : null;
    await App._updateRequestMetaByParentId(requestId, { activeResponseId });

    let response;
    if (activeResponseId) {
      response = await models.response.getById(activeResponseId);
    } else {
      response = await models.response.getLatestForRequest(requestId);
    }

    const requestVersionId = response ? response.requestVersionId : 'n/a';
    const request = await models.requestVersion.restore(requestVersionId);

    if (request) {
      // Refresh app to reflect changes. Using timeout because we need to
      // wait for the request update to propagate.
      setTimeout(() => this._wrapper._forceRequestPaneRefresh(), 500);
    } else {
      // Couldn't restore request. That's okay
    }
  }

  _requestCreateForWorkspace() {
    this._requestCreate(this.props.activeWorkspace._id);
  }

  _startDragSidebar() {
    this.setState({ draggingSidebar: true });
  }

  _resetDragSidebar() {
    // TODO: Remove setTimeout need be not triggering drag on double click
    setTimeout(() => this._handleSetSidebarWidth(DEFAULT_SIDEBAR_WIDTH), 50);
  }

  _startDragPaneHorizontal() {
    this.setState({ draggingPaneHorizontal: true });
  }

  _startDragPaneVertical() {
    this.setState({ draggingPaneVertical: true });
  }

  _resetDragPaneHorizontal() {
    // TODO: Remove setTimeout need be not triggering drag on double click
    setTimeout(() => this._handleSetPaneWidth(DEFAULT_PANE_WIDTH), 50);
  }

  _resetDragPaneVertical() {
    // TODO: Remove setTimeout need be not triggering drag on double click
    setTimeout(() => this._handleSetPaneHeight(DEFAULT_PANE_HEIGHT), 50);
  }

  _handleMouseMove(e) {
    if (this.state.draggingPaneHorizontal) {
      // Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
      const distance = this.props.paneWidth - this.state.paneWidth;
      if (!this.state.showDragOverlay && Math.abs(distance) > 0.02 /* % */) {
        this.setState({ showDragOverlay: true });
      }

      const requestPane = ReactDOM.findDOMNode(this._requestPane);
      const responsePane = ReactDOM.findDOMNode(this._responsePane);

      const requestPaneWidth = requestPane.offsetWidth;
      const responsePaneWidth = responsePane.offsetWidth;

      const pixelOffset = e.clientX - requestPane.offsetLeft;
      let paneWidth = pixelOffset / (requestPaneWidth + responsePaneWidth);
      paneWidth = Math.min(Math.max(paneWidth, MIN_PANE_WIDTH), MAX_PANE_WIDTH);

      this._handleSetPaneWidth(paneWidth);
    } else if (this.state.draggingPaneVertical) {
      // Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
      const distance = this.props.paneHeight - this.state.paneHeight;
      if (!this.state.showDragOverlay && Math.abs(distance) > 0.02 /* % */) {
        this.setState({ showDragOverlay: true });
      }

      const requestPane = ReactDOM.findDOMNode(this._requestPane);
      const responsePane = ReactDOM.findDOMNode(this._responsePane);

      const requestPaneHeight = requestPane.offsetHeight;
      const responsePaneHeight = responsePane.offsetHeight;

      const pixelOffset = e.clientY - requestPane.offsetTop;
      let paneHeight = pixelOffset / (requestPaneHeight + responsePaneHeight);
      paneHeight = Math.min(Math.max(paneHeight, MIN_PANE_HEIGHT), MAX_PANE_HEIGHT);

      this._handleSetPaneHeight(paneHeight);
    } else if (this.state.draggingSidebar) {
      // Only pop the overlay after we've moved it a bit (so we don't block doubleclick);
      const distance = this.props.sidebarWidth - this.state.sidebarWidth;
      if (!this.state.showDragOverlay && Math.abs(distance) > 2 /* ems */) {
        this.setState({ showDragOverlay: true });
      }

      const sidebar = ReactDOM.findDOMNode(this._sidebar);
      const currentPixelWidth = sidebar.offsetWidth;
      const ratio = (e.clientX - sidebar.offsetLeft) / currentPixelWidth;
      const width = this.state.sidebarWidth * ratio;

      let sidebarWidth = Math.min(width, MAX_SIDEBAR_REMS);

      if (sidebarWidth < COLLAPSE_SIDEBAR_REMS) {
        sidebarWidth = MIN_SIDEBAR_REMS;
      }

      this._handleSetSidebarWidth(sidebarWidth);
    }
  }

  _handleMouseUp() {
    if (this.state.draggingSidebar) {
      this.setState({ draggingSidebar: false, showDragOverlay: false });
    }

    if (this.state.draggingPaneHorizontal) {
      this.setState({ draggingPaneHorizontal: false, showDragOverlay: false });
    }

    if (this.state.draggingPaneVertical) {
      this.setState({ draggingPaneVertical: false, showDragOverlay: false });
    }
  }

  _handleKeyDown(e) {
    for (const [definition, callback] of this._globalKeyMap) {
      executeHotKey(e, definition, callback);
    }
  }

  _handleToggleMenuBar(hide) {
    for (const win of remote.BrowserWindow.getAllWindows()) {
      if (win.isMenuBarAutoHide() !== hide) {
        win.setAutoHideMenuBar(hide);
        win.setMenuBarVisibility(!hide);
      }
    }
  }

  async _handleToggleSidebar() {
    const sidebarHidden = !this.props.sidebarHidden;
    await this._handleSetSidebarHidden(sidebarHidden);
  }

  _handleShowExportRequestsModal() {
    showModal(ExportRequestsModal);
  }

  _setWrapperRef(n) {
    this._wrapper = n;
  }

  async _handleReloadPlugins() {
    const { settings } = this.props;
    await plugins.getPlugins(true);
    templating.reload();
    themes.setTheme(settings.theme);
    console.log('[plugins] reloaded');
  }

  /**
   * Update document.title to be "Workspace (Environment) – Request"
   * @private
   */
  _updateDocumentTitle() {
    const { activeWorkspace, activeEnvironment, activeRequest } = this.props;

    let title = activeWorkspace.name;

    if (activeEnvironment) {
      title += ` (${activeEnvironment.name})`;
    }

    if (activeRequest) {
      title += ` – ${activeRequest.name}`;
    }

    document.title = title;
  }

  componentDidUpdate(prevProps) {
    this._updateDocumentTitle();

    // Force app refresh if login state changes
    if (prevProps.isLoggedIn !== this.props.isLoggedIn) {
      this.setState(state => ({
        forceRefreshCounter: state.forceRefreshCounter + 1,
      }));
    }
  }

  async _updateVCS(activeWorkspace) {
    // Get the vcs and set it to null in the state while we update it
    let vcs = this.state.vcs;
    this.setState({ vcs: null });

    if (!vcs) {
      const directory = path.join(getDataDirectory(), 'version-control');
      const driver = new FileSystemDriver({ directory });
      vcs = new VCS(driver, async conflicts => {
        return new Promise(resolve => {
          showModal(SyncMergeModal, {
            conflicts,
            handleDone: conflicts => resolve(conflicts),
          });
        });
      });
    }

    await vcs.switchProject(activeWorkspace._id);

    this.setState({ vcs });
  }

  async componentDidMount() {
    // Bind mouse and key handlers
    document.addEventListener('mouseup', this._handleMouseUp);
    document.addEventListener('mousemove', this._handleMouseMove);
    this._setGlobalKeyMap();

    // Update title
    this._updateDocumentTitle();

    // Update VCS
    await this._updateVCS(this.props.activeWorkspace);

    db.onChange(async changes => {
      let needsRefresh = false;

      for (const change of changes) {
        const [type, doc, fromSync] = change;

        const { vcs } = this.state;
        const { activeRequest } = this.props;

        // Force refresh if environment changes
        // TODO: Only do this for environments in this workspace (not easy because they're nested)
        if (doc.type === models.environment.type) {
          console.log('[App] Forcing update from environment change', change);
          needsRefresh = true;
        }

        // Force refresh if sync changes the active request
        if (fromSync && activeRequest && doc._id === activeRequest._id) {
          needsRefresh = true;
          console.log('[App] Forcing update from request change', change);
        }

        // Delete VCS project if workspace deleted
        if (vcs && doc.type === models.workspace.type && type === db.CHANGE_REMOVE) {
          await vcs.removeProjectsForRoot(doc._id);
        }
      }

      if (needsRefresh) {
        setTimeout(() => this._wrapper._forceRequestPaneRefresh(), 300);
      }
    });

    ipcRenderer.on('toggle-preferences', () => {
      showModal(SettingsModal);
    });

    ipcRenderer.on('reload-plugins', this._handleReloadPlugins);

    ipcRenderer.on('toggle-preferences-shortcuts', () => {
      showModal(SettingsModal, TAB_INDEX_SHORTCUTS);
    });

    ipcRenderer.on('run-command', (e, commandUri) => {
      const parsed = urlParse(commandUri, true);

      const command = `${parsed.hostname}${parsed.pathname}`;
      const args = JSON.parse(JSON.stringify(parsed.query));
      args.workspaceId = args.workspaceId || this.props.activeWorkspace._id;

      this.props.handleCommand(command, args);
    });

    // NOTE: This is required for "drop" event to trigger.
    document.addEventListener(
      'dragover',
      e => {
        e.preventDefault();
      },
      false,
    );

    document.addEventListener(
      'drop',
      async e => {
        e.preventDefault();
        const { activeWorkspace, handleImportUriToWorkspace } = this.props;
        if (!activeWorkspace) {
          return;
        }

        if (e.dataTransfer.files.length === 0) {
          console.log('[drag] Ignored drop event because no files present');
          return;
        }

        const file = e.dataTransfer.files[0];
        const { path } = file;
        const uri = `file://${path}`;

        await showAlert({
          title: 'Confirm Data Import',
          message: (

              Import {path}?

          ),
          addCancel: true,
        });

        handleImportUriToWorkspace(activeWorkspace._id, uri);
      },
      false,
    );

    ipcRenderer.on('toggle-sidebar', this._handleToggleSidebar);

    // handle this
    this._handleToggleMenuBar(this.props.settings.autoHideMenuBar);

    // Give it a bit before letting the backend know it's ready
    setTimeout(() => ipcRenderer.send('window-ready'), 500);
  }

  componentWillUnmount() {
    // Remove mouse and key handlers
    document.removeEventListener('mouseup', this._handleMouseUp);
    document.removeEventListener('mousemove', this._handleMouseMove);
  }

  async _ensureWorkspaceChildren(props) {
    const { activeWorkspace, activeCookieJar, environments } = props;
    const baseEnvironments = environments.filter(e => e.parentId === activeWorkspace._id);

    // Nothing to do
    if (baseEnvironments.length && activeCookieJar) {
      return;
    }

    // We already started migrating. Let it finish.
    if (this._isMigratingChildren) {
      return;
    }

    // Prevent rendering of everything
    this._isMigratingChildren = true;

    const flushId = await db.bufferChanges();
    await models.environment.getOrCreateForWorkspace(activeWorkspace);
    await models.cookieJar.getOrCreateForParentId(activeWorkspace._id);
    await db.flushChanges(flushId);

    this._isMigratingChildren = false;
  }

  componentWillReceiveProps(nextProps) {
    this._ensureWorkspaceChildren(nextProps);

    // Update VCS if needed
    const { activeWorkspace } = this.props;
    if (nextProps.activeWorkspace._id !== activeWorkspace._id) {
      this._updateVCS(nextProps.activeWorkspace);
    }
  }

  componentWillMount() {
    this._ensureWorkspaceChildren(this.props);
  }

  render() {
    if (this._isMigratingChildren) {
      console.log('[app] Waiting for migration to complete');
      return null;
    }

    const { activeWorkspace } = this.props;

    const {
      paneWidth,
      paneHeight,
      sidebarWidth,
      isVariableUncovered,
      vcs,
      forceRefreshCounter,
      forceRefreshHeaderCounter,
    } = this.state;

    const uniquenessKey = `${forceRefreshCounter}::${activeWorkspace._id}`;

    return (
      <KeydownBinder onKeydown={this._handleKeyDown}>

          <ErrorBoundary showAlert>
            <Wrapper
              {...this.props}
              ref={this._setWrapperRef}
              paneWidth={paneWidth}
              paneHeight={paneHeight}
              sidebarWidth={sidebarWidth}
              handleCreateRequestForWorkspace={this._requestCreateForWorkspace}
              handleSetRequestPinned={this._handleSetRequestPinned}
              handleSetRequestGroupCollapsed={this._handleSetRequestGroupCollapsed}
              handleActivateRequest={this._handleSetActiveRequest}
              handleSetRequestPaneRef={this._setRequestPaneRef}
              handleSetResponsePaneRef={this._setResponsePaneRef}
              handleSetSidebarRef={this._setSidebarRef}
              handleStartDragSidebar={this._startDragSidebar}
              handleResetDragSidebar={this._resetDragSidebar}
              handleStartDragPaneHorizontal={this._startDragPaneHorizontal}
              handleStartDragPaneVertical={this._startDragPaneVertical}
              handleResetDragPaneHorizontal={this._resetDragPaneHorizontal}
              handleResetDragPaneVertical={this._resetDragPaneVertical}
              handleCreateRequest={this._requestCreate}
              handleRender={this._handleRenderText}
              handleGetRenderContext={this._handleGetRenderContext}
              handleDuplicateRequest={this._requestDuplicate}
              handleDuplicateRequestGroup={App._requestGroupDuplicate}
              handleMoveRequestGroup={App._requestGroupMove}
              handleDuplicateWorkspace={this._workspaceDuplicate}
              handleCreateRequestGroup={this._requestGroupCreate}
              handleGenerateCode={App._handleGenerateCode}
              handleGenerateCodeForActiveRequest={this._handleGenerateCodeForActiveRequest}
              handleCopyAsCurl={this._handleCopyAsCurl}
              handleSetResponsePreviewMode={this._handleSetResponsePreviewMode}
              handleSetResponseFilter={this._handleSetResponseFilter}
              handleSendRequestWithEnvironment={this._handleSendRequestWithEnvironment}
              handleSendAndDownloadRequestWithEnvironment={
                this._handleSendAndDownloadRequestWithEnvironment
              }
              handleSetActiveResponse={this._handleSetActiveResponse}
              handleSetActiveRequest={this._handleSetActiveRequest}
              handleSetActiveEnvironment={this._handleSetActiveEnvironment}
              handleSetSidebarFilter={this._handleSetSidebarFilter}
              handleToggleMenuBar={this._handleToggleMenuBar}
              handleUpdateRequestMimeType={this._handleUpdateRequestMimeType}
              handleShowExportRequestsModal={this._handleShowExportRequestsModal}
              handleUpdateDownloadPath={this._handleUpdateDownloadPath}
              isVariableUncovered={isVariableUncovered}
              headerEditorKey={forceRefreshHeaderCounter + ''}
              vcs={vcs}
            />
          </ErrorBoundary>

          <ErrorBoundary showAlert>
            <Toast />
          </ErrorBoundary>

          {/* Block all mouse activity by showing an overlay while dragging */}
          {this.state.showDragOverlay ?
 : null}

      </KeydownBinder>
    );
  }
}

App.propTypes = {
  // Required
  sidebarWidth: PropTypes.number.isRequired,
  paneWidth: PropTypes.number.isRequired,
  paneHeight: PropTypes.number.isRequired,
  handleCommand: PropTypes.func.isRequired,
  settings: PropTypes.object.isRequired,
  isLoggedIn: PropTypes.bool.isRequired,
  activeWorkspace: PropTypes.shape({
    _id: PropTypes.string.isRequired,
  }).isRequired,
  handleSetActiveWorkspace: PropTypes.func.isRequired,

  // Optional
  activeRequest: PropTypes.object,
  activeEnvironment: PropTypes.shape({
    _id: PropTypes.string.isRequired,
  }),
};

function mapStateToProps(state, props) {
  const { entities, global } = state;

  const { isLoading, loadingRequestIds, isLoggedIn } = global;

  // Entities
  const entitiesLists = selectEntitiesLists(state, props);
  const {
    workspaces,
    environments,
    requests,
    requestGroups,
    requestMetas,
    requestVersions,
  } = entitiesLists;

  const settings = entitiesLists.settings[0];

  // Workspace stuff
  const workspaceMeta = selectActiveWorkspaceMeta(state, props) || {};
  const activeWorkspace = selectActiveWorkspace(state, props);
  const activeWorkspaceClientCertificates = selectActiveWorkspaceClientCertificates(state, props);
  const sidebarHidden = workspaceMeta.sidebarHidden || false;
  const sidebarFilter = workspaceMeta.sidebarFilter || '';
  const sidebarWidth = workspaceMeta.sidebarWidth || DEFAULT_SIDEBAR_WIDTH;
  const paneWidth = workspaceMeta.paneWidth || DEFAULT_PANE_WIDTH;
  const paneHeight = workspaceMeta.paneHeight || DEFAULT_PANE_HEIGHT;

  // Request stuff
  const requestMeta = selectActiveRequestMeta(state, props) || {};
  const activeRequest = selectActiveRequest(state, props);
  const responsePreviewMode = requestMeta.previewMode || PREVIEW_MODE_SOURCE;
  const responseFilter = requestMeta.responseFilter || '';
  const responseFilterHistory = requestMeta.responseFilterHistory || [];
  const responseDownloadPath = requestMeta.downloadPath || null;

  // Cookie Jar
  const activeCookieJar = selectActiveCookieJar(state, props);

  // Response stuff
  const activeRequestResponses = selectActiveRequestResponses(state, props) || [];
  const activeResponse = selectActiveResponse(state, props) || null;

  // Environment stuff
  const activeEnvironmentId = workspaceMeta.activeEnvironmentId;
  const activeEnvironment = entities.environments[activeEnvironmentId];

  // OAuth2Token stuff
  const oAuth2Token = selectActiveOAuth2Token(state, props);

  // Find other meta things
  const loadStartTime = loadingRequestIds[activeRequest ? activeRequest._id : 'n/a'] || -1;
  const sidebarChildren = selectSidebarChildren(state, props);
  const workspaceChildren = selectWorkspaceRequestsAndRequestGroups(state, props);
  const unseenWorkspaces = selectUnseenWorkspaces(state, props);

  // Sync stuff
  const syncItems = selectSyncItems(state, props);

  return Object.assign({}, state, {
    activeCookieJar,
    activeEnvironment,
    activeRequest,
    activeRequestResponses,
    activeResponse,
    activeWorkspace,
    activeWorkspaceClientCertificates,
    environments,
    isLoading,
    isLoggedIn,
    loadStartTime,
    oAuth2Token,
    paneHeight,
    paneWidth,
    requestGroups,
    requestMetas,
    requestVersions,
    requests,
    responseDownloadPath,
    responseFilter,
    responseFilterHistory,
    responsePreviewMode,
    settings,
    sidebarChildren,
    sidebarFilter,
    sidebarHidden,
    sidebarWidth,
    syncItems,
    unseenWorkspaces,
    workspaceChildren,
    workspaces,
  });
}

function mapDispatchToProps(dispatch) {
  const global = bindActionCreators(globalActions, dispatch);

  return {
    handleStartLoading: global.loadRequestStart,
    handleStopLoading: global.loadRequestStop,

    handleSetActiveWorkspace: global.setActiveWorkspace,
    handleImportFileToWorkspace: global.importFile,
    handleImportUriToWorkspace: global.importUri,
    handleCommand: global.newCommand,
    handleExportFile: global.exportWorkspacesToFile,
    handleExportRequestsToFile: global.exportRequestsToFile,
    handleMoveDoc: _moveDoc,
  };
}

async function _moveDoc(docToMove, parentId, targetId, targetOffset) {
  // Nothing to do. We are in the same spot as we started
  if (docToMove._id === targetId) {
    return;
  }

  // Don't allow dragging things into itself or children. This will disconnect
  // the node from the tree and cause the item to no longer show in the UI.
  const descendents = await db.withDescendants(docToMove);
  for (const doc of descendents) {
    if (doc._id === parentId) {
      return;
    }
  }

  function __updateDoc(doc, patch) {
    models.getModel(docToMove.type).update(doc, patch);
  }

  if (targetId === null) {
    // We are moving to an empty area. No sorting required
    await __updateDoc(docToMove, { parentId });
    return;
  }

  // NOTE: using requestToTarget's parentId so we can switch parents!
  let docs = [
    ...(await models.request.findByParentId(parentId)),
    ...(await models.requestGroup.findByParentId(parentId)),
  ].sort((a, b) => (a.metaSortKey < b.metaSortKey ? -1 : 1));

  // Find the index of doc B so we can re-order and save everything
  for (let i = 0; i < docs.length; i++) {
    const doc = docs[i];

    if (doc._id === targetId) {
      let before, after;
      if (targetOffset < 0) {
        // We're moving to below
        before = docs[i];
        after = docs[i + 1];
      } else {
        // We're moving to above
        before = docs[i - 1];
        after = docs[i];
      }

      const beforeKey = before ? before.metaSortKey : docs[0].metaSortKey - 100;
      const afterKey = after ? after.metaSortKey : docs[docs.length - 1].metaSortKey + 100;

      if (Math.abs(afterKey - beforeKey) < 0.000001) {
        // If sort keys get too close together, we need to redistribute the list. This is
        // not performant at all (need to update all siblings in DB), but it is extremely rare
        // anyway
        console.log(`[app] Recreating Sort Keys ${beforeKey} ${afterKey}`);

        await db.bufferChanges(300);
        docs.map((r, i) => __updateDoc(r, { metaSortKey: i * 100, parentId }));
      } else {
        const metaSortKey = afterKey - (afterKey - beforeKey) / 2;
        __updateDoc(docToMove, { metaSortKey, parentId });
      }

      break;
    }
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);


text:
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using System.Windows;
using Caliburn.Micro;
using Cobalt.Services;
using MahApps.Metro.Controls;

namespace Cobalt.Extensibility
{
    public delegate void StartupTask();

    public class StartupTasks
    {
        private readonly IServiceLocator _serviceLocator;

        [ImportingConstructor]
        public StartupTasks(IServiceLocator serviceLocator)
        {
            _serviceLocator = serviceLocator;
        }

        [Export(typeof(StartupTask))]
        public void ApplyBindingScopeOverride()
        {
            var getNamedElements = BindingScope.GetNamedElements;
            Caliburn.Micro.BindingScope.GetNamedElements = o =>
                {
                    var metroWindow = o as MetroWindow;
                    if (metroWindow == null)
                    {
                        return getNamedElements(o);
                    }

                    var list = new List<FrameworkElement>(getNamedElements(o));
                    var type = o.GetType();
                    var fields =
                        o.GetType()
                         .GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
                         .Where(f => f.DeclaringType == type);
                    var flyouts =
                        fields.Where(f => f.FieldType == typeof(FlyoutsControl))
                              .Select(f => f.GetValue(o))
                              .Cast<FlyoutsControl>();
                    list.AddRange(flyouts);
                    return list;
                };
        }

        [Export(typeof(StartupTask))]
        public void ApplyViewLocatorOverride()
        {
            var viewLocator = _serviceLocator.GetInstance<IViewLocator>();
            Caliburn.Micro.ViewLocator.GetOrCreateViewType = viewLocator.GetOrCreateViewType;
        }
    }
}


text:
title: "Rest in Peace Miss F. Collins"
subtitle: "Obituary"
summary: "Emotions are running low as many, not only in Luxor but also in Cairo, are mourning the death of a local favorite, Miss F. Collins"
authors: [AustinKim]
tags: ["news", "passing"]
categories: [curiosities]
date: 2019-10-22
featured: false
draft: false
image:
  caption: "Unfortunate & unexpected passing"
  focal_point: "Smart"
  preview_only: false
projects: []
---
I was looking through my papers and noticed an interesting article that didn't seem to align with the theme of the local newsletter. In the _Egyptian Gazette_, mostly everything is generalized and for the benefit of the reader but there was a section titled [Obituary](https://www.baltimoresun.com/news/bs-xpm-2002-02-17-0202160131-story.html) and it was interesting because it was on one specific person. As I read I realized that even though she did just seem like another local, the point of the article was to show how [every citizen is important](http://www.globalthinkersforum.org/our-impact/each-and-everyone-of-us-is-unique-and-important-and-no-one-is-insignificant/) and it made sure to explain how she was a quality person and her death was to be mourned as many of the locals in Luxor and Cairo cared for her.

>"Each and Everyone of Us Is Unique and Important, and No-one Is Insignificant"

I find this fascinating because there are two other instances I found while browsing through the blog posts in which the _Egyptian Gazette_ has done this.
For example:

1. [In the death of Haicalis Pasha](https://dig-eg-gaz.github.io/post/2017-04-23-baenen-deathofthepasha/)
2. [Death of Mr. Hanna](https://dig-eg-gaz.github.io/post/18-blog-fitzpatrick/)

In both instances the newsletter is reporting local deaths and it shows that the community do care about its people and that no passing is meaningless.

One of the important things that was pointed out in the Obituary is how Miss Collins spent a majority of her time at church and actually passed while at a Sunday Service. It is safe to assume that she is now in [Heaven](https://www.everystudent.com/journeys/then.html) because not only did she realize God but also lived an honest life.


text:
module.exports = (function () {
  'use strict';

  /**
   * Stemmer token constructor.
   *
   * @param {String} string Token string.
   */
  var Token = function (string) {
    this.vowels   = '';
    this.regions  = {};
    this.string   = string;
    this.original = string;
  }

  /**
   * Set vowels.
   *
   * @param  {String|Array} vowels List of vowels.
   * @return {Token}               Token instance.
   */
  Token.prototype.usingVowels = function (vowels) {
    this.vowels = vowels;
    return this;
  };

  /**
   * Marks a region by defining its starting index or providing a callback
   * function that does.
   *
   * @param  {String}       region   Region name.
   * @param  {Array|Number} args     Callback arguments or region start index.
   * @param  {Function}     callback Function that determines the start index (optional).
   * @param  {Object}       context  Callback context (optional, defaults to this).
   * @return {Token}                 Token instance.
   */
  Token.prototype.markRegion = function (region, args, callback, context) {
    if (typeof callback === 'function') {
      this.regions[region] = callback.apply(context || this, [].concat(args));

    } else if (!isNaN(args)) {
      this.regions[region] = args;
    }

    return this;
  };

  /**
   * Replaces all instances of a string with another.
   *
   * @param  {String} find    String to be replaced.
   * @param  {String} replace Replacement string.
   * @return {Token}          Token instance.
   */
  Token.prototype.replaceAll = function (find, replace) {
    this.string = this.string.split(find).join(replace);
    return this;
  };

  /**
   * Replaces the token suffix if in a region.
   *
   * @param  {String} suffix  Suffix to replace.
   * @param  {String} replace Replacement string.
   * @param  {String} region  Region name.
   * @return {Token}          Token instance.
   */
  Token.prototype.replaceSuffixInRegion = function (suffix, replace, region) {
    var suffixes = [].concat(suffix);
    for (var i = 0; i < suffixes.length; i++) {
      if (this.hasSuffixInRegion(suffixes[i], region)) {
        this.string = this.string.slice(0, -suffixes[i].length) + replace;
        return this;
      }
    }
    return this;
  };

  /**
   * Determines whether the token has a vowel at the provided index.
   *
   * @param  {Integer} index Character index.
   * @return {Boolean}       Whether the token has a vowel at the provided index.
   */
  Token.prototype.hasVowelAtIndex = function (index) {
    return this.vowels.indexOf(this.string[index]) !== -1;
  };

  /**
   * Finds the next vowel in the token.
   *
   * @param  {Integer} start Starting index offset.
   * @return {Integer}       Vowel index, or the end of the string.
   */
  Token.prototype.nextVowelIndex = function (start) {
    var index = (start >= 0 && start < this.string.length) ? start : this.string.length;
    while (index < this.string.length && !this.hasVowelAtIndex(index)) {
      index++;
    }
    return index;
  };

  /**
   * Finds the next consonant in the token.
   *
   * @param  {Integer} start Starting index offset.
   * @return {Integer}       Consonant index, or the end of the string.
   */
  Token.prototype.nextConsonantIndex = function (start) {
    var index = (start >= 0 && start < this.string.length) ? start : this.string.length;
    while (index < this.string.length && this.hasVowelAtIndex(index)) {
      index++;
    }
    return index;
  };

  /**
   * Determines whether the token has the provided suffix.
   * @param  {String}  suffix Suffix to match.
   * @return {Boolean}        Whether the token string ends in suffix.
   */
  Token.prototype.hasSuffix = function (suffix) {
    return this.string.slice(-suffix.length) === suffix;
  };

  /**
   * Determines whether the token has the provided suffix within the specified
   * region.
   *
   * @param  {String}  suffix Suffix to match.
   * @param  {String}  region Region name.
   * @return {Boolean}        Whether the token string ends in suffix.
   */
  Token.prototype.hasSuffixInRegion = function (suffix, region) {
    var regionStart = this.regions[region] || 0,
      suffixStart   = this.string.length - suffix.length;
    return this.hasSuffix(suffix) && suffixStart >= regionStart;
  };

  return Token;
})();


text:
<?php
namespace BlogMurAPI\V1\Rest\Message;

class MessageMapperFactory
{
    public function __invoke($services)
    {
        $adapter = $services->get('Zend\Db\Adapter\Adapter');
        return new MessageMapper($adapter);
    }
}


text:
<?php

namespace Rebuy\EanIsbn\Tests\Converter;

use Rebuy\EanIsbn\Converter\Converter;
use Rebuy\EanIsbn\Converter\ConverterInterface;
use Rebuy\EanIsbn\Exception\ConversionException;
use Rebuy\EanIsbn\Identifier\Ean13;
use Rebuy\EanIsbn\Identifier\Ean8;

class ConverterTest extends AbstractConverterTest
{
    public function setUp()
    {
        $this->converter = new Converter();
    }

    /**
     * @test
     * @expectedException \Rebuy\EanIsbn\Exception\ConversionException
     */
    public function convert_tries_all_specialized_converters_before_throwing()
    {
        $value = 'VALUE';
        $exception = new ConversionException('');

        $specializedConverter1 = $this->getMock(ConverterInterface::class);
        $specializedConverter1
            ->expects($this->once())
            ->method('convert')
            ->with($value)
            ->willThrowException($exception);
        $specializedConverter2 = $this->getMock(ConverterInterface::class);
        $specializedConverter2
            ->expects($this->once())
            ->method('convert')
            ->with($value)
            ->willThrowException($exception);

        $converter = new Converter([Ean13::class => [$specializedConverter1, $specializedConverter2]]);
        $converter->convert(new Ean13($value));
    }

    /**
     * @test
     * @expectedException \Rebuy\EanIsbn\Exception\ConversionException
     */
    public function convert_ignores_specialized_converters_for_wrong_type()
    {
        $value = 'VALUE';

        $specializedConverter = $this->getMock(ConverterInterface::class);
        $specializedConverter->expects($this->never())->method('convert');

        $converter = new Converter([Ean13::class => [$specializedConverter]]);
        $converter->convert(new Ean8($value));
    }

    /**
     * @test
     */
    public function convert_passes_identifier_from_specialized_converter()
    {
        $value = 'VALUE';
        $identifier = new Ean13($value);
        $expectedIdentifier = new Ean8($value);

        $specializedConverter = $this->getMock(ConverterInterface::class);
        $specializedConverter
            ->expects($this->once())
            ->method('convert')
            ->with($identifier)
            ->willReturn($expectedIdentifier);

        $converter = new Converter([Ean13::class => [$specializedConverter]]);
        $convertedIdentifier = $converter->convert($identifier);

        $this->assertEquals($expectedIdentifier, $convertedIdentifier);
    }
}