<commit_before>/**
 * @author James Cardona
 */
/**
 * Utilities to convert a GeoJSON Feature to an ESRI REST Feature. Both are similar,
 * but have different formats for geometries.
 *
 * This code was written to convert features queried from a Geocommons (www.geocommons.com) dataset
 * to ESRI REST features so they could be put on an ESRI javascript API map. Geocommons only
 * returns features in WGS84 using decimal degrees, so no check is made for the spatial
 * reference of the GeoJSON geometries.
 */
"use strict";

/**
 * Take a GeoJSON geometry type and make an object that has information about
 * what the ESRI geometry should hold. Includes the ESRI geometry type, the name
 * of the member that holds coordinate information, and a function that compares
 * a GeoJSON geometry type and ESRI geometry type to see if they can be safely
 * put together in a single ESRI feature. ESRI features must only have one
 * geometry type, point, line, polygon
 *
 * Input is a GeoJSON geometry type - Point,LineString,Polygon, etc
 *
 */
function geojson2esri() {
	var jcon = {};

  function isCompatible( esriGeomType, gcGeomType ) {
    var compatible = false;
    if (( esriGeomType === "esriGeometryPoint" || esriGeomType === "esriGeometryMultipoint" ) && ( gcGeomType === "Point" || gcGeomType === "MultiPoint" )) {
      compatible = true;
    } else if ( esriGeomType === "esriGeometryPolyline" && ( gcGeomType === "LineString" || gcGeomType === "MultiLineString" )) {
      compatible = true;
    } else if ( esriGeomType === "esriGeometryPolygon" && ( gcGeomType === "Polygon" || gcGeomType === "MultiPolygon" )) {
      compatible = true;
    }
    return compatible;
  }

	jcon.gcGeomTypeToEsriGeomInfo = function( gcType ) {
		var esriType,
      geomHolderId,
      compatibilityFunction;

    if ( gcType === "Point" ) {
      esriType = "esriGeometryPoint";
		} else if ( gcType === "MultiPoint" ) {
			esriType = "esriGeometryMultipoint";
			geomHolderId = "points";
		}	else if ( gcType === "LineString" || gcType === "MultiLineString" ) {
			esriType = "esriGeometryPolyline";
			geomHolderId = "paths";
		}	else if ( gcType === "Polygon" || gcType === "MultiPolygon" ) {
			esriType = "esriGeometryPolygon";
			geomHolderId = "rings";
		}

		return {
      type: esriType,
      geomHolder: geomHolderId
    };
	};

	/**
	 Wraps GeoJSON coordinates in an array if necessary so code can iterate
	 through array of points, rings, or lines and add them to an ESRI geometry

	 * Input is a GeoJSON geometry object. A GeoJSON GeometryCollection is not a
	 * valid input */
	jcon.gcCoordinatesToEsriCoordinates = function( gcGeom ) {
		var i, esriCoords;

		if ( gcGeom.type === "MultiPoint" || gcGeom.type === "MultiLineString" || gcGeom.type === "Polygon" ) {
			esriCoords = gcGeom.coordinates;
    } else if ( gcGeom.type === "Point" || gcGeom.type === "LineString" ) {
			esriCoords = [ gcGeom.coordinates ];
		} else if ( gcGeom.type === "MultiPolygon" ) {
			esriCoords = [];
			for ( i = 0; i < gcGeom.coordinates.length; i++ ) {
				esriCoords.push( gcGeom.coordinates[i] );
			}
		}
		return esriCoords;
	};

	/**
	 * Converts GeoJSON geometry to ESRI geometry. The ESRI geometry is
	 * only allowed to contain one type of geometry, so if the GeoJSON
	 * geometry is a GeometryCollection, then only geometries compatible
	 * with the first geometry type in the collection are added to the ESRI geometry
	 *
	 * Input parameter is a GeoJSON geometry object.
	 */
	jcon.gcGeometryToEsriGeometry = function( gcGeom ) {
		var esriGeometry,
      esriGeomInfo,
      gcGeometriesToConvert,
      i;

		//if geometry collection, get info about first geometry in collection
		if ( gcGeom.type === "GeometryCollection" ) {
			gcGeometriesToConvert = [ gcGeom.geometries.shift() ];
			esriGeomInfo = jcon.gcGeomTypeToEsriGeomInfo( gcGeometriesToConvert[0].type );

			//loop through collection and only add compatible geometries to the array
			//of geometries that will be converted
			for ( i = 0; i < gcGeom.geometries.length; i++ ) {
				if ( isCompatible( esriGeomInfo.type,gcGeom.geometries[i].type ) ) {
					gcGeometriesToConvert.push( gcGeom.geometries[i] );
				}
			}
		} else {
			esriGeomInfo = jcon.gcGeomTypeToEsriGeomInfo( gcGeom.type );
			gcGeometriesToConvert = [ gcGeom ];
		}

		//if a collection contained multiple points, change the ESRI geometry
		//type to MultiPoint
		if ( esriGeomInfo.type === "esriGeometryPoint" && gcGeometriesToConvert.length > 1 ) {
			esriGeomInfo = jcon.gcGeomTypeToEsriGeomInfo( "MultiPoint" );
		}

		//make new empty ESRI geometry object
		esriGeometry = { type: esriGeomInfo.type, spatialReference: { wkid:4326 }};

		//perform conversion
		if ( esriGeomInfo.type === "esriGeometryPoint") {
			if ( gcGeometriesToConvert[0].coordinates.length === 0 ) {
				esriGeometry.x = null;
				esriGeometry.y = null;
			} else {
				esriGeometry.x = gcGeometriesToConvert[0].coordinates[0];
				esriGeometry.y = gcGeometriesToConvert[0].coordinates[1];
			}
		} else {
			var g, coords;
			esriGeometry[ esriGeomInfo.geomHolder ] = [];

			for ( i=0; i < gcGeometriesToConvert.length; i++ ) {
				coords = jcon.gcCoordinatesToEsriCoordinates( gcGeometriesToConvert[i] );
				for ( g = 0; g < coords.length; g++ ) {
					esriGeometry[ esriGeomInfo.geomHolder ].push( coords[g] );
				}
			}
		}
		return esriGeometry;
	};

	/**
	 * Converts GeoJSON feature to ESRI REST Feature.
	 * Input parameter is a GeoJSON Feature object
	 */
	jcon.gcFeatureToEsriFeature = function ( gcFeature ) {
		var esriFeat = null, prop;
		if ( gcFeature ) {
			esriFeat = {};
			if ( gcFeature.geometry ) {
				esriFeat.geometry = jcon.gcGeometryToEsriGeometry( gcFeature.geometry );
			}
			if ( gcFeature.properties) {
				var esriAttribs = {};
				for( prop in gcFeature.properties ) {
					esriAttribs[ prop ] = gcFeature.properties[ prop ];
				}
				esriFeat.attributes = esriAttribs;
			}
		}
		return esriFeat;
	};

	return jcon;
}
<commit_msg>removed tabs<commit_after>/**
 * @author James Cardona
 */

/**
 * Utilities to convert a GeoJSON Feature to an ESRI REST Feature. Both are similar,
 * but have different formats for geometries.
 *
 * This code was written to convert features queried from a Geocommons (www.geocommons.com) dataset
 * to ESRI REST features so they could be put on an ESRI javascript API map. Geocommons only
 * returns features in WGS84 using decimal degrees, so no check is made for the spatial
 * reference of the GeoJSON geometries.
 */
"use strict";

/**
 * Take a GeoJSON geometry type and make an object that has information about
 * what the ESRI geometry should hold. Includes the ESRI geometry type, the name
 * of the member that holds coordinate information, and a function that compares
 * a GeoJSON geometry type and ESRI geometry type to see if they can be safely
 * put together in a single ESRI feature. ESRI features must only have one
 * geometry type, point, line, polygon
 *
 * Input is a GeoJSON geometry type - Point,LineString,Polygon, etc
 *
 */
function geojson2esri() {
  var jcon = {};

  function isCompatible( esriGeomType, gcGeomType ) {
    var compatible = false;
    if (( esriGeomType === "esriGeometryPoint" || esriGeomType === "esriGeometryMultipoint" ) && ( gcGeomType === "Point" || gcGeomType === "MultiPoint" )) {
      compatible = true;
    } else if ( esriGeomType === "esriGeometryPolyline" && ( gcGeomType === "LineString" || gcGeomType === "MultiLineString" )) {
      compatible = true;
    } else if ( esriGeomType === "esriGeometryPolygon" && ( gcGeomType === "Polygon" || gcGeomType === "MultiPolygon" )) {
      compatible = true;
    }
    return compatible;
  }

  jcon.gcGeomTypeToEsriGeomInfo = function( gcType ) {
    var esriType,
      geomHolderId,
      compatibilityFunction;

    if ( gcType === "Point" ) {
      esriType = "esriGeometryPoint";
    } else if ( gcType === "MultiPoint" ) {
      esriType = "esriGeometryMultipoint";
      geomHolderId = "points";
    }  else if ( gcType === "LineString" || gcType === "MultiLineString" ) {
      esriType = "esriGeometryPolyline";
      geomHolderId = "paths";
    }  else if ( gcType === "Polygon" || gcType === "MultiPolygon" ) {
      esriType = "esriGeometryPolygon";
      geomHolderId = "rings";
    }

    return {
      type: esriType,
      geomHolder: geomHolderId
    };
  };

  /**
   Wraps GeoJSON coordinates in an array if necessary so code can iterate
   through array of points, rings, or lines and add them to an ESRI geometry

   * Input is a GeoJSON geometry object. A GeoJSON GeometryCollection is not a
   * valid input */
  jcon.gcCoordinatesToEsriCoordinates = function( gcGeom ) {
    var i, esriCoords;

    if ( gcGeom.type === "MultiPoint" || gcGeom.type === "MultiLineString" || gcGeom.type === "Polygon" ) {
      esriCoords = gcGeom.coordinates;
    } else if ( gcGeom.type === "Point" || gcGeom.type === "LineString" ) {
      esriCoords = [ gcGeom.coordinates ];
    } else if ( gcGeom.type === "MultiPolygon" ) {
      esriCoords = [];
      for ( i = 0; i < gcGeom.coordinates.length; i++ ) {
        esriCoords.push( gcGeom.coordinates[i] );
      }
    }
    return esriCoords;
  };

  /**
   * Converts GeoJSON geometry to ESRI geometry. The ESRI geometry is
   * only allowed to contain one type of geometry, so if the GeoJSON
   * geometry is a GeometryCollection, then only geometries compatible
   * with the first geometry type in the collection are added to the ESRI geometry
   *
   * Input parameter is a GeoJSON geometry object.
   */
  jcon.gcGeometryToEsriGeometry = function( gcGeom ) {
    var esriGeometry,
      esriGeomInfo,
      gcGeometriesToConvert,
      i;

    //if geometry collection, get info about first geometry in collection
    if ( gcGeom.type === "GeometryCollection" ) {
      gcGeometriesToConvert = [ gcGeom.geometries.shift() ];
      esriGeomInfo = jcon.gcGeomTypeToEsriGeomInfo( gcGeometriesToConvert[0].type );

      //loop through collection and only add compatible geometries to the array
      //of geometries that will be converted
      for ( i = 0; i < gcGeom.geometries.length; i++ ) {
        if ( isCompatible( esriGeomInfo.type,gcGeom.geometries[i].type ) ) {
          gcGeometriesToConvert.push( gcGeom.geometries[i] );
        }
      }
    } else {
      esriGeomInfo = jcon.gcGeomTypeToEsriGeomInfo( gcGeom.type );
      gcGeometriesToConvert = [ gcGeom ];
    }

    //if a collection contained multiple points, change the ESRI geometry
    //type to MultiPoint
    if ( esriGeomInfo.type === "esriGeometryPoint" && gcGeometriesToConvert.length > 1 ) {
      esriGeomInfo = jcon.gcGeomTypeToEsriGeomInfo( "MultiPoint" );
    }

    //make new empty ESRI geometry object
    esriGeometry = { type: esriGeomInfo.type, spatialReference: { wkid:4326 }};

    //perform conversion
    if ( esriGeomInfo.type === "esriGeometryPoint") {
      if ( gcGeometriesToConvert[0].coordinates.length === 0 ) {
        esriGeometry.x = null;
        esriGeometry.y = null;
      } else {
        esriGeometry.x = gcGeometriesToConvert[0].coordinates[0];
        esriGeometry.y = gcGeometriesToConvert[0].coordinates[1];
      }
    } else {
      var g, coords;
      esriGeometry[ esriGeomInfo.geomHolder ] = [];

      for ( i=0; i < gcGeometriesToConvert.length; i++ ) {
        coords = jcon.gcCoordinatesToEsriCoordinates( gcGeometriesToConvert[i] );
        for ( g = 0; g < coords.length; g++ ) {
          esriGeometry[ esriGeomInfo.geomHolder ].push( coords[g] );
        }
      }
    }
    return esriGeometry;
  };

  /**
   * Converts GeoJSON feature to ESRI REST Feature.
   * Input parameter is a GeoJSON Feature object
   */
  jcon.gcFeatureToEsriFeature = function ( gcFeature ) {
    var esriFeat = null, prop;
    if ( gcFeature ) {
      esriFeat = {};
      if ( gcFeature.geometry ) {
        esriFeat.geometry = jcon.gcGeometryToEsriGeometry( gcFeature.geometry );
      }
      if ( gcFeature.properties) {
        var esriAttribs = {};
        for( prop in gcFeature.properties ) {
          esriAttribs[ prop ] = gcFeature.properties[ prop ];
        }
        esriFeat.attributes = esriAttribs;
      }
    }
    return esriFeat;
  };

  return jcon;
}
<|endoftext|>


<commit_before>// click handler for delete library button
$(function(){
	$('#btnDeleteLibrary').on('click',function(){
		var yes = confirm('Are you sure you want to delete this library: "'+DATABASE.libraries[libraryIndex].name+'"?');
		if(yes){
			deleteLibrary();
		}
	});

	// hover over menu item
	$('.libraryMenuItem').on('hover',function(e){
		log('hover');
		$(this).children('.menuItemSubtext').addClass('hover');
	}).on('mouseout',function(){
		$(this).children('.menuItemSubtext').removeClass('hover');
	});

	$('#btnLibraries').on('click',function(){
		if($('#dropDownLibraries').css('display') === 'none'){
			$('.dropDown').hide();
			$('.toggleButton').removeClass('pressed');
			$('#dropDownLibraries').slideDown(100);
		}
		else{
			$('#dropDownLibraries').slideUp(100);
		}
	});

	// when losing focus, hide dropdown
	$(document).on('click',function(e){
		// if the click is not on library dropdown and not on the library button and not on one of the library dropdown children, then close the dropdown
		if(e.target !== document.getElementById('dropDownLibraries') && e.target !== $('#btnLibraries').get(0) && $('#dropDownLibraries').has(e.target).length === 0) {
			$('#dropDownLibraries').hide();
			$('#btnLibraries').removeClass('pressed');
		}
	});
});

var deleteLibrary = function(index){
		// push library to history to restore if needed
		pushHistoryItem({
			restoreType:'deleted_library',
			index:libraryIndex,
			data:DATABASE.libraries[libraryIndex]
		});
		log('pushed ' + history[history.length-1] + ' to history.');

		// notify user of deletion
		notify('Deleted user library "' + DATABASE.libraries[libraryIndex].name + '".','warning');

		DATABASE.libraries.splice(libraryIndex,1); // remove the library from the local copy of the db
		if(DATABASE.libraries.length < 1){
			// if left with no libraries, create a default one at index 0 before switching libraries
			DATABASE.libraries.splice(0,0,{
				name:'Library1',
				date_created:generateTimestamp(),
				collections:[{
					name:'My Collection',
					date_created:generateTimestamp(),
					items:[]
			}]});
		}

		// switch the library to one that exists, either backwards 1 or just library 0.
		popLibrariesDropdown();
		changeLibrary(libraryIndex > 0 ? libraryIndex - 1 : 0);

		applyChanges(); // apply changes to perma db

}

var newLibrary = function(libraryName){
	if(libraryName !== '' && libraryName.length > 1){
		DATABASE.libraries.push({
			name:libraryName,
			date_created:generateTimestamp(),
			collections:[{name:'Default',date_created:generateTimestamp(),items:[]}]
		});

		applyChanges();
		popLibrariesDropdown();
		changeLibrary(DATABASE.libraries.length - 1);
		notify('New library "' + libraryName + '" created.');
	}
	else{
		log('Name too short!');
	}
};

var popLibrariesDropdown = function(){
	var dropdown = $('#dropDownLibraries');
	dropdown.children('.dropDownMenuItem').remove();
	$('#blueCircle').hide() // hide the blue dot until it finds a home
	$(DATABASE.libraries).each(function(i,item){
		dropdown.append('
'+item.name+''+item.collections.length+' collections
');
		if(i === libraryIndex){
			dropdown.children('.libraryMenuItem').last().append('');
		}
	});
	dropdown.append('
New Library
');

	$('.btnNewLibrary').on('click',function(){
		setTimeout(function(){
		var result = prompt('Enter a name for the library.');
		if(result !== null){
			newLibrary(result);
		}
		},100)
	});

	$('.libraryMenuItem').not('.btnNewLibrary').on('click',function(){
		var newIndex = parseInt($(this).attr('name').replace('library',''));
		$('#blueCircle').remove();
		$(this).append('');
		setTimeout(function(){
			changeLibrary(newIndex);
			$('#dropDownLibraries').hide(); // hide the dropdown
		},50);
	});
};

var changeLibrary = function(index){

	if(index > -1 && index !== libraryIndex){
		libraryIndex = index;
		collectionIndex = 0;

			// change the global variables to reflect the change
		collections = DATABASE.libraries[index].collections;
		imageDB = collections[collectionIndex];

		lastVisited.library = libraryIndex;
		lastVisited.collection = collectionIndex;
		setLastVisited();

		popLibrariesDropdown();
		popDropdown();
		changeCollection(0);
		List();

		//$('#btnLibraries').html(DATABASE.libraries[libraryIndex].name);
		notify('Switched to library "' + DATABASE.libraries[libraryIndex].name + '".');
	}
	else{
		if(index < 0){
			alert('selected index < 0');
		}
	}

}<commit_msg>fixed naming and behavior of libraries button and dropdown<commit_after>// click handler for delete library button
$(function(){
	$('#btnDeleteLibrary').on('click',function(){
		var yes = confirm('Are you sure you want to delete this library: "'+DATABASE.libraries[libraryIndex].name+'"?');
		if(yes){
			deleteLibrary();
		}
	});

	// hover over menu item
	$('.libraryMenuItem').on('hover',function(e){
		log('hover');
		$(this).children('.menuItemSubtext').addClass('hover');
	}).on('mouseout',function(){
		$(this).children('.menuItemSubtext').removeClass('hover');
	});

	$('#btnLibraries').on('click',function(){
		if($('#dropDownLibraries').css('display') === 'none'){
			$('.dropDown').hide();
			$('.toggleButton').removeClass('pressed');
			$('#dropDownLibraries').slideDown(100);
		}
		else{
			$('#dropDownLibraries').slideUp(100);
		}
	});

	// when losing focus, hide dropdown
	$(document).on('click',function(e){
		// if the click is not on library dropdown and not on the library button and not on one of the library dropdown children, then close the dropdown
		if(e.target !== document.getElementById('dropDownLibraries') && e.target !== $('#btnLibraries').get(0) && $('#dropDownLibraries').has(e.target).length === 0) {
			$('#dropDownLibraries').hide();
			$('#btnLibraries').removeClass('pressed');
		}
	});
});

var deleteLibrary = function(index){
		// push library to history to restore if needed
		pushHistoryItem({
			restoreType:'deleted_library',
			index:libraryIndex,
			data:DATABASE.libraries[libraryIndex]
		});
		log('pushed ' + history[history.length-1] + ' to history.');

		// notify user of deletion
		notify('Deleted user library "' + DATABASE.libraries[libraryIndex].name + '".','warning');

		DATABASE.libraries.splice(libraryIndex,1); // remove the library from the local copy of the db
		if(DATABASE.libraries.length < 1){
			// if left with no libraries, create a default one at index 0 before switching libraries
			DATABASE.libraries.splice(0,0,{
				name:'Library1',
				date_created:generateTimestamp(),
				collections:[{
					name:'My Collection',
					date_created:generateTimestamp(),
					items:[]
			}]});
		}

		// switch the library to one that exists, either backwards 1 or just library 0.
		popLibrariesDropdown();
		changeLibrary(libraryIndex > 0 ? libraryIndex - 1 : 0);

		applyChanges(); // apply changes to perma db

}

var newLibrary = function(libraryName){
	if(libraryName !== '' && libraryName.length > 1){
		DATABASE.libraries.push({
			name:libraryName,
			date_created:generateTimestamp(),
			collections:[{name:'Default',date_created:generateTimestamp(),items:[]}]
		});

		applyChanges();
		popLibrariesDropdown();
		changeLibrary(DATABASE.libraries.length - 1);
		notify('New library "' + libraryName + '" created.');
	}
	else{
		log('Name too short!');
	}
};

var popLibrariesDropdown = function(){
	var dropdown = $('#dropDownLibraries');
	dropdown.children('.dropDownMenuItem').remove();
	$('#blueCircle').hide() // hide the blue dot until it finds a home
	$(DATABASE.libraries).each(function(i,item){
		dropdown.append('
'+item.name+''+item.collections.length+' collections
');
		if(i === libraryIndex){
			dropdown.children('.dropDownMenuItem').last().append('');
		}
	});
	dropdown.append('
New Library
');

	$('.btnNewLibrary').on('click',function(){
		setTimeout(function(){
		var result = prompt('Enter a name for the library.');
		if(result !== null){
			newLibrary(result);
		}
		},100)
	});

	$('#dropDownLibraries .dropDownMenuItem').not('.btnNewLibrary').on('click',function(){4
		log('library menu item clicked');
		var newIndex = parseInt($(this).attr('name').replace('library',''));
		$('#blueCircle').remove();
		$(this).append('');
		setTimeout(function(){
			changeLibrary(newIndex);
			$('#btnLibraries').click(); // hide the dropdown
		},50);
	});
};

var changeLibrary = function(index){

	if(index > -1 && index !== libraryIndex){
		libraryIndex = index;
		collectionIndex = 0;

			// change the global variables to reflect the change
		collections = DATABASE.libraries[index].collections;
		imageDB = collections[collectionIndex];

		lastVisited.library = libraryIndex;
		lastVisited.collection = collectionIndex;
		setLastVisited();

		popLibrariesDropdown();
		popDropdown();
		changeCollection(0);
		List();

		//$('#btnLibraries').html(DATABASE.libraries[libraryIndex].name);
		notify('Switched to library "' + DATABASE.libraries[libraryIndex].name + '".');
	}
	else{
		if(index < 0){
			alert('selected index < 0');
		}
	}

}<|endoftext|>


<commit_before>/*
 *  This file is part of SYZOJ.
 *
 *  Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
 *
 *  SYZOJ is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  SYZOJ is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public
 *  License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

Array.prototype.forEachAsync = Array.prototype.mapAsync = async function (fn) {
  return Promise.all(this.map(fn));
};

Array.prototype.filterAsync = async function (fn) {
  let a = await this.mapAsync(fn);
  return this.filter((x, i) => a[i]);
};

global.ErrorMessage = class ErrorMessage {
  constructor(message, nextUrls, details) {
    this.message = message;
    this.nextUrls = nextUrls || {};
    this.details = details;
  }
};

let Promise = require('bluebird');
let path = require('path');
let fs = Promise.promisifyAll(require('fs-extra'));
let util = require('util');
let renderer = require('moemark-renderer');
let moment = require('moment');
let url = require('url');
let querystring = require('querystring');
let pygmentize = require('pygmentize-bundled-cached');
let gravatar = require('gravatar');
let filesize = require('file-size');
let AsyncLock = require('async-lock');

function escapeHTML(s) {
  // Code from http://stackoverflow.com/questions/5251520/how-do-i-escape-some-html-in-javascript/5251551
  return s.replace(/[^0-9A-Za-z ]/g, (c) => {
    return "&#" + c.charCodeAt(0) + ";";
  });
}

function highlightPygmentize(code, lang, cb) {
  pygmentize({
    lang: lang,
    format: 'html',
    options: {
      nowrap: true,
      classprefix: 'pl-'
    }
  }, code, (err, res) => {
    if (err || res.toString() === 'undefined') {
      cb(escapeHTML(code));
    } else {
      cb(res);
    }
  });
}

renderer.config.highlight = highlightPygmentize;

module.exports = {
  resolvePath(s) {
    let a = Array.from(arguments);
    a.unshift(__dirname);
    return path.resolve.apply(null, a);
  },
  markdown(obj, keys, noReplaceUI) {
    let XSS = require('xss');
    let CSSFilter = require('cssfilter');
    let whiteList = Object.assign({}, require('xss/lib/default').whiteList);
    delete whiteList.audio;
    delete whiteList.video;
    for (let tag in whiteList) whiteList[tag] = whiteList[tag].concat(['id', 'style', 'class']);
    let xss = new XSS.FilterXSS({
      css: {
        whiteList: Object.assign({}, require('cssfilter/lib/default').whiteList, {
          'vertical-align': true,
          top: true,
          bottom: true,
          left: true,
          right: true,
          "white-space": true
        })
      },
      whiteList: whiteList,
      stripIgnoreTag: true
    });
    let replaceXSS = s => {
      s = xss.process(s);
      if (s) {
        s = `<div style="position: relative; overflow: hidden; ">${s}</div>`;
      }
      return s;
    };
    let replaceUI = s => {
      if (noReplaceUI) return s;

      s = s.split('
').join('

').split('
').join('

')
           .split('
').join('
')
           .split('
').join('
').split('
').join('
');

      let cheerio = require('cheerio');
      let $ = cheerio.load('');
      let body = $('body');
      body.html(s);

      let a = $('img:only-child');
      for (let img of Array.from(a)) {
        if (!img.prev && !img.next) {
          $(img).css('display', 'block');
          $(img).css('margin', '0 auto');
        }
      }

      return body.html();
    };
    return new Promise((resolve, reject) => {
      if (!keys) {
        if (!obj || !obj.trim()) resolve("");
        else renderer(obj, { mathjaxUseHtml: true }, s => {
            resolve(replaceUI(replaceXSS(s)));
        });
      } else {
        let res = obj, cnt = keys.length;
        for (let key of keys) {
          renderer(res[key], { mathjaxUseHtml: true }, (s) => {
            res[key] = replaceUI(replaceXSS(s));
            if (!--cnt) resolve(res);
          });
        }
      }
    });
  },
  formatDate(ts, format) {
    let m = moment(ts * 1000);
    m.locale('eu');
    return m.format(format || 'L H:mm:ss');
  },
  formatTime(x) {
    let sgn = x < 0 ? '-' : '';
    x = Math.abs(x);
    function toStringWithPad(x) {
      x = parseInt(x);
      if (x < 10) return '0' + x.toString();
      else return x.toString();
    }
    return sgn + util.format('%s:%s:%s', toStringWithPad(x / 3600), toStringWithPad(x / 60 % 60), toStringWithPad(x % 60));
  },
  formatSize(x) {
    let res = filesize(x, { fixed: 1 }).calculate();
    if (res.result === parseInt(res.result)) res.fixed = res.result.toString();
    if (res.suffix.startsWith('Byte')) res.suffix = 'B';
    else res.suffix = res.suffix.replace('iB', '');
    return res.fixed + ' ' + res.suffix;
  },
  parseDate(s) {
    return parseInt(+new Date(s) / 1000);
  },
  getCurrentDate() {
    return parseInt(+new Date / 1000);
  },
  makeUrl(req_params, form) {
    let res = '';
    if (!req_params) res = '/';
    else if (req_params.originalUrl) {
      let u = url.parse(req_params.originalUrl);
      res = u.pathname;
    } else {
      if (!Array.isArray(req_params)) req_params = [req_params];
      for (let param of req_params) res += '/' + param;
    }
    let encoded = querystring.encode(form);
    if (encoded) res += '?' + encoded;
    return res;
  },
  escapeHTML: escapeHTML,
  highlight(code, lang) {
    return new Promise((resolve, reject) => {
      highlightPygmentize(code, lang, res => {
        resolve(res);
      });
    });
  },
  gravatar(email, size) {
    return gravatar.url(email, { s: size, d: 'mm' }).replace('www', 'cn');
  },
  async parseTestdata(dir, submitAnswer) {
    if (!await syzoj.utils.isDir(dir)) return null;

    try {
      // Get list of *files*
      let list = await (await fs.readdirAsync(dir)).filterAsync(async x => await syzoj.utils.isFile(path.join(dir, x)));

      let res = [];
      if (!list.includes('data_rule.txt')) {
        res[0] = {};
        res[0].cases = [];
        for (let file of list) {
          let parsedName = path.parse(file);
          if (parsedName.ext === '.in') {
            if (list.includes(`${parsedName.name}.out`)) {
              let o = {
                input: file,
                output: `${parsedName.name}.out`
              };
              if (submitAnswer) o.answer = `${parsedName.name}.out`;
              res[0].cases.push(o);
            }

            if (list.includes(`${parsedName.name}.ans`)) {
              let o = {
                input: file,
                output: `${parsedName.name}.ans`
              };
              if (submitAnswer) o.answer = `${parsedName.name}.out`;
              res[0].cases.push(o);
            }
          }
        }

        res[0].type = 'sum';
        res[0].score = 100;
        res[0].cases.sort((a, b) => {
          function getLastInteger(s) {
            let re = /(\d+)\D*$/;
            let x = re.exec(s);
            if (x) return parseInt(x[1]);
            else return -1;
          }

          return getLastInteger(a.input) - getLastInteger(b.input);
        });
      } else {
        let lines = (await fs.readFileAsync(path.join(dir, 'data_rule.txt'))).toString().split('\r').join('').split('\n').filter(x => x.length !== 0);

        let input, output, answer;
        if (submitAnswer) {
          if (lines.length < 4) throw '无效的数据配置文件（data_rule.txt）。';
          input = lines[lines.length - 3];
          output = lines[lines.length - 2];
          answer = lines[lines.length - 1];
        } else {
          if (lines.length < 3) throw '无效的数据配置文件（data_rule.txt）。';
          input = lines[lines.length - 2];
          output = lines[lines.length - 1];
        }

        for (let s = 0; s < lines.length - (submitAnswer ? 3 : 2); ++s) {
          res[s] = {};
          res[s].cases = [];
          let numbers = lines[s].split(' ').filter(x => x);
          if (numbers[0].includes(':')) {
            let tokens = numbers[0].split(':');
            res[s].type = tokens[0] || 'sum';
            res[s].score = parseFloat(tokens[1]) || (100 / (lines.length - 2));
            numbers.shift();
          } else {
            res[s].type = 'sum';
            res[s].score = 100;
          }
          for (let i of numbers) {
            let testcase = {
              input: input.replace('#', i),
              output: output.replace('#', i)
            };

            if (submitAnswer) testcase.answer = answer.replace('#', i);

            if (testcase.input !== '-' && !list.includes(testcase.input)) throw `找不到文件 ${testcase.input}`;
            if (testcase.output !== '-' && !list.includes(testcase.output)) throw `找不到文件 ${testcase.output}`;
            res[s].cases.push(testcase);
          }
        }

        res = res.filter(x => x.cases && x.cases.length !== 0);
      }

      res.spj = list.some(s => s.startsWith('spj_'));
      return res;
    } catch (e) {
      console.log(e);
      return { error: e };
    }
  },
  ansiToHTML(s) {
    let Convert = require('ansi-to-html');
    let convert = new Convert({ escapeXML: true });
    return convert.toHtml(s);
  },
  paginate(count, currPage, perPage) {
    currPage = parseInt(currPage);
    if (!currPage || currPage < 1) currPage = 1;

    let pageCnt = Math.ceil(count / perPage);
    if (currPage > pageCnt) currPage = pageCnt;

    return {
      currPage: currPage,
      perPage: perPage,
      pageCnt: pageCnt,
      toSQL: () => {
        if (!pageCnt) return '';
        else return ` LIMIT ${(currPage - 1) * perPage},${perPage}`
      }
    };
  },
  removeTitleTag(s) {
    return s.replace(/「[\S\s]+?」/, '');
  },
  md5(data) {
    let crypto = require('crypto');
    let md5 = crypto.createHash('md5');
    md5.update(data);
    return md5.digest('hex');
  },
  isValidUsername(s) {
    return /^[a-zA-Z0-9\-\_]+$/.test(s);
  },
  locks: [],
  lock(key, cb) {
    let s = JSON.stringify(key);
    if (!this.locks[s]) this.locks[s] = new AsyncLock();
    return this.locks[s].acquire(s, cb);
  },
  encrypt(buffer, password) {
    if (typeof buffer === 'string') buffer = Buffer.from(buffer);
    let crypto = require('crypto');
    let cipher = crypto.createCipher('aes-256-ctr', password);
    return Buffer.concat([cipher.update(buffer), cipher.final()]);
  },
  decrypt(buffer, password) {
    let crypto = require('crypto');
    let decipher = crypto.createDecipher('aes-256-ctr', password);
    return Buffer.concat([decipher.update(buffer), decipher.final()]);
  },
  async isFile(path) {
    try {
      return (await fs.statAsync(path)).isFile();
    } catch (e) {
      return false;
    }
  },
  async isDir(path) {
    try {
      return (await fs.statAsync(path)).isDirectory();
    } catch (e) {
      return false;
    }
  }
};
<commit_msg>Disallow id attrubute in XSS<commit_after>/*
 *  This file is part of SYZOJ.
 *
 *  Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
 *
 *  SYZOJ is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  SYZOJ is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public
 *  License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
 */

'use strict';

Array.prototype.forEachAsync = Array.prototype.mapAsync = async function (fn) {
  return Promise.all(this.map(fn));
};

Array.prototype.filterAsync = async function (fn) {
  let a = await this.mapAsync(fn);
  return this.filter((x, i) => a[i]);
};

global.ErrorMessage = class ErrorMessage {
  constructor(message, nextUrls, details) {
    this.message = message;
    this.nextUrls = nextUrls || {};
    this.details = details;
  }
};

let Promise = require('bluebird');
let path = require('path');
let fs = Promise.promisifyAll(require('fs-extra'));
let util = require('util');
let renderer = require('moemark-renderer');
let moment = require('moment');
let url = require('url');
let querystring = require('querystring');
let pygmentize = require('pygmentize-bundled-cached');
let gravatar = require('gravatar');
let filesize = require('file-size');
let AsyncLock = require('async-lock');

function escapeHTML(s) {
  // Code from http://stackoverflow.com/questions/5251520/how-do-i-escape-some-html-in-javascript/5251551
  return s.replace(/[^0-9A-Za-z ]/g, (c) => {
    return "&#" + c.charCodeAt(0) + ";";
  });
}

function highlightPygmentize(code, lang, cb) {
  pygmentize({
    lang: lang,
    format: 'html',
    options: {
      nowrap: true,
      classprefix: 'pl-'
    }
  }, code, (err, res) => {
    if (err || res.toString() === 'undefined') {
      cb(escapeHTML(code));
    } else {
      cb(res);
    }
  });
}

renderer.config.highlight = highlightPygmentize;

module.exports = {
  resolvePath(s) {
    let a = Array.from(arguments);
    a.unshift(__dirname);
    return path.resolve.apply(null, a);
  },
  markdown(obj, keys, noReplaceUI) {
    let XSS = require('xss');
    let CSSFilter = require('cssfilter');
    let whiteList = Object.assign({}, require('xss/lib/default').whiteList);
    delete whiteList.audio;
    delete whiteList.video;
    for (let tag in whiteList) whiteList[tag] = whiteList[tag].concat(['style', 'class']);
    let xss = new XSS.FilterXSS({
      css: {
        whiteList: Object.assign({}, require('cssfilter/lib/default').whiteList, {
          'vertical-align': true,
          top: true,
          bottom: true,
          left: true,
          right: true,
          "white-space": true
        })
      },
      whiteList: whiteList,
      stripIgnoreTag: true
    });
    let replaceXSS = s => {
      s = xss.process(s);
      if (s) {
        s = `<div style="position: relative; overflow: hidden; ">${s}</div>`;
      }
      return s;
    };
    let replaceUI = s => {
      if (noReplaceUI) return s;

      s = s.split('
').join('

').split('
').join('

')
           .split('
').join('
')
           .split('
').join('
').split('
').join('
');

      let cheerio = require('cheerio');
      let $ = cheerio.load('');
      let body = $('body');
      body.html(s);

      let a = $('img:only-child');
      for (let img of Array.from(a)) {
        if (!img.prev && !img.next) {
          $(img).css('display', 'block');
          $(img).css('margin', '0 auto');
        }
      }

      return body.html();
    };
    return new Promise((resolve, reject) => {
      if (!keys) {
        if (!obj || !obj.trim()) resolve("");
        else renderer(obj, { mathjaxUseHtml: true }, s => {
            resolve(replaceUI(replaceXSS(s)));
        });
      } else {
        let res = obj, cnt = keys.length;
        for (let key of keys) {
          renderer(res[key], { mathjaxUseHtml: true }, (s) => {
            res[key] = replaceUI(replaceXSS(s));
            if (!--cnt) resolve(res);
          });
        }
      }
    });
  },
  formatDate(ts, format) {
    let m = moment(ts * 1000);
    m.locale('eu');
    return m.format(format || 'L H:mm:ss');
  },
  formatTime(x) {
    let sgn = x < 0 ? '-' : '';
    x = Math.abs(x);
    function toStringWithPad(x) {
      x = parseInt(x);
      if (x < 10) return '0' + x.toString();
      else return x.toString();
    }
    return sgn + util.format('%s:%s:%s', toStringWithPad(x / 3600), toStringWithPad(x / 60 % 60), toStringWithPad(x % 60));
  },
  formatSize(x) {
    let res = filesize(x, { fixed: 1 }).calculate();
    if (res.result === parseInt(res.result)) res.fixed = res.result.toString();
    if (res.suffix.startsWith('Byte')) res.suffix = 'B';
    else res.suffix = res.suffix.replace('iB', '');
    return res.fixed + ' ' + res.suffix;
  },
  parseDate(s) {
    return parseInt(+new Date(s) / 1000);
  },
  getCurrentDate() {
    return parseInt(+new Date / 1000);
  },
  makeUrl(req_params, form) {
    let res = '';
    if (!req_params) res = '/';
    else if (req_params.originalUrl) {
      let u = url.parse(req_params.originalUrl);
      res = u.pathname;
    } else {
      if (!Array.isArray(req_params)) req_params = [req_params];
      for (let param of req_params) res += '/' + param;
    }
    let encoded = querystring.encode(form);
    if (encoded) res += '?' + encoded;
    return res;
  },
  escapeHTML: escapeHTML,
  highlight(code, lang) {
    return new Promise((resolve, reject) => {
      highlightPygmentize(code, lang, res => {
        resolve(res);
      });
    });
  },
  gravatar(email, size) {
    return gravatar.url(email, { s: size, d: 'mm' }).replace('www', 'cn');
  },
  async parseTestdata(dir, submitAnswer) {
    if (!await syzoj.utils.isDir(dir)) return null;

    try {
      // Get list of *files*
      let list = await (await fs.readdirAsync(dir)).filterAsync(async x => await syzoj.utils.isFile(path.join(dir, x)));

      let res = [];
      if (!list.includes('data_rule.txt')) {
        res[0] = {};
        res[0].cases = [];
        for (let file of list) {
          let parsedName = path.parse(file);
          if (parsedName.ext === '.in') {
            if (list.includes(`${parsedName.name}.out`)) {
              let o = {
                input: file,
                output: `${parsedName.name}.out`
              };
              if (submitAnswer) o.answer = `${parsedName.name}.out`;
              res[0].cases.push(o);
            }

            if (list.includes(`${parsedName.name}.ans`)) {
              let o = {
                input: file,
                output: `${parsedName.name}.ans`
              };
              if (submitAnswer) o.answer = `${parsedName.name}.out`;
              res[0].cases.push(o);
            }
          }
        }

        res[0].type = 'sum';
        res[0].score = 100;
        res[0].cases.sort((a, b) => {
          function getLastInteger(s) {
            let re = /(\d+)\D*$/;
            let x = re.exec(s);
            if (x) return parseInt(x[1]);
            else return -1;
          }

          return getLastInteger(a.input) - getLastInteger(b.input);
        });
      } else {
        let lines = (await fs.readFileAsync(path.join(dir, 'data_rule.txt'))).toString().split('\r').join('').split('\n').filter(x => x.length !== 0);

        let input, output, answer;
        if (submitAnswer) {
          if (lines.length < 4) throw '无效的数据配置文件（data_rule.txt）。';
          input = lines[lines.length - 3];
          output = lines[lines.length - 2];
          answer = lines[lines.length - 1];
        } else {
          if (lines.length < 3) throw '无效的数据配置文件（data_rule.txt）。';
          input = lines[lines.length - 2];
          output = lines[lines.length - 1];
        }

        for (let s = 0; s < lines.length - (submitAnswer ? 3 : 2); ++s) {
          res[s] = {};
          res[s].cases = [];
          let numbers = lines[s].split(' ').filter(x => x);
          if (numbers[0].includes(':')) {
            let tokens = numbers[0].split(':');
            res[s].type = tokens[0] || 'sum';
            res[s].score = parseFloat(tokens[1]) || (100 / (lines.length - 2));
            numbers.shift();
          } else {
            res[s].type = 'sum';
            res[s].score = 100;
          }
          for (let i of numbers) {
            let testcase = {
              input: input.replace('#', i),
              output: output.replace('#', i)
            };

            if (submitAnswer) testcase.answer = answer.replace('#', i);

            if (testcase.input !== '-' && !list.includes(testcase.input)) throw `找不到文件 ${testcase.input}`;
            if (testcase.output !== '-' && !list.includes(testcase.output)) throw `找不到文件 ${testcase.output}`;
            res[s].cases.push(testcase);
          }
        }

        res = res.filter(x => x.cases && x.cases.length !== 0);
      }

      res.spj = list.some(s => s.startsWith('spj_'));
      return res;
    } catch (e) {
      console.log(e);
      return { error: e };
    }
  },
  ansiToHTML(s) {
    let Convert = require('ansi-to-html');
    let convert = new Convert({ escapeXML: true });
    return convert.toHtml(s);
  },
  paginate(count, currPage, perPage) {
    currPage = parseInt(currPage);
    if (!currPage || currPage < 1) currPage = 1;

    let pageCnt = Math.ceil(count / perPage);
    if (currPage > pageCnt) currPage = pageCnt;

    return {
      currPage: currPage,
      perPage: perPage,
      pageCnt: pageCnt,
      toSQL: () => {
        if (!pageCnt) return '';
        else return ` LIMIT ${(currPage - 1) * perPage},${perPage}`
      }
    };
  },
  removeTitleTag(s) {
    return s.replace(/「[\S\s]+?」/, '');
  },
  md5(data) {
    let crypto = require('crypto');
    let md5 = crypto.createHash('md5');
    md5.update(data);
    return md5.digest('hex');
  },
  isValidUsername(s) {
    return /^[a-zA-Z0-9\-\_]+$/.test(s);
  },
  locks: [],
  lock(key, cb) {
    let s = JSON.stringify(key);
    if (!this.locks[s]) this.locks[s] = new AsyncLock();
    return this.locks[s].acquire(s, cb);
  },
  encrypt(buffer, password) {
    if (typeof buffer === 'string') buffer = Buffer.from(buffer);
    let crypto = require('crypto');
    let cipher = crypto.createCipher('aes-256-ctr', password);
    return Buffer.concat([cipher.update(buffer), cipher.final()]);
  },
  decrypt(buffer, password) {
    let crypto = require('crypto');
    let decipher = crypto.createDecipher('aes-256-ctr', password);
    return Buffer.concat([decipher.update(buffer), decipher.final()]);
  },
  async isFile(path) {
    try {
      return (await fs.statAsync(path)).isFile();
    } catch (e) {
      return false;
    }
  },
  async isDir(path) {
    try {
      return (await fs.statAsync(path)).isDirectory();
    } catch (e) {
      return false;
    }
  }
};
<|endoftext|>


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

const Console = console;

function getRGBA (color) {
  const chromaColor = chroma(color);
  const rgb = chromaColor.rgb();
  return {
    r: rgb[0] / 255,
    g: rgb[1] / 255,
    b: rgb[2] / 255,
    a: chromaColor.alpha()
  };
}

class GlobalStyles {
  constructor () {
    this.styles = {
      colorText: 'rgb(214, 214, 214)',
      colorTextDisabled: 'rgb(129, 129, 129)',
      colorTraffic: {
        normal: 'rgb(186, 213, 237)',
        normalDonut: 'rgb(91, 91, 91)',
        warning: 'rgb(268, 185, 73)',
        danger: 'rgb(184, 36, 36)'
      },
      colorNormalDimmed: 'rgb(101, 117, 128)',
      colorBackgroundDark: 'rgb(35, 35, 35)',
      colorLabelBorder: 'rgb(16, 17, 18)',
      colorLabelText: 'rgb(0, 0, 0)',
      colorDonutInternalColor: 'rgb(35, 35, 35)',
      colorDonutInternalColorHighlighted: 'rgb(255, 255, 255)',
      colorConnectionLine: 'rgb(91, 91, 91)',
      colorPageBackground: 'rgb(45, 45, 45)',
      colorPageBackgroundTransparent: 'rgba(45, 45, 45, 0)',
      colorBorderLines: 'rgb(137, 137, 137)',
      colorArcBackground: 'rgb(60, 60, 60)'
    };

    this.updateComputedStyles();
  }

  getColorTraffic (key, highlighted) {
    const color = !highlighted ? this.styles.colorTraffic[key] : this.styles.colorTrafficHighlighted[key];
    if (!color) {
      Console.warn(`Attempted to get a color for key '${key}', but does not exist. Returned color for key 'normal' instead`);
      return !highlighted ? this.styles.colorTraffic.normal : this.styles.colorTrafficHighlighted.normal;
    }
    return color;
  }

  getColorTrafficRGBA (key, highlighted) {
    const color = highlighted ? this.rgba.colorTrafficHighlighted[key] : this.rgba.colorTraffic[key];
    if (!color) {
      Console.warn(`Attempted to get a computed color for key '${key}', but does not exist. Returned color for key 'normal' instead`);
      return highlighted ? this.rgba.colorTrafficHighlighted.normal : this.rgba.colorTraffic.normal;
    }
    return color;
  }

  getColorSeverityRGBA (key) {
    return [
      this.rgba.colorTraffic.normal,
      this.rgba.colorTraffic.warning,
      this.rgba.colorTraffic.danger
    ][key];
  }

  updateStyles (styles) {
    _.merge(this.styles, styles);
    this.updateComputedStyles(styles);
  }

  updateComputedStyles (styles) {
    this.styles.colorTrafficHighlighted = _.reduce(this.styles.colorTraffic, (acc, value, key) => {
      if (styles && styles.colorTrafficHighlighted && styles.colorTrafficHighlighted[key]) {
        acc[key] = styles.colorTrafficHighlighted[key];
      } else {
        acc[key] = chroma(value).brighten(3).css();
      }

      return acc;
    }, {});

    this.rgba = {
      colorArcBackground: getRGBA(this.styles.colorArcBackground),
      colorConnectionLine: getRGBA(this.styles.colorConnectionLine),
      colorDonutInternalColor: getRGBA(this.styles.colorDonutInternalColor),
      colorPageBackground: getRGBA(this.styles.colorPageBackground),
      colorTraffic: _.reduce(this.styles.colorTraffic, (acc, value, key) => {
        acc[key] = getRGBA(value);
        return acc;
      }, {}),
      colorTrafficHighlighted: _.reduce(this.styles.colorTrafficHighlighted, (acc, value, key) => {
        acc[key] = getRGBA(value);
        return acc;
      }, {}),
    };
  }
}

export default new GlobalStyles();
<commit_msg>Remove comment<commit_after>/**
 *
 *  Copyright 2016 Netflix, Inc.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 *
 */
import _ from 'lodash';
import chroma from 'chroma-js';

const Console = console;

function getRGBA (color) {
  const chromaColor = chroma(color);
  const rgb = chromaColor.rgb();
  return {
    r: rgb[0] / 255,
    g: rgb[1] / 255,
    b: rgb[2] / 255,
    a: chromaColor.alpha()
  };
}

class GlobalStyles {
  constructor () {
    this.styles = {
      colorText: 'rgb(214, 214, 214)',
      colorTextDisabled: 'rgb(129, 129, 129)',
      colorTraffic: {
        normal: 'rgb(186, 213, 237)',
        normalDonut: 'rgb(91, 91, 91)',
        warning: 'rgb(268, 185, 73)',
        danger: 'rgb(184, 36, 36)'
      },
      colorNormalDimmed: 'rgb(101, 117, 128)',
      colorBackgroundDark: 'rgb(35, 35, 35)',
      colorLabelBorder: 'rgb(16, 17, 18)',
      colorLabelText: 'rgb(0, 0, 0)',
      colorDonutInternalColor: 'rgb(35, 35, 35)',
      colorDonutInternalColorHighlighted: 'rgb(255, 255, 255)',
      colorConnectionLine: 'rgb(91, 91, 91)',
      colorPageBackground: 'rgb(45, 45, 45)',
      colorPageBackgroundTransparent: 'rgba(45, 45, 45, 0)',
      colorBorderLines: 'rgb(137, 137, 137)',
      colorArcBackground: 'rgb(60, 60, 60)'
    };

    this.updateComputedStyles();
  }

  getColorTraffic (key, highlighted) {
    const color = !highlighted ? this.styles.colorTraffic[key] : this.styles.colorTrafficHighlighted[key];
    if (!color) {
      Console.warn(`Attempted to get a color for key '${key}', but does not exist. Returned color for key 'normal' instead`);
      return !highlighted ? this.styles.colorTraffic.normal : this.styles.colorTrafficHighlighted.normal;
    }
    return color;
  }

  getColorTrafficRGBA (key, highlighted) {
    const color = highlighted ? this.rgba.colorTrafficHighlighted[key] : this.rgba.colorTraffic[key];
    if (!color) {
      Console.warn(`Attempted to get a computed color for key '${key}', but does not exist. Returned color for key 'normal' instead`);
      return highlighted ? this.rgba.colorTrafficHighlighted.normal : this.rgba.colorTraffic.normal;
    }
    return color;
  }

  getColorSeverityRGBA (key) {
    return [
      this.rgba.colorTraffic.normal,
      this.rgba.colorTraffic.warning,
      this.rgba.colorTraffic.danger
    ][key];
  }

  updateStyles (styles) {
    _.merge(this.styles, styles);
    this.updateComputedStyles(styles);
  }

  updateComputedStyles (styles) {
    this.styles.colorTrafficHighlighted = _.reduce(this.styles.colorTraffic, (acc, value, key) => {
      if (styles && styles.colorTrafficHighlighted && styles.colorTrafficHighlighted[key]) {
        acc[key] = styles.colorTrafficHighlighted[key];
      } else {
        acc[key] = chroma(value).brighten(3).css();
      }
      // acc[key] = styles.colorTrafficHighlighted && styles.colorTrafficHighlighted[key] ? styles.colorTrafficHighlighted[key] : chroma(value).brighten(3).css();
      return acc;
    }, {});

    this.rgba = {
      colorArcBackground: getRGBA(this.styles.colorArcBackground),
      colorConnectionLine: getRGBA(this.styles.colorConnectionLine),
      colorDonutInternalColor: getRGBA(this.styles.colorDonutInternalColor),
      colorPageBackground: getRGBA(this.styles.colorPageBackground),
      colorTraffic: _.reduce(this.styles.colorTraffic, (acc, value, key) => {
        acc[key] = getRGBA(value);
        return acc;
      }, {}),
      colorTrafficHighlighted: _.reduce(this.styles.colorTrafficHighlighted, (acc, value, key) => {
        acc[key] = getRGBA(value);
        return acc;
      }, {}),
    };
  }
}

export default new GlobalStyles();
<|endoftext|>


<commit_before>var engine = new _engine();

setInterval(function() {
	engine.updateCount("counter");
}, 1000);<commit_msg>THA MAIN JS<commit_after>var engine = (function(){ return new _engine(); }());

setInterval(function() {
	engine.updateCount("counter");
}, 1000);<|endoftext|>


<commit_before>import parse from '../subscription/parse'
import { subscribe } from 'brisky-struct'
import bs from 'brisky-stamp'
import send from './send'
import createClient from '../client/create'
import { removeClient } from './remove'
import { cache } from './cache'

export default (hub, socket, data) => {
  const payload = data[0]
  const meta = data[1]
  var client = socket.client

  if (meta) {
    let t
    if (client) {
      t = hub
      if ('context' in meta && client.context != meta.context) { // eslint-disable-line
        removeClient(client)
        create(hub, socket, meta, payload)
      } else if (meta.subscriptions) {
        if (payload) setPayload(t, payload, client)
        incomingSubscriptions(t, client, meta, client.key)
        bs.close()
      }
    } else {
      create(hub, socket, meta, payload)
    }
  } else {
    setPayload(client.parent(2), payload, client)
    bs.close()
  }
}

const addToCache = (client, hub, payload) => {
  if (typeof payload === 'object' && payload) {
    for (let key in payload) {
      if (key !== 'val' && key !== 'stamp') {
        let struct = hub[key]
        if (struct && struct.isHub) {
          addToCache(client, hub[key], payload[key])
        }
      }
    }
    if (payload.val !== void 0 && payload.stamp) {
      cache(client, hub, payload.stamp)
    }
  }
}

const setPayload = (hub, payload, client) => {
  hub.set(payload, false)
  addToCache(client, hub, payload)
}

const set = (meta, socket, t, payload) => {
  const stamp = bs.create()
  const id = meta.id
  const context = meta.context
  // const ip = socket._socket.remoteAddress
  const client = socket.client = createClient(
    t, { socket, context }, stamp, socket.useragent, id
  )
  if (payload) setPayload(t, payload, client)
  if (meta.subscriptions) incomingSubscriptions(t, client, meta, id)
  bs.close()
}

const create = (hub, socket, meta, payload) => {
  const t = meta.context ? hub.getContext(meta.context, socket) : hub
  if (!t.inherits && t.then) {
    t.then((t) => {
      if (socket.external !== null) {
        console.log('client connected and found informations')
        set(meta, socket, t, payload)
      } else {
        console.log('client discconected when logging in')
      }
    }).catch(err => hub.emit('error', err))
  } else {
    set(meta, socket, t, payload)
  }
}

const incomingSubscriptions = (hub, client, meta, id) => {
  const update = (t, type, subs, tree) => send(hub, client, t, type, subs, tree)
  if (!client.upstreamSubscriptions) client.upstreamSubscriptions = {}
  for (let key in meta.subscriptions) {
    const uid = key + '-' + id
    if (!client.upstreamSubscriptions[uid]) {
      const subs = parse(meta.subscriptions[key], hub, void 0, client)
      client.upstreamSubscriptions[uid] = subs
      console.log('subscribe!', subs.clients && subs.clients.$any && subs.clients.$any.$keys)
      subscribe(hub, subs, update)
      hub.subscriptions[hub.subscriptions.length - 1]._uid_ = id
    }
  }
}
<commit_msg>removed log<commit_after>import parse from '../subscription/parse'
import { subscribe } from 'brisky-struct'
import bs from 'brisky-stamp'
import send from './send'
import createClient from '../client/create'
import { removeClient } from './remove'
import { cache } from './cache'

export default (hub, socket, data) => {
  const payload = data[0]
  const meta = data[1]
  var client = socket.client

  if (meta) {
    let t
    if (client) {
      t = hub
      if ('context' in meta && client.context != meta.context) { // eslint-disable-line
        removeClient(client)
        create(hub, socket, meta, payload)
      } else if (meta.subscriptions) {
        if (payload) setPayload(t, payload, client)
        incomingSubscriptions(t, client, meta, client.key)
        bs.close()
      }
    } else {
      create(hub, socket, meta, payload)
    }
  } else {
    setPayload(client.parent(2), payload, client)
    bs.close()
  }
}

const addToCache = (client, hub, payload) => {
  if (typeof payload === 'object' && payload) {
    for (let key in payload) {
      if (key !== 'val' && key !== 'stamp') {
        let struct = hub[key]
        if (struct && struct.isHub) {
          addToCache(client, hub[key], payload[key])
        }
      }
    }
    if (payload.val !== void 0 && payload.stamp) {
      cache(client, hub, payload.stamp)
    }
  }
}

const setPayload = (hub, payload, client) => {
  hub.set(payload, false)
  addToCache(client, hub, payload)
}

const set = (meta, socket, t, payload) => {
  const stamp = bs.create()
  const id = meta.id
  const context = meta.context
  // const ip = socket._socket.remoteAddress
  const client = socket.client = createClient(
    t, { socket, context }, stamp, socket.useragent, id
  )
  if (payload) setPayload(t, payload, client)
  if (meta.subscriptions) incomingSubscriptions(t, client, meta, id)
  bs.close()
}

const create = (hub, socket, meta, payload) => {
  const t = meta.context ? hub.getContext(meta.context, socket) : hub
  if (!t.inherits && t.then) {
    t.then((t) => {
      if (socket.external !== null) {
        console.log('client connected and found informations')
        set(meta, socket, t, payload)
      } else {
        console.log('client discconected when logging in')
      }
    }).catch(err => hub.emit('error', err))
  } else {
    set(meta, socket, t, payload)
  }
}

const incomingSubscriptions = (hub, client, meta, id) => {
  const update = (t, type, subs, tree) => send(hub, client, t, type, subs, tree)
  if (!client.upstreamSubscriptions) client.upstreamSubscriptions = {}
  for (let key in meta.subscriptions) {
    const uid = key + '-' + id
    if (!client.upstreamSubscriptions[uid]) {
      const subs = parse(meta.subscriptions[key], hub, void 0, client)
      client.upstreamSubscriptions[uid] = subs
      subscribe(hub, subs, update)
      hub.subscriptions[hub.subscriptions.length - 1]._uid_ = id
    }
  }
}
<|endoftext|>


<commit_before>import { module } from 'angular';

export default module('app.services.online', [])
  .service('online', class OnlineService {
    constructor($http, $window) {
      'ngInject';
      this.$http = $http;
      this.check();

      // update when browser detects online/offline changes
      const boundCheck = this.check.bind(this);
      $window.addEventListener('online', boundCheck);
      $window.addEventListener('offline', boundCheck);
    }

    check() {
      this.isOnline = undefined;
      this.$http.head('https://paperhive.org/api/').then(
        () => this.isOnline = true,
        () => this.isOnline = false
      );
    }
  });
<commit_msg>online: add short delay to check()<commit_after>import { module } from 'angular';

export default module('app.services.online', [])
  .service('online', class OnlineService {
    constructor($http, $timeout, $window) {
      'ngInject';
      this.$http = $http;
      this.$timeout = $timeout;
      this.check();

      // update when browser detects online/offline changes
      const boundCheck = this.check.bind(this);
      $window.addEventListener('online', boundCheck);
      $window.addEventListener('offline', boundCheck);
    }

    check() {
      // wait a bit before actually checking (network may not be fully initialized)
      this.$timeout(() => this.$http.head('https://paperhive.org/api/').then(
        () => this.isOnline = true,
        () => this.isOnline = false
      ), 100);
    }
  });
<|endoftext|>


<commit_before>var HELP_TEXT = `
Hi there! I’m NeuroBotX, your NeuroTechX Slack bot. I currently only have 1 response when you send me a message, so here is everything you need to know about NeuroTechX and this Slack.

If you are looking for help or have a question about NeuroTechX, the quickest way to get an answer is to message one of the directors. @yannick and @sydneyneurotechx are the more active Directors on slack.

Here is a list of all the directors and their usernames:

Executive Director: @yannick
Operations Director: @sydneyneurotechx
Communications Director: @lucia.gallardo
Science Director:@ melanie

If you are looking for online Neurotech  resources, check out NeuroTechEDU

Link: http://www.neurotechedu.com/

If you are looking for a chapter to get connected to, please explore the different Slack public chapter channels. All city chapters channels have a “_” at the beginning of it.

To find a list of cities where we are located in, please visit http://neurotechx.com/ and see if there is a chapter near you!

If you are looking for archived Slack text, go to:

https://github.com/NeuroTechX/ntx_slack_archive
`
var WELCOME_TEXT = `
Welcome to the NeuroTechX Slack! This Slack brings together hundreds of people from across the world to communicate about Neurotechnology. Everyone here is passionate about the domain and is willing to help out.

Here is how you should get started:

The first thing you should do is read our code of conduct. It can be found at: http://ntx-dockit.readthedocs.io/en/latest/Conduct.html

The second thing you should do is go to the #introductions channel in introduce yourself! Everyone here is very friendly!

The third thing you should do is send a message to the directors of NeuroTechX and say hello! Not everyone is active at all times but you should get a response from most of them. Here are their usernames:

Executive Director: @yannick
Operations Director: @sydneyneurotechx
Communications Director: @lucia.gallardo
Science Director: @melanie

If you are new to the domain of Neurotechnology, we urge you to check out our NeurotechEDU page! There is a list of amazing resources that exist on it that will help you to advance your knowledge in the domain. If you have questions related to the content, feel free to ask them in Slack

Link: http://www.neurotechedu.com/

Since NeurotechX is a bootstrapped non-profit, we are unable to pay for the premium version of Slack and therefore all messages after 10,000 are deleted. We have built a temporary solution by archiving all of the text onto our Github page. You can visit the list of archived text here. We only track public channels.

https://github.com/NeuroTechX/ntx_slack_archive

Finally take a look at the different chapters that exist in NeuroTechX. You may discover that there is a local community for you to get connected to.  All city chapter channels have a “_” at the beginning of it.

To find a list of cities where we are located in, please visit http://neurotechx.com/ and see if there is a chapter near you!
`
exports.HELP_TEXT = HELP_TEXT;
exports.WELCOME_TEXT = WELCOME_TEXT;
<commit_msg>Edited help and welcome message<commit_after>var HELP_TEXT = `
Hi there! I’m NeuroBotX, your NeuroTechX Slack bot. I currently only have 1 response when you send me a message, so here is everything you need to know about NeuroTechX and this Slack.

If you are looking for help or have a question about NeuroTechX, the quickest way to get an answer is to message one of the directors. @yannick and @sydneyneurotechx are the more active Directors on slack.

Here is a list of all the directors and their usernames:

Executive Director: @yannick
Operations Director: @sydneyneurotechx
Communications Director: @lucia.gallardo
Science Director:@ melanie

If you are looking for online Neurotech  resources, check out NeuroTechEDU

Link: http://www.neurotechedu.com/

If you are looking for a chapter to get connected to, please explore the different Slack public chapter channels. All city chapters channels have a “_” at the beginning of it.

To find a list of cities where we are located in, please visit http://neurotechx.com/ and see if there is a chapter near you!

If you are looking for archived Slack text, go to:

https://github.com/NeuroTechX/ntx_slack_archive

If you'd like to get involved, now is the time! Have a look at our 2017 Committees at : http://neurotechx.com/opportunities/index.html.
`
var WELCOME_TEXT = `
Welcome to the NeuroTechX Slack! This Slack brings together hundreds of people from across the world to communicate about Neurotechnology. Everyone here is passionate about the domain and is willing to help out.

Here is how you should get started:

The first thing you should do is read our code of conduct. It can be found at: http://ntx-dockit.readthedocs.io/en/latest/Conduct.html

The second thing you should do is go to the #introductions channel in introduce yourself! Everyone here is very friendly!

The third thing you should do is send a message to the directors of NeuroTechX and say hello! Not everyone is active at all times but you should get a response from most of them. Here are their usernames:

Executive Director: @yannick
Operations Director: @sydneyneurotechx
Communications Director: @lucia.gallardo
Science Director: @melanie

If you are new to the domain of Neurotechnology, we urge you to check out our NeurotechEDU page! There is a list of amazing resources that exist on it that will help you to advance your knowledge in the domain. If you have questions related to the content, feel free to ask them in Slack

Link: http://www.neurotechedu.com/

Since NeurotechX is a bootstrapped non-profit, we are unable to pay for the premium version of Slack and therefore all messages after 10,000 are deleted. We have built a temporary solution by archiving all of the text onto our Github page. You can visit the list of archived text here. We only track public channels.

https://github.com/NeuroTechX/ntx_slack_archive

Finally take a look at the different chapters that exist in NeuroTechX. You may discover that there is a local community for you to get connected to.  All city chapter channels have a “_” at the beginning of it.

To find a list of cities where we are located in, please visit http://neurotechx.com/ and see if there is a chapter near you!

If you'd like to get involved, now is the time! Have a look at our 2017 Committees at : http://neurotechx.com/opportunities/index.html.
`
exports.HELP_TEXT = HELP_TEXT;
exports.WELCOME_TEXT = WELCOME_TEXT;
<|endoftext|>


<commit_before>
import { shallow, mount } from "enzyme";
import { getSingleSpecWrapper } from "../../../util/tests/data-spec.js";

import SideBySide from "../SideBySide";

const generateComponent = (additionalProps={}) => (
    <SideBySide {...additionalProps} />
);

it ('should render', () => {
  let wrapper = mount(generateComponent());

  expect(wrapper.html()).toMatchSnapshot();
});
<commit_msg>added beforeEach and afterEach functions to SideBySide<commit_after>
import { shallow, mount } from "enzyme";
import { reset, startBuffering } from "aphrodite/lib/inject";
import { getSingleSpecWrapper } from "../../../util/tests/data-spec.js";

import SideBySide from "../SideBySide";

const generateComponent = (additionalProps={}) => (
    <SideBySide {...additionalProps} />
);

beforeEach(() => {
  reset();
  startBuffering();
});

afterEach(() => {
  reset();
});

it ('should render', () => {
  let wrapper = mount(generateComponent());

  expect(wrapper.html()).toMatchSnapshot();
});
<|endoftext|>


<commit_before>'use strict';

const Assert = require('chai').assert;
const github = require('../support/github');
const request = require('../support/request');
const sdapi = require('../support/sdapi');
const { defineSupportCode } = require('cucumber');

const TIMEOUT = 240 * 1000;

defineSupportCode(({ Before, Given, When, Then }) => {
    Before({
        tags: '@workflow'
    }, function hook() {
        this.repoOrg = this.testOrg;
        this.repoName = 'functional-workflow';
        this.pipelineId = null;
        this.builds = null;
    });

    Given(/^an existing pipeline on "(.*)" branch with the workflow jobs:$/, {
        timeout: TIMEOUT
    }, function step(branch, table) {
        return this.getJwt(this.apiToken)
            .then((response) => {
                this.jwt = response.body.token;

                return github.createBranch(this.gitToken, branch, this.repoOrg, this.repoName);
            })
            // wait not to trigger builds when create a pipeline
            .then(() => this.promiseToWait(3))
            .then(() => this.createPipeline(this.repoName, branch))
            .then((response) => {
                Assert.oneOf(response.statusCode, [409, 201]);

                if (response.statusCode === 201) {
                    this.pipelineId = response.body.id;
                } else {
                    this.pipelineId = response.body.message.split(/\s*:\s*/)[1];
                }

                return this.getPipeline(this.pipelineId);
            })
            .then((response) => {
                const expectedJobs = table.hashes();

                this.jobs = response.body;

                for (let i = 0; i < expectedJobs.length; i += 1) {
                    const job = this.jobs.find(j => j.name === expectedJobs[i].job);

                    Assert.ok(job, 'Given job does not exist on pipeline');

                    const requiresList = expectedJobs[i].requires.split(/\s*,\s*/);
                    const requires = job.permutations[0].requires;

                    for (let j = 0; j < requiresList.length; j += 1) {
                        Assert.ok(requires.includes(requiresList[j]),
                            'pipeline should have specific edges');
                    }
                }
            });
    });

    When(/^a new commit is pushed to "(.*)" branch$/, {
        timeout: TIMEOUT
    }, function step(branch) {
        return github.createBranch(this.gitToken, branch, this.repoOrg, this.repoName)
            .then(() => github.createFile(this.gitToken, branch, this.repoOrg, this.repoName))
            .then(() => this.promiseToWait(5))
            .then(() => request({
                json: true,
                method: 'GET',
                uri: `${this.instance}/v4/pipelines/${this.pipelineId}`,
                auth: {
                    bearer: this.jwt
                }
            }))
            .then((response) => {
                this.eventId = response.body.lastEventId;
            });
    });

    When(/^a pull request is opened to "(.*)" branch$/, (branch, callback) => {
        // Write code here that turns the phrase above into concrete actions
        callback(null, 'pending');
    });

    Then(/^the "(.*)" job is triggered$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const job = this.jobs.find(j => j.name === jobName);
                const build = this.builds.find(b => b.jobId === job.id);

                Assert.ok(build, 'Expected job was not triggered.');

                this.buildId = build.id;
            });
    });

    Then(/^the "(.*)" job is triggered from "([^"]*)"$/, {
        timeout: TIMEOUT
    }, function step(triggeredJobName, parentJobName) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const parentJob = this.jobs.find(j => j.name === parentJobName);
                const parentBuild = this.builds.find(b => b.jobId === parentJob.id);
                const triggeredJob = this.jobs.find(j => j.name === triggeredJobName);
                const triggeredBuild = this.builds.find(b => b.jobId === triggeredJob.id);

                Assert.equal(parentBuild.id, triggeredBuild.parentBuildId);

                this.buildId = triggeredBuild.id;
            });
    });

    Then(/^the "(.*)" job is triggered from "([^"]*)" and "([^"]*)"$/, {
        timeout: TIMEOUT
    }, function step(joinJobName, parentJobName1, parentJobName2) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const joinJob = this.jobs.find(j => j.name === joinJobName);
                const joinBuild = this.builds.find(b => b.jobId === joinJob.id);

                [parentJobName1, parentJobName2].forEach((jobName) => {
                    const parentJob = this.jobs.find(j => j.name === jobName);
                    const parentBuild = this.builds.find(b => b.jobId === parentJob.id);

                    Assert.oneOf(parentBuild.id, joinBuild.parentBuildId);
                });
            });
    });

    Then(/^the "(.*)" job is not triggered$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const job = this.jobs.find(j => j.name === jobName);
                const build = this.builds.find(b => b.jobId === job.id);

                Assert.ok(!build, 'Unexpected job was triggered.');
            });
    });

    Then(/^that "(.*)" build uses the same SHA as the "(.*)" build$/, {
        timeout: TIMEOUT
    }, function step(jobName1, jobName2) {
        const job1 = this.jobs.find(j => j.name === jobName1);
        const job2 = this.jobs.find(j => j.name === jobName2);
        const build1 = this.builds.find(b => b.jobId === job1.id);
        const build2 = this.builds.find(b => b.jobId === job2.id);

        Assert.equal(build1.sha, build2.sha);
    });

    Then(/^the "(.*)" build succeeded$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return this.waitForBuild(this.buildId).then((resp) => {
            Assert.equal(resp.statusCode, 200);
            Assert.equal(resp.body.status, 'SUCCESS', `Unexpected build status: ${jobName}`);
        });
    });

    Then(/^the "(.*)" build failed$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return this.waitForBuild(this.buildId).then((resp) => {
            Assert.equal(resp.statusCode, 200);
            Assert.equal(resp.body.status, 'FAILURE', `Unexpected build status: ${jobName}`);
        });
    });
});
<commit_msg>fix: Fix workflow test (#1348)<commit_after>'use strict';

const Assert = require('chai').assert;
const github = require('../support/github');
const sdapi = require('../support/sdapi');
const { defineSupportCode } = require('cucumber');

const TIMEOUT = 240 * 1000;

defineSupportCode(({ Before, Given, When, Then }) => {
    Before({
        tags: '@workflow'
    }, function hook() {
        this.repoOrg = this.testOrg;
        this.repoName = 'functional-workflow';
        this.pipelineId = null;
        this.builds = null;
    });

    Given(/^an existing pipeline on "(.*)" branch with the workflow jobs:$/, {
        timeout: TIMEOUT
    }, function step(branch, table) {
        return this.getJwt(this.apiToken)
            .then((response) => {
                this.jwt = response.body.token;

                return github.createBranch(this.gitToken, branch, this.repoOrg, this.repoName);
            })
            // wait not to trigger builds when create a pipeline
            .then(() => this.promiseToWait(3))
            .then(() => this.createPipeline(this.repoName, branch))
            .then((response) => {
                Assert.oneOf(response.statusCode, [409, 201]);

                if (response.statusCode === 201) {
                    this.pipelineId = response.body.id;
                } else {
                    this.pipelineId = response.body.message.split(/\s*:\s*/)[1];
                }

                return this.getPipeline(this.pipelineId);
            })
            .then((response) => {
                const expectedJobs = table.hashes();

                this.jobs = response.body;

                for (let i = 0; i < expectedJobs.length; i += 1) {
                    const job = this.jobs.find(j => j.name === expectedJobs[i].job);

                    Assert.ok(job, 'Given job does not exist on pipeline');

                    const requiresList = expectedJobs[i].requires.split(/\s*,\s*/);
                    const requires = job.permutations[0].requires;

                    for (let j = 0; j < requiresList.length; j += 1) {
                        Assert.ok(requires.includes(requiresList[j]),
                            'pipeline should have specific edges');
                    }
                }
            });
    });

    When(/^a new commit is pushed to "(.*)" branch$/, {
        timeout: TIMEOUT
    }, function step(branch) {
        return github.createBranch(this.gitToken, branch, this.repoOrg, this.repoName)
            .then(() => github.createFile(this.gitToken, branch, this.repoOrg, this.repoName))
            .then((data) => {
                this.sha = data.commit.sha;
            });
    });

    When(/^a pull request is opened to "(.*)" branch$/, (branch, callback) => {
        // Write code here that turns the phrase above into concrete actions
        callback(null, 'pending');
    });

    Then(/^the "(.*)" job is triggered$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return sdapi.searchForBuild({
            instance: this.instance,
            pipelineId: this.pipelineId,
            desiredSha: this.sha,
            desiredStatus: ['QUEUED', 'RUNNING', 'SUCCESS', 'FAILURE'],
            jobName,
            jwt: this.jwt
        })
            .then((build) => {
                this.eventId = build.eventId;
                const job = this.jobs.find(j => j.name === jobName);

                Assert.equal(build.jobId, job.id);

                this.buildId = build.id;
            });
    });

    Then(/^the "(.*)" job is triggered from "([^"]*)"$/, {
        timeout: TIMEOUT
    }, function step(triggeredJobName, parentJobName) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const parentJob = this.jobs.find(j => j.name === parentJobName);
                const parentBuild = this.builds.find(b => b.jobId === parentJob.id);
                const triggeredJob = this.jobs.find(j => j.name === triggeredJobName);
                const triggeredBuild = this.builds.find(b => b.jobId === triggeredJob.id);

                Assert.equal(parentBuild.id, triggeredBuild.parentBuildId);

                this.buildId = triggeredBuild.id;
            });
    });

    Then(/^the "(.*)" job is triggered from "([^"]*)" and "([^"]*)"$/, {
        timeout: TIMEOUT
    }, function step(joinJobName, parentJobName1, parentJobName2) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const joinJob = this.jobs.find(j => j.name === joinJobName);
                const joinBuild = this.builds.find(b => b.jobId === joinJob.id);

                [parentJobName1, parentJobName2].forEach((jobName) => {
                    const parentJob = this.jobs.find(j => j.name === jobName);
                    const parentBuild = this.builds.find(b => b.jobId === parentJob.id);

                    Assert.oneOf(parentBuild.id, joinBuild.parentBuildId);
                });
            });
    });

    Then(/^the "(.*)" job is not triggered$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return sdapi.findEventBuilds({
            instance: this.instance,
            eventId: this.eventId,
            jwt: this.jwt
        })
            .then((builds) => {
                this.builds = builds;
                const job = this.jobs.find(j => j.name === jobName);
                const build = this.builds.find(b => b.jobId === job.id);

                Assert.ok(!build, 'Unexpected job was triggered.');
            });
    });

    Then(/^that "(.*)" build uses the same SHA as the "(.*)" build$/, {
        timeout: TIMEOUT
    }, function step(jobName1, jobName2) {
        return Promise.all([
            sdapi.searchForBuild({
                instance: this.instance,
                pipelineId: this.pipelineId,
                desiredSha: this.sha,
                desiredStatus: ['QUEUED', 'RUNNING', 'SUCCESS', 'FAILURE'],
                jobName: jobName1,
                jwt: this.jwt
            }),
            sdapi.searchForBuild({
                instance: this.instance,
                pipelineId: this.pipelineId,
                desiredSha: this.sha,
                desiredStatus: ['QUEUED', 'RUNNING', 'SUCCESS', 'FAILURE'],
                jobName: jobName2,
                jwt: this.jwt
            })
        ])
            .then(([build1, build2]) => Assert.equal(build1.sha, build2.sha));
    });

    Then(/^the "(.*)" build succeeded$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return this.waitForBuild(this.buildId).then((resp) => {
            Assert.equal(resp.statusCode, 200);
            Assert.equal(resp.body.status, 'SUCCESS', `Unexpected build status: ${jobName}`);
        });
    });

    Then(/^the "(.*)" build failed$/, {
        timeout: TIMEOUT
    }, function step(jobName) {
        return this.waitForBuild(this.buildId).then((resp) => {
            Assert.equal(resp.statusCode, 200);
            Assert.equal(resp.body.status, 'FAILURE', `Unexpected build status: ${jobName}`);
        });
    });
});
<|endoftext|>