<commit_before>// Add after: Qualtrics.SurveyEngine.addOnload call

/*
This code is licensed under a BSD 3-Clause License.
Copyright (c) 2016, University of Texas at Austin, Los Alamos National Laboratory.
All rights reserved.

Contributor: Terry Turton

*/
/* The 2 Alternative Forced Choice module is used to run a 2AFC experiment.
The module assumes that there is a set of baseline images and a set of stimuli images. Each stimuli image level may
consist of multiple variants.  The baseline images are in a single array.  One baseline image will be randomly chosen
to be used in each comparison.  The stimuli images are an array of arrays where the first index is the stimuli level
and the second index goes over the level variants.  An array of single stimuli images, one for each level of the
stimuli, will be built from one randomly chosen variant image from each level.
The image arrays will need to be modified for the user specific images.

The prefixes and code for the Embedded Data can also be changed as needed
to fit the specific variable names defined by the user.
*/

// List of Baseline and Stimuli images.
// One Stimuli image for each level will be chosen from each set of variants and that will be compared to
// one of the possible Baseline images.
// If a Baseline image should be compared to one of the Baseline images (typical 2AFC procedure), then the set of
// Baseline images should be included as one of the possible sets of Stimuli images.
// The test images vary the value from V=0 (black) through varying shades of grey.  There are five variants for
// each value level (value**-00 through value**-04).

var imgURL = "http://000.0.00.00/TestImages/ValueVarying/";
var allBaselineNames = [
"value00-00.png",
"value00-01.png",
"value00-02.png",
"value00-03.png",
"value00-04.png"
];
var allStimuliNames = [
	["value00-00.png", "value00-01.png", "value00-02.png", "value00-03.png", "value00-04.png"],
	["value05-00.png", "value05-01.png", "value05-02.png", "value05-03.png", "value05-04.png"],
	["value10-00.png", "value10-01.png", "value10-02.png", "value10-03.png", "value10-04.png"],
	["value15-00.png", "value15-01.png", "value15-02.png", "value15-03.png", "value15-04.png"],
	["value25-00.png", "value25-01.png", "value25-02.png", "value25-03.png", "value25-04.png"],
	["value35-00.png", "value35-01.png", "value35-02.png", "value35-03.png", "value35-04.png"],
	["value45-00.png", "value45-01.png", "value45-02.png", "value45-03.png", "value45-04.png"],
];
var stmliNames = [], whichStmli;
var bases = [], baseNames = [];
var imgs = [], imgNames = [];
var whichBase;

// Create containers, attach global container to Qualtrics Question Container
var imgContainer = document.getElementById('imageContainer');
var glbContainer =  this.getQuestionContainer();
var quesText = document.getElementById('myQuesText')
imgContainer.className = "pairBox";

// Create buttons with needed attributes and text
// blankBtn is used to add space between left/right buttons
var leftBtn = document.createElement("BUTTON");
var rightBtn = document.createElement("BUTTON");
var nextBtn = document.createElement("BUTTON");
var nextText = document.createTextNode("NEXT Image Pair");
var leftText = document.createTextNode("LEFT");
var rightText = document.createTextNode("RIGHT");
var blankBtn = document.createElement("BUTTON");

nextBtn.className = "nextButton";
leftBtn.className = "choiceButton leftButton";
rightBtn.className = "choiceButton rightButton";
blankBtn.className = "blankButton";
leftBtn.setAttribute = ("type", "radio");
rightBtn.setAttribute = ("type", "radio");
nextBtn.appendChild(nextText);
leftBtn.appendChild(leftText);
rightBtn.appendChild(rightText);

var imgPairs = [];
var imgPairsIndex = 0;
var imgPairNames = [];
var didflip = false;
var currentIndex = 0;
var that;

// Create variables for write-out to Qualtrics Embedded Data
var choicePre = "c";
var choiceEmbData;
var namePre = "n";
var nameEmbData;
var currentName = " ";
var currentChoice;

// Create variables to check if participant only selects one button
var allLeft = true;
var allRight = true;
var nameAllOneSide = "AllOneSide";

// Create misc variables
var timeDelay = 250;  // number of milliseconds pause before next image pair is shown
var doTimeDelay = false; // implement option to turn on/off time delay

function preloadImages(arr){ // preload imaegs
    var newimages=[]
	//force arr parameter to always be an array
    for (var i=0; i<arr.length; i++){
    var arr=(typeof arr!="object")? [arr] : arr
        newimages[i]=new Image()
        newimages[i].src=arr[i]
    }
}

function shuffle(array) {  // Fisher-Yates Shuffle from stackoverflow
    var arrIndex = array.length, temporaryValue, randomIndex;

    while (0 !== arrIndex) { // While there remain elements to shuffle...

     // Pick a remaining element...
     randomIndex = Math.floor(Math.random() * arrIndex);
     arrIndex -= 1;

     // And swap it with the current element.
     temporaryValue = array[arrIndex];
     array[arrIndex] = array[randomIndex];
     array[randomIndex] = temporaryValue;
   }
  return array;
}

function show2Images(outContainer, inContainer, imgA, imgB) {
// Add two images to the study container; randomly show the images
// as A vs. B or B vs. A
	var img1 = document.createElement("img");
	var img2 = document.createElement("img");
	var docFrag = document.createDocumentFragment();
	var someSpace = document.createTextNode("xxxxx");
	flipped = false;

	inContainer.style.color = "#CCCCCC";
	if (Math.random() < 0.5) {
		img1.src = imgA;
		img2.src = imgB;
		} else {
		flipped = true;
		img2.src = imgA;
		img1.src = imgB;
	}
	if (doTimeDelay) {  // flag to put a delay between the images
		img1.style.opacity = 0.0;
		img2.style.opacity = 0.0;
		img1.style.filter  = 'alpha(opacity=0)'; // IE fallback
		img2.style.filter  = 'alpha(opacity=0)'; // IE fallback
	} else {
		img1.style.opacity = 1.0;
		img2.style.opacity = 1.0;
		img1.style.filter  = 'alpha(opacity=100)'; // IE fallback
		img2.style.filter  = 'alpha(opacity=100)'; // IE fallback
	}

	docFrag.appendChild(someSpace);
	docFrag.appendChild(img1);
	docFrag.appendChild(someSpace);
	docFrag.appendChild(img2);
	inContainer.appendChild(docFrag);
	inContainer.appendChild(document.createElement('br'));
	inContainer.appendChild(leftBtn);
	inContainer.appendChild(blankBtn);
	inContainer.appendChild(rightBtn);
	inContainer.appendChild(document.createElement('br'));
	inContainer.appendChild(nextBtn);

	outContainer.appendChild(inContainer);
	setTimeout(function() {
	img1.style.opacity = 1.0;
	img2.style.opacity = 1.0;
	img1.style.filter  = 'alpha(opacity=100)'; // IE fallback
	img2.style.filter  = 'alpha(opacity=100)'; // IE fallback
	}, timeDelay);

	return flipped;
}

function clearContainer(elementID) {
    while (elementID.firstChild) {
		elementID.removeChild(elementID.firstChild)}
}

function endStudy(inContainer) {
	var endTextTitle = document.createTextNode("Please click on SUBMIT to record your choices.");
	var docFrag = document.createDocumentFragment();

	quesText.style.display = "none";
	inContainer.style.color = "Black";
	inContainer.style.backgroundColor = "#CC5500";
	inContainer.style.fontSize="200%";
	inContainer.style.fontWeight="bold" ;
	docFrag.appendChild(endTextTitle);
	inContainer.appendChild(docFrag);
	that.showNextButton();
}

function findOrigIndex (arr, key, iSearchValue) {
	for (var ii = 0; ii<arr.length; ii++) {
		if (arr[ii][key] == iSearchValue) {
			return ii;
		}
	}
	return null;
}

// Start 2 Alternative Forced Choice
that = this;
that.hideNextButton();

// Build an array from the stimuli images, choosing one stimuli image per level from each set of variants.
// Feed those into the stmliNames[].
for (var i = 0; i < allStimuliNames.length; i++) {
	whichStmli = Math.floor(allStimuliNames[i].length*Math.random());
	stmliNames[i] = allStimuliNames[i][whichStmli];
}

// Build the image arrays from the URL, baseNames and stmliNames array; preload images
// Shuffle the images, choose a random baseline for each of the stimuli level images
// creating an array of image pairs.
for (var i=0; i<stmliNames.length; i++) {
	imgs[i] = imgURL + stmliNames[i];
	imgNames.push( {iIndex: i, sName: stmliNames[i], iName: imgs[i], iResult: -1} );
}
for (var i=0; i<allBaselineNames.length; i++) {
	bases[i] = imgURL + allBaselineNames[i];
	baseNames.push( {iIndex: i, bName: allBaselineNames, iName: bases[i], iResult: -1} );
}
preloadImages(imgs);
preloadImages(bases);
clearContainer(imgContainer);

// Use an array of objects to determine which image pairs are to be shown.
// Shuffle the images. Choose one of the baseline images.  Build pairs.
shuffle(imgNames);
for (var j = 0; j<imgNames.length; j++) {
	whichBase = Math.floor(bases.length*Math.random());
	imgPairs[j] =  ({iA: whichBase, iB: j, iAname: baseNames[whichBase].iName, iBname: imgNames[j].iName});
}

// Showing initial image pair
nextBtn.disabled = true;
didflip = show2Images(glbContainer, imgContainer, imgPairs[0].iAname, imgPairs[0].iBname);

// Click "Next" button to see next image pair
nextBtn.addEventListener('click', function() {

	clearContainer(imgContainer);
	leftBtn.style.backgroundColor = "burlywood";
	rightBtn.style.backgroundColor = "burlywood";
	nextBtn.style.backgroundColor = "burlywood";
	nextBtn.disabled = true;
	imgPairsIndex++;
	currentIndex = imgPairsIndex;
	if (imgPairsIndex < imgPairs.length) {
	didflip = show2Images(glbContainer, imgContainer, imgPairs[imgPairsIndex].iAname, imgPairs[imgPairsIndex].iBname);
	} else {
		endStudy(imgContainer);
// Write output to Embedded Data Variables in image order;
		Qualtrics.SurveyEngine.setEmbeddedData(nameAllOneSide, allLeft||allRight);
		for (var iLoop = 0; iLoop<stmliNames.length; iLoop++) {
			origIndex = findOrigIndex(imgNames, "iIndex", iLoop);
			choiceEmbData = choicePre + iLoop;
			nameEmbData = namePre + iLoop;
			currentName = imgNames[origIndex].sName;
			currentChoice = imgNames[origIndex].iResult;
	        Qualtrics.SurveyEngine.setEmbeddedData(nameEmbData, currentName);
	        Qualtrics.SurveyEngine.setEmbeddedData(choiceEmbData, currentChoice);
		}
	}
});

leftBtn.addEventListener('click', function(){
 // Note choice for Left Button; by default Baseline is LEFT
	leftBtn.style.backgroundColor = "white";
	rightBtn.style.backgroundColor = "burlywood";
	nextBtn.style.backgroundColor = "GreenYellow";
	nextBtn.disabled = false;
	allRight = false;
	if (didflip) {
		imgNames[currentIndex].iResult = 0;  // non-Baseline chosen
	} else {
		imgNames[currentIndex].iResult = 1;  // Baseline chosen
	};
 });
 rightBtn.addEventListener('click', function(){
 // Note choice for Right Button; by default diff image is RIGHT
	rightBtn.style.backgroundColor = "white";
	leftBtn.style.backgroundColor = "burlywood";
	nextBtn.style.backgroundColor = "GreenYellow";
	nextBtn.disabled = false;
	allLeft = false;
	if (didflip) {
		imgNames[currentIndex].iResult = 1;  // Baseline chosen
	} else {
		imgNames[currentIndex].iResult = 0;  // non-Baseline chosen
	};
 });

 /*
Copyright (c) 2016, University of Texas at Austin, Los Alamos National Laboratory.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
<commit_msg>Correct missing semi-colon<commit_after>// Add after: Qualtrics.SurveyEngine.addOnload call

/*
This code is licensed under a BSD 3-Clause License.
Copyright (c) 2016, University of Texas at Austin, Los Alamos National Laboratory.
All rights reserved.

Contributor: Terry Turton

*/
/* The 2 Alternative Forced Choice module is used to run a 2AFC experiment.
The module assumes that there is a set of baseline images and a set of stimuli images. Each stimuli image level may
consist of multiple variants.  The baseline images are in a single array.  One baseline image will be randomly chosen
to be used in each comparison.  The stimuli images are an array of arrays where the first index is the stimuli level
and the second index goes over the level variants.  An array of single stimuli images, one for each level of the
stimuli, will be built from one randomly chosen variant image from each level.
The image arrays will need to be modified for the user specific images.

The prefixes and code for the Embedded Data can also be changed as needed
to fit the specific variable names defined by the user.
*/

// List of Baseline and Stimuli images.
// One Stimuli image for each level will be chosen from each set of variants and that will be compared to
// one of the possible Baseline images.
// If a Baseline image should be compared to one of the Baseline images (typical 2AFC procedure), then the set of
// Baseline images should be included as one of the possible sets of Stimuli images.
// The test images vary the value from V=0 (black) through varying shades of grey.  There are five variants for
// each value level (value**-00 through value**-04).

var imgURL = "http://000.0.00.00/TestImages/ValueVarying/";
var allBaselineNames = [
"value00-00.png",
"value00-01.png",
"value00-02.png",
"value00-03.png",
"value00-04.png"
];
var allStimuliNames = [
	["value00-00.png", "value00-01.png", "value00-02.png", "value00-03.png", "value00-04.png"],
	["value05-00.png", "value05-01.png", "value05-02.png", "value05-03.png", "value05-04.png"],
	["value10-00.png", "value10-01.png", "value10-02.png", "value10-03.png", "value10-04.png"],
	["value15-00.png", "value15-01.png", "value15-02.png", "value15-03.png", "value15-04.png"],
	["value25-00.png", "value25-01.png", "value25-02.png", "value25-03.png", "value25-04.png"],
	["value35-00.png", "value35-01.png", "value35-02.png", "value35-03.png", "value35-04.png"],
	["value45-00.png", "value45-01.png", "value45-02.png", "value45-03.png", "value45-04.png"],
];
var stmliNames = [], whichStmli;
var bases = [], baseNames = [];
var imgs = [], imgNames = [];
var whichBase;

// Create containers, attach global container to Qualtrics Question Container
var imgContainer = document.getElementById('imageContainer');
var glbContainer =  this.getQuestionContainer();
var quesText = document.getElementById('myQuesText');
imgContainer.className = "pairBox";

// Create buttons with needed attributes and text
// blankBtn is used to add space between left/right buttons
var leftBtn = document.createElement("BUTTON");
var rightBtn = document.createElement("BUTTON");
var nextBtn = document.createElement("BUTTON");
var nextText = document.createTextNode("NEXT Image Pair");
var leftText = document.createTextNode("LEFT");
var rightText = document.createTextNode("RIGHT");
var blankBtn = document.createElement("BUTTON");

nextBtn.className = "nextButton";
leftBtn.className = "choiceButton leftButton";
rightBtn.className = "choiceButton rightButton";
blankBtn.className = "blankButton";
leftBtn.setAttribute = ("type", "radio");
rightBtn.setAttribute = ("type", "radio");
nextBtn.appendChild(nextText);
leftBtn.appendChild(leftText);
rightBtn.appendChild(rightText);

var imgPairs = [];
var imgPairsIndex = 0;
var imgPairNames = [];
var didflip = false;
var currentIndex = 0;
var that;

// Create variables for write-out to Qualtrics Embedded Data
var choicePre = "c";
var choiceEmbData;
var namePre = "n";
var nameEmbData;
var currentName = " ";
var currentChoice;

// Create variables to check if participant only selects one button
var allLeft = true;
var allRight = true;
var nameAllOneSide = "AllOneSide";

// Create misc variables
var timeDelay = 250;  // number of milliseconds pause before next image pair is shown
var doTimeDelay = false; // implement option to turn on/off time delay

function preloadImages(arr){ // preload imaegs
    var newimages=[]
	//force arr parameter to always be an array
    for (var i=0; i<arr.length; i++){
    var arr=(typeof arr!="object")? [arr] : arr
        newimages[i]=new Image()
        newimages[i].src=arr[i]
    }
}

function shuffle(array) {  // Fisher-Yates Shuffle from stackoverflow
    var arrIndex = array.length, temporaryValue, randomIndex;

    while (0 !== arrIndex) { // While there remain elements to shuffle...

     // Pick a remaining element...
     randomIndex = Math.floor(Math.random() * arrIndex);
     arrIndex -= 1;

     // And swap it with the current element.
     temporaryValue = array[arrIndex];
     array[arrIndex] = array[randomIndex];
     array[randomIndex] = temporaryValue;
   }
  return array;
}

function show2Images(outContainer, inContainer, imgA, imgB) {
// Add two images to the study container; randomly show the images
// as A vs. B or B vs. A
	var img1 = document.createElement("img");
	var img2 = document.createElement("img");
	var docFrag = document.createDocumentFragment();
	var someSpace = document.createTextNode("xxxxx");
	flipped = false;

	inContainer.style.color = "#CCCCCC";
	if (Math.random() < 0.5) {
		img1.src = imgA;
		img2.src = imgB;
		} else {
		flipped = true;
		img2.src = imgA;
		img1.src = imgB;
	}
	if (doTimeDelay) {  // flag to put a delay between the images
		img1.style.opacity = 0.0;
		img2.style.opacity = 0.0;
		img1.style.filter  = 'alpha(opacity=0)'; // IE fallback
		img2.style.filter  = 'alpha(opacity=0)'; // IE fallback
	} else {
		img1.style.opacity = 1.0;
		img2.style.opacity = 1.0;
		img1.style.filter  = 'alpha(opacity=100)'; // IE fallback
		img2.style.filter  = 'alpha(opacity=100)'; // IE fallback
	}

	docFrag.appendChild(someSpace);
	docFrag.appendChild(img1);
	docFrag.appendChild(someSpace);
	docFrag.appendChild(img2);
	inContainer.appendChild(docFrag);
	inContainer.appendChild(document.createElement('br'));
	inContainer.appendChild(leftBtn);
	inContainer.appendChild(blankBtn);
	inContainer.appendChild(rightBtn);
	inContainer.appendChild(document.createElement('br'));
	inContainer.appendChild(nextBtn);

	outContainer.appendChild(inContainer);
	setTimeout(function() {
	img1.style.opacity = 1.0;
	img2.style.opacity = 1.0;
	img1.style.filter  = 'alpha(opacity=100)'; // IE fallback
	img2.style.filter  = 'alpha(opacity=100)'; // IE fallback
	}, timeDelay);

	return flipped;
}

function clearContainer(elementID) {
    while (elementID.firstChild) {
		elementID.removeChild(elementID.firstChild)}
}

function endStudy(inContainer) {
	var endTextTitle = document.createTextNode("Please click on SUBMIT to record your choices.");
	var docFrag = document.createDocumentFragment();

	quesText.style.display = "none";
	inContainer.style.color = "Black";
	inContainer.style.backgroundColor = "#CC5500";
	inContainer.style.fontSize="200%";
	inContainer.style.fontWeight="bold" ;
	docFrag.appendChild(endTextTitle);
	inContainer.appendChild(docFrag);
	that.showNextButton();
}

function findOrigIndex (arr, key, iSearchValue) {
	for (var ii = 0; ii<arr.length; ii++) {
		if (arr[ii][key] == iSearchValue) {
			return ii;
		}
	}
	return null;
}

// Start 2 Alternative Forced Choice
that = this;
that.hideNextButton();

// Build an array from the stimuli images, choosing one stimuli image per level from each set of variants.
// Feed those into the stmliNames[].
for (var i = 0; i < allStimuliNames.length; i++) {
	whichStmli = Math.floor(allStimuliNames[i].length*Math.random());
	stmliNames[i] = allStimuliNames[i][whichStmli];
}

// Build the image arrays from the URL, baseNames and stmliNames array; preload images
// Shuffle the images, choose a random baseline for each of the stimuli level images
// creating an array of image pairs.
for (var i=0; i<stmliNames.length; i++) {
	imgs[i] = imgURL + stmliNames[i];
	imgNames.push( {iIndex: i, sName: stmliNames[i], iName: imgs[i], iResult: -1} );
}
for (var i=0; i<allBaselineNames.length; i++) {
	bases[i] = imgURL + allBaselineNames[i];
	baseNames.push( {iIndex: i, bName: allBaselineNames, iName: bases[i], iResult: -1} );
}
preloadImages(imgs);
preloadImages(bases);
clearContainer(imgContainer);

// Use an array of objects to determine which image pairs are to be shown.
// Shuffle the images. Choose one of the baseline images.  Build pairs.
shuffle(imgNames);
for (var j = 0; j<imgNames.length; j++) {
	whichBase = Math.floor(bases.length*Math.random());
	imgPairs[j] =  ({iA: whichBase, iB: j, iAname: baseNames[whichBase].iName, iBname: imgNames[j].iName});
}

// Showing initial image pair
nextBtn.disabled = true;
didflip = show2Images(glbContainer, imgContainer, imgPairs[0].iAname, imgPairs[0].iBname);

// Click "Next" button to see next image pair
nextBtn.addEventListener('click', function() {

	clearContainer(imgContainer);
	leftBtn.style.backgroundColor = "burlywood";
	rightBtn.style.backgroundColor = "burlywood";
	nextBtn.style.backgroundColor = "burlywood";
	nextBtn.disabled = true;
	imgPairsIndex++;
	currentIndex = imgPairsIndex;
	if (imgPairsIndex < imgPairs.length) {
	didflip = show2Images(glbContainer, imgContainer, imgPairs[imgPairsIndex].iAname, imgPairs[imgPairsIndex].iBname);
	} else {
		endStudy(imgContainer);
// Write output to Embedded Data Variables in image order;
		Qualtrics.SurveyEngine.setEmbeddedData(nameAllOneSide, allLeft||allRight);
		for (var iLoop = 0; iLoop<stmliNames.length; iLoop++) {
			origIndex = findOrigIndex(imgNames, "iIndex", iLoop);
			choiceEmbData = choicePre + iLoop;
			nameEmbData = namePre + iLoop;
			currentName = imgNames[origIndex].sName;
			currentChoice = imgNames[origIndex].iResult;
	        Qualtrics.SurveyEngine.setEmbeddedData(nameEmbData, currentName);
	        Qualtrics.SurveyEngine.setEmbeddedData(choiceEmbData, currentChoice);
		}
	}
});

leftBtn.addEventListener('click', function(){
 // Note choice for Left Button; by default Baseline is LEFT
	leftBtn.style.backgroundColor = "white";
	rightBtn.style.backgroundColor = "burlywood";
	nextBtn.style.backgroundColor = "GreenYellow";
	nextBtn.disabled = false;
	allRight = false;
	if (didflip) {
		imgNames[currentIndex].iResult = 0;  // non-Baseline chosen
	} else {
		imgNames[currentIndex].iResult = 1;  // Baseline chosen
	};
 });
 rightBtn.addEventListener('click', function(){
 // Note choice for Right Button; by default diff image is RIGHT
	rightBtn.style.backgroundColor = "white";
	leftBtn.style.backgroundColor = "burlywood";
	nextBtn.style.backgroundColor = "GreenYellow";
	nextBtn.disabled = false;
	allLeft = false;
	if (didflip) {
		imgNames[currentIndex].iResult = 1;  // Baseline chosen
	} else {
		imgNames[currentIndex].iResult = 0;  // non-Baseline chosen
	};
 });

 /*
Copyright (c) 2016, University of Texas at Austin, Los Alamos National Laboratory.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
<|endoftext|>


<commit_before>var _ = require('lodash');
var httpProxy = require('http-proxy');
var routingProxy = httpProxy.createProxy();

var logger = require('../logger');
var config = require('../config');

var cache = null;

var buildProxyCache = function() {

  // once the cache is hydrated with configs just return it
  if (cache) {
    return;
  }

  cache = [];

  if (config.options.user) {
    config.headers = {
      'RemoteUser': config.options.user
    };
  }

  // for each server configuration...
  _.each(config.options.servers, function(server) {

    // if proxy flag is not true just continue
    if (!server.proxy) {
      return;
    }

    // ... get the proxy configuration and push into cache
    _.each(server.proxies, function(proxy) {
      var proxyConfig = {
        port: server.port,
        host: server.host || 'localhost',
        headers: _.merge({}, config.headers, server.headers, proxy.headers),
        context: proxy.context,
        rewrite: proxy.rewrite
      };
      logger.info('proxy created for context[%s] host[%s:%s] user[%s]', proxyConfig.context, proxyConfig.host, proxyConfig.port, proxyConfig.headers.RemoteUser);
      if (proxyConfig.rewrite) {
        logger.info('rewrite rule created for: [ %s ==> %s]', proxyConfig.rewrite.from, proxyConfig.rewrite.to);
      }
      cache.push(proxyConfig);
    });

  });

};

// Rewrite the url and add the appropriate header if applicable
var buildRequest = function(req, proxyConfig) {
  if (proxyConfig.rewrite) {
    req.url = req.url.replace(new RegExp(proxyConfig.rewrite.from), proxyConfig.rewrite.to);
  }

  // _.merge recursively copies over values that are not undefined within the source object
  // _.defaults only assigns to those properties that are still undefined in the target
  if (proxyConfig.headers) {
    _.merge(req.headers, proxyConfig.headers);
  }

};

module.exports = function proxy() {

  buildProxyCache();

  return function(req, res, next) {

    var proxyConfig = _.find(cache, function(_config) {
      return req.url.match(new RegExp('^\\' + _config.context + '.*'));
    });

    if (proxyConfig) {

      buildRequest(req, proxyConfig);

      var options = {
        target: {
          host: proxyConfig.host,
          port: proxyConfig.port
        }
      };

      routingProxy.web(req, res, options, function(e) {
        logger.error(e);
      });

    } else {
      next();
    }

  };

};
<commit_msg>fix string interpolation for native errors<commit_after>var _ = require('lodash');
var httpProxy = require('http-proxy');
var routingProxy = httpProxy.createProxy();

var logger = require('../logger');
var config = require('../config');

var cache = null;

var buildProxyCache = function() {

  // once the cache is hydrated with configs just return it
  if (cache) {
    return;
  }

  cache = [];

  if (config.options.user) {
    config.headers = {
      'RemoteUser': config.options.user
    };
  }

  // for each server configuration...
  _.each(config.options.servers, function(server) {

    // if proxy flag is not true just continue
    if (!server.proxy) {
      return;
    }

    // ... get the proxy configuration and push into cache
    _.each(server.proxies, function(proxy) {
      var proxyConfig = {
        port: server.port,
        host: server.host || 'localhost',
        headers: _.merge({}, config.headers, server.headers, proxy.headers),
        context: proxy.context,
        rewrite: proxy.rewrite
      };
      logger.info('proxy created for context[%s] host[%s:%s] user[%s]', proxyConfig.context, proxyConfig.host, proxyConfig.port, proxyConfig.headers.RemoteUser);
      if (proxyConfig.rewrite) {
        logger.info('rewrite rule created for: [ %s ==> %s]', proxyConfig.rewrite.from, proxyConfig.rewrite.to);
      }
      cache.push(proxyConfig);
    });

  });

};

// Rewrite the url and add the appropriate header if applicable
var buildRequest = function(req, proxyConfig) {
  if (proxyConfig.rewrite) {
    req.url = req.url.replace(new RegExp(proxyConfig.rewrite.from), proxyConfig.rewrite.to);
  }

  // _.merge recursively copies over values that are not undefined within the source object
  // _.defaults only assigns to those properties that are still undefined in the target
  if (proxyConfig.headers) {
    _.merge(req.headers, proxyConfig.headers);
  }

};

module.exports = function proxy() {

  buildProxyCache();

  return function(req, res, next) {

    var proxyConfig = _.find(cache, function(_config) {
      return req.url.match(new RegExp('^\\' + _config.context + '.*'));
    });

    if (proxyConfig) {

      buildRequest(req, proxyConfig);

      var options = {
        target: {
          host: proxyConfig.host,
          port: proxyConfig.port
        }
      };

      routingProxy.web(req, res, options, function(e) {
        logger.error('{red:%s', e);
      });

    } else {
      next();
    }

  };

};
<|endoftext|>


<commit_before>/**
 * @file TablePagination component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Pagination from '../Pagination';
import BriefPagination from '../BriefPagination';

import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';
import ComponentUtil from '../_vendors/ComponentUtil';

class TablePagination extends Component {

    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            page: props.page,
            pageSize: props.pageSize
        };

    }

    getPaginationComponent = () => {
        const {useFullPagination} = this.props;
        return useFullPagination ? Pagination : BriefPagination;
    };

    getSelectedCount = () => {

        const {selectMode, value} = this.props;

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                return value.length;
            case SelectMode.SINGLE_SELECT:
                return value ? 1 : 0;
        }

        return 0;

    };

    handlePaginationChange = pagination => {
        this.setState(pagination, () => {
            const {onPaginationChange} = this.props;
            onPaginationChange && onPaginationChange(pagination);
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            page: ComponentUtil.getDerivedState(props, state, 'page'),
            pageSize: ComponentUtil.getDerivedState(props, state, 'pageSize')
        };
    }

    render() {

        const {
                className, style, data, paginationSelectionVisible, paginationSelectionRenderer,
                paginationTotalVisible, paginationTotalRenderer,
                paginationPageSizeVisible, paginationPageSizesVisible,
                paginationPrevIconCls, paginationNextIconCls, paginationFirstIconCls, paginationLastIconCls,
                ...restProps
            } = this.props,
            PaginationComponent = this.getPaginationComponent();

        return (

                <PaginationComponent {...restProps}
                                     total={data && data.length || 0}
                                     selectedCount={this.getSelectedCount()}
                                     selectionVisible={paginationSelectionVisible}
                                     totalVisible={paginationTotalVisible}
                                     pageSizeVisible={paginationPageSizeVisible}
                                     pageSizesVisible={paginationPageSizesVisible}
                                     prevIconCls={paginationPrevIconCls}
                                     nextIconCls={paginationNextIconCls}
                                     firstIconCls={paginationFirstIconCls}
                                     lastIconCls={paginationLastIconCls}
                                     selectionRenderer={paginationSelectionRenderer}
                                     totalRenderer={paginationTotalRenderer}
                                     onChange={this.handlePaginationChange}/>

        );

    }
}

TablePagination.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The select mode of table.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.array,
    value: PropTypes.any,

    page: PropTypes.number,
    pageSize: PropTypes.number,
    pageSizes: PropTypes.array,
    useFullPagination: PropTypes.bool,

    pageSizeValueField: PropTypes.string,
    pageSizeDisplayField: PropTypes.string,

    paginationSelectionVisible: PropTypes.bool,
    paginationTotalVisible: PropTypes.bool,
    paginationPageSizeVisible: PropTypes.bool,
    paginationPageSizesVisible: PropTypes.bool,

    pageSizeRightIconCls: PropTypes.string,
    paginationPrevIconCls: PropTypes.string,
    paginationNextIconCls: PropTypes.string,
    paginationFirstIconCls: PropTypes.string,
    paginationLastIconCls: PropTypes.string,

    paginationSelectionRenderer: PropTypes.func,
    paginationTotalRenderer: PropTypes.func,
    onPaginationChange: PropTypes.func,
    onPageChange: PropTypes.func,
    onPageSizeChange: PropTypes.func

};

TablePagination.defaultProps = {

    selectMode: SelectMode.SINGLE_SELECT,

    page: 0,
    pageSize: 10,
    pageSizes: [5, 10, 15, 20],
    useFullPagination: false,

    pageSizeValueField: 'value',
    pageSizeDisplayField: 'text',

    paginationSelectionVisible: false,
    paginationTotalVisible: true,
    paginationPageSizeVisible: true,
    paginationPageSizesVisible: true,

    pageSizeRightIconCls: 'fas fa-angle-down',
    paginationPrevIconCls: 'fas fa-angle-left',
    paginationNextIconCls: 'fas fa-angle-right',
    paginationFirstIconCls: 'fas fa-angle-double-left',
    paginationLastIconCls: 'fas fa-angle-double-right'

};

export default TablePagination;
<commit_msg>update TablePagination<commit_after>/**
 * @file TablePagination component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Pagination from '../Pagination';
import BriefPagination from '../BriefPagination';

import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';
import ComponentUtil from '../_vendors/ComponentUtil';

class TablePagination extends Component {

    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            page: props.page,
            pageSize: props.pageSize
        };

    }

    getPaginationComponent = () => {
        const {useFullPagination} = this.props;
        return useFullPagination ? Pagination : BriefPagination;
    };

    handlePaginationChange = pagination => {
        this.setState(pagination, () => {
            const {onPaginationChange} = this.props;
            onPaginationChange && onPaginationChange(pagination);
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            page: ComponentUtil.getDerivedState(props, state, 'page'),
            pageSize: ComponentUtil.getDerivedState(props, state, 'pageSize')
        };
    }

    render() {

        const {
                className, style, data, value, paginationSelectionVisible, paginationSelectionRenderer,
                paginationTotalVisible, paginationTotalRenderer,
                paginationPageSizeVisible, paginationPageSizesVisible,
                paginationPrevIconCls, paginationNextIconCls, paginationFirstIconCls, paginationLastIconCls,
                ...restProps
            } = this.props,
            PaginationComponent = this.getPaginationComponent();

        return (

                <PaginationComponent {...restProps}
                                     total={data && data.length || 0}
                                     selectedCount={value && value.length || 0}
                                     selectionVisible={paginationSelectionVisible}
                                     totalVisible={paginationTotalVisible}
                                     pageSizeVisible={paginationPageSizeVisible}
                                     pageSizesVisible={paginationPageSizesVisible}
                                     prevIconCls={paginationPrevIconCls}
                                     nextIconCls={paginationNextIconCls}
                                     firstIconCls={paginationFirstIconCls}
                                     lastIconCls={paginationLastIconCls}
                                     selectionRenderer={paginationSelectionRenderer}
                                     totalRenderer={paginationTotalRenderer}
                                     onChange={this.handlePaginationChange}/>

        );

    }
}

TablePagination.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The select mode of table.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.array,
    value: PropTypes.any,

    page: PropTypes.number,
    pageSize: PropTypes.number,
    pageSizes: PropTypes.array,
    useFullPagination: PropTypes.bool,

    pageSizeValueField: PropTypes.string,
    pageSizeDisplayField: PropTypes.string,

    paginationSelectionVisible: PropTypes.bool,
    paginationTotalVisible: PropTypes.bool,
    paginationPageSizeVisible: PropTypes.bool,
    paginationPageSizesVisible: PropTypes.bool,

    pageSizeRightIconCls: PropTypes.string,
    paginationPrevIconCls: PropTypes.string,
    paginationNextIconCls: PropTypes.string,
    paginationFirstIconCls: PropTypes.string,
    paginationLastIconCls: PropTypes.string,

    paginationSelectionRenderer: PropTypes.func,
    paginationTotalRenderer: PropTypes.func,
    onPaginationChange: PropTypes.func,
    onPageChange: PropTypes.func,
    onPageSizeChange: PropTypes.func

};

TablePagination.defaultProps = {

    selectMode: SelectMode.SINGLE_SELECT,

    page: 0,
    pageSize: 10,
    pageSizes: [5, 10, 15, 20],
    useFullPagination: false,

    pageSizeValueField: 'value',
    pageSizeDisplayField: 'text',

    paginationSelectionVisible: false,
    paginationTotalVisible: true,
    paginationPageSizeVisible: true,
    paginationPageSizesVisible: true,

    pageSizeRightIconCls: 'fas fa-angle-down',
    paginationPrevIconCls: 'fas fa-angle-left',
    paginationNextIconCls: 'fas fa-angle-right',
    paginationFirstIconCls: 'fas fa-angle-double-left',
    paginationLastIconCls: 'fas fa-angle-double-right'

};

export default TablePagination;
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import * as Kb from '../../../../common-adapters'
import * as Styles from '../../../../styles'
import {formatTimeForPopup, formatTimeForRevoked} from '../../../../util/timestamp'
import {isAndroid} from '../../../../constants/platform'
import type {DeviceType} from '../../../../constants/types/devices'

const iconNameForDeviceType = Styles.isMobile
  ? (deviceType: string, isRevoked: boolean): Kb.IconType => {
      switch (deviceType) {
        case 'mobile':
          return isRevoked ? 'icon-fancy-revoked-phone-183-x-96' : 'icon-fancy-encrypted-phone-183-x-96'
        default:
          return isRevoked ? 'icon-fancy-revoked-computer-226-x-96' : 'icon-fancy-encrypted-computer-226-x-96'
      }
    }
  : (deviceType: string, isRevoked: boolean): Kb.IconType => {
      switch (deviceType) {
        case 'mobile':
          return isRevoked ? 'icon-fancy-revoked-phone-122-x-64' : 'icon-fancy-encrypted-phone-122-x-64'
        default:
          return isRevoked ? 'icon-fancy-revoked-computer-150-x-64' : 'icon-fancy-encrypted-computer-150-x-64'
      }
    }

const MessagePopupHeader = (props: {
  author: string,
  deviceName: string,
  deviceRevokedAt: ?number,
  deviceType: DeviceType,
  isLast?: boolean,
  timestamp: number,
  yourMessage: boolean,
}) => {
  const {author, deviceName, deviceRevokedAt, deviceType, isLast, timestamp, yourMessage} = props
  const iconName = iconNameForDeviceType(deviceType, !!deviceRevokedAt)
  const whoRevoked = yourMessage ? 'You' : author
  return (
    <Kb.Box style={styles.headerContainer}>
      <Kb.Icon type={iconName} style={Kb.iconCastPlatformStyles(styles.headerIcon)} />
      <Kb.Box style={Styles.globalStyles.flexBoxRow}>
        <Kb.Text
          type="BodySmall"
          style={{color: deviceRevokedAt ? Styles.globalColors.black_40 : Styles.globalColors.green}}
        >
          ENCRYPTED
        </Kb.Text>
        <Kb.Text
          type="BodySmall"
          style={{color: deviceRevokedAt ? Styles.globalColors.black_40 : Styles.globalColors.green}}
        >
           & SIGNED
        </Kb.Text>
      </Kb.Box>
      <Kb.Box2 direction="horizontal">
        <Kb.Text type="BodySmall" style={styles.colorBlack40}>
          by
        </Kb.Text>
        <Kb.Box2 direction="horizontal" gap="xtiny" gapStart={true} style={styles.alignItemsCenter}>
          <Kb.Avatar username={author} size={16} clickToProfile="tracker" />
          <Kb.ConnectedUsernames
            onUsernameClicked="profile"
            colorFollowing={true}
            colorYou={true}
            usernames={[author]}
            underline={true}
            type="BodySmallSemibold"
          />
        </Kb.Box2>
      </Kb.Box2>
      <Kb.Box style={styles.headerDetailsContainer}>
        <Kb.Text type="BodySmall">
          from device
          <Kb.Text type="BodySmallSemibold">{deviceName}</Kb.Text>
        </Kb.Text>
      </Kb.Box>
      <Kb.Text type="BodySmall">{formatTimeForPopup(timestamp)}</Kb.Text>
      {deviceRevokedAt && (
        <Kb.Box2
          gap="small"
          fullWidth={true}
          gapStart={true}
          direction="vertical"
          style={Styles.collapseStyles([isLast && styles.revokedAtContainerLast])}
        >
          <Kb.PopupHeaderText color={Styles.globalColors.white} backgroundColor={Styles.globalColors.blue}>
            {whoRevoked} revoked this device on {formatTimeForRevoked(deviceRevokedAt)}.
          </Kb.PopupHeaderText>
        </Kb.Box2>
      )}
    </Kb.Box>
  )
}

// The mobile special casing below is because RN doesn't support overflow on Android
const iconSpacing = Styles.isMobile ? 96 - (isAndroid ? 16 : 30) : 64
const styles = Styles.styleSheetCreate({
  alignItemsCenter: {alignItems: 'center'},
  colorBlack40: {color: Styles.globalColors.black_40},
  headerContainer: Styles.platformStyles({
    common: {
      ...Styles.globalStyles.flexBoxColumn,
      alignItems: 'center',
      width: '100%',
    },
    isElectron: {
      maxWidth: 240,
      paddingTop: iconSpacing,
    },
    isMobile: {
      paddingBottom: Styles.globalMargins.medium,
      paddingTop: Styles.globalMargins.medium + iconSpacing,
    },
  }),
  headerDetailsContainer: {
    ...Styles.globalStyles.flexBoxRow,
    paddingLeft: Styles.globalMargins.small,
    paddingRight: Styles.globalMargins.small,
  },
  headerIcon: Styles.platformStyles({
    common: {
      marginBottom: Styles.globalMargins.tiny,
      position: 'absolute',
    },
    isAndroid: {
      top: 0,
    },
    isElectron: {marginTop: Styles.globalMargins.small, top: -25},
    isIOS: {top: -15},
    isMobile: {
      marginTop: 0,
    },
  }),
  revokedAtContainerLast: {
    borderBottomLeftRadius: 3,
    borderBottomRightRadius: 3,
    marginBottom: -Styles.globalMargins.small,
    overflow: 'hidden',
  },
})

export default MessagePopupHeader
<commit_msg>Fix mobile crasher (#14672)<commit_after>// @flow
import * as React from 'react'
import * as Kb from '../../../../common-adapters'
import * as Styles from '../../../../styles'
import {formatTimeForPopup, formatTimeForRevoked} from '../../../../util/timestamp'
import {isAndroid} from '../../../../constants/platform'
import type {DeviceType} from '../../../../constants/types/devices'

const iconNameForDeviceType = Styles.isMobile
  ? (deviceType: string, isRevoked: boolean): Kb.IconType => {
      switch (deviceType) {
        case 'mobile':
          return isRevoked ? 'icon-fancy-revoked-phone-183-x-96' : 'icon-fancy-encrypted-phone-183-x-96'
        default:
          return isRevoked ? 'icon-fancy-revoked-computer-226-x-96' : 'icon-fancy-encrypted-computer-226-x-96'
      }
    }
  : (deviceType: string, isRevoked: boolean): Kb.IconType => {
      switch (deviceType) {
        case 'mobile':
          return isRevoked ? 'icon-fancy-revoked-phone-122-x-64' : 'icon-fancy-encrypted-phone-122-x-64'
        default:
          return isRevoked ? 'icon-fancy-revoked-computer-150-x-64' : 'icon-fancy-encrypted-computer-150-x-64'
      }
    }

const MessagePopupHeader = (props: {
  author: string,
  deviceName: string,
  deviceRevokedAt: ?number,
  deviceType: DeviceType,
  isLast?: boolean,
  timestamp: number,
  yourMessage: boolean,
}) => {
  const {author, deviceName, deviceRevokedAt, deviceType, isLast, timestamp, yourMessage} = props
  const iconName = iconNameForDeviceType(deviceType, !!deviceRevokedAt)
  const whoRevoked = yourMessage ? 'You' : author
  return (
    <Kb.Box style={styles.headerContainer}>
      <Kb.Icon type={iconName} style={Kb.iconCastPlatformStyles(styles.headerIcon)} />
      <Kb.Box style={Styles.globalStyles.flexBoxRow}>
        <Kb.Text
          type="BodySmall"
          style={{color: deviceRevokedAt ? Styles.globalColors.black_40 : Styles.globalColors.green}}
        >
          ENCRYPTED
        </Kb.Text>
        <Kb.Text
          type="BodySmall"
          style={{color: deviceRevokedAt ? Styles.globalColors.black_40 : Styles.globalColors.green}}
        >
           & SIGNED
        </Kb.Text>
      </Kb.Box>
      <Kb.Box2 direction="horizontal">
        <Kb.Text type="BodySmall" style={styles.colorBlack40}>
          by
        </Kb.Text>
        <Kb.Box2 direction="horizontal" gap="xtiny" gapStart={true} style={styles.alignItemsCenter}>
          <Kb.Avatar username={author} size={16} clickToProfile="tracker" />
          <Kb.ConnectedUsernames
            onUsernameClicked="profile"
            colorFollowing={true}
            colorYou={true}
            usernames={[author]}
            underline={true}
            type="BodySmallSemibold"
          />
        </Kb.Box2>
      </Kb.Box2>
      <Kb.Box style={styles.headerDetailsContainer}>
        <Kb.Text type="BodySmall">
          from device
          <Kb.Text type="BodySmallSemibold">{deviceName}</Kb.Text>
        </Kb.Text>
      </Kb.Box>
      <Kb.Text type="BodySmall">{formatTimeForPopup(timestamp)}</Kb.Text>
      {!!deviceRevokedAt && (
        <Kb.Box2
          gap="small"
          fullWidth={true}
          gapStart={true}
          direction="vertical"
          style={Styles.collapseStyles([isLast && styles.revokedAtContainerLast])}
        >
          <Kb.PopupHeaderText color={Styles.globalColors.white} backgroundColor={Styles.globalColors.blue}>
            {whoRevoked} revoked this device on {formatTimeForRevoked(deviceRevokedAt)}.
          </Kb.PopupHeaderText>
        </Kb.Box2>
      )}
    </Kb.Box>
  )
}

// The mobile special casing below is because RN doesn't support overflow on Android
const iconSpacing = Styles.isMobile ? 96 - (isAndroid ? 16 : 30) : 64
const styles = Styles.styleSheetCreate({
  alignItemsCenter: {alignItems: 'center'},
  colorBlack40: {color: Styles.globalColors.black_40},
  headerContainer: Styles.platformStyles({
    common: {
      ...Styles.globalStyles.flexBoxColumn,
      alignItems: 'center',
      width: '100%',
    },
    isElectron: {
      maxWidth: 240,
      paddingTop: iconSpacing,
    },
    isMobile: {
      paddingBottom: Styles.globalMargins.medium,
      paddingTop: Styles.globalMargins.medium + iconSpacing,
    },
  }),
  headerDetailsContainer: {
    ...Styles.globalStyles.flexBoxRow,
    paddingLeft: Styles.globalMargins.small,
    paddingRight: Styles.globalMargins.small,
  },
  headerIcon: Styles.platformStyles({
    common: {
      marginBottom: Styles.globalMargins.tiny,
      position: 'absolute',
    },
    isAndroid: {
      top: 0,
    },
    isElectron: {marginTop: Styles.globalMargins.small, top: -25},
    isIOS: {top: -15},
    isMobile: {
      marginTop: 0,
    },
  }),
  revokedAtContainerLast: {
    borderBottomLeftRadius: 3,
    borderBottomRightRadius: 3,
    marginBottom: -Styles.globalMargins.small,
    overflow: 'hidden',
  },
})

export default MessagePopupHeader
<|endoftext|>


<commit_before>/* @flow */

import { getCurrentConference } from '../base/conference';
import {
    PARTICIPANT_JOINED,
    PARTICIPANT_LEFT,
    PARTICIPANT_ROLE,
    PARTICIPANT_UPDATED,
    getParticipantById,
    getParticipantDisplayName
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';

import {
    clearNotifications,
    showNotification,
    showParticipantJoinedNotification
} from './actions';
import { NOTIFICATION_TIMEOUT } from './constants';
import { joinLeaveNotificationsDisabled } from './functions';

declare var interfaceConfig: Object;

/**
 * Middleware that captures actions to display notifications.
 *
 * @param {Store} store - The redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    switch (action.type) {
    case PARTICIPANT_JOINED: {
        const result = next(action);
        const { participant: p } = action;
        const { dispatch, getState } = store;

        if (!p.local && !joinLeaveNotificationsDisabled() && !p.isReplacing) {
            dispatch(showParticipantJoinedNotification(
                getParticipantDisplayName(getState, p.id)
            ));
        }

        if (typeof interfaceConfig === 'object'
                && !interfaceConfig.DISABLE_FOCUS_INDICATOR && p.role === PARTICIPANT_ROLE.MODERATOR) {
            // Do not show the notification for mobile and also when the focus indicator is disabled.
            const displayName = getParticipantDisplayName(getState, p.id);

            if (!p.isReplacing) {
                dispatch(showNotification({
                    descriptionArguments: { to: displayName || '$t(notify.somebody)' },
                    descriptionKey: 'notify.grantedTo',
                    titleKey: 'notify.somebody',
                    title: displayName
                },
                NOTIFICATION_TIMEOUT));
            }
        }

        return result;
    }
    case PARTICIPANT_LEFT: {
        if (!joinLeaveNotificationsDisabled()) {
            const participant = getParticipantById(
                store.getState(),
                action.participant.id
            );

            if (typeof interfaceConfig === 'object'
                && participant
                && !participant.local
                && !action.participant.isReplaced) {
                store.dispatch(showNotification({
                    descriptionKey: 'notify.disconnected',
                    titleKey: 'notify.somebody',
                    title: participant.name
                }, NOTIFICATION_TIMEOUT));
            }
        }

        return next(action);
    }
    case PARTICIPANT_UPDATED: {
        if (typeof interfaceConfig === 'undefined' || interfaceConfig.DISABLE_FOCUS_INDICATOR) {
            // Do not show the notification for mobile and also when the focus indicator is disabled.
            return next(action);
        }

        const { id, role } = action.participant;
        const state = store.getState();
        const oldParticipant = getParticipantById(state, id);
        const oldRole = oldParticipant?.role;

        if (oldRole && oldRole !== role && role === PARTICIPANT_ROLE.MODERATOR) {
            const displayName = getParticipantDisplayName(state, id);

            store.dispatch(showNotification({
                descriptionArguments: { to: displayName || '$t(notify.somebody)' },
                descriptionKey: 'notify.grantedTo',
                titleKey: 'notify.somebody',
                title: displayName
            },
            NOTIFICATION_TIMEOUT));
        }

        return next(action);
    }
    }

    return next(action);
});

/**
 * StateListenerRegistry provides a reliable way to detect the leaving of a
 * conference, where we need to clean up the notifications.
 */
StateListenerRegistry.register(
    /* selector */ state => getCurrentConference(state),
    /* listener */ (conference, { dispatch }) => {
        if (!conference) {
            dispatch(clearNotifications());
        }
    }
);
<commit_msg>fix(notifications) skip participant joined notifications before we join<commit_after>/* @flow */

import { getCurrentConference } from '../base/conference';
import {
    PARTICIPANT_JOINED,
    PARTICIPANT_LEFT,
    PARTICIPANT_ROLE,
    PARTICIPANT_UPDATED,
    getParticipantById,
    getParticipantDisplayName
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';

import {
    clearNotifications,
    showNotification,
    showParticipantJoinedNotification
} from './actions';
import { NOTIFICATION_TIMEOUT } from './constants';
import { joinLeaveNotificationsDisabled } from './functions';

declare var interfaceConfig: Object;

/**
 * Middleware that captures actions to display notifications.
 *
 * @param {Store} store - The redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    switch (action.type) {
    case PARTICIPANT_JOINED: {
        const result = next(action);
        const { participant: p } = action;
        const { dispatch, getState } = store;
        const state = getState();
        const { conference } = state['features/base/conference'];

        if (conference && !p.local && !joinLeaveNotificationsDisabled() && !p.isReplacing) {
            dispatch(showParticipantJoinedNotification(
                getParticipantDisplayName(state, p.id)
            ));
        }

        if (typeof interfaceConfig === 'object'
                && !interfaceConfig.DISABLE_FOCUS_INDICATOR && p.role === PARTICIPANT_ROLE.MODERATOR) {
            // Do not show the notification for mobile and also when the focus indicator is disabled.
            const displayName = getParticipantDisplayName(state, p.id);

            if (!p.isReplacing) {
                dispatch(showNotification({
                    descriptionArguments: { to: displayName || '$t(notify.somebody)' },
                    descriptionKey: 'notify.grantedTo',
                    titleKey: 'notify.somebody',
                    title: displayName
                },
                NOTIFICATION_TIMEOUT));
            }
        }

        return result;
    }
    case PARTICIPANT_LEFT: {
        if (!joinLeaveNotificationsDisabled()) {
            const participant = getParticipantById(
                store.getState(),
                action.participant.id
            );

            if (typeof interfaceConfig === 'object'
                && participant
                && !participant.local
                && !action.participant.isReplaced) {
                store.dispatch(showNotification({
                    descriptionKey: 'notify.disconnected',
                    titleKey: 'notify.somebody',
                    title: participant.name
                }, NOTIFICATION_TIMEOUT));
            }
        }

        return next(action);
    }
    case PARTICIPANT_UPDATED: {
        if (typeof interfaceConfig === 'undefined' || interfaceConfig.DISABLE_FOCUS_INDICATOR) {
            // Do not show the notification for mobile and also when the focus indicator is disabled.
            return next(action);
        }

        const { id, role } = action.participant;
        const state = store.getState();
        const oldParticipant = getParticipantById(state, id);
        const oldRole = oldParticipant?.role;

        if (oldRole && oldRole !== role && role === PARTICIPANT_ROLE.MODERATOR) {
            const displayName = getParticipantDisplayName(state, id);

            store.dispatch(showNotification({
                descriptionArguments: { to: displayName || '$t(notify.somebody)' },
                descriptionKey: 'notify.grantedTo',
                titleKey: 'notify.somebody',
                title: displayName
            },
            NOTIFICATION_TIMEOUT));
        }

        return next(action);
    }
    }

    return next(action);
});

/**
 * StateListenerRegistry provides a reliable way to detect the leaving of a
 * conference, where we need to clean up the notifications.
 */
StateListenerRegistry.register(
    /* selector */ state => getCurrentConference(state),
    /* listener */ (conference, { dispatch }) => {
        if (!conference) {
            dispatch(clearNotifications());
        }
    }
);
<|endoftext|>


<commit_before>import { createAsyncAction } from '../../lib/reduxHelpers';
import * as api from '../../lib/serverApi/topics';

export const FETCH_TOPIC_SPLIT_STORY_COUNT = 'FETCH_TOPIC_SENTENCE_COUNT';
// pass in topicId, snapshotId, timespanId
export const fetchTopicSplitStoryCounts = createAsyncAction(FETCH_TOPIC_SPLIT_STORY_COUNT, api.topicSplitStoryCounts);

export const FETCH_TOPIC_FOCAL_SET_SPLIT_STORY_COUNTS = 'FETCH_TOPIC_FOCAL_SET_SENTENCE_COUNTS';
// pass in topicId, focalSetId, filters
export const fetchTopicFocalSetSplitStoryCounts = createAsyncAction(FETCH_TOPIC_FOCAL_SET_SPLIT_STORY_COUNTS, api.topicFocalSetSplitStoryCounts);
<commit_msg>Change constants from sentence to story<commit_after>import { createAsyncAction } from '../../lib/reduxHelpers';
import * as api from '../../lib/serverApi/topics';

export const FETCH_TOPIC_SPLIT_STORY_COUNT = 'FETCH_TOPIC_SPLIT_STORY_COUNT';
// pass in topicId, snapshotId, timespanId
export const fetchTopicSplitStoryCounts = createAsyncAction(FETCH_TOPIC_SPLIT_STORY_COUNT, api.topicSplitStoryCounts);

export const FETCH_TOPIC_FOCAL_SET_SPLIT_STORY_COUNTS = 'FETCH_TOPIC_FOCAL_SET_SPLIT_STORY_COUNTS';
// pass in topicId, focalSetId, filters
export const fetchTopicFocalSetSplitStoryCounts = createAsyncAction(FETCH_TOPIC_FOCAL_SET_SPLIT_STORY_COUNTS, api.topicFocalSetSplitStoryCounts);
<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import {
  View,
  StyleSheet,
  Animated,
  Dimensions,
  BackAndroid,
  Platform,
} from 'react-native';

import Overlay from './Overlay';

import DefaultAnimation from '../animations/DefaultAnimation';
import type { DialogType } from '../Type';

const { width: screenWidth, height: screenHeight } = Dimensions.get('window');

// dialog states
const DIALOG_OPENING: string = 'opening';
const DIALOG_OPENED: string = 'opened';
const DIALOG_CLOSING: string = 'closing';
const DIALOG_CLOSED: string = 'closed';

// default dialog config
const DEFAULT_ANIMATION_DURATION: number = 150;
const DEFAULT_WIDTH: number = screenWidth;
const DEFAULT_HEIGHT: number = 300;
const CLOSE_ON_TOUCH_OUTSIDE: boolean = true;
const CLOSE_ON_HARDWARE_BACK_PRESS: boolean = true;
const HAVE_OVERLAY: boolean = true;

// event types
// only for android
const HARDWARE_BACK_PRESS_EVENT: string = 'hardwareBackPress';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    position: 'absolute',
    top: 0,
    left: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  dialog: {
    borderRadius: 8,
    backgroundColor: '#ffffff',
  },
  hidden: {
    top: -10000,
    left: 0,
    height: 0,
    width: 0,
  },
});

class Dialog extends Component {
  state: {
    dialogState: string,
  }

  props: DialogType

  static defaultProps = {
    animationDuration: DEFAULT_ANIMATION_DURATION,
    dialogAnimation: new DefaultAnimation({ animationDuration: DEFAULT_ANIMATION_DURATION }),
    width: DEFAULT_WIDTH,
    height: DEFAULT_HEIGHT,
    closeOnTouchOutside: CLOSE_ON_TOUCH_OUTSIDE,
    closeOnHardwareBackPress: CLOSE_ON_HARDWARE_BACK_PRESS,
    haveOverlay: HAVE_OVERLAY,
    onOpened: () => {},
    onClosed: () => {},
  }

  onOverlayPress: Function

  constructor(props: DialogType) {
    super(props);
    // opened, opening, closed, closing,
    this.state = {
      dialogState: DIALOG_CLOSED,
    };

    this.onOverlayPress = this.onOverlayPress.bind(this);
  }

  componentDidMount() {
    if (this.props.open) {
      this.open(this.props.onOpened);
    }

    if (Platform.OS === 'android') {
      BackAndroid.addEventListener(HARDWARE_BACK_PRESS_EVENT, () => {
        if (this.props.closeOnHardwareBackPress) {
          this.close(this.props.onClosed);
        }
      });
    }
  }

  componentWillReceiveProps(nextProps: DialogType) {
    if (this.props.open !== nextProps.open) {
      if (nextProps.open) {
        this.open(nextProps.onOpened);
      } else {
        this.close(nextProps.onClosed);
      }
    }
  }

  componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackAndroid.removeEventListener(HARDWARE_BACK_PRESS_EVENT);
    }
  }

  onOverlayPress() {
    if (this.props.closeOnTouchOutside) {
      this.close(this.props.onClosed);
    }
  }

  setDialogState(toValue: number, callback: ?Function) {
    let dialogState = toValue ? DIALOG_OPENING : DIALOG_CLOSING;

    // to make sure has passed the dialogAnimation prop and the dialogAnimation has toValue method
    if (this.props.dialogAnimation && this.props.dialogAnimation.toValue) {
      this.props.dialogAnimation.toValue(toValue);
    }

    this.setState({ dialogState });

    setTimeout(() => {
      dialogState = dialogState === DIALOG_CLOSING ? DIALOG_CLOSED : DIALOG_OPENED;
      this.setState({ dialogState });
      if (typeof callback === 'function') {
        callback();
      }
    }, this.props.animationDuration);
  }

  open(onOpened?: Function = () => {}) {
    this.setDialogState(1, onOpened);
  }

  close(onClosed?: Function = () => {}) {
    this.setDialogState(0, onClosed);
  }

  get pointerEvents(): string {
    if (this.props.overlayPointerEvents) {
      return this.props.overlayPointerEvents;
    }
    return this.state.dialogState === DIALOG_OPENED ? 'auto' : 'none';
  }

  get dialogSize(): Object {
    let { width, height } = this.props;

    if (width && width > 0.0 && width <= 1.0) {
      width *= screenWidth;
    }
    if (height && height > 0.0 && height <= 1.0) {
      height *= screenHeight;
    }

    return { width, height };
  }

  render() {
    const dialogState = this.state.dialogState;
    const overlayPointerEvents = this.pointerEvents;
    const dialogSize = this.dialogSize;
    const hidden = dialogState === DIALOG_CLOSED && styles.hidden;
    const isShowOverlay = (
      [DIALOG_OPENING, DIALOG_OPENED].includes(dialogState) && this.props.haveOverlay
    );
    const dimensions = {
      width: Dimensions.get('window').width,
      height: Dimensions.get('window').height,
    };

    return (
      <View style={[styles.container, hidden, dimensions]}>
        <Overlay
          pointerEvents={overlayPointerEvents}
          showOverlay={isShowOverlay}
          onPress={this.onOverlayPress}
          backgroundColor={this.props.overlayBackgroundColor}
          opacity={this.props.overlayOpacity}
          animationDuration={this.props.animationDuration}
        />
        <Animated.View
          style={[
            styles.dialog,
            dialogSize,
            this.props.dialogStyle,
            this.props.dialogAnimation.animations,
          ]}
        >
          {this.props.children}
          {this.props.actions}
        </Animated.View>
      </View>
    );
  }
}

export default Dialog;
<commit_msg>code refactoring and should return true/false on the handler of BackAndroid hardware_back_press_event<commit_after>// @flow

import React, { Component } from 'react';
import {
  View,
  StyleSheet,
  Animated,
  Dimensions,
  BackAndroid,
  Platform,
} from 'react-native';

import Overlay from './Overlay';

import DefaultAnimation from '../animations/DefaultAnimation';
import type { DialogType } from '../Type';

const { width: screenWidth, height: screenHeight } = Dimensions.get('window');

// dialog states
const DIALOG_OPENING: string = 'opening';
const DIALOG_OPENED: string = 'opened';
const DIALOG_CLOSING: string = 'closing';
const DIALOG_CLOSED: string = 'closed';

// default dialog config
const DEFAULT_ANIMATION_DURATION: number = 150;
const DEFAULT_WIDTH: number = screenWidth;
const DEFAULT_HEIGHT: number = 300;
const CLOSE_ON_TOUCH_OUTSIDE: boolean = true;
const CLOSE_ON_HARDWARE_BACK_PRESS: boolean = true;
const HAVE_OVERLAY: boolean = true;

// event types
// only for android
const HARDWARE_BACK_PRESS_EVENT: string = 'hardwareBackPress';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    position: 'absolute',
    top: 0,
    left: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  dialog: {
    borderRadius: 8,
    backgroundColor: '#ffffff',
  },
  hidden: {
    top: -10000,
    left: 0,
    height: 0,
    width: 0,
  },
});

class Dialog extends Component {
  state: {
    dialogState: string,
  }

  props: DialogType

  static defaultProps = {
    animationDuration: DEFAULT_ANIMATION_DURATION,
    dialogAnimation: new DefaultAnimation({ animationDuration: DEFAULT_ANIMATION_DURATION }),
    width: DEFAULT_WIDTH,
    height: DEFAULT_HEIGHT,
    closeOnTouchOutside: CLOSE_ON_TOUCH_OUTSIDE,
    closeOnHardwareBackPress: CLOSE_ON_HARDWARE_BACK_PRESS,
    haveOverlay: HAVE_OVERLAY,
    onOpened: () => {},
    onClosed: () => {},
  }

  onOverlayPress: Function

  constructor(props: DialogType) {
    super(props);
    // opened, opening, closed, closing,
    this.state = {
      dialogState: DIALOG_CLOSED,
    };

    this.onOverlayPress = this.onOverlayPress.bind(this);
  }

  componentDidMount() {
    if (this.props.open) {
      this.open(this.props.onOpened);
    }

    if (Platform.OS === 'android') {
      BackAndroid.addEventListener(HARDWARE_BACK_PRESS_EVENT, () => {
        if (this.props.closeOnHardwareBackPress && this.state.dialogState === DIALOG_OPENED) {
          this.close(this.props.onClosed);
          return false;
        }
        return true;
      });
    }
  }

  componentWillReceiveProps(nextProps: DialogType) {
    if (this.props.open !== nextProps.open) {
      if (nextProps.open) {
        this.open(nextProps.onOpened);
      } else {
        this.close(nextProps.onClosed);
      }
    }
  }

  componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackAndroid.removeEventListener(HARDWARE_BACK_PRESS_EVENT);
    }
  }

  onOverlayPress() {
    if (this.props.closeOnTouchOutside) {
      this.close(this.props.onClosed);
    }
  }

  setDialogState(toValue: number, callback?: Function = () => {}) {
    let dialogState = toValue ? DIALOG_OPENING : DIALOG_CLOSING;

    // to make sure has passed the dialogAnimation prop and the dialogAnimation has toValue method
    if (this.props.dialogAnimation && this.props.dialogAnimation.toValue) {
      this.props.dialogAnimation.toValue(toValue);
    }

    this.setState({ dialogState });

    setTimeout(() => {
      dialogState = dialogState === DIALOG_CLOSING ? DIALOG_CLOSED : DIALOG_OPENED;
      this.setState({ dialogState });
      callback();
    }, this.props.animationDuration);
  }

  open(onOpened?: Function = () => {}) {
    this.setDialogState(1, onOpened);
  }

  close(onClosed?: Function = () => {}) {
    this.setDialogState(0, onClosed);
  }

  get pointerEvents(): string {
    if (this.props.overlayPointerEvents) {
      return this.props.overlayPointerEvents;
    }
    return this.state.dialogState === DIALOG_OPENED ? 'auto' : 'none';
  }

  get dialogSize(): Object {
    let { width, height } = this.props;

    if (width && width > 0.0 && width <= 1.0) {
      width *= screenWidth;
    }
    if (height && height > 0.0 && height <= 1.0) {
      height *= screenHeight;
    }

    return { width, height };
  }

  render() {
    const dialogState = this.state.dialogState;
    const overlayPointerEvents = this.pointerEvents;
    const dialogSize = this.dialogSize;
    const hidden = dialogState === DIALOG_CLOSED && styles.hidden;
    const isShowOverlay = (
      [DIALOG_OPENING, DIALOG_OPENED].includes(dialogState) && this.props.haveOverlay
    );
    const dimensions = {
      width: Dimensions.get('window').width,
      height: Dimensions.get('window').height,
    };

    return (
      <View style={[styles.container, hidden, dimensions]}>
        <Overlay
          pointerEvents={overlayPointerEvents}
          showOverlay={isShowOverlay}
          onPress={this.onOverlayPress}
          backgroundColor={this.props.overlayBackgroundColor}
          opacity={this.props.overlayOpacity}
          animationDuration={this.props.animationDuration}
        />
        <Animated.View
          style={[
            styles.dialog,
            dialogSize,
            this.props.dialogStyle,
            this.props.dialogAnimation.animations,
          ]}
        >
          {this.props.children}
          {this.props.actions}
        </Animated.View>
      </View>
    );
  }
}

export default Dialog;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Shortcuts
// @namespace    http://your.homepage/
// @version      0.1.15
// @description  Salesforce keyboard shortcuts
// @author       motiko
// @match        https://*.force.com/*
// @match        https://*.salesforce.com/*
// @require      mousetrap.min.js
// @grant        none
// ==/UserScript==

(function(){

var userId;
function inject(fn) {
    var script = document.createElement('script');
    script.setAttribute("type", "application/javascript");
    script.textContent = '(' + fn + ')();';
    document.body.appendChild(script); // run the script
    document.body.removeChild(script); // clean up
}

function sendBackUserId(){
    if(window.UserContext){
        window.postMessage({type:"userId",content:UserContext.userId},"*");
    }
}

window.addEventListener("message", function(event) {
    if(event.data.type === "userId"){
        userId = event.data.content;
    }
});

inject(sendBackUserId);

Mousetrap.bind(['ctrl+alt+d','command+d'],function(e){
    window.open("/setup/ui/listApexTraces.apexp?user_id="+ userId+"&user_logging=true",'_blank');
});

shortcut('s',"/_ui/platform/schema/ui/schemabuilder/SchemaBuilderUi?setupid=SchemaBuilder");
shortcut('u',"/005?setupid=ManageUsers");
shortcut('o',"/p/setup/custent/CustomObjectsPage");

function shortcut(char,url){
    Mousetrap.bind(['ctrl+alt+' + char,'command+' + char],function(e){
        document.location.assign(url);
     });
}

})();
<commit_msg> fir open new tab for GM<commit_after>// ==UserScript==
// @name         Shortcuts
// @namespace    http://your.homepage/
// @version      0.1.15
// @description  Salesforce keyboard shortcuts
// @author       motiko
// @match        https://*.force.com/*
// @match        https://*.salesforce.com/*
// @require      mousetrap.min.js
// @grant        GM_openInTab
// ==/UserScript==

(function(){

var userId;
function inject(fn) {
    var script = document.createElement('script');
    script.setAttribute("type", "application/javascript");
    script.textContent = '(' + fn + ')();';
    document.body.appendChild(script); // run the script
    document.body.removeChild(script); // clean up
}

function sendBackUserId(){
    if(window.UserContext){
        window.postMessage({type:"userId",content:UserContext.userId},"*");
    }
}

window.addEventListener("message", function(event) {
    if(event.data.type === "userId"){
        userId = event.data.content;
    }
});

inject(sendBackUserId);

Mousetrap.bind(['ctrl+alt+d','command+d'],function(e){
    GM_openInTab("/setup/ui/listApexTraces.apexp?user_id="+ userId+"&user_logging=true");
});

shortcut('s',"/_ui/platform/schema/ui/schemabuilder/SchemaBuilderUi?setupid=SchemaBuilder");
shortcut('u',"/005?setupid=ManageUsers");
shortcut('o',"/p/setup/custent/CustomObjectsPage");

function shortcut(char,url){
    Mousetrap.bind(['ctrl+alt+' + char,'command+' + char],function(e){
        document.location.assign(url);
     });
}

})();
<|endoftext|>


<commit_before>/* ************************************ */
/* Define helper functions */
/* ************************************ */
function getDisplayElement() {
  $('

').appendTo('body')
  return $('

').appendTo('body')
}

function evalAttentionChecks() {
  var check_percent = 1
  if (run_attention_checks) {
    var attention_check_trials = jsPsych.data.getTrialsOfType('attention-check')
    var checks_passed = 0
    for (var i = 0; i < attention_check_trials.length; i++) {
      if (attention_check_trials[i].correct === true) {
        checks_passed += 1
      }
    }
    check_percent = checks_passed / attention_check_trials.length
  }
  return check_percent
}

function addID() {
  jsPsych.data.addDataToLastTrial({
    'exp_id': 'discount_titrate'
  })
}

var getInstructFeedback = function() {
  return '

' + feedback_instruct_text +
    '

'
}

// Function to generate random numbers from normal distribution
// Adapted from https://github.com/robbrit/randgen/blob/master/lib/randgen.js
function rnorm(mean, stdev) {
  var u1, u2, v1, v2, s;
  if (mean === undefined) {
    mean = 0.0;
  }
  if (stdev === undefined) {
    stdev = 1.0;
  }
  if (rnorm.v2 === null) {
    do {
      u1 = Math.random();
      u2 = Math.random();

      v1 = 2 * u1 - 1;
      v2 = 2 * u2 - 1;
      s = v1 * v1 + v2 * v2;
    } while (s === 0 || s >= 1);

    rnorm.v2 = v2 * Math.sqrt(-2 * Math.log(s) / s);
    return stdev * v1 * Math.sqrt(-2 * Math.log(s) / s) + mean;
  }

  v2 = rnorm.v2;
  rnorm.v2 = null;
  return stdev * v2 + mean;
}

rnorm.v2 = null;

//Funciton to repeat items in an array desired number of times
function fillArray(value, len) {
  var arr = [];
  for (var i = 0; i < len; i++) {
    for (var j = 0; j < value.length; j++) {
      arr.push(value[j]);
    }
  }
  return arr;
}

/* ************************************ */
/* Define experimental variables */
/* ************************************ */
// generic task variables
var run_attention_checks = true
var attention_check_thresh = 0.65
var sumInstructTime = 0 //ms
var instructTimeThresh = 0 ///in seconds

// task specific variables
//First generate smaller amounts (mean = 20, sd = 10, clipped at 5 and 40)
var small_amts = [];
for (i = 0; i < 36; i++) {
  small_amts[i] = Math.round(rnorm(20, 10) * 100) / 100

  if (small_amts[i] < 5) {
    small_amts[i] = 5;
  }
  if (small_amts[i] > 40) {
    small_amts[i] = 40;
  }
}

//Based on smaller amounts generate larger amounts (1, 5, 10, 15, 20, 25, 30, 50, 75% higher)
var rel_dif = fillArray([1.01, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.50, 1.75], 4)

var large_amts = [];
for (i = 0; i < 36; i++) {
  large_amts[i] = Math.round(small_amts[i] * rel_dif[i] * 100) / 100;
}

//Generate sooner delays (today or in 2 weeks - 18 each)
var sooner_dels = fillArray(["today"], 18).concat(fillArray(["2 weeks"], 18));

//Finally determine later delays (interval 2 or 4 weeks so half of the now trials: 2 weeks, half of now: 4 weeks, half of not now: 4 weeks, half of not now: 6 weeks)
var later_dels = fillArray(["2 weeks"], 9).concat(fillArray(["4 weeks"], 18)).concat(fillArray([
  "6 weeks"
], 9));

//Put all options in same object
var options = {
  small_amt: small_amts,
  sooner_del: sooner_dels,
  large_amt: large_amts,
  later_del: later_dels
}

var trials = [];

//loop through each option to create html
for (var i = 0; i < options.small_amt.length; i++) {
  trials[i] =
    "

Please select the option that you would prefer pressing 'q' for left 'p' for right:

$" +
    options.small_amt[i] + "
" + options.sooner_del[i] +
    "

$" + options.large_amt[i] +
    "
" + options.later_del[i] + "

"
}

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */
// Set up attention check node
var attention_check_block = {
  type: 'attention-check',
  data: {
    trial_id: "attention_check"
  },
  timing_response: 180000,
  response_ends_trial: true,
  timing_post_trial: 200
}

var attention_node = {
  timeline: [attention_check_block],
  conditional_function: function() {
    return run_attention_checks
  }
}

/* define static blocks */
var welcome_block = {
  type: 'poldrack-text',
  data: {
    trial_id: "welcome"
  },
  timing_response: 180000,
  text: '

Welcome to the experiment. Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 0
};

var feedback_instruct_text =
  'Starting with instructions.  Press  Enter  to continue.'
var feedback_instruct_block = {
  type: 'poldrack-text',
  data: {
    trial_id: "instructions"
  },
  cont_key: [13],
  text: getInstructFeedback,
  timing_post_trial: 0,
  timing_response: 180000
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var instruction_trials = []
var instructions_block = {
  type: 'poldrack-instructions',
  data: {
    trial_id: "instructions"
  },
  pages: [
    '

In this experiment you will be presented with two amounts of money to choose between. These amounts will be available at different time points. Your job is to indicate which option you would prefer by pressing "q" for the left option and "p" for the right option.

You should indicate your true preference because at the end of the experiment a random trial will be chosen and you will receive a bonus payment proportional to the option you selected at the time point you chose.

',
  ],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000
};
instruction_trials.push(feedback_instruct_block)
instruction_trials.push(instructions_block)

var instruction_node = {
  timeline: instruction_trials,
  /* This function defines stopping criteria */
  loop_function: function(data) {
    for (var i = 0; i < data.length; i++) {
      if ((data[i].trial_type == 'poldrack-instructions') && (data[i].rt != -1)) {
        rt = data[i].rt
        sumInstructTime = sumInstructTime + rt
      }
    }
    if (sumInstructTime <= instructTimeThresh * 1000) {
      feedback_instruct_text =
        'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
      return true
    } else if (sumInstructTime > instructTimeThresh * 1000) {
      feedback_instruct_text =
        'Done with instructions. Press enter to continue.'
      return false
    }
  }
}

var start_practice_block = {
  type: 'poldrack-text',
  timing_response: 180000,
  data: {
    trial_id: "practice_intro"
  },
  text: '

Here is a sample trial. Your choice for this trial will not be included in your bonus payment.

Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 1000
};

var practice_block = {
  type: 'poldrack-single-stim',
  data: {
    trial_id: "stim",
    exp_stage: "practice"
  },
  stimuli: [
    "

Please select the option that you would prefer pressing 'q' for left 'p' for right:

$20.58
today

$25.93
2 weeks

"
  ],
  is_html: true,
  choices: ['q', 'p']
};

var start_test_block = {
  type: 'poldrack-text',
  data: {
    trial_id: "test_intro"
  },
  timing_response: 180000,
  text: '

You are now ready to begin the survey.

Remember to indicate your true preferences.

Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 1000
};

var test_block = {
  type: 'poldrack-single-stim',
  stimuli: trials,
  data: {
    trial_id: "stim",
    exp_stage: "test"
  },
  is_html: true,
  choices: ['q', 'p'],
  randomize_order: true
};

var end_block = {
  type: 'poldrack-text',
  timing_response: 180000,
  data: {
    trial_id: "end"
  },
  text: '

Thanks for completing this task!

Press enter to continue.

',
  cont_key: [13],
  timing_post_trial: 0
};

//Set up experiment
var discount_titrate_experiment = []
discount_titrate_experiment.push(welcome_block);
discount_titrate_experiment.push(instruction_node);
discount_titrate_experiment.push(start_practice_block);
discount_titrate_experiment.push(practice_block);
discount_titrate_experiment.push(start_test_block);
discount_titrate_experiment.push(test_block);
discount_titrate_experiment.push(attention_node)
discount_titrate_experiment.push(end_block)<commit_msg>This fixed #116 and fixed #117.  I made the stims appear, I corrected experiment length (num_trials), and I changed the data to output both smaller and larger amounts with their respective delays, and added a chosen delay+amount column<commit_after>/* ************************************ */
/* Define helper functions */
/* ************************************ */
function getDisplayElement() {
  $('

').appendTo('body')
  return $('

').appendTo('body')
}

function evalAttentionChecks() {
  var check_percent = 1
  if (run_attention_checks) {
    var attention_check_trials = jsPsych.data.getTrialsOfType('attention-check')
    var checks_passed = 0
    for (var i = 0; i < attention_check_trials.length; i++) {
      if (attention_check_trials[i].correct === true) {
        checks_passed += 1
      }
    }
    check_percent = checks_passed / attention_check_trials.length
  }
  return check_percent
}

function addID() {
  jsPsych.data.addDataToLastTrial({
    'exp_id': 'discount_titrate'
  })
}

var getInstructFeedback = function() {
  return '

' + feedback_instruct_text +
    '

'
}

// Function to generate random numbers from normal distribution
// Adapted from https://github.com/robbrit/randgen/blob/master/lib/randgen.js
function rnorm(mean, stdev) {
  var u1, u2, v1, v2, s;
  if (mean === undefined) {
    mean = 0.0;
  }
  if (stdev === undefined) {
    stdev = 1.0;
  }
  if (rnorm.v2 === null) {
    do {
      u1 = Math.random();
      u2 = Math.random();

      v1 = 2 * u1 - 1;
      v2 = 2 * u2 - 1;
      s = v1 * v1 + v2 * v2;
    } while (s === 0 || s >= 1);

    rnorm.v2 = v2 * Math.sqrt(-2 * Math.log(s) / s);
    return stdev * v1 * Math.sqrt(-2 * Math.log(s) / s) + mean;
  }

  v2 = rnorm.v2;
  rnorm.v2 = null;
  return stdev * v2 + mean;
}

rnorm.v2 = null;

//Funciton to repeat items in an array desired number of times
function fillArray(value, len) {
  var arr = [];
  for (var i = 0; i < len; i++) {
    for (var j = 0; j < value.length; j++) {
      arr.push(value[j]);
    }
  }
  return arr;
}

var testCount = 0
var appendTestData = function(){
	smaller_amount= options.small_amt[testCount]
	sooner_delay= options.sooner_del[testCount]
	larger_amount= options.large_amt[testCount]
	later_delay = options.later_del[testCount]
	global_trial = jsPsych.progress().current_trial_global
	whichKey = jsPsych.data.getDataByTrialIndex(global_trial).key_press

	if(whichKey == 81){
		chosen_amount = smaller_amount
		chosen_delay = sooner_delay
	} else if (whichKey == 80){
		chosen_amount = larger_amount
		chosen_delay = later_delay
	}

	jsPsych.data.addDataToLastTrial({chosen_amount: chosen_amount, chosen_delay: chosen_delay, smaller_amount: smaller_amount, sooner_delay: sooner_delay, larger_amount: larger_amount, later_delay: later_delay})
	testCount = testCount+1
}

var getStim = function(){
	tempStim = trials.shift()
	return tempStim
}

/* ************************************ */
/* Define experimental variables */
/* ************************************ */
// generic task variables
var run_attention_checks = true
var attention_check_thresh = 0.65
var sumInstructTime = 0 //ms
var instructTimeThresh = 0 ///in seconds

// task specific variables
//First generate smaller amounts (mean = 20, sd = 10, clipped at 5 and 40)
var small_amts = [];
for (i = 0; i < 36; i++) {
  small_amts[i] = Math.round(rnorm(20, 10) * 100) / 100

  if (small_amts[i] < 5) {
    small_amts[i] = 5;
  }
  if (small_amts[i] > 40) {
    small_amts[i] = 40;
  }
}

//Based on smaller amounts generate larger amounts (1, 5, 10, 15, 20, 25, 30, 50, 75% higher)
var rel_dif = fillArray([1.01, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.50, 1.75], 4)

var large_amts = [];
for (i = 0; i < 36; i++) {
  large_amts[i] = Math.round(small_amts[i] * rel_dif[i] * 100) / 100;
}

//Generate sooner delays (today or in 2 weeks - 18 each)
var sooner_dels = fillArray(["today"], 18).concat(fillArray(["2 weeks"], 18));

//Finally determine later delays (interval 2 or 4 weeks so half of the now trials: 2 weeks, half of now: 4 weeks, half of not now: 4 weeks, half of not now: 6 weeks)
var later_dels = fillArray(["2 weeks"], 9).concat(fillArray(["4 weeks"], 18)).concat(fillArray([
  "6 weeks"
], 9));

//Put all options in same object
var options = {
  small_amt: small_amts,
  sooner_del: sooner_dels,
  large_amt: large_amts,
  later_del: later_dels
}

var trials = [];

//loop through each option to create html
for (var i = 0; i < options.small_amt.length; i++) {
  trials[i] =
    "

Please select the option that you would prefer pressing 'q' for left 'p' for right:

$" +
    options.small_amt[i] + "
" + options.sooner_del[i] +
    "

$" + options.large_amt[i] +
    "
" + options.later_del[i] + "

"
}

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */
// Set up attention check node
var attention_check_block = {
  type: 'attention-check',
  data: {
    trial_id: "attention_check"
  },
  timing_response: 180000,
  response_ends_trial: true,
  timing_post_trial: 200
}

var attention_node = {
  timeline: [attention_check_block],
  conditional_function: function() {
    return run_attention_checks
  }
}

/* define static blocks */
var welcome_block = {
  type: 'poldrack-text',
  data: {
    trial_id: "welcome"
  },
  timing_response: 180000,
  text: '

Welcome to the experiment. Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 0
};

var feedback_instruct_text =
  'Starting with instructions.  Press  Enter  to continue.'
var feedback_instruct_block = {
  type: 'poldrack-text',
  data: {
    trial_id: "instructions"
  },
  cont_key: [13],
  text: getInstructFeedback,
  timing_post_trial: 0,
  timing_response: 180000
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var instruction_trials = []
var instructions_block = {
  type: 'poldrack-instructions',
  data: {
    trial_id: "instructions"
  },
  pages: [
    '

In this experiment you will be presented with two amounts of money to choose between. These amounts will be available at different time points. Your job is to indicate which option you would prefer by pressing "q" for the left option and "p" for the right option.

You should indicate your true preference because at the end of the experiment a random trial will be chosen and you will receive a bonus payment proportional to the option you selected at the time point you chose.

',
  ],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000
};
instruction_trials.push(feedback_instruct_block)
instruction_trials.push(instructions_block)

var instruction_node = {
  timeline: instruction_trials,
  /* This function defines stopping criteria */
  loop_function: function(data) {
    for (var i = 0; i < data.length; i++) {
      if ((data[i].trial_type == 'poldrack-instructions') && (data[i].rt != -1)) {
        rt = data[i].rt
        sumInstructTime = sumInstructTime + rt
      }
    }
    if (sumInstructTime <= instructTimeThresh * 1000) {
      feedback_instruct_text =
        'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
      return true
    } else if (sumInstructTime > instructTimeThresh * 1000) {
      feedback_instruct_text =
        'Done with instructions. Press enter to continue.'
      return false
    }
  }
}

var start_practice_block = {
  type: 'poldrack-text',
  timing_response: 180000,
  data: {
    trial_id: "practice_intro"
  },
  text: '

Here is a sample trial. Your choice for this trial will not be included in your bonus payment.

Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 1000
};

var practice_block = {
  type: 'poldrack-single-stim',
  data: {
    trial_id: "stim",
    exp_stage: "practice",
    smaller_amount: '20.58',
    sooner_delay: 'today',
    larger_amount: '25.93',
    later_delay: '2 weeks'
  },
  stimulus: [
    "

Please select the option that you would prefer pressing 'q' for left 'p' for right:

$20.58
today

$25.93
2 weeks

"
  ],
  is_html: true,
  choices: [80,81],
  response_ends_trial: true,
};

var start_test_block = {
  type: 'poldrack-text',
  data: {
    trial_id: "test_intro"
  },
  timing_response: 180000,
  text: '

You are now ready to begin the survey.

Remember to indicate your true preferences.

Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 1000
};

var test_block = {
  type: 'poldrack-single-stim',
  stimulus: getStim,
  data: {
    trial_id: "stim",
    exp_stage: "test"
  },
  is_html: true,
  choices: [80,81],
  randomize_order: true,
  response_ends_trial: true,
  on_finish: appendTestData,
};

var end_block = {
  type: 'poldrack-text',
  timing_response: 180000,
  data: {
    trial_id: "end"
  },
  text: '

Thanks for completing this task!

Press enter to continue.

',
  cont_key: [13],
  timing_post_trial: 0
};

//Set up experiment
var discount_titrate_experiment = []
discount_titrate_experiment.push(welcome_block);
discount_titrate_experiment.push(instruction_node);
discount_titrate_experiment.push(start_practice_block);
discount_titrate_experiment.push(practice_block);
discount_titrate_experiment.push(start_test_block);
for(i=0;i<trials.length;i++){
discount_titrate_experiment.push(test_block);
}
discount_titrate_experiment.push(attention_node)
discount_titrate_experiment.push(end_block)<|endoftext|>


<commit_before>/**
 *
 * @param {*number} height
 * @param {*number} width
 */
import Base from '../../Base/Base'
/**
 *
 * Calculating the length of the image element block on the given area
 * @param {*number} height : Defines the height
 * @param {*number} width : Defines the width
 * @param {*number} blockNumber: Defines the number of blocks
 */
function getLength(width, height, blockNumber = 100) {
  const area = Math.round((height * width) / blockNumber)
  return Math.round(Math.sqrt(area))
}
/**
 * Fuction to create image blocks
 * @param {*object} root : root element to which the bock need to be appended
 * @param {*number} height : height of the image
 * @param {*number} width : width of the image
 */
function createBlocks(width, height) {
  const paper = this.getFromEnv('paper')
  const imgGroup = this.getFromStore('gridGroup')
  // Defining the element's property
  const len = getLength(width, height)
  let xCord = 0
  let yCord = 0
  const gridGroup = []
  let mask
  for (let i = 0; i < 10; i += 1) {
    xCord = 0
    gridGroup[i] = []
    for (let j = 0; j < 10; j += 1) {
      // Creating the image element
      const img = paper.image('', 0, 0, width, height)
      // Create the mask
      mask = paper
        .rect(xCord, yCord, xCord + len, yCord + len)
        .attr({
          fill: '#fff',
        })
      img.attr({
        mask,
      })
      // Adding image to the gorup
      imgGroup.add(img)
      // Setting the x coordinate for the next image
      xCord += len
      gridGroup[i].push({
        img,
        mask,
      })
    }
    // Setting the y coordinate for the next image
    yCord += len
  }
  return gridGroup
}

class Grid extends Base {
  /**
   *
   * @param {*object} parent
   * @param {*object} root : Element to which the blocks will be appended
   */
  constructor(parent, parentGroup) {
    super(parent)
    const config = this.getFromEnv('config')
    const paper = this.getFromEnv('paper')

    const gridGroup = paper
      .group()
      .attr({
        id: 'blocks-gallery-gird',
      })
    parentGroup.add(gridGroup)
    this.addToStore('gridGroup', gridGroup)
    const height = config.height
    const width = config.width
    // Initializing all the image elements and adding them to the store
    this.addToStore('gridBlocks', createBlocks.call(this, width, height))
  }
  /**
   * To be used to iterate over all the div and image elements
   * and call the callback
   * @param {*function} callback
   */
  iterate(callback) {
    for (let i = 0; i < 10; i += 1) {
      for (let j = 0; j < 10; j += 1) {
        const block = this.getFromStore('gridBlocks')[i][j]
        callback(block.img, block.div, i, j)
      }
    }
  }
}

export default Grid
<commit_msg>Added block logic<commit_after>/**
 *
 * @param {*number} height
 * @param {*number} width
 */
import Base from '../../Base/Base'
/**
 *
 * Calculating the length of the image element block on the given area
 * @param {*number} height : Defines the height
 * @param {*number} width : Defines the width
 * @param {*number} blockNumber: Defines the number of blocks
 */
function getLength(width, height, length) {
  return {
    length,
    xBlocks: (width / length) % 10 === 0 ? width / length : (width / length) + 1,
    yBlocks: (height / length) % 10 === 0 ? height / length : (height / length) + 1,
  }
  // const area = Math.round((height * width) / blockNumber)
  // return Math.round(Math.sqrt(area))
}
/**
 * Fuction to create image blocks
 * @param {*object} root : root element to which the bock need to be appended
 * @param {*number} height : height of the image
 * @param {*number} width : width of the image
 */
function createBlocks(width, height, length = 60) {
  const paper = this.getFromEnv('paper')
  const imgGroup = this.getFromStore('gridGroup')
  // Defining the element's property
  const block = getLength(width, height, length)
  let xCord = 0
  let yCord = 0
  const gridGroup = []
  let mask
  for (let i = 0; i < block.yBlocks; i += 1) {
    xCord = 0
    gridGroup[i] = []
    for (let j = 0; j < block.xBlocks; j += 1) {
      // Creating the image element
      const img = paper.image('', 0, 0, width, height)
      // Create the mask
      mask = paper
        .rect(xCord, yCord, xCord + block.length, yCord + block.length)
        .attr({
          fill: '#fff',
        })
      img.attr({
        mask,
      })
      const fn = () => (Math.random() * 266) << 0
      const ttmpBox = paper
        .rect(xCord, yCord, block.length, block.length)
        .attr({
          stroke: `rgba(${fn()},${fn()},${fn()},1)`,
          'stroke-width': '1',
          fill: 'rgba(0,0,0,0)',
        })
      // Adding image to the gorup
      imgGroup.add(img)
      imgGroup.add(ttmpBox)
      // Setting the x coordinate for the next image
      xCord += block.length
      gridGroup[i].push({
        img,
        mask,
      })
    }
    // Setting the y coordinate for the next image
    yCord += block.length
  }
  return gridGroup
}

class Grid extends Base {
  /**
   *
   * @param {*object} parent
   * @param {*object} root : Element to which the blocks will be appended
   */
  constructor(parent, parentGroup) {
    super(parent)
    const config = this.getFromEnv('config')
    const paper = this.getFromEnv('paper')

    const gridGroup = paper
      .group()
      .attr({
        id: 'blocks-gallery-gird',
      })
    parentGroup.add(gridGroup)
    this.addToStore('gridGroup', gridGroup)
    const height = config.height
    const width = config.width
    // Initializing all the image elements and adding them to the store
    this.addToStore('gridBlocks', createBlocks.call(this, width, height))
  }
  /**
   * To be used to iterate over all the div and image elements
   * and call the callback
   * @param {*function} callback
   */
  iterate(callback) {
    for (let i = 0; i < 10; i += 1) {
      for (let j = 0; j < 10; j += 1) {
        const block = this.getFromStore('gridBlocks')[i][j]
        callback(block.img, block.div, i, j)
      }
    }
  }
}

export default Grid
<|endoftext|>