<commit_before>var fs = require('fs');
var opn = require('opn');
const format = require('pretty-format')  // https://github.com/facebook/jest/tree/master/packages/pretty-format
const path = require('path')

const OUT_FILENAME = "out.html"

function visualise_digraph(brain) {
  let sites = new Set()
  let result = ''
  let site_dot = '[shape=box,color=grey,fillcolor=lightgrey,fontcolor=Red]'
  for (let python of brain.pythons) {
    result += `  "${r(python.path)}" [shape=box,color=green,fillcolor=lightgreen,fontsize=18]\n`
    result += `  "${r(python.path)}" -> "${r(python.pip_module_site_package_path)}" [style=dotted]\n`
    sites.add(r(python.pip_module_site_package_path))
  }
  for (let pip of brain.pips) {
    result += `  "${r(pip.path)}" [shape=box]\n`
    result += `  "${r(pip.path)}" -> "${r(pip.site_package_path)}" [style=dotted]\n`
    sites.add(r(pip.site_package_path))

    let relationship
    Object.keys(pip.site_relationships).forEach( key => {
      if (pip.site_relationships[key])
        result += `  "${r(pip.path)}" -> "${r(key)}" [color=blue]\n`  // pip to python relationship
    });

  }
  for (let site of sites)
    result += `  "${r(site)}" ${site_dot};\n`
  return `digraph G {\n${result}\n}`
}
function visualise_digraphs(brain) {
  // returns multiple digraphs in a list of dicts
  let results = []
  let site_dot = '[shape=box,color=grey,fillcolor=lightgrey,fontcolor=Red]'
  for (let python of brain.pythons) {
    let sites = new Set()
    let result = ''
    result += `  "${r(python.path)}" [shape=box,color=green,fillcolor=lightgreen,fontsize=18]\n`
    result += `  "${r(python.path)}" -> "${r(python.pip_module_site_package_path)}" [style=dotted]\n`
    sites.add(r(python.pip_module_site_package_path))

    for (let pip of brain.pips) {
      let relationship
      Object.keys(pip.site_relationships).forEach( key => {
        if (key == python.path && pip.site_relationships[key]) {
          result += `  "${r(pip.path)}" [shape=box]\n`
          result += `  "${r(pip.path)}" -> "${r(pip.site_package_path)}" [style=dotted]\n`
          sites.add(r(pip.site_package_path))

          result += `  "${r(pip.path)}" -> "${r(key)}" [color=blue]\n`  // pip to python relationship
        }
      });
    }
    for (let site of sites)
      result += `  "${r(site)}" ${site_dot};\n`

    let final_obj = {}
    final_obj.digraph = `digraph G {\n${result}\n}`
    final_obj.python = python
    results.push( final_obj )
  }
  return results
}

function r(s) {
  // return s.replace(/\//g, ' / ')
  // return s.replace(/\/usr/g, '/usr\n')
  return s
}

function viz1(digraph_text) {
  let template = `

Pipfix Visualisation

Blue lines are pip's pointing to pythons.
    Square boxes are site packages directories.
    Dotted lines are pointers to site package locations.

    `
    return template
}

function viz2(digraph_text) {
  let template = `

    <title>Pipfix Visualisation</title>

    <link href="vis.min.css" rel="stylesheet" type="text/css" />

Pipfix Visualisation

Blue lines are pip's pointing to pythons.
    Square boxes are site packages directories.
    Dotted lines are pointers to site package locations.

    `
    return template
}

function viz3(digraph_text) {
  let template = `
  <!doctype html>

    <title>Viz</title>

      Hi

    `
    return template
}

function viz3_multiple(digraph_objs) {
  let template = `
  <!doctype html>

    <title>Pipfix Visualisation</title>

    <link href="out.css" rel="stylesheet" type="text/css" />

      Pipfix Report

    `
  return template
}

function write_to_file(html_text) {
  fs.writeFile(OUT_FILENAME, html_text, function(err) {
      if(err) {
          return console.log(err);
      }

      console.log(`The file '${OUT_FILENAME}' was saved!`);

      // opens the url in the default browser
      opn(OUT_FILENAME);
      console.log('done opening file')
      process.exit()

      // specify the app to open in
      // opn('http://sindresorhus.com', {app: 'firefox'});
  });
}

function visualise_all_in_one(brain, logger) {
  let digraph_text = visualise_digraph(brain)
  logger.debug(digraph_text)  // verbose
  let html = viz3(digraph_text)
  write_to_file(html)

}

function visualise(brain, logger) {
  let digraph_objs = visualise_digraphs(brain)
  for (let digraph_obj of digraph_objs) {
    logger.debug(digraph_objs.digraph)  // verbose
  }
  let html = viz3_multiple(digraph_objs)
  write_to_file(html)
}

exports.visualise = visualise

<commit_msg>improved styling<commit_after>var fs = require('fs');
var opn = require('opn');
const format = require('pretty-format')  // https://github.com/facebook/jest/tree/master/packages/pretty-format
const path = require('path')

const OUT_FILENAME = "out.html"
const site_dot = '[shape=box,color=grey,fontcolor=dimgrey]'

function visualise_digraph(brain) {
  let sites = new Set()
  let result = ''
  for (let python of brain.pythons) {
    result += `  "${r(python.path)}" [shape=box,color=green,fillcolor=lightgreen,fontsize=18]\n`
    result += `  "${r(python.path)}" -> "${r(python.pip_module_site_package_path)}" [style=dotted]\n`
    sites.add(r(python.pip_module_site_package_path))
  }
  for (let pip of brain.pips) {
    result += `  "${r(pip.path)}" [shape=box]\n`
    result += `  "${r(pip.path)}" -> "${r(pip.site_package_path)}" [style=dotted]\n`
    sites.add(r(pip.site_package_path))

    let relationship
    Object.keys(pip.site_relationships).forEach( key => {
      if (pip.site_relationships[key])
        result += `  "${r(pip.path)}" -> "${r(key)}" [color=blue]\n`  // pip to python relationship
    });

  }
  for (let site of sites)
    result += `  "${r(site)}" ${site_dot};\n`
  return `digraph G {\n${result}\n}`
}
function visualise_digraphs(brain) {
  // returns multiple digraphs in a list of dicts
  let results = []
  for (let python of brain.pythons) {
    let sites = new Set()
    let result = ''
    result += `  "${r(python.path)}" [shape=component,style="bold",color=green,fillcolor=lightgreen,fontsize=18]\n`
    result += `  "${r(python.path)}" -> "${r(python.pip_module_site_package_path)}" [style=dotted]\n`
    sites.add(r(python.pip_module_site_package_path))

    for (let pip of brain.pips) {
      let relationship
      Object.keys(pip.site_relationships).forEach( key => {
        if (key == python.path && pip.site_relationships[key]) {
          result += `  "${r(pip.path)}" [shape=box]\n`
          result += `  "${r(pip.path)}" -> "${r(pip.site_package_path)}" [style=dotted]\n`
          sites.add(r(pip.site_package_path))

          result += `  "${r(pip.path)}" -> "${r(key)}" [color=blue]\n`  // pip to python relationship
        }
      });
    }
    for (let site of sites)
      result += `  "${r(site)}" ${site_dot};\n`

    let final_obj = {}
    final_obj.digraph = `digraph G {\n${result}\n}`
    final_obj.python = python
    results.push( final_obj )
  }
  return results
}

function r(s) {
  // return s.replace(/\//g, ' / ')
  // return s.replace(/\/usr/g, '/usr\n')
  return s
}

function viz1(digraph_text) {
  let template = `

Pipfix Visualisation

Blue lines are pip's pointing to pythons.
    Square boxes are site packages directories.
    Dotted lines are pointers to site package locations.

    `
    return template
}

function viz2(digraph_text) {
  let template = `

    <title>Pipfix Visualisation</title>

    <link href="vis.min.css" rel="stylesheet" type="text/css" />

Pipfix Visualisation

Blue lines are pip's pointing to pythons.
    Square boxes are site packages directories.
    Dotted lines are pointers to site package locations.

    `
    return template
}

function viz3(digraph_text) {
  let template = `
  <!doctype html>

    <title>Viz</title>

      Hi

    `
    return template
}

function viz3_multiple(digraph_objs) {
  let template = `
  <!doctype html>

    <title>Pipfix Visualisation</title>

    <link href="out.css" rel="stylesheet" type="text/css" />

      Pipfix Report

    `
  return template
}

function write_to_file(html_text) {
  fs.writeFile(OUT_FILENAME, html_text, function(err) {
      if(err) {
          return console.log(err);
      }

      console.log(`The file '${OUT_FILENAME}' was saved!`);

      // opens the url in the default browser
      opn(OUT_FILENAME);
      console.log('done opening file')
      process.exit()

      // specify the app to open in
      // opn('http://sindresorhus.com', {app: 'firefox'});
  });
}

function visualise_all_in_one(brain, logger) {
  let digraph_text = visualise_digraph(brain)
  logger.debug(digraph_text)  // verbose
  let html = viz3(digraph_text)
  write_to_file(html)

}

function visualise(brain, logger) {
  let digraph_objs = visualise_digraphs(brain)
  for (let digraph_obj of digraph_objs) {
    logger.debug(digraph_objs.digraph)  // verbose
  }
  let html = viz3_multiple(digraph_objs)
  write_to_file(html)
}

exports.visualise = visualise

<|endoftext|>


<commit_before>var Timer = require('../util/timer');
var Thread = require('./thread');
var execute = require('./execute.js');

var Sequencer = function (runtime) {
    /**
     * A utility timer for timing thread sequencing.
     * @type {!Timer}
     */
    this.timer = new Timer();

    /**
     * Reference to the runtime owning this sequencer.
     * @type {!Runtime}
     */
    this.runtime = runtime;
};

/**
 * Time to run a warp-mode thread, in ms.
 * @type {number}
 */
Sequencer.WARP_TIME = 500;

/**
 * Step through all threads in `this.runtime.threads`, running them in order.
 * @return {Array.<!Thread>} List of inactive threads after stepping.
 */
Sequencer.prototype.stepThreads = function () {
    // Work time is 75% of the thread stepping interval.
    var WORK_TIME = 0.75 * this.runtime.currentStepTime;
    // Start counting toward WORK_TIME.
    this.timer.start();
    // Count of active threads.
    var numActiveThreads = Infinity;
    // Whether `stepThreads` has run through a full single tick.
    var ranFirstTick = false;
    var doneThreads = [];
    // Conditions for continuing to stepping threads:
    // 1. We must have threads in the list, and some must be active.
    // 2. Time elapsed must be less than WORK_TIME.
    // 3. Either turbo mode, or no redraw has been requested by a primitive.
    while (this.runtime.threads.length > 0 &&
           numActiveThreads > 0 &&
           this.timer.timeElapsed() < WORK_TIME &&
           (this.runtime.turboMode || !this.runtime.redrawRequested)) {
        numActiveThreads = 0;
        // Attempt to run each thread one time.
        for (var i = 0; i < this.runtime.threads.length; i++) {
            var activeThread = this.runtime.threads[i];
            if (activeThread.stack.length === 0 ||
                activeThread.status === Thread.STATUS_DONE) {
                // Finished with this thread.
                if (doneThreads.indexOf(activeThread) < 0) {
                    doneThreads.push(activeThread);
                }
                continue;
            }
            if (activeThread.status === Thread.STATUS_YIELD_TICK &&
                !ranFirstTick) {
                // Clear single-tick yield from the last call of `stepThreads`.
                activeThread.status = Thread.STATUS_RUNNING;
            }
            if (activeThread.status === Thread.STATUS_RUNNING ||
                activeThread.status === Thread.STATUS_YIELD) {
                // Normal-mode thread: step.
                this.stepThread(activeThread);
                activeThread.warpTimer = null;
            }
            if (activeThread.status === Thread.STATUS_RUNNING) {
                numActiveThreads++;
            }
        }
        // We successfully ticked once. Prevents running STATUS_YIELD_TICK
        // threads on the next tick.
        ranFirstTick = true;
    }
    // Filter inactive threads from `this.runtime.threads`.
    this.runtime.threads = this.runtime.threads.filter(function (thread) {
        if (doneThreads.indexOf(thread) > -1) {
            return false;
        }
        return true;
    });
    return doneThreads;
};

/**
 * Step the requested thread for as long as necessary.
 * @param {!Thread} thread Thread object to step.
 */
Sequencer.prototype.stepThread = function (thread) {
    var currentBlockId = thread.peekStack();
    if (!currentBlockId) {
        // A "null block" - empty branch.
        thread.popStack();
    }
    while (thread.peekStack()) {
        var isWarpMode = thread.peekStackFrame().warpMode;
        if (isWarpMode && !thread.warpTimer) {
            // Initialize warp-mode timer if it hasn't been already.
            // This will start counting the thread toward `Sequencer.WARP_TIME`.
            thread.warpTimer = new Timer();
            thread.warpTimer.start();
        }
        // Execute the current block.
        // Save the current block ID to notice if we did control flow.
        currentBlockId = thread.peekStack();
        execute(this, thread);
        thread.blockGlowInFrame = currentBlockId;
        // If the thread has yielded or is waiting, yield to other threads.
        if (thread.status === Thread.STATUS_YIELD) {
            // Mark as running for next iteration.
            thread.status = Thread.STATUS_RUNNING;
            // In warp mode, yielded blocks are re-executed immediately.
            if (isWarpMode &&
                thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) {
                continue;
            }
            return;
        } else if (thread.status === Thread.STATUS_PROMISE_WAIT) {
            // A promise was returned by the primitive. Yield the thread
            // until the promise resolves. Promise resolution should reset
            // thread.status to Thread.STATUS_RUNNING.
            return;
        }
        // If no control flow has happened, switch to next block.
        if (thread.peekStack() === currentBlockId) {
            thread.goToNextBlock();
        }
        // If no next block has been found at this point, look on the stack.
        while (!thread.peekStack()) {
            thread.popStack();

            if (thread.stack.length === 0) {
                // No more stack to run!
                thread.status = Thread.STATUS_DONE;
                return;
            }

			var stackFrame = thread.peekStackFrame();
	        isWarpMode = stackFrame.warpMode;

            if (stackFrame.isLoop) {
                // The current level of the stack is marked as a loop.
                // Return to yield for the frame/tick in general.
                // Unless we're in warp mode - then only return if the
                // warp timer is up.
                if (!isWarpMode ||
                    thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
                    // Don't do anything to the stack, since loops need
                    // to be re-executed.
                    return;
                } else {
                    // Don't go to the next block for this level of the stack,
                    // since loops need to be re-executed.
                    continue;
                }
            } else if (stackFrame.waitingReporter) {
                // This level of the stack was waiting for a value.
                // This means a reporter has just returned - so don't go
                // to the next block for this level of the stack.
                return;
            }
            // Get next block of existing block on the stack.
            thread.goToNextBlock();
        }
    }
};

/**
 * Step a thread into a block's branch.
 * @param {!Thread} thread Thread object to step to branch.
 * @param {Number} branchNum Which branch to step to (i.e., 1, 2).
 * @param {Boolean} isLoop Whether this block is a loop.
 */
Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) {
    if (!branchNum) {
        branchNum = 1;
    }
    var currentBlockId = thread.peekStack();
    var branchId = thread.target.blocks.getBranch(
        currentBlockId,
        branchNum
    );
    thread.peekStackFrame().isLoop = isLoop;
    if (branchId) {
        // Push branch ID to the thread's stack.
        thread.pushStack(branchId);
    } else {
        thread.pushStack(null);
    }
};

/**
 * Step a procedure.
 * @param {!Thread} thread Thread object to step to procedure.
 * @param {!string} procedureCode Procedure code of procedure to step to.
 */
Sequencer.prototype.stepToProcedure = function (thread, procedureCode) {
    var definition = thread.target.blocks.getProcedureDefinition(procedureCode);
    if (!definition) {
        return;
    }
    // Check if the call is recursive.
    // If so, set the thread to yield after pushing.
    var isRecursive = thread.isRecursiveCall(procedureCode);
    // To step to a procedure, we put its definition on the stack.
    // Execution for the thread will proceed through the definition hat
    // and on to the main definition of the procedure.
    // When that set of blocks finishes executing, it will be popped
    // from the stack by the sequencer, returning control to the caller.
    thread.pushStack(definition);
    // In known warp-mode threads, only yield when time is up.
    if (thread.peekStackFrame().warpMode &&
        thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
        thread.status = Thread.STATUS_YIELD;
    } else {
        // Look for warp-mode flag on definition, and set the thread
        // to warp-mode if needed.
        var definitionBlock = thread.target.blocks.getBlock(definition);
        var doWarp = definitionBlock.mutation.warp;
        if (doWarp) {
            thread.peekStackFrame().warpMode = true;
        } else {
            // In normal-mode threads, yield any time we have a recursive call.
            if (isRecursive) {
                thread.status = Thread.STATUS_YIELD;
            }
        }
    }
};

/**
 * Retire a thread in the middle, without considering further blocks.
 * @param {!Thread} thread Thread object to retire.
 */
Sequencer.prototype.retireThread = function (thread) {
    thread.stack = [];
    thread.stackFrame = [];
    thread.requestScriptGlowInFrame = false;
    thread.status = Thread.STATUS_DONE;
};

module.exports = Sequencer;
<commit_msg>Fix whitespace issues<commit_after>var Timer = require('../util/timer');
var Thread = require('./thread');
var execute = require('./execute.js');

var Sequencer = function (runtime) {
    /**
     * A utility timer for timing thread sequencing.
     * @type {!Timer}
     */
    this.timer = new Timer();

    /**
     * Reference to the runtime owning this sequencer.
     * @type {!Runtime}
     */
    this.runtime = runtime;
};

/**
 * Time to run a warp-mode thread, in ms.
 * @type {number}
 */
Sequencer.WARP_TIME = 500;

/**
 * Step through all threads in `this.runtime.threads`, running them in order.
 * @return {Array.<!Thread>} List of inactive threads after stepping.
 */
Sequencer.prototype.stepThreads = function () {
    // Work time is 75% of the thread stepping interval.
    var WORK_TIME = 0.75 * this.runtime.currentStepTime;
    // Start counting toward WORK_TIME.
    this.timer.start();
    // Count of active threads.
    var numActiveThreads = Infinity;
    // Whether `stepThreads` has run through a full single tick.
    var ranFirstTick = false;
    var doneThreads = [];
    // Conditions for continuing to stepping threads:
    // 1. We must have threads in the list, and some must be active.
    // 2. Time elapsed must be less than WORK_TIME.
    // 3. Either turbo mode, or no redraw has been requested by a primitive.
    while (this.runtime.threads.length > 0 &&
           numActiveThreads > 0 &&
           this.timer.timeElapsed() < WORK_TIME &&
           (this.runtime.turboMode || !this.runtime.redrawRequested)) {
        numActiveThreads = 0;
        // Attempt to run each thread one time.
        for (var i = 0; i < this.runtime.threads.length; i++) {
            var activeThread = this.runtime.threads[i];
            if (activeThread.stack.length === 0 ||
                activeThread.status === Thread.STATUS_DONE) {
                // Finished with this thread.
                if (doneThreads.indexOf(activeThread) < 0) {
                    doneThreads.push(activeThread);
                }
                continue;
            }
            if (activeThread.status === Thread.STATUS_YIELD_TICK &&
                !ranFirstTick) {
                // Clear single-tick yield from the last call of `stepThreads`.
                activeThread.status = Thread.STATUS_RUNNING;
            }
            if (activeThread.status === Thread.STATUS_RUNNING ||
                activeThread.status === Thread.STATUS_YIELD) {
                // Normal-mode thread: step.
                this.stepThread(activeThread);
                activeThread.warpTimer = null;
            }
            if (activeThread.status === Thread.STATUS_RUNNING) {
                numActiveThreads++;
            }
        }
        // We successfully ticked once. Prevents running STATUS_YIELD_TICK
        // threads on the next tick.
        ranFirstTick = true;
    }
    // Filter inactive threads from `this.runtime.threads`.
    this.runtime.threads = this.runtime.threads.filter(function (thread) {
        if (doneThreads.indexOf(thread) > -1) {
            return false;
        }
        return true;
    });
    return doneThreads;
};

/**
 * Step the requested thread for as long as necessary.
 * @param {!Thread} thread Thread object to step.
 */
Sequencer.prototype.stepThread = function (thread) {
    var currentBlockId = thread.peekStack();
    if (!currentBlockId) {
        // A "null block" - empty branch.
        thread.popStack();
    }
    while (thread.peekStack()) {
        var isWarpMode = thread.peekStackFrame().warpMode;
        if (isWarpMode && !thread.warpTimer) {
            // Initialize warp-mode timer if it hasn't been already.
            // This will start counting the thread toward `Sequencer.WARP_TIME`.
            thread.warpTimer = new Timer();
            thread.warpTimer.start();
        }
        // Execute the current block.
        // Save the current block ID to notice if we did control flow.
        currentBlockId = thread.peekStack();
        execute(this, thread);
        thread.blockGlowInFrame = currentBlockId;
        // If the thread has yielded or is waiting, yield to other threads.
        if (thread.status === Thread.STATUS_YIELD) {
            // Mark as running for next iteration.
            thread.status = Thread.STATUS_RUNNING;
            // In warp mode, yielded blocks are re-executed immediately.
            if (isWarpMode &&
                thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) {
                continue;
            }
            return;
        } else if (thread.status === Thread.STATUS_PROMISE_WAIT) {
            // A promise was returned by the primitive. Yield the thread
            // until the promise resolves. Promise resolution should reset
            // thread.status to Thread.STATUS_RUNNING.
            return;
        }
        // If no control flow has happened, switch to next block.
        if (thread.peekStack() === currentBlockId) {
            thread.goToNextBlock();
        }
        // If no next block has been found at this point, look on the stack.
        while (!thread.peekStack()) {
            thread.popStack();

            if (thread.stack.length === 0) {
                // No more stack to run!
                thread.status = Thread.STATUS_DONE;
                return;
            }

            var stackFrame = thread.peekStackFrame();
            isWarpMode = stackFrame.warpMode;

            if (stackFrame.isLoop) {
                // The current level of the stack is marked as a loop.
                // Return to yield for the frame/tick in general.
                // Unless we're in warp mode - then only return if the
                // warp timer is up.
                if (!isWarpMode ||
                    thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
                    // Don't do anything to the stack, since loops need
                    // to be re-executed.
                    return;
                } else {
                    // Don't go to the next block for this level of the stack,
                    // since loops need to be re-executed.
                    continue;
                }
            } else if (stackFrame.waitingReporter) {
                // This level of the stack was waiting for a value.
                // This means a reporter has just returned - so don't go
                // to the next block for this level of the stack.
                return;
            }
            // Get next block of existing block on the stack.
            thread.goToNextBlock();
        }
    }
};

/**
 * Step a thread into a block's branch.
 * @param {!Thread} thread Thread object to step to branch.
 * @param {Number} branchNum Which branch to step to (i.e., 1, 2).
 * @param {Boolean} isLoop Whether this block is a loop.
 */
Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) {
    if (!branchNum) {
        branchNum = 1;
    }
    var currentBlockId = thread.peekStack();
    var branchId = thread.target.blocks.getBranch(
        currentBlockId,
        branchNum
    );
    thread.peekStackFrame().isLoop = isLoop;
    if (branchId) {
        // Push branch ID to the thread's stack.
        thread.pushStack(branchId);
    } else {
        thread.pushStack(null);
    }
};

/**
 * Step a procedure.
 * @param {!Thread} thread Thread object to step to procedure.
 * @param {!string} procedureCode Procedure code of procedure to step to.
 */
Sequencer.prototype.stepToProcedure = function (thread, procedureCode) {
    var definition = thread.target.blocks.getProcedureDefinition(procedureCode);
    if (!definition) {
        return;
    }
    // Check if the call is recursive.
    // If so, set the thread to yield after pushing.
    var isRecursive = thread.isRecursiveCall(procedureCode);
    // To step to a procedure, we put its definition on the stack.
    // Execution for the thread will proceed through the definition hat
    // and on to the main definition of the procedure.
    // When that set of blocks finishes executing, it will be popped
    // from the stack by the sequencer, returning control to the caller.
    thread.pushStack(definition);
    // In known warp-mode threads, only yield when time is up.
    if (thread.peekStackFrame().warpMode &&
        thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
        thread.status = Thread.STATUS_YIELD;
    } else {
        // Look for warp-mode flag on definition, and set the thread
        // to warp-mode if needed.
        var definitionBlock = thread.target.blocks.getBlock(definition);
        var doWarp = definitionBlock.mutation.warp;
        if (doWarp) {
            thread.peekStackFrame().warpMode = true;
        } else {
            // In normal-mode threads, yield any time we have a recursive call.
            if (isRecursive) {
                thread.status = Thread.STATUS_YIELD;
            }
        }
    }
};

/**
 * Retire a thread in the middle, without considering further blocks.
 * @param {!Thread} thread Thread object to retire.
 */
Sequencer.prototype.retireThread = function (thread) {
    thread.stack = [];
    thread.stackFrame = [];
    thread.requestScriptGlowInFrame = false;
    thread.status = Thread.STATUS_DONE;
};

module.exports = Sequencer;
<|endoftext|>


<commit_before>/**
 * The Markdown Renderer uses the underlying MDReactComponent class to render markdown.
 * We use special options and functions to turn the Markdown AST Nodes into React components.
 * In our case we turn them into Spectacle Presentation Framework Components. Additionally,
 * we've added the support for inline HTML nodes and classes.
 */

import React from 'react';
import Debug from 'debug';
import katex from 'katex';
import mkitm from 'markdown-it-math';
import isString from 'lodash/isString';
import { S, Code, BlockQuote, Quote, Heading, Image, Link, Text, ListItem, List } from "spectacle/lib/index";
import Decorate from 'markdown-it-decorate';
import MarkdownContainer from '../../util/markdown-it-container';

import MDReactComponent from './MDReactComponent';
import Math from './Math';
import Spoiler from './Spoiler';
import MarkdownHTMLElement from './MarkdownHTMLElement';
import Highlight from './Highlight';
import DefaultWrapper from './DefaultWrapper';

const debug = Debug('webbox:presentation:markdownRenderer');

const EMPTY_HTML_ELEMENTS = {
  'area': true,
  'base': true,
  'br': true,
  'col': true,
  'colgroup': false,
  'command': true,
  'embed': true,
  'hr': true,
  'img': true,
  'input': true,
  'keygen': true,
  'link': true,
  'meta': true,
  'param': true,
  'source': true,
  'track': true,
  'wbr': true
};

function regexIndexOf (str, regex, startpos=0) {
  const indexOf = str.substring(startpos).search(regex);
  return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf;
}

/**
 * Transforms any inline HTML from text to real HTML (react)
 */
function makeChildren(children, props, options) {
  let result = [];
  let keyCounter = 0;
  let openTags = 0;
  let htmlContent = [];

  if (!Array.isArray(children)) {
    children = [].push(children);
  }

  /**
   * Push all normal children to result.
   * If we find an opening tag, we push the content to the htmlContent
   *  -> now every child is pushed to htmlContent until we find an closing tag
   *  -> if there are multiple opening tags, we wait until we found the last closing one
   *
   * WARNING: This will fail if somebody injects errornous HTML code, which prevents to render the node at all.
   */

  // ToDo: Handle tags without children: https://github.com/component/domify/blob/master/index.js
  // ToDo: Check if we something left on the stack and print an error message, that the user has input invalid HTML

  for (let child of children) {
    if (child.type && child.type === 'htmlinline') {
      let groups = /<([\w:]+)/.exec(child.content);
      let elementTag = groups && groups.length === 2 ? groups[1] : null;
      let isEmptyElement = EMPTY_HTML_ELEMENTS[elementTag] === true;

      // Skip forbidden tags
      if (isString(elementTag) &&  options.escapeTags[elementTag.toLowerCase()] === true) {
        continue;
      }

      // 1. Is opening Tag?
      if (regexIndexOf(child.content, /<\s*\/.*>/) < 0 && !isEmptyElement) {
        openTags += 1;
        htmlContent.push(child.content);
      } else if (isEmptyElement) {
        // when we deal with an empty element, just add this and proceed
        let {key, ...rest} = props; // Extract key from props
        keyCounter += 1;
        result.push(<MarkdownHTMLElement key={`${key}-inlinehtml-${keyCounter}`} {...rest} displayMode={false} content={child.content} />);
      } else {
        // closing tag
        openTags -= 1;
        htmlContent.push(child.content);

        // done
        if (openTags === 0) {
          let {key, ...rest} = props; // Extract key from props
          result.push(<MarkdownHTMLElement key={`${key}-inlinehtml-${keyCounter}`} {...rest} displayMode={false} content={htmlContent.join('')} />);
          keyCounter += 1;
          htmlContent = []; // Reset
        }
      }
    } else if (openTags > 0) {
      // add child to html
      htmlContent.push(child);
    } else {
      result.push(child);
    }
  }

  return result;
}

/**
 * Markdown Options that are passed to MDReactComponent and then to markdown-it
 */
export const mdOptions = {
  onIterate: function onIterate(tag, props, children, level, options) {
    /**
     * The onIterate function is doing the actual work. It transforms the Mardown Nodes
     * to React Nodes and passes in the props and children.
     */
    let lang;
    let content;
    let displayMode;
    let source;
    let matches;
    let summary;

    if (props.class != null) {
      props.className = props.class;
    }

    // Maybe we need here a more generic solution with a simple mapping
    if (props['max-width']) {
      if (props.style == null) {
        props.style = {};
      }

      props.style.maxWidth = props['max-width'];
    }

    // Remove propagated source prop
    if (props.source) {
      delete props.source;
    }

    switch (tag) {
      case 'a':
        content = makeChildren(children, props, options.htmlOptions);
        return <Link href={props.href} target="_blank" {...props}>{content}</Link>;

      case 'code':
        content = Array.isArray(children) ? children.join('') : children;
        return {content};

      case 'pre':
        if (isString(children[0])) {
          source = children[0];
        } else {
          source = children[0].props.children;
        }

        if (Array.isArray(source)) {
          source = source.join('');
        }

        lang = children[0].props['data-language'] || null;
        return <Highlight lang={lang} source={source} {...props} />;

      case 'p':
        content = makeChildren(children, props, options.htmlOptions);
        return <Text lineHeight={1.2} {...props}>{content}</Text>;

      case 'img':
        return <Image src={props.src} {...props} />;

      case 'h1':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={1} {...props}>{content}</Heading>;
      case 'h2':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={2} {...props}>{content}</Heading>;
      case 'h3':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={3} {...props}>{content}</Heading>;
      case 'h4':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={4} {...props}>{content}</Heading>;
      case 'h5':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={5} {...props}>{content}</Heading>;
      case 'h6':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={6} {...props}>{content}</Heading>;

      case 'em':
        content = makeChildren(children, props, options.htmlOptions);
        return <S type='italic' {...props}>{content}</S>;

      case 'del':
        content = makeChildren(children, props, options.htmlOptions);
        return <S type='strikethrough' {...props}>{content}</S>;

      case 'strong':
        content = makeChildren(children, props, options.htmlOptions);
        return <S type='bold' {...props}>{content}</S>;

      case 'blockquote':
        content = makeChildren(children, props, options.htmlOptions);
        return
<Quote>{content}</Quote>
;

      case 'li':
        content = makeChildren(children, props, options.htmlOptions);
        return <ListItem {...props}>{content}</ListItem>;

      case 'ul':
        return <List {...props}>{children}</List>;

      case 'ol':
        return <List ordered {...props}>{children}</List>;

      case 'math':
        displayMode = props.display === 'inline' ? false : true;
        children = Array.isArray(children) ? children.join('') : children;
        return <Math displayMode={displayMode} {...props}>{children}</Math>;

      /* Spoiler (markdown-it container) rule */
      case 'div':
        matches = props['data-info'].trim().match(/^spoiler\s+(.*)$/);

        if (matches != null && matches.length === 2) {
          summary = matches[1];
        } else {
          // no matches, so just use createElement
          return null;
        }

        content = makeChildren(children, props, options.htmlOptions);
        return <Spoiler summary={summary} {...props}>{content}</Spoiler>;

      case 'htmlblock':
        content = Array.isArray(children) ? children.join('') : children;
        return <MarkdownHTMLElement content={content} {...props} />;

      /**
       * Special inline HTML treatment to embed the HTML as HTML-Nodes and not as escaped text.
       * Requires to use makeChildren function to get the transformed list of children.
       */
      case 'htmlinline':
        content = Array.isArray(children) ? children.join('') : children;
        return {
          type: 'htmlinline',
          content
        };

      case 'wrapper':
        return <DefaultWrapper {...props}>{children}</DefaultWrapper>;

      default:
        return null; // now MDReactComponent is going to create the tag with React.createElement
    }

  },
  plugins: [
    Decorate,
    {
      plugin: mkitm,
      args: [{
        inlineOpen: '$',
        inlineClose: '$',
        blockOpen: '$$',
        blockClose: '$$',
        inlineRenderer: function (str) {
          return katex.renderToString(str, {
            displayMode: false,
            throwOnError: false
          });
        },
        blockRenderer: function (str) {
          return katex.renderToString(str, {
            displayMode: true,
            throwOnError: false
          });
        }
      }]
    },
    {
      plugin: MarkdownContainer,
      args: [
        'spoiler',
        {
          validate: function(params) {
            console.log('spoiler validate function', params);
            return params.trim().match(/^spoiler\s+(.*)$/);
          },
          render: function (tokens, idx) {
            console.log('spoiler render function');
            var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);

            if (tokens[idx].nesting === 1) {
              // opening tag
              return '' + m[1] + '\n';

            } else {
              // closing tag
              return '\n';
            }
          }
        }
      ]
    }
  ],
  markdownOptions: {
    html: true,
    linkify: true
  },
  htmlOptions: {
    escapeTags: {
      script: true
    }
  }
};

/**
 * Transforms markdown text to React Nodes. Highlights code with highlight.js, uses KaTeX for math rendering.
 * Allows to use inline html inside paragraphs and list items.
 */
export function toMarkdownComponent(props) {
  return <MDReactComponent text={props.source} {...mdOptions} {...props} />;
}<commit_msg>Fixes broken rendering of code fences inside markdown<commit_after>/**
 * The Markdown Renderer uses the underlying MDReactComponent class to render markdown.
 * We use special options and functions to turn the Markdown AST Nodes into React components.
 * In our case we turn them into Spectacle Presentation Framework Components. Additionally,
 * we've added the support for inline HTML nodes and classes.
 */

import React from 'react';
import Debug from 'debug';
import katex from 'katex';
import mkitm from 'markdown-it-math';
import isString from 'lodash/isString';
import { S, Code, BlockQuote, Quote, Heading, Image, Link, Text, ListItem, List } from "spectacle/lib/index";
import Decorate from 'markdown-it-decorate';
import MarkdownContainer from '../../util/markdown-it-container';

import MDReactComponent from './MDReactComponent';
import Math from './Math';
import Spoiler from './Spoiler';
import MarkdownHTMLElement from './MarkdownHTMLElement';
import Highlight from './Highlight';
import DefaultWrapper from './DefaultWrapper';

const debug = Debug('webbox:presentation:markdownRenderer');

const EMPTY_HTML_ELEMENTS = {
  'area': true,
  'base': true,
  'br': true,
  'col': true,
  'colgroup': false,
  'command': true,
  'embed': true,
  'hr': true,
  'img': true,
  'input': true,
  'keygen': true,
  'link': true,
  'meta': true,
  'param': true,
  'source': true,
  'track': true,
  'wbr': true
};

function regexIndexOf (str, regex, startpos=0) {
  const indexOf = str.substring(startpos).search(regex);
  return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf;
}

/**
 * Transforms any inline HTML from text to real HTML (react)
 */
function makeChildren(children, props, options) {
  let result = [];
  let keyCounter = 0;
  let openTags = 0;
  let htmlContent = [];

  if (!Array.isArray(children)) {
    children = [].push(children);
  }

  /**
   * Push all normal children to result.
   * If we find an opening tag, we push the content to the htmlContent
   *  -> now every child is pushed to htmlContent until we find an closing tag
   *  -> if there are multiple opening tags, we wait until we found the last closing one
   *
   * WARNING: This will fail if somebody injects errornous HTML code, which prevents to render the node at all.
   */

  // ToDo: Handle tags without children: https://github.com/component/domify/blob/master/index.js
  // ToDo: Check if we something left on the stack and print an error message, that the user has input invalid HTML

  for (let child of children) {
    if (child.type && child.type === 'htmlinline') {
      let groups = /<([\w:]+)/.exec(child.content);
      let elementTag = groups && groups.length === 2 ? groups[1] : null;
      let isEmptyElement = EMPTY_HTML_ELEMENTS[elementTag] === true;

      // Skip forbidden tags
      if (isString(elementTag) &&  options.escapeTags[elementTag.toLowerCase()] === true) {
        continue;
      }

      // 1. Is opening Tag?
      if (regexIndexOf(child.content, /<\s*\/.*>/) < 0 && !isEmptyElement) {
        openTags += 1;
        htmlContent.push(child.content);
      } else if (isEmptyElement) {
        // when we deal with an empty element, just add this and proceed
        let {key, ...rest} = props; // Extract key from props
        keyCounter += 1;
        result.push(<MarkdownHTMLElement key={`${key}-inlinehtml-${keyCounter}`} {...rest} displayMode={false} content={child.content} />);
      } else {
        // closing tag
        openTags -= 1;
        htmlContent.push(child.content);

        // done
        if (openTags === 0) {
          let {key, ...rest} = props; // Extract key from props
          result.push(<MarkdownHTMLElement key={`${key}-inlinehtml-${keyCounter}`} {...rest} displayMode={false} content={htmlContent.join('')} />);
          keyCounter += 1;
          htmlContent = []; // Reset
        }
      }
    } else if (openTags > 0) {
      // add child to html
      htmlContent.push(child);
    } else {
      result.push(child);
    }
  }

  return result;
}

/**
 * Markdown Options that are passed to MDReactComponent and then to markdown-it
 */
export const mdOptions = {
  onIterate: function onIterate(tag, props, children, level, options) {
    /**
     * The onIterate function is doing the actual work. It transforms the Mardown Nodes
     * to React Nodes and passes in the props and children.
     */
    let lang;
    let content;
    let displayMode;
    let source;
    let matches;
    let summary;

    if (props.class != null) {
      props.className = props.class;
    }

    // Maybe we need here a more generic solution with a simple mapping
    if (props['max-width']) {
      if (props.style == null) {
        props.style = {};
      }

      props.style.maxWidth = props['max-width'];
    }

    // Remove propagated source prop
    if (props.source) {
      delete props.source;
    }

    switch (tag) {
      case 'a':
        content = makeChildren(children, props, options.htmlOptions);
        return <Link href={props.href} target="_blank" {...props}>{content}</Link>;

      case 'code':
        content = Array.isArray(children) ? children.join('') : children;
        return {content};

      case 'pre':
        if (isString(children[0])) {
          source = children[0];
        } else {
          source = children[0].props.children;
        }

        if (Array.isArray(source)) {
          source = source.join('');
        }

        lang = children[0].props['data-language'] || null;
        return <Highlight mode={lang} code={source} {...props} />;

      case 'p':
        content = makeChildren(children, props, options.htmlOptions);
        return <Text lineHeight={1.2} {...props}>{content}</Text>;

      case 'img':
        return <Image src={props.src} {...props} />;

      case 'h1':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={1} {...props}>{content}</Heading>;
      case 'h2':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={2} {...props}>{content}</Heading>;
      case 'h3':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={3} {...props}>{content}</Heading>;
      case 'h4':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={4} {...props}>{content}</Heading>;
      case 'h5':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={5} {...props}>{content}</Heading>;
      case 'h6':
        content = makeChildren(children, props, options.htmlOptions);
        return <Heading size={6} {...props}>{content}</Heading>;

      case 'em':
        content = makeChildren(children, props, options.htmlOptions);
        return <S type='italic' {...props}>{content}</S>;

      case 'del':
        content = makeChildren(children, props, options.htmlOptions);
        return <S type='strikethrough' {...props}>{content}</S>;

      case 'strong':
        content = makeChildren(children, props, options.htmlOptions);
        return <S type='bold' {...props}>{content}</S>;

      case 'blockquote':
        content = makeChildren(children, props, options.htmlOptions);
        return
<Quote>{content}</Quote>
;

      case 'li':
        content = makeChildren(children, props, options.htmlOptions);
        return <ListItem {...props}>{content}</ListItem>;

      case 'ul':
        return <List {...props}>{children}</List>;

      case 'ol':
        return <List ordered {...props}>{children}</List>;

      case 'math':
        displayMode = props.display === 'inline' ? false : true;
        children = Array.isArray(children) ? children.join('') : children;
        return <Math displayMode={displayMode} {...props}>{children}</Math>;

      /* Spoiler (markdown-it container) rule */
      case 'div':
        matches = props['data-info'].trim().match(/^spoiler\s+(.*)$/);

        if (matches != null && matches.length === 2) {
          summary = matches[1];
        } else {
          // no matches, so just use createElement
          return null;
        }

        content = makeChildren(children, props, options.htmlOptions);
        return <Spoiler summary={summary} {...props}>{content}</Spoiler>;

      case 'htmlblock':
        content = Array.isArray(children) ? children.join('') : children;
        return <MarkdownHTMLElement content={content} {...props} />;

      /**
       * Special inline HTML treatment to embed the HTML as HTML-Nodes and not as escaped text.
       * Requires to use makeChildren function to get the transformed list of children.
       */
      case 'htmlinline':
        content = Array.isArray(children) ? children.join('') : children;
        return {
          type: 'htmlinline',
          content
        };

      case 'wrapper':
        return <DefaultWrapper {...props}>{children}</DefaultWrapper>;

      default:
        return null; // now MDReactComponent is going to create the tag with React.createElement
    }

  },
  plugins: [
    Decorate,
    {
      plugin: mkitm,
      args: [{
        inlineOpen: '$',
        inlineClose: '$',
        blockOpen: '$$',
        blockClose: '$$',
        inlineRenderer: function (str) {
          return katex.renderToString(str, {
            displayMode: false,
            throwOnError: false
          });
        },
        blockRenderer: function (str) {
          return katex.renderToString(str, {
            displayMode: true,
            throwOnError: false
          });
        }
      }]
    },
    {
      plugin: MarkdownContainer,
      args: [
        'spoiler',
        {
          validate: function(params) {
            console.log('spoiler validate function', params);
            return params.trim().match(/^spoiler\s+(.*)$/);
          },
          render: function (tokens, idx) {
            console.log('spoiler render function');
            var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);

            if (tokens[idx].nesting === 1) {
              // opening tag
              return '' + m[1] + '\n';

            } else {
              // closing tag
              return '\n';
            }
          }
        }
      ]
    }
  ],
  markdownOptions: {
    html: true,
    linkify: true
  },
  htmlOptions: {
    escapeTags: {
      script: true
    }
  }
};

/**
 * Transforms markdown text to React Nodes. Highlights code with highlight.js, uses KaTeX for math rendering.
 * Allows to use inline html inside paragraphs and list items.
 */
export function toMarkdownComponent(props) {
  return <MDReactComponent text={props.source} {...mdOptions} {...props} />;
}<|endoftext|>


<commit_before>import { filterPackageName } from './utils'

export const SUPPORTED_FRAMEWORKS = [
    'mocha', // https://github.com/webdriverio/webdriverio/tree/master/packages/wdio-mocha-framework
    'jasmine' // https://github.com/webdriverio/webdriverio/tree/master/packages/wdio-jasmine-framework
]

export const SUPPORTED_REPORTER = [
    ' dot - https://www.npmjs.com/package/@wdio/dot-reporter',
    ' spec - https://www.npmjs.com/package/@wdio/spec-reporter',
    ' junit - https://www.npmjs.com/package/@wdio/junit-reporter',
    ' allure - https://www.npmjs.com/package/@wdio/allure-reporter',
    ' sumologic - https://www.npmjs.com/package/@wdio/sumologic-reporter',
    ' concise - https://www.npmjs.com/package/@wdio/concise-reporter',
    ' reportportal - https://www.npmjs.com/package/wdio-reportportal-reporter',
    ' video - https://www.npmjs.com/package/wdio-video-reporter',
    ' html - https://www.npmjs.com/package/@rpii/wdio-html-reporter',
    ' json - https://www.npmjs.com/package/wdio-json-reporter'
]

export const SUPPORTED_SERVICES = [
    ' sauce - https://www.npmjs.com/package/@wdio/sauce-service',
    ' testingbot - https://www.npmjs.com/package/@wdio/testingbot-service',
    ' firefox-profile - https://www.npmjs.com/package/@wdio/firefox-profile-service',
    ' selenium-standalone - https://www.npmjs.com/package/@wdio/selenium-standalone-service',
    ' devtools - https://www.npmjs.com/package/@wdio/devtools-service',
    ' applitools - https://www.npmjs.com/package/@wdio/applitools-service',
    ' browserstack - https://www.npmjs.com/package/@wdio/browserstack-service',
    ' appium - https://www.npmjs.com/package/@wdio/appium-service',
    ' chromedriver - https://www.npmjs.com/package/wdio-chromedriver-service',
    ' intercept - https://www.npmjs.com/package/wdio-intercept-service',
    ' zafira-listener - https://www.npmjs.com/package/wdio-zafira-listener-service',
    ' reportportal - https://www.npmjs.com/package/wdio-reportportal-service',
    ' docker - https://www.npmjs.com/package/wdio-docker-service'
]

export const SUPPORTED_RUNNERS = [
    ' local - https://www.npmjs.com/package/@wdio/local-runner'
]

const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'silent']

export const USAGE = `
WebdriverIO CLI runner

Usage: wdio [options] [configFile]
Usage: wdio config
Usage: wdio repl <browserName>

config file defaults to wdio.conf.js
The [options] object will override values from the config file.
An optional list of spec files can be piped to wdio that will override configured specs.
Same applies to the exclude option. It can take a list of specs to exclude for a given run
and it also overrides the exclude key from the config file.`

export const CONFIG_HELPER_INTRO = `
=========================
WDIO Configuration Helper
=========================
`

export const CONFIG_HELPER_SUCCESS_MESSAGE = `
Configuration file was created successfully!
To run your tests, execute:
$ wdio wdio.conf.js
`

export const CLI_PARAMS = [{
    name: 'help',
    description: 'prints WebdriverIO help menu'
}, {
    name: 'version',
    description: 'prints WebdriverIO version'
}, {
    name: 'hostname',
    alias: 'h',
    describe: 'automation driver host address',
    type: 'string'
}, {
    name: 'port',
    alias: 'p',
    describe: 'automation driver port',
    type: 'number'
}, {
    name: 'user',
    alias: 'u',
    description: 'username if using a cloud service as automation backend',
    type: 'string'
}, {
    name: 'key',
    alias: 'k',
    description: 'corresponding access key to the user',
    type: 'string'
}, {
    name: 'watch',
    description: 'watch specs for changes',
    type: 'boolean'
}, {
    name: 'logLevel',
    alias: 'l',
    description: 'level of logging verbosity',
    choices: LOG_LEVELS
}, {
    name: 'bail',
    description: 'stop test runner after specific amount of tests have failed',
    type: 'number'
}, {
    name: 'baseUrl',
    description: 'shorten url command calls by setting a base url',
    type: 'string'
}, {
    name: 'waitforTimeout',
    alias: 'w',
    description: 'timeout for all waitForXXX commands',
    type: 'number'
}, {
    name: 'framework',
    alias: 'f',
    description: 'defines the framework (Mocha, Jasmine or Cucumber) to run the specs',
    type: 'string'
}, {
    name: 'reporters',
    alias: 'r',
    description: 'reporters to print out the results on stdout',
    type: 'array'
}, {
    name: 'suite',
    description: 'overwrites the specs attribute and runs the defined suite',
    type: 'array'
}, {
    name: 'spec',
    description: 'run only a certain spec file - overrides specs piped from stdin',
    type: 'array'
}, {
    name: 'exclude',
    description: 'exclude certain spec file from the test run - overrides exclude piped from stdin',
    type: 'array'
}, {
    name: 'mochaOpts',
    description: 'Mocha options'
}, {
    name: 'jasmineOpts',
    description: 'Jasmine options'
}, {
    name: 'cucumberOpts',
    description: 'Cucumber options'
}]

export const QUESTIONNAIRE = [{
    type: 'list',
    name: 'runner',
    message: 'Where should your tests be launched?',
    choices: SUPPORTED_RUNNERS,
    filter: (runner) => runner.split(/-/)[0].trim()
}, {
    type: 'confirm',
    name: 'installRunner',
    message: 'Shall I install the runner plugin for you?',
    default: true
}, {
    type: 'list',
    name: 'backend',
    message: 'Where is your automation backend located?',
    choices: [
        'On my local machine',
        'In the cloud using Sauce Labs',
        'In the cloud using Browserstack or Testingbot or a different service',
        'I have my own Selenium cloud'
    ]
}, {
    type: 'input',
    name: 'hostname',
    message: 'What is the host address of that cloud service?',
    when: (answers) => answers.backend.indexOf('different service') > -1
}, {
    type: 'input',
    name: 'port',
    message: 'What is the port on which that service is running?',
    default: '80',
    when: (answers) => answers.backend.indexOf('different service') > -1
}, {
    type: 'input',
    name: 'env_user',
    message: 'Environment variable for username',
    default: 'BROWSERSTACK_USER',
    when: (answers) => answers.backend.startsWith('In the cloud using Browserstack')
}, {
    type: 'input',
    name: 'env_key',
    message: 'Environment variable for access key',
    default: 'BROWSERSTACK_ACCESSKEY',
    when: (answers) => answers.backend.startsWith('In the cloud using Browserstack')
}, {
    type: 'input',
    name: 'env_user',
    message: 'Environment variable for username',
    default: 'SAUCE_USERNAME',
    when: (answers) => answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'input',
    name: 'env_key',
    message: 'Environment variable for access key',
    default: 'SAUCE_ACCESS_KEY',
    when: (answers) => answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'confirm',
    name: 'headless',
    message: 'Do you want to run your test on Sauce Headless? (https://saucelabs.com/products/web-testing/sauce-headless)',
    default: false,
    when: (answers) => answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'list',
    name: 'region',
    message: 'In which region do you want to run your Sauce Labs tests in?',
    choices: [
        'us',
        'eu'
    ],
    when: (answers) => !answers.headless && answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'input',
    name: 'hostname',
    message: 'What is the IP or URI to your Selenium standalone or grid server?',
    default: 'localhost',
    when: (answers) => answers.backend.indexOf('own Selenium cloud') > -1
}, {
    type: 'input',
    name: 'port',
    message: 'What is the port which your Selenium standalone or grid server is running on?',
    default: '4444',
    when: (answers) => answers.backend.indexOf('own Selenium cloud') > -1
}, {
    type: 'input',
    name: 'path',
    message: 'What is the path to your Selenium standalone or grid server?',
    default: '/wd/hub',
    when: (answers) => answers.backend.indexOf('own Selenium cloud') > -1
}, {
    type: 'list',
    name: 'framework',
    message: 'Which framework do you want to use?',
    choices: SUPPORTED_FRAMEWORKS
}, {
    type: 'confirm',
    name: 'installFramework',
    message: 'Shall I install the framework adapter for you?',
    default: true
}, {
    type: 'list',
    name: 'executionMode',
    message: 'Do you want to run WebdriverIO commands synchronous or asynchronous?',
    choices: [
        'sync',
        'async'
    ]
}, {
    type: 'input',
    name: 'specs',
    message: 'Where are your test specs located?',
    default: './test/specs/**/*.js',
    when: (answers) => answers.framework.match(/(mocha|jasmine)/)
}, {
    type: 'input',
    name: 'specs',
    message: 'Where are your feature files located?',
    default: './features/**/*.feature',
    when: (answers) => answers.framework === 'cucumber'
}, {
    type: 'input',
    name: 'stepDefinitions',
    message: 'Where are your step definitions located?',
    default: './features/step-definitions',
    when: (answers) => answers.framework === 'cucumber'
}, {
    type: 'checkbox',
    name: 'reporters',
    message: 'Which reporter do you want to use?',
    choices: SUPPORTED_REPORTER,
    filter: filterPackageName('reporter')
}, {
    type: 'confirm',
    name: 'installReporter',
    message: 'Shall I install the reporter library for you?',
    default: true,
    when: (answers) => answers.reporters.length > 0
}, {
    type: 'checkbox',
    name: 'services',
    message: 'Do you want to add a service to your test setup?',
    choices: SUPPORTED_SERVICES,
    filter: filterPackageName('service')
}, {
    type: 'confirm',
    name: 'installServices',
    message: 'Shall I install the services for you?',
    default: true,
    when: (answers) => answers.services.length > 0
}, {
    type: 'input',
    name: 'outputDir',
    message: 'In which directory should the xunit reports get stored?',
    default: './',
    when: (answers) => answers.reporters.includes('junit')
}, {
    type: 'input',
    name: 'outputDir',
    message: 'In which directory should the json reports get stored?',
    default: './',
    when: (answers) => answers.reporters.includes('json')
}, {
    type: 'input',
    name: 'outputDir',
    message: 'In which directory should the mochawesome json reports get stored?',
    default: './',
    when: (answers) => answers.reporters.includes('mochawesome')
}, {
    type: 'list',
    name: 'logLevel',
    message: 'Level of logging verbosity',
    default: 'info',
    choices: LOG_LEVELS
}, {
    type: 'input',
    name: 'baseUrl',
    message: 'What is the base url?',
    default: 'http://localhost'
}]
<commit_msg>add mochawesome to cli reporter option<commit_after>import { filterPackageName } from './utils'

export const SUPPORTED_FRAMEWORKS = [
    'mocha', // https://github.com/webdriverio/webdriverio/tree/master/packages/wdio-mocha-framework
    'jasmine' // https://github.com/webdriverio/webdriverio/tree/master/packages/wdio-jasmine-framework
]

export const SUPPORTED_REPORTER = [
    ' dot - https://www.npmjs.com/package/@wdio/dot-reporter',
    ' spec - https://www.npmjs.com/package/@wdio/spec-reporter',
    ' junit - https://www.npmjs.com/package/@wdio/junit-reporter',
    ' allure - https://www.npmjs.com/package/@wdio/allure-reporter',
    ' sumologic - https://www.npmjs.com/package/@wdio/sumologic-reporter',
    ' concise - https://www.npmjs.com/package/@wdio/concise-reporter',
    ' reportportal - https://www.npmjs.com/package/wdio-reportportal-reporter',
    ' video - https://www.npmjs.com/package/wdio-video-reporter',
    ' html - https://www.npmjs.com/package/@rpii/wdio-html-reporter',
    ' json - https://www.npmjs.com/package/wdio-json-reporter',
    ' mochawesome - https://www.npmjs.com/package/wdio-mochawesome-reporter'
]

export const SUPPORTED_SERVICES = [
    ' sauce - https://www.npmjs.com/package/@wdio/sauce-service',
    ' testingbot - https://www.npmjs.com/package/@wdio/testingbot-service',
    ' firefox-profile - https://www.npmjs.com/package/@wdio/firefox-profile-service',
    ' selenium-standalone - https://www.npmjs.com/package/@wdio/selenium-standalone-service',
    ' devtools - https://www.npmjs.com/package/@wdio/devtools-service',
    ' applitools - https://www.npmjs.com/package/@wdio/applitools-service',
    ' browserstack - https://www.npmjs.com/package/@wdio/browserstack-service',
    ' appium - https://www.npmjs.com/package/@wdio/appium-service',
    ' chromedriver - https://www.npmjs.com/package/wdio-chromedriver-service',
    ' intercept - https://www.npmjs.com/package/wdio-intercept-service',
    ' zafira-listener - https://www.npmjs.com/package/wdio-zafira-listener-service',
    ' reportportal - https://www.npmjs.com/package/wdio-reportportal-service',
    ' docker - https://www.npmjs.com/package/wdio-docker-service'
]

export const SUPPORTED_RUNNERS = [
    ' local - https://www.npmjs.com/package/@wdio/local-runner'
]

const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'silent']

export const USAGE = `
WebdriverIO CLI runner

Usage: wdio [options] [configFile]
Usage: wdio config
Usage: wdio repl <browserName>

config file defaults to wdio.conf.js
The [options] object will override values from the config file.
An optional list of spec files can be piped to wdio that will override configured specs.
Same applies to the exclude option. It can take a list of specs to exclude for a given run
and it also overrides the exclude key from the config file.`

export const CONFIG_HELPER_INTRO = `
=========================
WDIO Configuration Helper
=========================
`

export const CONFIG_HELPER_SUCCESS_MESSAGE = `
Configuration file was created successfully!
To run your tests, execute:
$ wdio wdio.conf.js
`

export const CLI_PARAMS = [{
    name: 'help',
    description: 'prints WebdriverIO help menu'
}, {
    name: 'version',
    description: 'prints WebdriverIO version'
}, {
    name: 'hostname',
    alias: 'h',
    describe: 'automation driver host address',
    type: 'string'
}, {
    name: 'port',
    alias: 'p',
    describe: 'automation driver port',
    type: 'number'
}, {
    name: 'user',
    alias: 'u',
    description: 'username if using a cloud service as automation backend',
    type: 'string'
}, {
    name: 'key',
    alias: 'k',
    description: 'corresponding access key to the user',
    type: 'string'
}, {
    name: 'watch',
    description: 'watch specs for changes',
    type: 'boolean'
}, {
    name: 'logLevel',
    alias: 'l',
    description: 'level of logging verbosity',
    choices: LOG_LEVELS
}, {
    name: 'bail',
    description: 'stop test runner after specific amount of tests have failed',
    type: 'number'
}, {
    name: 'baseUrl',
    description: 'shorten url command calls by setting a base url',
    type: 'string'
}, {
    name: 'waitforTimeout',
    alias: 'w',
    description: 'timeout for all waitForXXX commands',
    type: 'number'
}, {
    name: 'framework',
    alias: 'f',
    description: 'defines the framework (Mocha, Jasmine or Cucumber) to run the specs',
    type: 'string'
}, {
    name: 'reporters',
    alias: 'r',
    description: 'reporters to print out the results on stdout',
    type: 'array'
}, {
    name: 'suite',
    description: 'overwrites the specs attribute and runs the defined suite',
    type: 'array'
}, {
    name: 'spec',
    description: 'run only a certain spec file - overrides specs piped from stdin',
    type: 'array'
}, {
    name: 'exclude',
    description: 'exclude certain spec file from the test run - overrides exclude piped from stdin',
    type: 'array'
}, {
    name: 'mochaOpts',
    description: 'Mocha options'
}, {
    name: 'jasmineOpts',
    description: 'Jasmine options'
}, {
    name: 'cucumberOpts',
    description: 'Cucumber options'
}]

export const QUESTIONNAIRE = [{
    type: 'list',
    name: 'runner',
    message: 'Where should your tests be launched?',
    choices: SUPPORTED_RUNNERS,
    filter: (runner) => runner.split(/-/)[0].trim()
}, {
    type: 'confirm',
    name: 'installRunner',
    message: 'Shall I install the runner plugin for you?',
    default: true
}, {
    type: 'list',
    name: 'backend',
    message: 'Where is your automation backend located?',
    choices: [
        'On my local machine',
        'In the cloud using Sauce Labs',
        'In the cloud using Browserstack or Testingbot or a different service',
        'I have my own Selenium cloud'
    ]
}, {
    type: 'input',
    name: 'hostname',
    message: 'What is the host address of that cloud service?',
    when: (answers) => answers.backend.indexOf('different service') > -1
}, {
    type: 'input',
    name: 'port',
    message: 'What is the port on which that service is running?',
    default: '80',
    when: (answers) => answers.backend.indexOf('different service') > -1
}, {
    type: 'input',
    name: 'env_user',
    message: 'Environment variable for username',
    default: 'BROWSERSTACK_USER',
    when: (answers) => answers.backend.startsWith('In the cloud using Browserstack')
}, {
    type: 'input',
    name: 'env_key',
    message: 'Environment variable for access key',
    default: 'BROWSERSTACK_ACCESSKEY',
    when: (answers) => answers.backend.startsWith('In the cloud using Browserstack')
}, {
    type: 'input',
    name: 'env_user',
    message: 'Environment variable for username',
    default: 'SAUCE_USERNAME',
    when: (answers) => answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'input',
    name: 'env_key',
    message: 'Environment variable for access key',
    default: 'SAUCE_ACCESS_KEY',
    when: (answers) => answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'confirm',
    name: 'headless',
    message: 'Do you want to run your test on Sauce Headless? (https://saucelabs.com/products/web-testing/sauce-headless)',
    default: false,
    when: (answers) => answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'list',
    name: 'region',
    message: 'In which region do you want to run your Sauce Labs tests in?',
    choices: [
        'us',
        'eu'
    ],
    when: (answers) => !answers.headless && answers.backend === 'In the cloud using Sauce Labs'
}, {
    type: 'input',
    name: 'hostname',
    message: 'What is the IP or URI to your Selenium standalone or grid server?',
    default: 'localhost',
    when: (answers) => answers.backend.indexOf('own Selenium cloud') > -1
}, {
    type: 'input',
    name: 'port',
    message: 'What is the port which your Selenium standalone or grid server is running on?',
    default: '4444',
    when: (answers) => answers.backend.indexOf('own Selenium cloud') > -1
}, {
    type: 'input',
    name: 'path',
    message: 'What is the path to your Selenium standalone or grid server?',
    default: '/wd/hub',
    when: (answers) => answers.backend.indexOf('own Selenium cloud') > -1
}, {
    type: 'list',
    name: 'framework',
    message: 'Which framework do you want to use?',
    choices: SUPPORTED_FRAMEWORKS
}, {
    type: 'confirm',
    name: 'installFramework',
    message: 'Shall I install the framework adapter for you?',
    default: true
}, {
    type: 'list',
    name: 'executionMode',
    message: 'Do you want to run WebdriverIO commands synchronous or asynchronous?',
    choices: [
        'sync',
        'async'
    ]
}, {
    type: 'input',
    name: 'specs',
    message: 'Where are your test specs located?',
    default: './test/specs/**/*.js',
    when: (answers) => answers.framework.match(/(mocha|jasmine)/)
}, {
    type: 'input',
    name: 'specs',
    message: 'Where are your feature files located?',
    default: './features/**/*.feature',
    when: (answers) => answers.framework === 'cucumber'
}, {
    type: 'input',
    name: 'stepDefinitions',
    message: 'Where are your step definitions located?',
    default: './features/step-definitions',
    when: (answers) => answers.framework === 'cucumber'
}, {
    type: 'checkbox',
    name: 'reporters',
    message: 'Which reporter do you want to use?',
    choices: SUPPORTED_REPORTER,
    filter: filterPackageName('reporter')
}, {
    type: 'confirm',
    name: 'installReporter',
    message: 'Shall I install the reporter library for you?',
    default: true,
    when: (answers) => answers.reporters.length > 0
}, {
    type: 'checkbox',
    name: 'services',
    message: 'Do you want to add a service to your test setup?',
    choices: SUPPORTED_SERVICES,
    filter: filterPackageName('service')
}, {
    type: 'confirm',
    name: 'installServices',
    message: 'Shall I install the services for you?',
    default: true,
    when: (answers) => answers.services.length > 0
}, {
    type: 'input',
    name: 'outputDir',
    message: 'In which directory should the xunit reports get stored?',
    default: './',
    when: (answers) => answers.reporters.includes('junit')
}, {
    type: 'input',
    name: 'outputDir',
    message: 'In which directory should the json reports get stored?',
    default: './',
    when: (answers) => answers.reporters.includes('json')
}, {
    type: 'input',
    name: 'outputDir',
    message: 'In which directory should the mochawesome json reports get stored?',
    default: './',
    when: (answers) => answers.reporters.includes('mochawesome')
}, {
    type: 'list',
    name: 'logLevel',
    message: 'Level of logging verbosity',
    default: 'info',
    choices: LOG_LEVELS
}, {
    type: 'input',
    name: 'baseUrl',
    message: 'What is the base url?',
    default: 'http://localhost'
}]
<|endoftext|>


<commit_before>var https = require('https'),
	app = {
		api: {
			hostname: 'pointhq.com',
			username: null,
			apitoken: null,
			timeout: 10000,
		}
	};

function validate(params, list, callback){
	for(key in list){
		if(!(list[key] in params)){
			callback('param not found: ' + list[key]);
			return false;
		}
	}
	return true;
}

app.zones = {
	list: function(params, callback){
		var query = '';
		if(typeof params !== 'undefined' && 'group' in params){
			query = '?group=' + params['group'];
		}
		app.req(200, 'GET', '/zones' + query, {'list_escape': 'zone'}, callback);
	}
}

app.zone = {
	add: function(fields, callback){
		app.req(201, 'POST', '/zones', {'escape': 'zone', 'fields': {'zone': fields}}, callback);
	},
	update: function(params, fields, callback){
		if(!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(202, 'PUT', '/zones' + params['zone_id'], {'escape': 'zone', 'fields': {'zone': fields}}, callback);
	},
	get: function(params, callback){
		if(!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(200, 'GET', '/zones/' + params['zone_id'], {'escape': 'zone'}, callback);
	},
	del: function(params, callback){
		if(!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(202, 'DELETE', '/zones/' + params['zone_id'], {'escape': 'zone'}, callback);
	}
}

app.records = {
	list: function(params, callback){
		if (!(validate(params, ['zone_id'], callback))){
			return;
		}
	app.req(200, 'GET', '/zones/' + params['zone_id'] + '/records/', {'list_escape': 'zone_record'}, callback);
	}
}

app.record = {
	add: function(params, fields, callback){ // zone_record
		if (!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(201, 'POST', '/zones/' + params['zone_id'] + '/records/', { 'escape': 'zone_record', 'fields':{'zone_record': fields}}, callback);
	},
	update: function(params, fields, callback){  // zone_record
		if (!(validate(params, ['zone_id', 'record_id'], callback))){
			return;
		}
		app.req(202, 'PUT', '/zones/' + params['zone_id'] + '/records/' + params['record_id'], { 'escape': 'zone_record', 'fields':{'zone_record': fields}}, callback);
	},
	get: function(params, callback){  // zone_record
		if (!(validate(params, ['zone_id', 'record_id'], callback))){
			return
		}
		app.req(200, 'GET', '/zones/' + params['zone_id'] + '/records/' + params['record_id'], {'escape': 'zone_record'}, callback);
	},
	del: function(params, callback){  //  zone_record
		if (!(validate(params, ['zone_id', 'record_id'], callback))){
			return;
		}
		app.req(202, 'DELETE', '/zones/' + params['zone_id'] + '/records/' + params['record_id'], {'escape': 'zone_record'}, callback);
	}
}
app.req = function(status, method, path, data, callback){
	// credentials
	if(!app.api.username || !app.api.apitoken){
		return callback('credentials missing');
	}

	var querystr = JSON.stringify(data['fields']),
		auth = 'Basic ' + new Buffer(app.api.username + ':' + app.api.apitoken).toString('base64'),
		headers = {
			'Accept': 'application/json',
			'User-Agent': 'Nodejs-pointDNS',
			'Authorization': auth,
		},
		options = {
			host: app.api.hostname,
			port: 443,
			path: path,
			method: method,
			headers: headers,
		}

	if(method.match(/(POST|PUT)/)){
		headers['Content-Type'] = 'application/json';
		headers['Content-Length'] = querystr.length;
	}

	//request
	var req = https.request(options, function(res){
		if(res.statusCode == 404){
			return callback('404 response. Incorrect or invalid fields');
		}
		if (res.statusCode != status){
			return callback('Invalid status code: '+res.statusCode);
		}
		var responseData = '';
		res.on('data', function(chunk){
			responseData += chunk;
		});
		res.on('end', function(){
			result = JSON.parse(responseData);
			if('escape' in data){
				result = result[data['escape']];
			}else if('list_escape' in data){
				tmp = [];
				result.forEach(function(entry){
					tmp.push(entry[data['list_escape']]);
				})
				result = tmp;
			}
			callback(null, result)
		});
	});

	// timeout
	req.on('socket', function(socket){
		if(app.api.timeout){
			socket.setTimeout(app.api.timeout)
			socket.on('timeout', function(){
				req.abort();
			})
		}
	})

	req.on('error', function(err){
		return callback(err);
	});

	if(method.match(/(POST|PUT)/)){
		req.end(querystr);
	}else{
		req.end();
	}
}
module.exports = function(setup){
	for(var k in setup){
		app.api[k] = setup[k];
	}
	return app;
}
<commit_msg>fixing some typos and other minor issues<commit_after>var https = require('https'),
	app = {
		api: {
			hostname: 'pointhq.com',
			username: null,
			apitoken: null,
			timeout: 10000,
		}
	};

function validate(params, list, callback){
	for(key in list){
		if(!(list[key] in params)){
			callback('param not found: ' + list[key]);
			return false;
		}
	}
	return true;
}

app.zones = {
	list: function(params, callback){
		var query = '';
		if(typeof params !== 'undefined' && 'group' in params){
			query = '?group=' + params['group'];
		}
		app.req(200, 'GET', '/zones' + query, {'list_escape': 'zone'}, callback);
	}
}

app.zone = {
	add: function(fields, callback){
		app.req(201, 'POST', '/zones', {'escape': 'zone', 'fields': {'zone': fields}}, callback);
	},
	update: function(params, fields, callback){
		if(!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(202, 'PUT', '/zones' + params['zone_id'], {'escape': 'zone', 'fields': {'zone': fields}}, callback);
	},
	get: function(params, callback){
		if(!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(200, 'GET', '/zones/' + params['zone_id'], {'escape': 'zone'}, callback);
	},
	del: function(params, callback){
		if(!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(202, 'DELETE', '/zones/' + params['zone_id'], {'escape': 'zone'}, callback);
	}
}

app.records = {
	list: function(params, callback){
		if (!(validate(params, ['zone_id'], callback))){
			return;
		}
	app.req(200, 'GET', '/zones/' + params['zone_id'] + '/records/', {'list_escape': 'zone_record'}, callback);
	}
}

app.record = {
	add: function(params, fields, callback){ // zone_record
		if (!(validate(params, ['zone_id'], callback))){
			return;
		}
		app.req(201, 'POST', '/zones/' + params['zone_id'] + '/records/', { 'escape': 'zone_record', 'fields':{'zone_record': fields}}, callback);
	},
	update: function(params, fields, callback){  // zone_record
		if (!(validate(params, ['zone_id', 'record_id'], callback))){
			return;
		}
		app.req(202, 'PUT', '/zones/' + params['zone_id'] + '/records/' + params['record_id'], { 'escape': 'zone_record', 'fields':{'zone_record': fields}}, callback);
	},
	get: function(params, callback){  // zone_record
		if (!(validate(params, ['zone_id', 'record_id'], callback))){
			return
		}
		app.req(200, 'GET', '/zones/' + params['zone_id'] + '/records/' + params['record_id'], {'escape': 'zone_record'}, callback);
	},
	del: function(params, callback){  //  zone_record
		if (!(validate(params, ['zone_id', 'record_id'], callback))){
			return;
		}
		app.req(202, 'DELETE', '/zones/' + params['zone_id'] + '/records/' + params['record_id'], {'escape': 'zone_record'}, callback);
	}
}
app.req = function(status, method, path, data, callback){
	// credentials
	if(!app.api.username || !app.api.apitoken){
		return callback('credentials missing');
	}

	var querystr = JSON.stringify(data['fields']),
		auth = 'Basic ' + new Buffer(app.api.username + ':' + app.api.apitoken).toString('base64'),
		headers = {
			'Accept': 'application/json',
			'User-Agent': 'Nodejs-pointDNS',
			'Authorization': auth,
		},
		options = {
			host: app.api.hostname,
			port: 443,
			path: path,
			method: method,
			headers: headers,
		}
	if(method.match(/(POST|PUT)/)){
		headers['Content-Type'] = 'application/json';
		headers['Content-Length'] = querystr.length;
	}

	//request
	var req = https.request(options, function(res){
		if(res.statusCode == 404){
			return callback('404 response. Incorrect or invalid fields');
		}
		if (res.statusCode != status){
			return callback('Invalid status code: '+res.statusCode);
		}
		var responseData = '';
		res.on('data', function(chunk){
			responseData += chunk;
		});
		res.on('end', function(){
			result = JSON.parse(responseData);
			if('escape' in data){
				result = result[data['escape']];
			}else if('list_escape' in data){
				tmp = [];
				result.forEach(function(entry){
					tmp.push(entry[data['list_escape']]);
				})
				result = tmp;
			}
			callback(null, result)
		});
	});

	// timeout
	req.on('socket', function(socket){
		if(app.api.timeout){
			socket.setTimeout(app.api.timeout)
			socket.on('timeout', function(){
				req.abort();
			})
		}
	})

	req.on('error', function(err){
		return callback(err);
	});

	if(method.match(/(POST|PUT)/)){
		req.end(querystr);
	}else{
		req.end();
	}
}
module.exports = function(setup){
	for(var k in setup){
		app.api[k] = setup[k];
	}
	return app;
}
<|endoftext|>


<commit_before>// Create global namespace for mybtcprice
if (!window.mybtcprice) {
  window.mybtcprice = {};
}

$(document).ready(function() {
  bitcoinprices.init({

    // Where we get bitcoinaverage data
    url: "https://api.bitcoinaverage.com/ticker/all",

    // Which of bitcoinaverages value we use to present prices
    marketRateVariable: "last",

    // Which currencies are in shown to the user
    currencies: ["USD", "BTC"],

    // Special currency symbol artwork
    //symbols: {
    //    "BTC": "B"
    //},

    // Which currency we show user by the default if
    // no currency is selected
    defaultCurrency: "USD",

    // How the user is able to interact with the prices
    ux : {
        // Make everything with data-btc-price HTML attribute clickable
        clickPrices : true,

        // Build Bootstrap dropdown menu for currency switching
        menu : true,

        // Allow user to cycle through currency choices in currency:
        clickableCurrencySymbol:  true
    },

    // Allows passing the explicit jQuery version to bitcoinprices.
    // This is useful if you are using modular javascript (AMD/UMD/require()),
    // but for most normal usage you don't need this
    jQuery: jQuery,

    // Price source data attribute
    priceAttribute: "data-btc-price",

    // Price source currency for data-btc-price attribute.
    // E.g. if your shop prices are in USD
    // but converted to BTC when you do Bitcoin
    // checkout, put USD here.
    priceOrignalCurrency: "BTC"
  });

  // Reload price on clicking "Refresh"
  $( "#refresh" ).click(function() {
    $('#refresh').text('Refreshing');
    $('#time').text('Refreshing time');
    bitcoinprices.loadData();
  });

  // What happens when the price is updated?
  $(document).bind("marketdataavailable", function() {
    myTime = new Date(bitcoinprices.data.timestamp);
    $('#time').text(myTime);
    $('#refresh').text('Refresh');
    calculate();
  });

  // Trigger calculation on text change.
  $( "input" ).change(function() {
    calculate(this.name);
  });
  $( "input" ).on('input', function() {
    calculate(this.name);
  });

  // Rounding value to the nearest x
  function nearest(n, v) {
    n = n / v;
    n = Math.round(n) * v;
    return n;
  }
  // Rounding value to the x decimal
  function precision(n, dp) {
    dp = Math.pow(10, dp);
    n = n * dp;
    n = Math.round(n);
    n = n / dp;
    return n;
  }

  function init() {
    // Set the commission
    $("input[name='commission']").val(mybtcprice.commission);
    $("input[name='commission']").prop('disabled', mybtcprice.commission_locked);
  }

  // Calculate other fields based on the "change" parameter.
  // "change" is the name of the field which has changed.
  function calculate(change) {
    buy_amount_USD = Number($("input[name='buy_amount_USD']").val());
    buy_amount_BTC = Number($("input[name='buy_amount_BTC']").val());
    buy_amount_mBTC = Number($("input[name='buy_amount_mBTC']").val());
    total_USD = Number($("input[name='total_USD']").val());
    commission = Number($("input[name='commission']").val());
    rate = bitcoinprices.data.USD.last;

    // If the user knows how much BTC they want.
    if (change == 'buy_amount_BTC') {
      // buy_amount_BTC * rate
      buy_amount_USD = precision(buy_amount_BTC * rate, 2);
    }
    if (change == 'buy_amount_mBTC') {
      // (buy_amount_mBTC / 1000) * rate
      buy_amount_USD = precision((buy_amount_mBTC / 1000) * rate, 2);
    }

    if (change != 'total_USD') {
      // buy_amount_USD * (1 + commission / 100)
      total_USD = nearest(buy_amount_USD / (1 - (commission / 100)), 1);
      total_USD = (total_USD <= buy_amount_USD && buy_amount_USD !== 0) ? total_USD + 5 : total_USD;
      $("input[name='total_USD']").val(total_USD);
    }
    if (change != 'buy_amount_USD') {
      // total_USD / (1 + commission / 100)
      buy_amount_USD = nearest(total_USD * (1 - (commission / 100)), 1);
      $("input[name='buy_amount_USD']").val(buy_amount_USD);
    }
    if (change != 'buy_amount_BTC' && change != 'buy_amount_mBTC') {
      // rate / buy_amount_USD
      buy_amount_BTC = precision(buy_amount_USD / rate, 8);
      $("input[name='buy_amount_BTC']").val(buy_amount_BTC);
    }
    if (change != 'buy_amount_BTC' && change == 'buy_amount_mBTC') {
      // buy_amount_mBTC / 1000
      buy_amount_BTC = precision(buy_amount_mBTC / 1000, 8);
      $("input[name='buy_amount_BTC']").val(buy_amount_BTC);
    }
    if (change != 'buy_amount_mBTC') {
      // buy_amount_BTC * 1000
      buy_amount_mBTC = precision(buy_amount_BTC * 1000, 8);
      $("input[name='buy_amount_mBTC']").val(buy_amount_mBTC);
    }

    // total_USD - buy_amount_USD
    $('#fee_total').text(total_USD - buy_amount_USD);
  }

  init();
});
<commit_msg>Allow decimals for commission percentage<commit_after>// Create global namespace for mybtcprice
if (!window.mybtcprice) {
  window.mybtcprice = {};
}

$(document).ready(function() {
  bitcoinprices.init({

    // Where we get bitcoinaverage data
    url: "https://api.bitcoinaverage.com/ticker/all",

    // Which of bitcoinaverages value we use to present prices
    marketRateVariable: "last",

    // Which currencies are in shown to the user
    currencies: ["USD", "BTC"],

    // Special currency symbol artwork
    //symbols: {
    //    "BTC": "B"
    //},

    // Which currency we show user by the default if
    // no currency is selected
    defaultCurrency: "USD",

    // How the user is able to interact with the prices
    ux : {
        // Make everything with data-btc-price HTML attribute clickable
        clickPrices : true,

        // Build Bootstrap dropdown menu for currency switching
        menu : true,

        // Allow user to cycle through currency choices in currency:
        clickableCurrencySymbol:  true
    },

    // Allows passing the explicit jQuery version to bitcoinprices.
    // This is useful if you are using modular javascript (AMD/UMD/require()),
    // but for most normal usage you don't need this
    jQuery: jQuery,

    // Price source data attribute
    priceAttribute: "data-btc-price",

    // Price source currency for data-btc-price attribute.
    // E.g. if your shop prices are in USD
    // but converted to BTC when you do Bitcoin
    // checkout, put USD here.
    priceOrignalCurrency: "BTC"
  });

  // Reload price on clicking "Refresh"
  $( "#refresh" ).click(function() {
    $('#refresh').text('Refreshing');
    $('#time').text('Refreshing time');
    bitcoinprices.loadData();
  });

  // What happens when the price is updated?
  $(document).bind("marketdataavailable", function() {
    myTime = new Date(bitcoinprices.data.timestamp);
    $('#time').text(myTime);
    $('#refresh').text('Refresh');
    calculate();
  });

  // Trigger calculation on text change.
  $( "input" ).change(function() {
    calculate(this.name);
  });
  $( "input" ).on('input', function() {
    calculate(this.name);
  });

  // Rounding value to the nearest x
  function nearest(n, v) {
    n = n / v;
    n = Math.round(n) * v;
    return n;
  }
  // Rounding value to the x decimal
  function precision(n, dp) {
    dp = Math.pow(10, dp);
    n = n * dp;
    n = Math.round(n);
    n = n / dp;
    return n;
  }

  function init() {
    // Set the commission
    $("input[name='commission']").val(mybtcprice.commission);
    $("input[name='commission']").prop('disabled', mybtcprice.commission_locked);
  }

  // Calculate other fields based on the "change" parameter.
  // "change" is the name of the field which has changed.
  function calculate(change) {
    buy_amount_USD = Number($("input[name='buy_amount_USD']").val());
    buy_amount_BTC = Number($("input[name='buy_amount_BTC']").val());
    buy_amount_mBTC = Number($("input[name='buy_amount_mBTC']").val());
    total_USD = Number($("input[name='total_USD']").val());
    commission = Number($("input[name='commission']").val());
    rate = bitcoinprices.data.USD.last;

    // If the user knows how much BTC they want.
    if (change == 'buy_amount_BTC') {
      // buy_amount_BTC * rate
      buy_amount_USD = precision(buy_amount_BTC * rate, 2);
    }
    if (change == 'buy_amount_mBTC') {
      // (buy_amount_mBTC / 1000) * rate
      buy_amount_USD = precision((buy_amount_mBTC / 1000) * rate, 2);
    }

    if (change != 'total_USD') {
      // buy_amount_USD * (1 + commission / 100)
      total_USD = nearest(buy_amount_USD / (1 - (commission / 100.00)), 1);
      total_USD = (total_USD <= buy_amount_USD && buy_amount_USD !== 0) ? total_USD + 5 : total_USD;
      $("input[name='total_USD']").val(total_USD);
    }
    if (change != 'buy_amount_USD') {
      // total_USD / (1 + commission / 100)
      buy_amount_USD = nearest(total_USD * (1 - (commission / 100.00)), 1);
      $("input[name='buy_amount_USD']").val(buy_amount_USD);
    }
    if (change != 'buy_amount_BTC' && change != 'buy_amount_mBTC') {
      // rate / buy_amount_USD
      buy_amount_BTC = precision(buy_amount_USD / rate, 8);
      $("input[name='buy_amount_BTC']").val(buy_amount_BTC);
    }
    if (change != 'buy_amount_BTC' && change == 'buy_amount_mBTC') {
      // buy_amount_mBTC / 1000
      buy_amount_BTC = precision(buy_amount_mBTC / 1000, 8);
      $("input[name='buy_amount_BTC']").val(buy_amount_BTC);
    }
    if (change != 'buy_amount_mBTC') {
      // buy_amount_BTC * 1000
      buy_amount_mBTC = precision(buy_amount_BTC * 1000, 8);
      $("input[name='buy_amount_mBTC']").val(buy_amount_mBTC);
    }

    // total_USD - buy_amount_USD
    $('#fee_total').text(total_USD - buy_amount_USD);
  }

  init();
});
<|endoftext|>