<commit_before>var fs      = require('fs')
var path    = require('path')
var mime    = require('mime')
var terraform = require('terraform')
var fse     = require('fs-extra')
var envy    = require('envy-json')

/**
 *
 * Normalize Url
 *
 * - removes querystring
 * - removes extra slashes
 * - changes `/` to `/index.html`
 */

exports.normalizeUrl = function(url){

  // take off query string
  var base = unescape(url.split('?')[0])

  /**
   * Normalize Path
   *
   * Note: This converts unix paths to windows path on windows
   * (not sure if this is a good thing)
   */
  var file_path = path.normalize(base)

  // index.html support
  if (path.sep == file_path[file_path.length - 1]) file_path += 'index.html'

  return file_path
}

/**
 *
 * Mime Type
 *
 * returns type of the file
 *
 * TODO: reference ext from terraform
 */

exports.mimeType = function(source){
  var ext = path.extname(source)

  if(['.jade', '.md', '.ejs'].indexOf(ext)  !== -1){
    return mime.lookup('html')
  }else if(['.less', '.styl', '.scss', '.sass'].indexOf(ext)  !== -1){
    return mime.lookup('css')
  }else{
    return mime.lookup(source)
  }

}

/**
 *
 * Walk directory for files
 *
 * recursive function that returns the directory tree
 * http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
 *
 */

var walk = function(dir, done) {
  var results = []

  fs.readdir(dir, function(err, list) {
    if (err){
      return done(err)
    }
    var pending = list.length

    if (!pending) return done(null, results);
    list.forEach(function(file) {
      file = path.resolve(dir, file)
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res)
            if (!--pending) done(null, results)
          })
        } else {
          results.push(file)
          if (!--pending) done(null, results)
        }
      })
    })
  })

}

/**
 *
 * Fetch all the file paths for a directory.
 * returns and array of all the relative paths.
 *
 */

exports.ls = function(dir, callback) {
  walk(dir, function(err, results){
    var files = []
    results.map(function(file){ files.push(path.relative(dir, file)) })
    callback(null, files)
  })
}

/**
 * Setup
 *
 * This is the style and configuration of a Harp Application.
 * returns object with contents of Harp.json and application style
 *
 *   {
 *     "projectPath" : "/path/to/app",
 *     "publicPath"  : "/path/to/app/public",
 *     "config"      : { ... }
 *   }
 */

exports.setup = function(projectPath, env){
  if(!env) env = "development"

  try{
    var configPath  = path.join(projectPath, "harp.json")
    var contents    = fs.readFileSync(configPath).toString()
    var publicPath  = path.join(projectPath, "public")
  }catch(e){
    try{
      var configPath  = path.join(projectPath, "_harp.json")
      var contents    = fs.readFileSync(configPath).toString()
      var publicPath  = projectPath
    }catch(e){
      var contents    = "{}"
      var publicPath  = projectPath
    }
  }

  // not sure what this does anymore.
  if(!contents || contents.replace(/^\s\s*/, '').replace(/\s\s*$/, '') == ''){
    contents = '{}'
  }

  // attempt to parse the file
  try{
    var cfg = JSON.parse(contents)
  }catch(e){
    e.source    = "JSON"
    e.dest      = "CONFIG"
    e.message   = e.message
    e.filename  = configPath
    e.stack     = contents
    e.lineno    = -1
    throw new terraform.helpers.TerraformError(e)
  }

  if(!cfg.hasOwnProperty('globals')) cfg['globals'] = {}

  cfg.globals.environment = process.env.NODE_ENV || env

  // replace values that look like environment variables
  // e.g. '$foo' -> process.env.foo
  cfg = envy(cfg)

  return {
    projectPath : projectPath,
    publicPath  : publicPath,
    config      : cfg
  }

}

/**
 *
 * Template for outputing Less errors.
 *
 */

exports.cssError = function(error){
  var body = '' +

  'body{' +
    'margin:0;' +
  '}' +

  'body:before {' +
    'display: block;'+
    'white-space: pre;' +
    'content: "'+ error.error.source +' -> ' + error.error.dest + ' (' + error.error.message + ') ' + error.error.filename + '";'+
    'color: #444;'+
    'background-color: #fefe96;' +
    'padding: 40px 40px;'+
    'margin: 0;'+
    'font-family: monospace;'+
    'font-size: 14px;'+
  '}'

  return body
}

/**
 *
 * Will Collide
 *
 * Returns true if first path is in the line of fire of the second path.
 * ie: if we delete the second path will the first path be affected?
 */

var willCollide = exports.willCollide = function(projectPath, outputPath){
  var projectPath   = path.resolve(projectPath)
  var outputPath    = path.resolve(outputPath)
  var relativePath  = path.relative(projectPath, outputPath)
  var arr           = relativePath.split("/")
  var result        = true;

  arr.forEach(function(i){
    if(i !== "..") result = false
  })

  /**
   * for @kennethormandy ;)
   */
  if (outputPath == "/") result = true

  return result
}

/**
 *
 * Will Allow
 *
 * Returns `true` if we feel projectPath is safe from the output path.
 * For this to be the case. The outputPath must live only one directory
 * back from the projectPath and the projectPath must live in a directory
 * starting with an underscore.
 */

exports.willAllow = function(projectPath, outputPath){
  var projectPath   = path.resolve(projectPath)
  var outputPath    = path.resolve(outputPath)
  var relativePath  = path.relative(projectPath, outputPath)
  var arr           = relativePath.split("/")

  if(willCollide(projectPath, outputPath)){
    if(relativePath === ".."){
      if(projectPath.split("/")[projectPath.split("/").length - 1][0] == "_"){
        return true
      }else{
        return false
      }
    }else{
      return false
    }
  }else{
    return true
  }
}

/**
 * Prime
 * (Disk I/O)
 *
 * Cleans out a directory but ignores one (optionally).
 *
 * primePath: Absolute Path
 * options: Object
 *    ignore: Absolute Path || Relative (to delete)
 *
 * This is a powerful Function so take it seriously.
 *
 */

exports.prime = function(primePath, options, callback){

  if(!callback){
    callback = options
    options = {}
  }

  /**
   * Options (only one)
   */
  var ignorePath = options.ignore
    ? path.resolve(primePath, options.ignore)
    : null

  // Absolute paths are predictable.
  var primePath = path.resolve(primePath)

  fse.mkdirp(primePath, function(){
    fse.readdir(primePath, function(error, contents){

      /**
       * Delete each item in the directory in parallel. Thanks Ry!
       */

      if(contents.length == 0) return callback()

      var total = contents.length
      var count = 0

      contents.forEach(function(i){
        var filePath  = path.resolve(primePath, i)
        var gitRegExp = new RegExp(/^.git/)

        /**
         * We leave `.git`, `.gitignore`, and project path.
         */
        if(filePath === ignorePath || i.match(gitRegExp)){
          count++
          if(count == total) callback()
        }else{
          fse.remove(filePath, function(err){
            count++
            if(count == total) callback()
          })
        }
      })

    })
  })

}

/**
 * Stacktrace
 *
 * Formats a stactrace
 *
 *
 * This is a powerful Function so take it seriously.
 *
 */

exports.stacktrace = function(str, options){
  var lineno  = options.lineno  || -1
  var context = options.context || 8
  var context = context = context / 2
  var lines   = ('\n' + str).split('\n')
  var start   = Math.max(lineno - context, 1)
  var end     = Math.min(lines.length, lineno + context)

  if(lineno === -1) end = lines.length

  var pad     = end.toString().length

  var context = lines.slice(start, end).map(function(line, i){
    var curr = i + start
    return (curr == lineno ? ' > ' : '   ')
      + Array(pad - curr.toString().length + 1).join(' ')
      + curr
      + '| '
      + line
  }).join('\n')

  return context
}
<commit_msg>Fixes root output path collision detection on Windows, by @sintaxi<commit_after>var fs      = require('fs')
var path    = require('path')
var mime    = require('mime')
var terraform = require('terraform')
var fse     = require('fs-extra')
var envy    = require('envy-json')

/**
 *
 * Normalize Url
 *
 * - removes querystring
 * - removes extra slashes
 * - changes `/` to `/index.html`
 */

exports.normalizeUrl = function(url){

  // take off query string
  var base = unescape(url.split('?')[0])

  /**
   * Normalize Path
   *
   * Note: This converts unix paths to windows path on windows
   * (not sure if this is a good thing)
   */
  var file_path = path.normalize(base)

  // index.html support
  if (path.sep == file_path[file_path.length - 1]) file_path += 'index.html'

  return file_path
}

/**
 *
 * Mime Type
 *
 * returns type of the file
 *
 * TODO: reference ext from terraform
 */

exports.mimeType = function(source){
  var ext = path.extname(source)

  if(['.jade', '.md', '.ejs'].indexOf(ext)  !== -1){
    return mime.lookup('html')
  }else if(['.less', '.styl', '.scss', '.sass'].indexOf(ext)  !== -1){
    return mime.lookup('css')
  }else{
    return mime.lookup(source)
  }

}

/**
 *
 * Walk directory for files
 *
 * recursive function that returns the directory tree
 * http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
 *
 */

var walk = function(dir, done) {
  var results = []

  fs.readdir(dir, function(err, list) {
    if (err){
      return done(err)
    }
    var pending = list.length

    if (!pending) return done(null, results);
    list.forEach(function(file) {
      file = path.resolve(dir, file)
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res)
            if (!--pending) done(null, results)
          })
        } else {
          results.push(file)
          if (!--pending) done(null, results)
        }
      })
    })
  })

}

/**
 *
 * Fetch all the file paths for a directory.
 * returns and array of all the relative paths.
 *
 */

exports.ls = function(dir, callback) {
  walk(dir, function(err, results){
    var files = []
    results.map(function(file){ files.push(path.relative(dir, file)) })
    callback(null, files)
  })
}

/**
 * Setup
 *
 * This is the style and configuration of a Harp Application.
 * returns object with contents of Harp.json and application style
 *
 *   {
 *     "projectPath" : "/path/to/app",
 *     "publicPath"  : "/path/to/app/public",
 *     "config"      : { ... }
 *   }
 */

exports.setup = function(projectPath, env){
  if(!env) env = "development"

  try{
    var configPath  = path.join(projectPath, "harp.json")
    var contents    = fs.readFileSync(configPath).toString()
    var publicPath  = path.join(projectPath, "public")
  }catch(e){
    try{
      var configPath  = path.join(projectPath, "_harp.json")
      var contents    = fs.readFileSync(configPath).toString()
      var publicPath  = projectPath
    }catch(e){
      var contents    = "{}"
      var publicPath  = projectPath
    }
  }

  // not sure what this does anymore.
  if(!contents || contents.replace(/^\s\s*/, '').replace(/\s\s*$/, '') == ''){
    contents = '{}'
  }

  // attempt to parse the file
  try{
    var cfg = JSON.parse(contents)
  }catch(e){
    e.source    = "JSON"
    e.dest      = "CONFIG"
    e.message   = e.message
    e.filename  = configPath
    e.stack     = contents
    e.lineno    = -1
    throw new terraform.helpers.TerraformError(e)
  }

  if(!cfg.hasOwnProperty('globals')) cfg['globals'] = {}

  cfg.globals.environment = process.env.NODE_ENV || env

  // replace values that look like environment variables
  // e.g. '$foo' -> process.env.foo
  cfg = envy(cfg)

  return {
    projectPath : projectPath,
    publicPath  : publicPath,
    config      : cfg
  }

}

/**
 *
 * Template for outputing Less errors.
 *
 */

exports.cssError = function(error){
  var body = '' +

  'body{' +
    'margin:0;' +
  '}' +

  'body:before {' +
    'display: block;'+
    'white-space: pre;' +
    'content: "'+ error.error.source +' -> ' + error.error.dest + ' (' + error.error.message + ') ' + error.error.filename + '";'+
    'color: #444;'+
    'background-color: #fefe96;' +
    'padding: 40px 40px;'+
    'margin: 0;'+
    'font-family: monospace;'+
    'font-size: 14px;'+
  '}'

  return body
}

/**
 *
 * Will Collide
 *
 * Returns true if first path is in the line of fire of the second path.
 * ie: if we delete the second path will the first path be affected?
 */

var willCollide = exports.willCollide = function(projectPath, outputPath){
  console.log('will collide: ', projectPath, outputPath);

  var projectPath   = path.resolve(projectPath)
  var outputPath    = path.resolve(outputPath)
  var relativePath  = path.relative(projectPath, outputPath)
  var arr           = relativePath.split(path.sep)
  var result        = true;

  arr.forEach(function(i){
    if(i !== "..") result = false
  })

  /**
   * for @kennethormandy ;)
   */
  if (outputPath == path.sep) result = true

  return result
}

/**
 *
 * Will Allow
 *
 * Returns `true` if we feel projectPath is safe from the output path.
 * For this to be the case. The outputPath must live only one directory
 * back from the projectPath and the projectPath must live in a directory
 * starting with an underscore.
 */

exports.willAllow = function(projectPath, outputPath){
  var projectPath   = path.resolve(projectPath)
  var outputPath    = path.resolve(outputPath)
  var relativePath  = path.relative(projectPath, outputPath)
  var arr           = relativePath.split("/")

  if(willCollide(projectPath, outputPath)){
    if(relativePath === ".."){
      if(projectPath.split("/")[projectPath.split("/").length - 1][0] == "_"){
        return true
      }else{
        return false
      }
    }else{
      return false
    }
  }else{
    return true
  }
}

/**
 * Prime
 * (Disk I/O)
 *
 * Cleans out a directory but ignores one (optionally).
 *
 * primePath: Absolute Path
 * options: Object
 *    ignore: Absolute Path || Relative (to delete)
 *
 * This is a powerful Function so take it seriously.
 *
 */

exports.prime = function(primePath, options, callback){

  if(!callback){
    callback = options
    options = {}
  }

  /**
   * Options (only one)
   */
  var ignorePath = options.ignore
    ? path.resolve(primePath, options.ignore)
    : null

  // Absolute paths are predictable.
  var primePath = path.resolve(primePath)

  fse.mkdirp(primePath, function(){
    fse.readdir(primePath, function(error, contents){

      /**
       * Delete each item in the directory in parallel. Thanks Ry!
       */

      if(contents.length == 0) return callback()

      var total = contents.length
      var count = 0

      contents.forEach(function(i){
        var filePath  = path.resolve(primePath, i)
        var gitRegExp = new RegExp(/^.git/)

        /**
         * We leave `.git`, `.gitignore`, and project path.
         */
        if(filePath === ignorePath || i.match(gitRegExp)){
          count++
          if(count == total) callback()
        }else{
          fse.remove(filePath, function(err){
            count++
            if(count == total) callback()
          })
        }
      })

    })
  })

}

/**
 * Stacktrace
 *
 * Formats a stactrace
 *
 *
 * This is a powerful Function so take it seriously.
 *
 */

exports.stacktrace = function(str, options){
  var lineno  = options.lineno  || -1
  var context = options.context || 8
  var context = context = context / 2
  var lines   = ('\n' + str).split('\n')
  var start   = Math.max(lineno - context, 1)
  var end     = Math.min(lines.length, lineno + context)

  if(lineno === -1) end = lines.length

  var pad     = end.toString().length

  var context = lines.slice(start, end).map(function(line, i){
    var curr = i + start
    return (curr == lineno ? ' > ' : '   ')
      + Array(pad - curr.toString().length + 1).join(' ')
      + curr
      + '| '
      + line
  }).join('\n')

  return context
}
<|endoftext|>


<commit_before>//debugData => contain all debug states.
//statesList => ListView

var currentSelectedState = null;
var currentDisplayedState = null;
var debugData = null;
var codeMap = null;
var locations = [];
var locationMap = {};
var breakpoints = {};

function init(data)
{
	jumpOutBackAction.enabled(false);
	jumpIntoBackAction.enabled(false);
	jumpIntoForwardAction.enabled(false);
	jumpOutForwardAction.enabled(false);
	jumpOverBackAction.enabled(false);
	jumpOverForwardAction.enabled(false);

	//breakpoints = {};
	if (data === null) {
		statesList.model.clear();
		statesSlider.maximumValue = 0;
		statesSlider.value = 0;
		currentSelectedState = null;
		currentDisplayedState = null;
		debugData = null;
		locations = [];
		locationMap = {};
		return;
	}

	debugData = data;
	currentSelectedState = 0;
	currentDisplayedState = 0;
	setupInstructions(currentSelectedState);
	setupCallData(currentSelectedState);
	initLocations();
	initSlider();
	selectState(currentSelectedState);
}

function updateMode()
{
	initSlider();
}

function initLocations()
{
	locations = [];
	if (debugData.states.length === 0)
		return;

	var nullLocation = { start: -1, end: -1, documentId: "", state: 0 };
	var prevLocation = nullLocation;

	for (var i = 0; i < debugData.states.length - 1; i++) {
		var code = debugData.states[i].code;
		var location = (code.documentId !== "") ? code.locations[codeStr(i)] : nullLocation;
		if (location && (location.start !== prevLocation.start || location.end !== prevLocation.end || code.documentId !== prevLocation.documentId))
		{
			prevLocation = { start: location.start, end: location.end, documentId: code.documentId, state: i };
			locations.push(prevLocation);
		}
		locationMap[i] = locations.length - 1;
	}
	locations.push({ start: -1, end: -1, documentId: code.documentId, state: i });
	locationMap[debugData.states.length - 1] = locations.length - 1;
}

function setBreakpoints(bp)
{
	breakpoints = bp;
}

function srcMode()
{
	return !assemblyMode && locations.length;
}

function initSlider()
{
	if (!debugData)
		statesSlider.maximumValue = 0;
	else if (srcMode()) {
		statesSlider.maximumValue = locations.length - 1;
	} else {
		statesSlider.maximumValue = debugData.states.length - 1;
	}
	statesSlider.value = 0;
}

function setupInstructions(stateIndex)
{
	var instructions = debugData.states[stateIndex].code.instructions;
	codeMap = {};
	statesList.model.clear();
	for (var i = 0; i < instructions.length; i++) {
		statesList.model.append(instructions[i]);
		codeMap[instructions[i].processIndex] = i;
	}
	callDataDump.listModel = debugData.states[stateIndex].callData.items;
}

function setupCallData(stateIndex)
{
	callDataDump.listModel = debugData.states[stateIndex].callData.items;
}

function moveSelection(incr)
{
	if (srcMode()) {
		var locationIndex = locationMap[currentSelectedState];
		if (locationIndex + incr >= 0 && locationIndex + incr < locations.length)
			selectState(locations[locationIndex + incr].state);
	} else {
		if (currentSelectedState + incr >= 0 && currentSelectedState + incr < debugData.states.length)
			selectState(currentSelectedState + incr);
	}
}

function display(stateIndex)
{
	if (stateIndex < 0)
		stateIndex = 0;
	if (stateIndex >= debugData.states.length)
		stateIndex = debugData.states.length - 1;
	if (debugData.states[stateIndex].codeIndex !== debugData.states[currentDisplayedState].codeIndex)
		setupInstructions(stateIndex);
	if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex)
		setupCallData(stateIndex);
	var codeLine = codeStr(stateIndex);
	var state = debugData.states[stateIndex];
	highlightSelection(codeLine);
	completeCtxInformation(state);
	currentDisplayedState = stateIndex;
	var docId = debugData.states[stateIndex].code.documentId;
	if (docId)
		debugExecuteLocation(docId, locations[locationMap[stateIndex]]);
}

function displayFrame(frameIndex)
{
	var state = debugData.states[currentSelectedState];
	if (frameIndex === 0)
		display(currentSelectedState);
	else
		display(state.levels[frameIndex - 1]);
}

function select(index)
{
	if (srcMode()) {
		selectState(locations[index].state);
	}
	else
		selectState(index);
}

function selectState(stateIndex)
{
	display(stateIndex);
	currentSelectedState = stateIndex;
	var state = debugData.states[stateIndex];
	jumpIntoForwardAction.enabled(stateIndex < debugData.states.length - 1)
	jumpIntoBackAction.enabled(stateIndex > 0);
	jumpOverForwardAction.enabled(stateIndex < debugData.states.length - 1);
	jumpOverBackAction.enabled(stateIndex > 0);
	jumpOutBackAction.enabled(state.levels.length > 1);
	jumpOutForwardAction.enabled(state.levels.length > 1);
	runForwardAction.enabled(stateIndex < debugData.states.length - 1)
	runBackAction.enabled(stateIndex > 0);

	var callStackData = [];
	for (var l = 0; l < state.levels.length; l++) {
		var address = debugData.states[state.levels[l] + 1].code.address;
		callStackData.push(address);
	}
	callStackData.push(debugData.states[0].code.address);
	callStack.listModel = callStackData;
	if (srcMode())
		statesSlider.value = locationMap[stateIndex];
	else
		statesSlider.value = stateIndex;
}

function codeStr(stateIndex)
{
	var state = debugData.states[stateIndex];
	return codeMap[state.curPC];
}

function highlightSelection(index)
{
	statesList.positionViewAtRow(index, ListView.Center);
	statesList.selection.clear();
	statesList.selection.select(index);
}

function completeCtxInformation(state)
{
	currentStep.update(state.step);
	mem.update(state.newMemSize.value() + " " + qsTr("words"));
	stepCost.update(state.gasCost.value());
	gasSpent.update(debugData.states[0].gas.subtract(state.gas).value());

	stack.listModel = state.debugStack;
	storage.listModel = state.debugStorage;
	memoryDump.listModel = state.debugMemory;
}

function isCallInstruction(index)
{
	var state = debugData.states[index];
	return state.instruction === "CALL" || state.instruction === "CREATE";
}

function isReturnInstruction(index)
{
	var state = debugData.states[index];
	return state.instruction === "RETURN"
}

function breakpointHit(i)
{
	var bpLocations = breakpoints[debugData.states[i].code.documentId];
	if (bpLocations) {
		var location = locations[locationMap[i]];
		if (location && location.start >=0 && location.end >= location.start) {
			for (var b = 0; b < bpLocations.length; b++) {
				var bpLocation = bpLocations[b];
				if ((bpLocation.start >= location.start && bpLocation.start <= location.end) || (bpLocation.end >= location.start && bpLocation.end <= location.end) ||
					(location.start >= bpLocation.start && location.start <= bpLocation.end) || (location.end >= bpLocation.start && location.end <= bpLocation.end))
					return true;
			}
		}
	}
	return false;
}

function stepIntoBack()
{
	moveSelection(-1);
}

function stepOverBack()
{
	if (currentSelectedState > 0 && isReturnInstruction(currentSelectedState - 1))
		stepOutBack();
	else
		moveSelection(-1);
}

function stepOverForward()
{
	if (isCallInstruction(currentSelectedState))
		stepOutForward();
	else
		moveSelection(1);
}

function stepIntoForward()
{
	moveSelection(1);
}

function runBack()
{
	var i = currentSelectedState - 1;
	while (i > 0 && !breakpointHit(i)) {
		--i;
	}
	selectState(i);
}

function runForward()
{
	var i = currentSelectedState + 1;
	while (i < debugData.states.length - 1 && !breakpointHit(i)) {
		++i;
	}
	selectState(i);
}

function stepOutBack()
{
	var i = currentSelectedState - 1;
	var depth = 0;
	while (--i >= 0) {
		if (breakpointHit(i))
			break;
		if (isCallInstruction(i))
			if (depth == 0)
				break;
			else depth--;
		else if (isReturnInstruction(i))
			depth++;
	}
	selectState(i);
}

function stepOutForward()
{
	var i = currentSelectedState;
	var depth = 0;
	while (++i < debugData.states.length) {
		if (breakpointHit(i))
			break;
		if (isReturnInstruction(i))
			if (depth == 0)
				break;
			else
				depth--;
		else if (isCallInstruction(i))
			depth++;
	}
	selectState(i + 1);
}

function jumpTo(value)
{
	select(value);
}
<commit_msg>more style fixes<commit_after>//debugData => contain all debug states.
//statesList => ListView

var currentSelectedState = null;
var currentDisplayedState = null;
var debugData = null;
var codeMap = null;
var locations = [];
var locationMap = {};
var breakpoints = {};

function init(data)
{
	jumpOutBackAction.enabled(false);
	jumpIntoBackAction.enabled(false);
	jumpIntoForwardAction.enabled(false);
	jumpOutForwardAction.enabled(false);
	jumpOverBackAction.enabled(false);
	jumpOverForwardAction.enabled(false);

	if (data === null) {
		statesList.model.clear();
		statesSlider.maximumValue = 0;
		statesSlider.value = 0;
		currentSelectedState = null;
		currentDisplayedState = null;
		debugData = null;
		locations = [];
		locationMap = {};
		return;
	}

	debugData = data;
	currentSelectedState = 0;
	currentDisplayedState = 0;
	setupInstructions(currentSelectedState);
	setupCallData(currentSelectedState);
	initLocations();
	initSlider();
	selectState(currentSelectedState);
}

function updateMode()
{
	initSlider();
}

function initLocations()
{
	locations = [];
	if (debugData.states.length === 0)
		return;

	var nullLocation = { start: -1, end: -1, documentId: "", state: 0 };
	var prevLocation = nullLocation;

	for (var i = 0; i < debugData.states.length - 1; i++) {
		var code = debugData.states[i].code;
		var location = code.documentId ? code.locations[codeStr(i)] : nullLocation;
		if (location.start !== prevLocation.start || location.end !== prevLocation.end || code.documentId !== prevLocation.documentId)
		{
			prevLocation = { start: location.start, end: location.end, documentId: code.documentId, state: i };
			locations.push(prevLocation);
		}
		locationMap[i] = locations.length - 1;
	}
	locations.push({ start: -1, end: -1, documentId: code.documentId, state: i });
	locationMap[debugData.states.length - 1] = locations.length - 1;
}

function setBreakpoints(bp)
{
	breakpoints = bp;
}

function srcMode()
{
	return !assemblyMode && locations.length;
}

function initSlider()
{
	if (!debugData)
		statesSlider.maximumValue = 0;
	else if (srcMode()) {
		statesSlider.maximumValue = locations.length - 1;
	} else {
		statesSlider.maximumValue = debugData.states.length - 1;
	}
	statesSlider.value = 0;
}

function setupInstructions(stateIndex)
{
	var instructions = debugData.states[stateIndex].code.instructions;
	codeMap = {};
	statesList.model.clear();
	for (var i = 0; i < instructions.length; i++) {
		statesList.model.append(instructions[i]);
		codeMap[instructions[i].processIndex] = i;
	}
	callDataDump.listModel = debugData.states[stateIndex].callData.items;
}

function setupCallData(stateIndex)
{
	callDataDump.listModel = debugData.states[stateIndex].callData.items;
}

function moveSelection(incr)
{
	if (srcMode()) {
		var locationIndex = locationMap[currentSelectedState];
		if (locationIndex + incr >= 0 && locationIndex + incr < locations.length)
			selectState(locations[locationIndex + incr].state);
	} else {
		if (currentSelectedState + incr >= 0 && currentSelectedState + incr < debugData.states.length)
			selectState(currentSelectedState + incr);
	}
}

function display(stateIndex)
{
	if (stateIndex < 0)
		stateIndex = 0;
	if (stateIndex >= debugData.states.length)
		stateIndex = debugData.states.length - 1;
	if (debugData.states[stateIndex].codeIndex !== debugData.states[currentDisplayedState].codeIndex)
		setupInstructions(stateIndex);
	if (debugData.states[stateIndex].dataIndex !== debugData.states[currentDisplayedState].dataIndex)
		setupCallData(stateIndex);
	var codeLine = codeStr(stateIndex);
	var state = debugData.states[stateIndex];
	highlightSelection(codeLine);
	completeCtxInformation(state);
	currentDisplayedState = stateIndex;
	var docId = debugData.states[stateIndex].code.documentId;
	if (docId)
		debugExecuteLocation(docId, locations[locationMap[stateIndex]]);
}

function displayFrame(frameIndex)
{
	var state = debugData.states[currentSelectedState];
	if (frameIndex === 0)
		display(currentSelectedState);
	else
		display(state.levels[frameIndex - 1]);
}

function select(index)
{
	if (srcMode())
		selectState(locations[index].state);
	else
		selectState(index);
}

function selectState(stateIndex)
{
	display(stateIndex);
	currentSelectedState = stateIndex;
	var state = debugData.states[stateIndex];
	jumpIntoForwardAction.enabled(stateIndex < debugData.states.length - 1)
	jumpIntoBackAction.enabled(stateIndex > 0);
	jumpOverForwardAction.enabled(stateIndex < debugData.states.length - 1);
	jumpOverBackAction.enabled(stateIndex > 0);
	jumpOutBackAction.enabled(state.levels.length > 1);
	jumpOutForwardAction.enabled(state.levels.length > 1);
	runForwardAction.enabled(stateIndex < debugData.states.length - 1)
	runBackAction.enabled(stateIndex > 0);

	var callStackData = [];
	for (var l = 0; l < state.levels.length; l++) {
		var address = debugData.states[state.levels[l] + 1].code.address;
		callStackData.push(address);
	}
	callStackData.push(debugData.states[0].code.address);
	callStack.listModel = callStackData;
	if (srcMode())
		statesSlider.value = locationMap[stateIndex];
	else
		statesSlider.value = stateIndex;
}

function codeStr(stateIndex)
{
	var state = debugData.states[stateIndex];
	return codeMap[state.curPC];
}

function highlightSelection(index)
{
	statesList.positionViewAtRow(index, ListView.Center);
	statesList.selection.clear();
	statesList.selection.select(index);
}

function completeCtxInformation(state)
{
	currentStep.update(state.step);
	mem.update(state.newMemSize.value() + " " + qsTr("words"));
	stepCost.update(state.gasCost.value());
	gasSpent.update(debugData.states[0].gas.subtract(state.gas).value());

	stack.listModel = state.debugStack;
	storage.listModel = state.debugStorage;
	memoryDump.listModel = state.debugMemory;
}

function isCallInstruction(index)
{
	var state = debugData.states[index];
	return state.instruction === "CALL" || state.instruction === "CREATE";
}

function isReturnInstruction(index)
{
	var state = debugData.states[index];
	return state.instruction === "RETURN"
}

function locationsIntersect(l1, l2)
{
	return l1.start <= l2.end && l1.end >= l2.start;
}

function breakpointHit(i)
{
	var bpLocations = breakpoints[debugData.states[i].code.documentId];
	if (bpLocations) {
		var location = locations[locationMap[i]];
		if (location.start >= 0 && location.end >= location.start)
			for (var b = 0; b < bpLocations.length; b++)
				if (locationsIntersect(location, bpLocations[b]))
					return true;
	}
	return false;
}

function stepIntoBack()
{
	moveSelection(-1);
}

function stepOverBack()
{
	if (currentSelectedState > 0 && isReturnInstruction(currentSelectedState - 1))
		stepOutBack();
	else
		moveSelection(-1);
}

function stepOverForward()
{
	if (isCallInstruction(currentSelectedState))
		stepOutForward();
	else
		moveSelection(1);
}

function stepIntoForward()
{
	moveSelection(1);
}

function runBack()
{
	var i = currentSelectedState - 1;
	while (i > 0 && !breakpointHit(i)) {
		--i;
	}
	selectState(i);
}

function runForward()
{
	var i = currentSelectedState + 1;
	while (i < debugData.states.length - 1 && !breakpointHit(i)) {
		++i;
	}
	selectState(i);
}

function stepOutBack()
{
	var i = currentSelectedState - 1;
	var depth = 0;
	while (--i >= 0) {
		if (breakpointHit(i))
			break;
		if (isCallInstruction(i))
			if (depth == 0)
				break;
			else depth--;
		else if (isReturnInstruction(i))
			depth++;
	}
	selectState(i);
}

function stepOutForward()
{
	var i = currentSelectedState;
	var depth = 0;
	while (++i < debugData.states.length) {
		if (breakpointHit(i))
			break;
		if (isReturnInstruction(i))
			if (depth == 0)
				break;
			else
				depth--;
		else if (isCallInstruction(i))
			depth++;
	}
	selectState(i + 1);
}

function jumpTo(value)
{
	select(value);
}
<|endoftext|>


<commit_before>(function () {
   'use strict';
}());

// http://stackoverflow.com/a/4234006
$.ajaxSetup({
  beforeSend: function(xhr) {
    if (xhr.overrideMimeType)
    {
      xhr.overrideMimeType('application/json');
    }
  }
});

function getSelectedType() {
  return document.getElementById('select-type').value;
}

var dataDir = 'data/';
var firstTooltipShown = false;

var climateMap = (function() {
  var lon = 0.0;
  var lat = 10.0;

  var map = new ol.Map({
    target: 'map',
    interactions: ol.interaction.defaults({keyboard:false}),  // disable because this moves the map when using the arrow keys to change the slider
  });

  var view = new ol.View({
    center: [0, 0],
    zoom: 3,
    projection: 'EPSG:3857'
  });

  view.setCenter(ol.proj.fromLonLat([lon, lat]));
  map.setView(view);

  var osmSource = new ol.source.OSM('OpenCycleMap');
  osmSource.setUrl('http://a.tile.opencyclemap.org/transport/{z}/{x}/{y}.png');
  //osmSource.setUrl('http://a.tile.openstreetmap.org/{z}/{x}/{y}.png');
  //osmSource.setUrl('https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png');
  //osmSource.setUrl('http://a.tile.stamen.com/toner/{z}/{x}/{y}.png');

  var osmLayer = new ol.layer.Tile({source: osmSource});
  map.addLayer(osmLayer);
  map.addControl(new ol.control.FullScreen());
  //map.addControl(new ol.control.ZoomSlider());

  return map;
})();

var contourPlot = (function() {
  var plotTypesMonthsLayers = {};
  var plotTypesMonthsImages = {};

  function hideAllContours() {
    var type;
    var month;
    for (type in plotTypesMonthsLayers) {
      for (month in plotTypesMonthsLayers[type]) {
        plotTypesMonthsLayers[type][month].setVisible(false);
      }
    }

    for (type in plotTypesMonthsImages) {
      for (month in plotTypesMonthsImages[type]) {
        plotTypesMonthsImages[type][month].setVisible(false);
      }
    }
  }

  function lineStyleFunction(feature, resolution) {
    var scaleForPixelDensity = 1.0; //dpi_x/96.0;
    var lineWidth = feature.get('stroke-width') * scaleForPixelDensity * Math.pow(climateMap.getView().getZoom()/2.0, 1.3);
    var lineStyle = new ol.style.Style({
      stroke: new ol.style.Stroke({
        color: feature.get('stroke'),
        width: lineWidth,
        opacity: 0.0 //feature.get('opacity')
        //            width: climateMap.getView().getZoom(),
      })
    });
    return lineStyle;
  }

  function createContoursLayer(dataType, monthNr) {
    var contourLayer = new ol.layer.VectorTile({
      source: new ol.source.VectorTile({
        url: dataDir + dataType + '/' + monthNr + '/tiles/{z}/{x}/{y}.geojson',
        format: new ol.format.GeoJSON(),
        projection: 'EPSG:3857',
        tileGrid: ol.tilegrid.createXYZ({
          maxZoom: 5,
          minZoom: 1,
          tileSize: [256, 256]
        }),
      }),
      style: lineStyleFunction
    });

    contourLayer.setZIndex(99);
    climateMap.addLayer(contourLayer);
    return contourLayer;
  }

  function getImageOpacity() {
    var zoomedOut = 0.7;
    var zoomLevelToStart = 5;
    var zoom = climateMap.getView().getZoom();
    if (zoom < zoomLevelToStart) {
      return zoomedOut;
    }
    return zoomedOut - (climateMap.getView().getZoom()-zoomLevelToStart) / 5.0;
  }

  function createImageLayer(dataType, monthNr) {
    //    var extent = ol.extent.applyTransform([-180, -85, 180, 85], ol.proj.getTransform('EPSG:4326', 'EPSG:3857'));
    //    console.log(extent);
    return new ol.layer.Tile({
      source: new ol.source.XYZ({
        url: dataDir + dataType + '/' + monthNr + '/maptiles/{z}/{x}/{-y}.png',
        projection: 'EPSG:3857',
        wrapX: false,
        tileGrid: ol.tilegrid.createXYZ({
          maxZoom: 5,
          minZoom: 1,
          tileSize: [256, 256]
        }),
      }),
      opacity: getImageOpacity()
    });
  }

  // increase contour line width when zooming
  climateMap.getView().on('change:resolution', function(evt) {
    for (var type in plotTypesMonthsLayers) {
      for (var month in plotTypesMonthsLayers[type]) {
        plotTypesMonthsLayers[type][month].setStyle(lineStyleFunction);
      }
    }
  });

  return Object.freeze({
    showOrCreateContour: function(monthNr) {
      hideAllContours();
      var selectedType = getSelectedType();
      if ( !this.plotExists(selectedType, monthNr) ) {
        this.addContours(selectedType, monthNr);
      }
      else {
        var monthImage = plotTypesMonthsImages[selectedType][monthNr];
        var monthLayer = plotTypesMonthsLayers[selectedType][monthNr];
        monthImage.setVisible(true);
        monthLayer.setVisible(true);
      }
    },
    addContours: function(dataType, monthNr) {
      var contourLayer = createContoursLayer(dataType, monthNr);
      if ( !(dataType in plotTypesMonthsLayers)) {
        plotTypesMonthsLayers[dataType] = {};
        plotTypesMonthsImages[dataType] = {};
      }
      var imageLayer = createImageLayer(dataType, monthNr);
      climateMap.addLayer(imageLayer);
      plotTypesMonthsImages[dataType][monthNr] = imageLayer;
      plotTypesMonthsLayers[dataType][monthNr] = contourLayer;
    },
    plotExists: function(typeName, monthNr) {
      return typeName in plotTypesMonthsLayers && plotTypesMonthsLayers[typeName].hasOwnProperty(monthNr);
    },
    updateOnZoom: function() {
      for (var type in plotTypesMonthsImages) {
        for (var month in plotTypesMonthsImages[type]) {
          plotTypesMonthsImages[type][month].setOpacity( getImageOpacity() );
        }
      }
    }
  });
})();

var colorbarLegend = (function() {
  return Object.freeze({
    update: function(dataType, monthNr) {
      var colorBarImage = document.getElementById('colorbar-legend-container');
      var imageUrl = dataDir + dataType + '/' + monthNr + '_colorbar.png';
      colorBarImage.style.backgroundImage = 'url(' + imageUrl + ')';
    }
  });
})();

var slider = (function() {
  var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan'];

  function slide(event, ui) {
    var monthNr = ui.value + 1;
    if (monthNr == 13) {
      $('#month-slider').slider('value', 0);
      monthNr = 1;
    }

    contourPlot.showOrCreateContour(monthNr);
    colorbarLegend.update(getSelectedType(), monthNr);
  }

  function update() {
    var monthNr = $('#month-slider').slider('value') + 1;
    if (monthNr == 13) {
      $('#month-slider').slider('value', 0);
      monthNr = 1;
    }

    contourPlot.showOrCreateContour(monthNr);
    colorbarLegend.update(getSelectedType(), monthNr);
  }

  function init() {
    $('#month-slider').slider({
      orientation: 'horizontal',
      range: 'min',
      min: 0,
      max: 12,
      value: 0,
      slide: slide,
      change: update
    })
    .slider('pips', {
      rest: 'label',
      labels: months
    });
    //    .slider('float');
  }

  return Object.freeze({
    update: update,
    init: init
  });
})();

var animationID = 0;

$(document).ready( function(){
  $('#info').hide();

  slider.init();

  function toggleAnimate() {
    function playAnimation() {
      var monthNr = $('#month-slider').slider('value');
      $('#month-slider').slider('value', monthNr+1);
    }

    var playButton = document.getElementById('animate-button');
    var selectedType = getSelectedType();
    if (playButton.innerHTML == 'Animate') {
      for (var i = 1; i < 13; ++i) {
        if ( !contourPlot.plotExists(selectedType, i) ) {
          contourPlot.addContours(selectedType, i);
        }
      }
      playButton.innerHTML = 'Stop';
      animationID = window.setInterval(playAnimation, 800);
    }
    else {
      window.clearInterval(animationID);
      playButton.innerHTML = 'Animate';
    }
  }

  document.getElementById('animate-button').onclick = toggleAnimate;

  document.getElementById('select-type').onchange = function() {
    var selection = getSelectedType();
    slider.update();
  };

  climateMap.on('moveend', function() {
    contourPlot.updateOnZoom();
  });

  climateMap.on('pointermove', function(evt) {
    var info = $('#info');

    function displayFeatureInfo(pixel) {
      info.css({
        left: (pixel[0] + 10) + 'px',
        top: (pixel[1] - 50) + 'px',
      });

      var feature = climateMap.forEachFeatureAtPixel(pixel, function(feature) {
        return feature;
      });

      if (feature) {
        var title = feature.get('title');
        if (title) {
          firstTooltipShown = true;
          info.text(title);
          info.show();
        } else if (!firstTooltipShown) {
          info.text('Tip: hover over lines to show their value.');
          info.show();
        } else {
          info.hide();
        }
      } else {
        info.hide();
      }
    }

    if (evt.dragging) {
      info.hide();
      return;
    }
    displayFeatureInfo(climateMap.getEventPixel(evt.originalEvent));
  });
});

window.onload = function() {
  var initialDataType = 'precipitation';
  var initialMonth = 1;

  function selectDataType(valueToSelect) {
    var element = document.getElementById('select-type');
    element.value = valueToSelect;
  }

  function setSliderValue(monthNr) {
    if (monthNr >= 13) {
      $('#month-slider').slider('value', 0);
    } else {
      $('#month-slider').slider('value', monthNr-1);
    }
  }

  selectDataType(initialDataType);
  setSliderValue(initialMonth);
  //    addContours(initialDataType, initialMonth);  // initial contour of January
};
<commit_msg>update opencyclemap transport tile url<commit_after>(function () {
   'use strict';
}());

// http://stackoverflow.com/a/4234006
$.ajaxSetup({
  beforeSend: function(xhr) {
    if (xhr.overrideMimeType)
    {
      xhr.overrideMimeType('application/json');
    }
  }
});

function getSelectedType() {
  return document.getElementById('select-type').value;
}

var dataDir = 'data/';
var firstTooltipShown = false;

var climateMap = (function() {
  var lon = 0.0;
  var lat = 10.0;

  var map = new ol.Map({
    target: 'map',
    interactions: ol.interaction.defaults({keyboard:false}),  // disable because this moves the map when using the arrow keys to change the slider
  });

  var view = new ol.View({
    center: [0, 0],
    zoom: 3,
    projection: 'EPSG:3857'
  });

  view.setCenter(ol.proj.fromLonLat([lon, lat]));
  map.setView(view);

  var osmSource = new ol.source.OSM('OpenCycleMap');
  osmSource.setUrl("https://a.tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=");  // needs an api key, get one at thunderforest.com
  //osmSource.setUrl('http://a.tile.openstreetmap.org/{z}/{x}/{y}.png');
  //osmSource.setUrl('https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png');
  //osmSource.setUrl('http://a.tile.stamen.com/toner/{z}/{x}/{y}.png');

  var osmLayer = new ol.layer.Tile({source: osmSource});
  map.addLayer(osmLayer);
  map.addControl(new ol.control.FullScreen());
  //map.addControl(new ol.control.ZoomSlider());

  return map;
})();

var contourPlot = (function() {
  var plotTypesMonthsLayers = {};
  var plotTypesMonthsImages = {};

  function hideAllContours() {
    var type;
    var month;
    for (type in plotTypesMonthsLayers) {
      for (month in plotTypesMonthsLayers[type]) {
        plotTypesMonthsLayers[type][month].setVisible(false);
      }
    }

    for (type in plotTypesMonthsImages) {
      for (month in plotTypesMonthsImages[type]) {
        plotTypesMonthsImages[type][month].setVisible(false);
      }
    }
  }

  function lineStyleFunction(feature, resolution) {
    var scaleForPixelDensity = 1.0; //dpi_x/96.0;
    var lineWidth = feature.get('stroke-width') * scaleForPixelDensity * Math.pow(climateMap.getView().getZoom()/2.0, 1.3);
    var lineStyle = new ol.style.Style({
      stroke: new ol.style.Stroke({
        color: feature.get('stroke'),
        width: lineWidth,
        opacity: 0.0 //feature.get('opacity')
        //            width: climateMap.getView().getZoom(),
      })
    });
    return lineStyle;
  }

  function createContoursLayer(dataType, monthNr) {
    var contourLayer = new ol.layer.VectorTile({
      source: new ol.source.VectorTile({
        url: dataDir + dataType + '/' + monthNr + '/tiles/{z}/{x}/{y}.geojson',
        format: new ol.format.GeoJSON(),
        projection: 'EPSG:3857',
        tileGrid: ol.tilegrid.createXYZ({
          maxZoom: 5,
          minZoom: 1,
          tileSize: [256, 256]
        }),
      }),
      style: lineStyleFunction
    });

    contourLayer.setZIndex(99);
    climateMap.addLayer(contourLayer);
    return contourLayer;
  }

  function getImageOpacity() {
    var zoomedOut = 0.7;
    var zoomLevelToStart = 5;
    var zoom = climateMap.getView().getZoom();
    if (zoom < zoomLevelToStart) {
      return zoomedOut;
    }
    return zoomedOut - (climateMap.getView().getZoom()-zoomLevelToStart) / 5.0;
  }

  function createImageLayer(dataType, monthNr) {
    //    var extent = ol.extent.applyTransform([-180, -85, 180, 85], ol.proj.getTransform('EPSG:4326', 'EPSG:3857'));
    //    console.log(extent);
    return new ol.layer.Tile({
      source: new ol.source.XYZ({
        url: dataDir + dataType + '/' + monthNr + '/maptiles/{z}/{x}/{-y}.png',
        projection: 'EPSG:3857',
        wrapX: false,
        tileGrid: ol.tilegrid.createXYZ({
          maxZoom: 5,
          minZoom: 1,
          tileSize: [256, 256]
        }),
      }),
      opacity: getImageOpacity()
    });
  }

  // increase contour line width when zooming
  climateMap.getView().on('change:resolution', function(evt) {
    for (var type in plotTypesMonthsLayers) {
      for (var month in plotTypesMonthsLayers[type]) {
        plotTypesMonthsLayers[type][month].setStyle(lineStyleFunction);
      }
    }
  });

  return Object.freeze({
    showOrCreateContour: function(monthNr) {
      hideAllContours();
      var selectedType = getSelectedType();
      if ( !this.plotExists(selectedType, monthNr) ) {
        this.addContours(selectedType, monthNr);
      }
      else {
        var monthImage = plotTypesMonthsImages[selectedType][monthNr];
        var monthLayer = plotTypesMonthsLayers[selectedType][monthNr];
        monthImage.setVisible(true);
        monthLayer.setVisible(true);
      }
    },
    addContours: function(dataType, monthNr) {
      var contourLayer = createContoursLayer(dataType, monthNr);
      if ( !(dataType in plotTypesMonthsLayers)) {
        plotTypesMonthsLayers[dataType] = {};
        plotTypesMonthsImages[dataType] = {};
      }
      var imageLayer = createImageLayer(dataType, monthNr);
      climateMap.addLayer(imageLayer);
      plotTypesMonthsImages[dataType][monthNr] = imageLayer;
      plotTypesMonthsLayers[dataType][monthNr] = contourLayer;
    },
    plotExists: function(typeName, monthNr) {
      return typeName in plotTypesMonthsLayers && plotTypesMonthsLayers[typeName].hasOwnProperty(monthNr);
    },
    updateOnZoom: function() {
      for (var type in plotTypesMonthsImages) {
        for (var month in plotTypesMonthsImages[type]) {
          plotTypesMonthsImages[type][month].setOpacity( getImageOpacity() );
        }
      }
    }
  });
})();

var colorbarLegend = (function() {
  return Object.freeze({
    update: function(dataType, monthNr) {
      var colorBarImage = document.getElementById('colorbar-legend-container');
      var imageUrl = dataDir + dataType + '/' + monthNr + '_colorbar.png';
      colorBarImage.style.backgroundImage = 'url(' + imageUrl + ')';
    }
  });
})();

var slider = (function() {
  var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan'];

  function slide(event, ui) {
    var monthNr = ui.value + 1;
    if (monthNr == 13) {
      $('#month-slider').slider('value', 0);
      monthNr = 1;
    }

    contourPlot.showOrCreateContour(monthNr);
    colorbarLegend.update(getSelectedType(), monthNr);
  }

  function update() {
    var monthNr = $('#month-slider').slider('value') + 1;
    if (monthNr == 13) {
      $('#month-slider').slider('value', 0);
      monthNr = 1;
    }

    contourPlot.showOrCreateContour(monthNr);
    colorbarLegend.update(getSelectedType(), monthNr);
  }

  function init() {
    $('#month-slider').slider({
      orientation: 'horizontal',
      range: 'min',
      min: 0,
      max: 12,
      value: 0,
      slide: slide,
      change: update
    })
    .slider('pips', {
      rest: 'label',
      labels: months
    });
    //    .slider('float');
  }

  return Object.freeze({
    update: update,
    init: init
  });
})();

var animationID = 0;

$(document).ready( function(){
  $('#info').hide();

  slider.init();

  function toggleAnimate() {
    function playAnimation() {
      var monthNr = $('#month-slider').slider('value');
      $('#month-slider').slider('value', monthNr+1);
    }

    var playButton = document.getElementById('animate-button');
    var selectedType = getSelectedType();
    if (playButton.innerHTML == 'Animate') {
      for (var i = 1; i < 13; ++i) {
        if ( !contourPlot.plotExists(selectedType, i) ) {
          contourPlot.addContours(selectedType, i);
        }
      }
      playButton.innerHTML = 'Stop';
      animationID = window.setInterval(playAnimation, 800);
    }
    else {
      window.clearInterval(animationID);
      playButton.innerHTML = 'Animate';
    }
  }

  document.getElementById('animate-button').onclick = toggleAnimate;

  document.getElementById('select-type').onchange = function() {
    var selection = getSelectedType();
    slider.update();
  };

  climateMap.on('moveend', function() {
    contourPlot.updateOnZoom();
  });

  climateMap.on('pointermove', function(evt) {
    var info = $('#info');

    function displayFeatureInfo(pixel) {
      info.css({
        left: (pixel[0] + 10) + 'px',
        top: (pixel[1] - 50) + 'px',
      });

      var feature = climateMap.forEachFeatureAtPixel(pixel, function(feature) {
        return feature;
      });

      if (feature) {
        var title = feature.get('title');
        if (title) {
          firstTooltipShown = true;
          info.text(title);
          info.show();
        } else if (!firstTooltipShown) {
          info.text('Tip: hover over lines to show their value.');
          info.show();
        } else {
          info.hide();
        }
      } else {
        info.hide();
      }
    }

    if (evt.dragging) {
      info.hide();
      return;
    }
    displayFeatureInfo(climateMap.getEventPixel(evt.originalEvent));
  });
});

window.onload = function() {
  var initialDataType = 'precipitation';
  var initialMonth = 1;

  function selectDataType(valueToSelect) {
    var element = document.getElementById('select-type');
    element.value = valueToSelect;
  }

  function setSliderValue(monthNr) {
    if (monthNr >= 13) {
      $('#month-slider').slider('value', 0);
    } else {
      $('#month-slider').slider('value', monthNr-1);
    }
  }

  selectDataType(initialDataType);
  setSliderValue(initialMonth);
  //    addContours(initialDataType, initialMonth);  // initial contour of January
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2017-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

const React = require("react");

const highlighterCode = `
function fn() {
  Array.prototype.forEach.call(
    document.querySelectorAll("pre"),
    hljs.highlightBlock
  );
};
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
  fn();
} else {
  document.addEventListener('DOMContentLoaded', fn);
}
`;

class Footer extends React.Component {
  render() {
    return (

    );
    const currentYear = new Date().getFullYear();
    return (

Docs

              Getting Started

              Examples

              FAQ

Community

              Discord

              Twitter

              Stack Overflow

More

            Blog
            GitHub

    );
  }
}

module.exports = Footer;
<commit_msg>remove pjax element from script<commit_after>/**
 * Copyright (c) 2017-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

const React = require("react");

const highlighterCode = `
function fn() {
  Array.prototype.forEach.call(
    document.querySelectorAll("pre"),
    hljs.highlightBlock
  );
};
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
  fn();
} else {
  document.addEventListener('DOMContentLoaded', fn);
}
`;

class Footer extends React.Component {
  render() {
    return (

    );
    const currentYear = new Date().getFullYear();
    return (

Docs

              Getting Started

              Examples

              FAQ

Community

              Discord

              Twitter

              Stack Overflow

More

            Blog
            GitHub

    );
  }
}

module.exports = Footer;
<|endoftext|>


<commit_before>function makeData() {
  "use strict";
  var data1 = [{name: "jon", y: 1, type: "q1"}, {name: "dan", y: 2, type: "q1"}, {name: "zoo", y: 1, type: "q1"}];
  var data2 = [{name: "jon", y: 2, type: "q2"}, {name: "dan", y: 4, type: "q2"}, {name: "zoo", y: 2, type: "q2"}];
  var data3 = [{name: "jon", y: 4, type: "q3"}, {name: "dan", y: 15, type: "q3"}, {name: "zoo", y: 15, type: "q3"}];
  return [data1, data2, data3];
}

function run(div, data, Plottable) {
  "use strict";
  var svg = div.append("svg").attr("height", 500);
  var xScale = new Plottable.Scale.Ordinal();
  var yScale = new Plottable.Scale.Linear();
  var colorScale = new Plottable.Scale.Color("10");

  var xAxis = new Plottable.Axis.Category(xScale, "bottom");
  var yAxis = new Plottable.Axis.Numeric(yScale, "left");

  if (!Plottable.Plot.StackedArea) {
    return;
  }
  var stackedAreaRenderer = new Plottable.Plot.StackedArea(xScale, yScale)
    .addDataset("d1", data[0])
    .addDataset("d2", data[1])
    .addDataset("d3", data[2])
    .project("x", "name", xScale)
    .project("y", "y", yScale)
    .project("fill", "type", colorScale);

  var center = stackedAreaRenderer.merge(new Plottable.Component.Legend(colorScale));

  new Plottable.Component.Table([
    [yAxis, center], [null, xAxis]
    ]).renderTo(svg);
}<commit_msg>Adding newline at end of file<commit_after>function makeData() {
  "use strict";
  var data1 = [{name: "jon", y: 1, type: "q1"}, {name: "dan", y: 2, type: "q1"}, {name: "zoo", y: 1, type: "q1"}];
  var data2 = [{name: "jon", y: 2, type: "q2"}, {name: "dan", y: 4, type: "q2"}, {name: "zoo", y: 2, type: "q2"}];
  var data3 = [{name: "jon", y: 4, type: "q3"}, {name: "dan", y: 15, type: "q3"}, {name: "zoo", y: 15, type: "q3"}];
  return [data1, data2, data3];
}

function run(div, data, Plottable) {
  "use strict";
  var svg = div.append("svg").attr("height", 500);
  var xScale = new Plottable.Scale.Ordinal();
  var yScale = new Plottable.Scale.Linear();
  var colorScale = new Plottable.Scale.Color("10");

  var xAxis = new Plottable.Axis.Category(xScale, "bottom");
  var yAxis = new Plottable.Axis.Numeric(yScale, "left");

  if (!Plottable.Plot.StackedArea) {
    return;
  }
  var stackedAreaRenderer = new Plottable.Plot.StackedArea(xScale, yScale)
    .addDataset("d1", data[0])
    .addDataset("d2", data[1])
    .addDataset("d3", data[2])
    .project("x", "name", xScale)
    .project("y", "y", yScale)
    .project("fill", "type", colorScale);

  var center = stackedAreaRenderer.merge(new Plottable.Component.Legend(colorScale));

  new Plottable.Component.Table([
    [yAxis, center], [null, xAxis]
    ]).renderTo(svg);
}
<|endoftext|>


<commit_before>var FuzzyTime = {
    format: function (diff) {
        var breakpoints = {
                1: ['second', 'seconds'],
                60: ['minute', 'minutes'],
                3600: ['hour', 'hours'],
                86400: ['day', 'days'],
                604800: ['week', 'weeks']
            },
            keys = [],
            i,
            prev_value = 0,
            text = '',
            value,
            unit,
            units;

        if (typeof diff !== 'number' || isNaN(diff)) {
            throw new Error('Only numbers are accepted');
        }

        if (diff === 0) {
            return 'now';
        }

        for (i in breakpoints) {
            if (breakpoints.hasOwnProperty(i)) {
                keys.push(i);
            }
        }

        do {
            value = keys.pop();
            unit = breakpoints[value];

            units = diff;
            if (prev_value) {
                units %= prev_value;
            }

            units /= value;
            units = Math.floor(Math.abs(units));

            if (units) {
                text += units + ' ' + unit[(units > 1) ? 1 : 0] + ' ';
            }

            prev_value = value;

        } while (keys.length);

        if (diff < 0) {
            text += 'before';
        } else {
            text += 'ago';
        }

        return text;
    }
};<commit_msg>Refactored the code for better readability<commit_after>var FuzzyTime = {
    _units: {
        1: ['second', 'seconds'],
        60: ['minute', 'minutes'],
        3600: ['hour', 'hours'],
        86400: ['day', 'days'],
        604800: ['week', 'weeks']
    },
    /**
     * Contains the breakpoint units.
     *
     * @type Array
     */
    _lengths: [],
    format: function (diff) {
        var keys = FuzzyTime._lengths.slice(0),
            previousLength = 0,
            text = '',
            length,
            unitName,
            unitCount;

        if (typeof diff !== 'number' || isNaN(diff)) {
            throw new Error('Only numbers are accepted');
        }

        if (diff === 0) {
            return 'now';
        }

        do {
            length = keys.pop();
            unitName = FuzzyTime._units[length];

            unitCount = diff;
            if (previousLength) {
                unitCount %= previousLength;
            }

            unitCount /= length;
            unitCount = Math.floor(Math.abs(unitCount));

            if (unitCount) {
                text += unitCount + ' ' + unitName[(unitCount > 1) ? 1 : 0] + ' ';
            }

            previousLength = length;

        } while (keys.length);

        if (diff < 0) {
            text += 'before';
        } else {
            text += 'ago';
        }

        return text;
    }
};

(function () {
    var i;

    for (i in FuzzyTime._units) {
        if (FuzzyTime._units.hasOwnProperty(i)) {
            FuzzyTime._lengths.push(parseInt(i, 10));
        }
    }

    /**
     * Ensure that our keys array is sorted, because the JS runtime does
     * not guarranty the order on which the for in iterator returns the keys.
     */
    FuzzyTime._lengths.sort(function (a, b) {
        if (a > b) {
            return 1;
        }

        if (a < b) {
            return -1;
        }

        return 0;
    });
}());<|endoftext|>


<commit_before>var Faker = require('../Faker');

// returns a single random number based on a range
exports.randomNumber = function(range) {
    var r = Math.floor(Math.random()*range);
    return r;
};

// takes an array and returns the array randomly sorted
exports.randomize = function(array) {
    var r = Math.floor(Math.random()*array.length);
    return array[r];
};

// parses string for a symbol and replace it with a random number from 1-10
exports.replaceSymbolWithNumber = function(string, symbol){

  // default symbol is '#'
  if(typeof symbol == 'undefined'){
    var symbol = '#';
  }

  var str = '';
  for(var i = 0; i < string.length; i++){
   if(string[i] == symbol){
     str += Math.floor(Math.random()*10);
   }
   else{
     str += string[i];
   }
  }
  return str;
};

// takes an array and returns it randomized
exports.shuffle = function(o){
  for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
  return o;
};

exports.generateDataSet = function(size){

};

exports.createCard = function(){

  return {
    "name":Faker.Name.findName(),
    "username":Faker.Internet.userName(),
    "email":Faker.Internet.email(),
    "address":{
      "streetA":Faker.Address.streetName(),
      "streetB":Faker.Address.streetAddress(),
      "streetC":Faker.Address.streetAddress(true),
      "streetD":Faker.Address.secondaryAddress(),
      "city":Faker.Address.city(),
      "ukCounty":Faker.Address.ukCounty(),
      "ukCountry":Faker.Address.ukCountry(),
      "zipcode":Faker.Address.zipCode()
    },
    "phone":Faker.PhoneNumber.phoneNumber(),
    "website":Faker.Internet.domainName(),
    "company":{
      "name":Faker.Company.companyName(),
      "catchPhrase":Faker.Company.catchPhrase(),
      "bs":Faker.Company.bs(),
    },
    "posts":[
     {
       "words":Faker.Lorem.words(),
       "sentence":Faker.Lorem.sentence(),
       "sentences":Faker.Lorem.sentences(),
       "paragraph":Faker.Lorem.paragraph()
     },
     {
       "words":Faker.Lorem.words(),
       "sentence":Faker.Lorem.sentence(),
       "sentences":Faker.Lorem.sentences(),
       "paragraph":Faker.Lorem.paragraph()
     },
     {
       "words":Faker.Lorem.words(),
       "sentence":Faker.Lorem.sentence(),
       "sentences":Faker.Lorem.sentences(),
       "paragraph":Faker.Lorem.paragraph()
     }
    ]
  };
};

exports.userCard = function(){

  return {
    "name":Faker.Name.findName(),
    "username":Faker.Internet.userName(),
    "email":Faker.Internet.email(),
    "address":{
      "street":Faker.Address.streetName(true),
      "suite":Faker.Address.secondaryAddress(),
      "city":Faker.Address.city(),
      "zipcode":Faker.Address.zipCode()
    },
    "phone":Faker.PhoneNumber.phoneNumber(),
    "website":Faker.Internet.domainName(),
    "company":{
      "name":Faker.Company.companyName(),
      "catchPhrase":Faker.Company.catchPhrase(),
      "bs":Faker.Company.bs(),
    },
  };
};

String.prototype.capitalize = function(){ //v1.0
    return this.replace(/\w+/g, function(a){
        return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase();
    });
};<commit_msg>linted helpers<commit_after>var Faker = require('../Faker');

// returns a single random number based on a range
exports.randomNumber = function (range) {
    var r = Math.floor(Math.random() * range);
    return r;
};

// takes an array and returns the array randomly sorted
exports.randomize = function (array) {
    var r = Math.floor(Math.random() * array.length);
    return array[r];
};

// parses string for a symbol and replace it with a random number from 1-10
exports.replaceSymbolWithNumber = function (string, symbol) {
    // default symbol is '#'
    if (typeof symbol == 'undefined') {
        symbol = '#';
    }

    var str = '';
    for (var i = 0; i < string.length; i++) {
        if (string[i] == symbol) {
            str += Math.floor(Math.random() * 10);
        }
        else {
            str += string[i];
        }
    }
    return str;
};

// takes an array and returns it randomized
exports.shuffle = function (o) {
    for (var j, x, i = o.length; i; j = parseInt(Math.random() * i, 10), x = o[--i], o[i] = o[j], o[j] = x) {
        return null; // dummy for lint.  This one-liner is a bad way to write a function.
    }
    return o;
};

var x = ['a', 'b', 'c'];
var y = exports.shuffle(x);
console.log(y);

exports.generateDataSet = function (size) {
    return undefined;
};

exports.createCard = function () {
    return {
        "name": Faker.Name.findName(),
        "username": Faker.Internet.userName(),
        "email": Faker.Internet.email(),
        "address": {
            "streetA": Faker.Address.streetName(),
            "streetB": Faker.Address.streetAddress(),
            "streetC": Faker.Address.streetAddress(true),
            "streetD": Faker.Address.secondaryAddress(),
            "city": Faker.Address.city(),
            "ukCounty": Faker.Address.ukCounty(),
            "ukCountry": Faker.Address.ukCountry(),
            "zipcode": Faker.Address.zipCode()
        },
        "phone": Faker.PhoneNumber.phoneNumber(),
        "website": Faker.Internet.domainName(),
        "company": {
            "name": Faker.Company.companyName(),
            "catchPhrase": Faker.Company.catchPhrase(),
            "bs": Faker.Company.bs(),
        },
        "posts": [
            {
                "words": Faker.Lorem.words(),
                "sentence": Faker.Lorem.sentence(),
                "sentences": Faker.Lorem.sentences(),
                "paragraph": Faker.Lorem.paragraph()
            },
            {
                "words": Faker.Lorem.words(),
                "sentence": Faker.Lorem.sentence(),
                "sentences": Faker.Lorem.sentences(),
                "paragraph": Faker.Lorem.paragraph()
            },
            {
                "words": Faker.Lorem.words(),
                "sentence": Faker.Lorem.sentence(),
                "sentences": Faker.Lorem.sentences(),
                "paragraph": Faker.Lorem.paragraph()
            }
        ]
    };
};

exports.userCard = function () {
    return {
        "name": Faker.Name.findName(),
        "username": Faker.Internet.userName(),
        "email": Faker.Internet.email(),
        "address": {
            "street": Faker.Address.streetName(true),
            "suite": Faker.Address.secondaryAddress(),
            "city": Faker.Address.city(),
            "zipcode": Faker.Address.zipCode()
        },
        "phone": Faker.PhoneNumber.phoneNumber(),
        "website": Faker.Internet.domainName(),
        "company": {
            "name": Faker.Company.companyName(),
            "catchPhrase": Faker.Company.catchPhrase(),
            "bs": Faker.Company.bs(),
        },
    };
};

String.prototype.capitalize = function () { //v1.0
    return this.replace(/\w+/g, function (a) {
        return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase();
    });
};
<|endoftext|>


<commit_before>'use strict';

const P = require('bluebird');
const mwUtil = require('../lib/mwUtil');
const Title = require('mediawiki-title').Title;
const HyperSwitch = require('hyperswitch');
const URI = HyperSwitch.URI;
const spec = HyperSwitch.utils.loadSpec(`${__dirname}/lists.yaml`);

class ReadingLists {
    /**
     * @param {!Object} options RESTBase options object.
     */
    constructor(options) {
        this.options = options;
    }

    /**
     * Transform the continuation data into a string so it is easier for clients to deal with.
     * @param {!Object|undefined} continuation Continuation object returned by the MediaWiki API.
     * @return {!String|undefined} Continuation string.
     */
    flattenContinuation(continuation) {
        return JSON.stringify(continuation);
    }

    /**
     * Inverse of flattenContinuation.
     * @param {!String|undefined} continuation Continuation string returned by flattenContinuation()
     * @return {!Object} Continuation object.
     */
    unflattenContinuation(continuation) {
        const sanitizedContinuation = {};
        if (typeof continuation === 'string') {
            try {
                continuation = JSON.parse(continuation);
                // Make sure nothing malicious can be done by splicing the continuation data
                // into the API parameters.
                const allowedKeys = ['continue', 'rlcontinue', 'rlecontinue'];
                for (const key of allowedKeys) {
                    if (typeof continuation[key] !== 'object') {
                        sanitizedContinuation[key] = continuation[key];
                    }
                }
            } catch (e) {
                this.options.log('error/unflatten', {
                    msg: e.message,
                    json: continuation,
                });
                throw new HyperSwitch.HTTPError({
                    status: 400,
                    body: {
                        type: 'server_error#invalid_paging_parameter',
                        title: 'Invalid paging parameter',
                        parameter: continuation,
                    },
                });
            }
        }
        return sanitizedContinuation;
    }

    /**
     * Convert an array of values into the format expected by the MediaWiki API.
     * @param {!Array} list A list containing strings and numbers.
     * @return {!String}
     */
    flattenMultivalue(list) {
        return list.join('|');
    }

    /**
     * Takes an array of integers and formats them as an array of {<keyword>: <id>} objects.
    * @param {!Array} ids
     * @param {!String} keyword
     * @return {!Array}
    */
    idsToObjects(ids, keyword) {
        return ids.map((id) => {
            // If the MW API has been updated to send objects, handle that gracefully.
            if (typeof id === 'object') {
                return id;
            }

            const o = {};
            o[keyword] = id;
            return o;
        });
    }

    /**
     * Get the sort parameters for the action API.
     * @param {!String} sort Sort mode ('name' or 'updated').
     * @return {!Object} { sort: <rlsort/rlesort parameter>, dir: <rldir/rledir parameter> }
     */
    getSortParameters(sort) {
        sort = sort || 'updated';
        return {
            sort,
            dir: (sort === 'updated') ? 'descending' : 'ascending',
        };
    }

    /**
     * Get a timestamp that's safe to use in GET /lists/changes/since/{timestamp} assuming
     * the client's state is based on the current response. This deals with things database rows
     * items being committed in a different order than their 'created' fields would suggest.
     * See T182706 for details.
     *
     * Normally the timstamp is just copied from the MediaWiki response, but for a transition
     * period we are going to generate it.
     * @param {!Object} responseBody The response object body.
     * @param {String} next The continuation parameter submitted by the client.
     * @return {!String} An ISO 8601 timestamp.
     */
    getContinueFrom(responseBody, next) {
        const timestamp = responseBody.query['readinglists-synctimestamp'];
        // Honor timestamps sent by the MW API.
        if (timestamp) {
            return timestamp;
        }
        // On continuation, it is expected to not have a timestamp - the client already received
        // it in an earlier request.
        if (next) {
            return undefined;
        }

        // Backdate by $wgMaxUserDBWriteDuration + 1 seconds.
        const lastSafeTime = new Date(Date.now() - 4000);
        return lastSafeTime.toISOString();
    }

    /**
     * Handle the /list/{id}/entries endpoint (get entries of a list).
     * @param {!HyperSwitch} hyper
     * @param {!Object} req The request object as provided by HyperSwitch.
     * @return {!Promise<Object>} A response promise.
     */
    getListEntries(hyper, req) {
        const sortParameters = this.getSortParameters(req.query.sort);
        return hyper.post({
            uri: new URI([req.params.domain, 'sys', 'action', 'rawquery']),
            body: {
                action: 'query',
                list: 'readinglistentries',
                rlelists: req.params.id,
                rlesort: sortParameters.sort,
                rledir: sortParameters.dir,
                rlelimit: 'max',
                continue: this.unflattenContinuation(req.query.next).continue,
                rlecontinue: this.unflattenContinuation(req.query.next).rlecontinue,
            },
        })
        .then((res) => {
            const entries = res.body.query.readinglistentries;
            const next = this.flattenContinuation(res.body.continue);

            return this.hydrateSummaries({
                status: 200,
                headers: {
                    'content-type': 'application/json; charset=utf-8;'
                        + 'profile="https://www.mediawiki.org/wiki/Specs/Lists/0.1"',
                    'cache-control': 'max-age=0, s-maxage=0',
                },
                body: {
                    entries,
                    next,
                }
            }, hyper, req);
        });
    }

    /**
     * Add data from the summary endpoint to an array of list entries.
     * @param {!Object} res Response object.
     * @param {!HyperSwitch} hyper
     * @param {!Object} req The request object as provided by HyperSwitch.
     * @return {!Promise<Array>} The objects, enriched with summaries.
     */
    hydrateSummaries(res, hyper, req) {
        return P.map(res.body.entries, (entry) => {
            return mwUtil.getSiteInfo(hyper, req, entry.project).then((siteinfo) => {
                const title = Title.newFromText(entry.title, siteinfo).getPrefixedDBKey();
                entry.summary = {
                    $merge: [
                        `${siteinfo.baseUri}/page/summary/${encodeURIComponent(title)}`,
                    ],
                };
            }).catch(() => {});
        })
        .then(() => mwUtil.hydrateResponse(res, uri => mwUtil.fetchSummary(hyper, uri)));
    }
}

module.exports = (options) => {
    const rl = new ReadingLists(options);

    return {
        spec,
        globals: {
            options,
            flattenContinuation: rl.flattenContinuation.bind(rl),
            unflattenContinuation: rl.unflattenContinuation.bind(rl),
            flattenMultivalue: rl.flattenMultivalue.bind(rl),
            idsToObjects: rl.idsToObjects.bind(rl),
            stringify: JSON.stringify.bind(JSON),
            getSortParameters: rl.getSortParameters.bind(rl),
            getContinueFrom: rl.getContinueFrom.bind(rl),
        },
        operations: {
            getListEntries: rl.getListEntries.bind(rl),
        },
    };
};
<commit_msg>Lists API: Minor: Fix param type in getContinueFrom()<commit_after>'use strict';

const P = require('bluebird');
const mwUtil = require('../lib/mwUtil');
const Title = require('mediawiki-title').Title;
const HyperSwitch = require('hyperswitch');
const URI = HyperSwitch.URI;
const spec = HyperSwitch.utils.loadSpec(`${__dirname}/lists.yaml`);

class ReadingLists {
    /**
     * @param {!Object} options RESTBase options object.
     */
    constructor(options) {
        this.options = options;
    }

    /**
     * Transform the continuation data into a string so it is easier for clients to deal with.
     * @param {!Object|undefined} continuation Continuation object returned by the MediaWiki API.
     * @return {!String|undefined} Continuation string.
     */
    flattenContinuation(continuation) {
        return JSON.stringify(continuation);
    }

    /**
     * Inverse of flattenContinuation.
     * @param {!String|undefined} continuation Continuation string returned by flattenContinuation()
     * @return {!Object} Continuation object.
     */
    unflattenContinuation(continuation) {
        const sanitizedContinuation = {};
        if (typeof continuation === 'string') {
            try {
                continuation = JSON.parse(continuation);
                // Make sure nothing malicious can be done by splicing the continuation data
                // into the API parameters.
                const allowedKeys = ['continue', 'rlcontinue', 'rlecontinue'];
                for (const key of allowedKeys) {
                    if (typeof continuation[key] !== 'object') {
                        sanitizedContinuation[key] = continuation[key];
                    }
                }
            } catch (e) {
                this.options.log('error/unflatten', {
                    msg: e.message,
                    json: continuation,
                });
                throw new HyperSwitch.HTTPError({
                    status: 400,
                    body: {
                        type: 'server_error#invalid_paging_parameter',
                        title: 'Invalid paging parameter',
                        parameter: continuation,
                    },
                });
            }
        }
        return sanitizedContinuation;
    }

    /**
     * Convert an array of values into the format expected by the MediaWiki API.
     * @param {!Array} list A list containing strings and numbers.
     * @return {!String}
     */
    flattenMultivalue(list) {
        return list.join('|');
    }

    /**
     * Takes an array of integers and formats them as an array of {<keyword>: <id>} objects.
    * @param {!Array} ids
     * @param {!String} keyword
     * @return {!Array}
    */
    idsToObjects(ids, keyword) {
        return ids.map((id) => {
            // If the MW API has been updated to send objects, handle that gracefully.
            if (typeof id === 'object') {
                return id;
            }

            const o = {};
            o[keyword] = id;
            return o;
        });
    }

    /**
     * Get the sort parameters for the action API.
     * @param {!String} sort Sort mode ('name' or 'updated').
     * @return {!Object} { sort: <rlsort/rlesort parameter>, dir: <rldir/rledir parameter> }
     */
    getSortParameters(sort) {
        sort = sort || 'updated';
        return {
            sort,
            dir: (sort === 'updated') ? 'descending' : 'ascending',
        };
    }

    /**
     * Get a timestamp that's safe to use in GET /lists/changes/since/{timestamp} assuming
     * the client's state is based on the current response. This deals with things database rows
     * items being committed in a different order than their 'created' fields would suggest.
     * See T182706 for details.
     *
     * Normally the timstamp is just copied from the MediaWiki response, but for a transition
     * period we are going to generate it.
     * @param {!Object} responseBody The response object body.
     * @param {!String} next The continuation parameter submitted by the client.
     * @return {!String} An ISO 8601 timestamp.
     */
    getContinueFrom(responseBody, next) {
        const timestamp = responseBody.query['readinglists-synctimestamp'];
        // Honor timestamps sent by the MW API.
        if (timestamp) {
            return timestamp;
        }
        // On continuation, it is expected to not have a timestamp - the client already received
        // it in an earlier request.
        if (next) {
            return undefined;
        }

        // Backdate by $wgMaxUserDBWriteDuration + 1 seconds.
        const lastSafeTime = new Date(Date.now() - 4000);
        return lastSafeTime.toISOString();
    }

    /**
     * Handle the /list/{id}/entries endpoint (get entries of a list).
     * @param {!HyperSwitch} hyper
     * @param {!Object} req The request object as provided by HyperSwitch.
     * @return {!Promise<Object>} A response promise.
     */
    getListEntries(hyper, req) {
        const sortParameters = this.getSortParameters(req.query.sort);
        return hyper.post({
            uri: new URI([req.params.domain, 'sys', 'action', 'rawquery']),
            body: {
                action: 'query',
                list: 'readinglistentries',
                rlelists: req.params.id,
                rlesort: sortParameters.sort,
                rledir: sortParameters.dir,
                rlelimit: 'max',
                continue: this.unflattenContinuation(req.query.next).continue,
                rlecontinue: this.unflattenContinuation(req.query.next).rlecontinue,
            },
        })
        .then((res) => {
            const entries = res.body.query.readinglistentries;
            const next = this.flattenContinuation(res.body.continue);

            return this.hydrateSummaries({
                status: 200,
                headers: {
                    'content-type': 'application/json; charset=utf-8;'
                        + 'profile="https://www.mediawiki.org/wiki/Specs/Lists/0.1"',
                    'cache-control': 'max-age=0, s-maxage=0',
                },
                body: {
                    entries,
                    next,
                }
            }, hyper, req);
        });
    }

    /**
     * Add data from the summary endpoint to an array of list entries.
     * @param {!Object} res Response object.
     * @param {!HyperSwitch} hyper
     * @param {!Object} req The request object as provided by HyperSwitch.
     * @return {!Promise<Array>} The objects, enriched with summaries.
     */
    hydrateSummaries(res, hyper, req) {
        return P.map(res.body.entries, (entry) => {
            return mwUtil.getSiteInfo(hyper, req, entry.project).then((siteinfo) => {
                const title = Title.newFromText(entry.title, siteinfo).getPrefixedDBKey();
                entry.summary = {
                    $merge: [
                        `${siteinfo.baseUri}/page/summary/${encodeURIComponent(title)}`,
                    ],
                };
            }).catch(() => {});
        })
        .then(() => mwUtil.hydrateResponse(res, uri => mwUtil.fetchSummary(hyper, uri)));
    }
}

module.exports = (options) => {
    const rl = new ReadingLists(options);

    return {
        spec,
        globals: {
            options,
            flattenContinuation: rl.flattenContinuation.bind(rl),
            unflattenContinuation: rl.unflattenContinuation.bind(rl),
            flattenMultivalue: rl.flattenMultivalue.bind(rl),
            idsToObjects: rl.idsToObjects.bind(rl),
            stringify: JSON.stringify.bind(JSON),
            getSortParameters: rl.getSortParameters.bind(rl),
            getContinueFrom: rl.getContinueFrom.bind(rl),
        },
        operations: {
            getListEntries: rl.getListEntries.bind(rl),
        },
    };
};
<|endoftext|>


<commit_before>'use babel'

const COLOR_EXTRACTION_REGEXP = /rgb\(\d+, ?\d+, ?\d+\)|rgba\(\d+, ?\d+, ?\d+, ?\d?\.?\d+\)|#[0-9a-f]{3,6}/g

export function isColor(textEditor, bufferPosition) {
  const lineText = textEditor.getTextInRange([[bufferPosition.row, 0], [bufferPosition.row, Infinity]])
  let matches
  while ((matches = COLOR_EXTRACTION_REGEXP.exec(lineText)) !== null) {
    const offsetStart = matches.index
    const offsetEnd = offsetStart + matches[0].length
    if (bufferPosition.column >= offsetStart && offsetEnd >= bufferPosition.column) {
      return true
    }
  }
  return false
}
<commit_msg>:bug: Reset regex state on exec<commit_after>'use babel'

export function isColor(textEditor, bufferPosition) {
  const colorRegexp = /rgb\(\d+, ?\d+, ?\d+\)|rgba\(\d+, ?\d+, ?\d+, ?\d?\.?\d+\)|#[0-9a-f]{3,6}/g
  const lineText = textEditor.getTextInRange([[bufferPosition.row, 0], [bufferPosition.row, Infinity]])

  let matches
  while ((matches = colorRegexp.exec(lineText)) !== null) {
    const offsetStart = matches.index
    const offsetEnd = offsetStart + matches[0].length
    if (bufferPosition.column >= offsetStart && offsetEnd >= bufferPosition.column) {
      return true
    }
  }

  return false
}
<|endoftext|>