<commit_before>// ==UserScript==
// @name        Y-marker
// @namespace   trespassersW
// @description Sets up keyboard shortcuts for placing bookmarks and navigation inside webpage.
// @include        http://*
// @include        https://*
// @include        file://*
// ** about:config -> greasemonkey.fileIsGreaseable <- true
// @version 16.03.04
// @license  MIT
// @released 2013-12-11
// @run-at document-end
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// ==/UserScript==
/*
 16.03.04 bookmarklets works in chrome
 16.02.26
  [+] hotkey configuration dialogue invoked from GM menu;
  [+] Bookmarlets interface:
   javascript:postMessage('Y-marker0','*') - jump to bookmark # 0
   javascript:postMessage('Y-marker0=','*') - set up bookmark #0
 16.02.22
  [+] press Alt-Shift-0 twice to set starting Y-position for whole domain;
* 13-12-12 click on msg removes all marks
* 1.1 don't run in editable fields
*/
(function(){ "use strict";
if(top!=self || !document || !document.body ) return;
var W = unsafeWindow || window; // localStorage doesn't live in sandbox?
/* key kombinations to invoke the skript */
var kShift = 1,  kCtrl = 2, kAlt = 4, kWin = 8;
var kJump = kAlt;
var kMark = kAlt+kShift;

/* sec; 0 to disable; -1 status bar only */
var tipShowtime = 2.2;

var kNobs = "-0123456789";
var kKeys = {
 173: 0, 48: 1, 49: 2, 50: 3, 51: 4, 52: 5, 53: 6, 54: 7, 55: 8, 56: 9, 57: 10,
 189: 0, /* chrome, opera caps lock */
 109: 0  /* opera */
};
var minus1="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
    min1=minus1.split(',');
var LH=location.hostname || "Ym", LHP=LH+location.pathname;

function kNob(k){ return kNobs.charAt(k); }

var U=undefined, D=window.document;
var _L=  function() {};
//_L=console.log.bind(console);

var statMsg;
function msg(t,k, y){
 statMsg = "";
 if(U!==k)
  statMsg += "["+kNob(k)+"] ";
 if(U!==y)
  statMsg += ""+pt(y)+"% ";
 statMsg += t;
}

var locStor,locStored;

// see SQlite manager -> %FFpath%\webappstore.sqlite -> find -> key -> contains Ym#

try {
  locStor = W.localStorage;
  locStored=locStor.getItem("Ym#"+location.pathname);
} catch(e){ locStor = locStored =null;  };

function pt(y) {
 try{
  y =Math.round(y*1000/(document.body.scrollHeight+1))/10;
  if(y>111.1) y=111.1;
 }catch(e){console.log("math\n"+e); return -1}
 return y;
}

var Ym;
var pz;

function ldYm(){
  var s,t,y;
  if(locStor && locStored){
    pz=locStored.split(",");
    svYm();
    locStor.removeItem("Ym#"+location.pathname);
    console.log('Ym: locS -> GM_S '+LHP);
    locStor = locStored = null;
  }
  s=GM_getValue(Ym,null);
  if(!s){ s=minus1;
  }else {
   try{ y=JSON.parse(s);
   }catch(e){console.log('ldYm: buggy data\n'+e)};
   s=y[LHP] || y[LH] || minus1;
  }

  pz=s.split(",");
  if(y && (y=y['#HotKeys#'])){
   kJump = +y[0], kMark = +y[1];
  }
}

function svYm(d){
 var y={},s=pz.join(",");
 try{
   y=JSON.parse(GM_getValue(Ym,'{}'));
 }catch(e){console.log('svYm: buggy data\n'+e);}
 if(!d) y[LHP]= s;
 else if(d==='#'){
   y['#HotKeys#']=[kJump,kMark];
 }else{ //  marker for whole domain
   y[LH]= s;
 }
 GM_setValue(Ym,JSON.stringify(y));
}

function rmYm(){
 var y={};
 try{
   y=JSON.parse(GM_getValue(Ym,'{}'));
 }catch(e){console.log('rmYm: buggy data\n'+e); y={}; }
  if(y[LH]) delete y[LH],  _L("rmH: " +LH);
  if(y[LHP]) delete y[LHP],  _L("rmHP: " +LHP);
 GM_setValue(Ym,JSON.stringify(y));
 pz=min1;
}

function jumpY(k){
 var y=pz[k];
 if(y > -1) {
   if( y!= scrollY ){
    pz[0] = scrollY;
    scroll(0,y);
    msg("jump",k,y);
   }else
    msg("here",k,y);
   return 1;
 }
 msg("none",k);
 return 0;
}

function markY(k){
 var d="";
 if(k==1 && scrollY == pz[k] ) d="!";
 pz[k]=scrollY;
 if(k>=1)
  svYm(d);
 msg("mark"+d,k,pz[k]);
 return 1;
}

var kMap= {16: kShift, 17:kCtrl, 18: kAlt, 91: kWin};
var keyMod=0;
var kkWin= (kMark | kJump) & kWin;

function klear(e) {
 var k= e.keyCode;
 if(!k) return;
 var km=kMap[k];
 if(km) {
  keyMod &= -1 ^ km;
 }
}

function onKeydown(e) {
 try{
 var rc=0, k= e.keyCode;
 if(!k) return;
 // Don't run in input, select, textarea etc.
 var E = D.activeElement.tagName.toLowerCase();
 if (E == "input" || E == "select" || E == "textarea" ||
    (E=D.activeElement.contentEditable) == "true" ||
     E == "")
   {  keyMod = 0; return; }

 var km =kMap[k]; // ff doesnt track metaKey. stsuko
 if(km) {
   keyMod |= km;
   if( kkWin && km === kWin ) //???
    e.preventDefault(), e.stopPropagation();
   return;
 }
 km=keyMod;
 if( U=== (k=kKeys[k]) ){ keyMod = 0; return; }
 if( km === kJump ) {
   rc = jumpY(k);
 }else if( km === kMark ) {
   rc = markY(k);
 }else {
  return;
 }
 statSay();
 if(rc) e.preventDefault(),e.stopPropagation();
 }catch(e){console.log('smth wromg\n'+e)};
}

function mk(p, t, id, s) {
  var e = D.createElement(t);
  e.id=id;
  e.style.cssText = s;
  return p.appendChild(e);
};

/* RIP status bar replacement */
var sb;
function _css(){
if(!sb) sb = mk(
//D.body,
D.documentElement, // 2014-06-30 ???!!1
'section', "Y-marker-userjs-inf",
"position: fixed!important;\
z-index: 214748!important;\
top: 0px; right: 1px; bottom: auto; left: auto;\
background: rgba(221,255,221,.75)!important;\
padding: 2px 3px 2px 8px; margin:0;\
border: none;\
border-radius: 12px 3px 3px 12px;\
color: #131!important;\
opacity: 1; display:none;\
font: normal 12px/14px sans-serif !important;\
text-shadow: #373 2px 2px 4px, #7F7 -2px -2px 4px;\
cursor:no-drop;\
"
);
/* click removes all  */
if(tipShowtime>0){
 sb.addEventListener("click",function (e) {
  rmYm(); //locStor.removeItem(Ym);
  ldYm(); noTout();
 },false);
};
}

var tO;
function onTout(){
 tO=0; sb.style.display="none";
}
function noTout(){
 if(tO) clearTimeout(tO), onTout();
}

function statSay(t) {
 if(statMsg){
 _css();
   if(tipShowtime)
     W.status=statMsg;
   if(tipShowtime>0){
     noTout();
     sb.innerHTML= statMsg;
     sb.style.display="block";
     tO=setTimeout(onTout, (t? t: tipShowtime)*1000);
   }
 }
 statMsg="";
}

Ym="Ym#";//+location.pathname;

ldYm();
for(var k=1; k<2; k++){ /* pz.length */
 if(pz[k]>-1){
  scroll(0,pz[k]);
  msg('jump',k,pz[k]);
  statSay(5);
  break;
 }
};

function $i(id, parent){
	return (parent || document).getElementById(id)
}

var hk;
function saveHk(){
 function K(id, b)
 { return $i('Ym-bt'+id).checked? b: 0 }
  kJump=K('JS',kShift)|K('JA',kAlt)|K('JW',kWin)|K('JC',kCtrl);
  kMark=K('MS',kShift)|K('MA',kAlt)|K('MW',kWin)|K('MC',kCtrl);
  svYm('#');
}

function setKeys(){
 function J(j){return kJump&j? 'checked': ''}
 function M(m){return kMark&m? 'checked': ''}
  if(hk)   hk.parentNode.removeChild(hk);
  hk = D.createElement('section');
  hk.style.cssText =
"position: fixed!important;\
z-index: 214748!important;\
top: 10px; left: 16px; bottom: auto; right: auto;\
background: rgb(221,255,221)!important;\
padding: 4px; margin:0;\
border: 1px solid #131;\
border-radius: 3px; \
color: #131!important;\
opacity: 1; display:block;\
font: normal 12px/14px sans-serif !important;\
";
  hk.innerHTML=('\
<form style="padding:0;margin:0">\
Y-marker hotkeys\
Jump:   \
Shift<input type=checkbox id=Ym-btJS '+J(kShift)+'>   \
Alt<input type=checkbox id=Ym-btJA '+J(kAlt)+'>   \
Win<input type=checkbox id=Ym-btJW '+J(kWin)+'>   \
Ctrl<input type=checkbox id=Ym-btJC '+J(kCtrl)+'>
\
Mark:   \
Shift<input type=checkbox id=Ym-btMS '+M(kShift)+'>   \
Alt<input type=checkbox id=Ym-btMA '+M(kAlt)+'>   \
Win<input type=checkbox id=Ym-btMW '+M(kWin)+'>   \
Ctrl<input type=checkbox id=Ym-btMC '+M(kCtrl)+'>
\
<input type="button" tabIndex=1 value="Save" id="Ym-btSave">  \
<input type="button" tabIndex=2 value="Cancel" id="Ym-btCancel">\
</form>\
');
hk.addEventListener("click",function(e){
 var t= e.target,c=0;
 if(!t || !t.id) return;
 if(t.id=="Ym-btSave")try{
   c=1;
   saveHk();
 }catch(e){console.log("Ym: problem in hotkey dialog\n"+e)}
 else if(t.id=="Ym-btCancel"){
   c=1;
 }
 if(c){
   e.preventDefault(), e.stopPropagation();
   hk.parentNode.removeChild(hk),hk=null;
 }
},false);
 D.documentElement.appendChild(hk);
}

function wMsg(e){
 //event.source!=window in Chrome
 if(typeof e.data==='string' && e.data.substr(0,8)==='Y-marker'){
  e.stopPropagation();
  if(e.data === 'Y-marker hotkeys')
    return setKeys();
  var m=e.data.match(/Y-marker\s*([0-9-])(\=?)/);
  var p = (m[1]==='-')?0: +m[1]+1;
  if(m[2])
    markY(p);
  else
    jumpY(p);
  statSay();
 }
}

addEventListener("keydown",onKeydown,false);
addEventListener("keyup",klear,false);
GM_registerMenuCommand("Y-marker hotkeys", setKeys);

window.addEventListener("message", wMsg, false);

})();
<commit_msg>changes in github MD<commit_after>![pic](../res/ymark.png) | **Y-marker** **[[`source`]](../src/Y-marker.user.js)** **[[`Install`]](/../../raw/master/src/Y-marker.user.js  "You must have GreaseMonkey installed")**
--- |-----
description | Sets up keyboard shortcuts for placing bookmarks and navigation inside webpage

You can save current position in displayed webpage using shortcuts  **Shift-Alt-0‥9**;   later, you may return to saved position by pressing **Alt -0‥9**.    Markers created in this way are kept in browser's persistent memory and they will be restored next time you open the page.

------

![shot](../res/ymshot.png "www.gutenberg.org")

------

### notes

* Before jumping to marker the script stores the current page position, so you can get back by pressing **Alt-minus** (that on the right of zero.)
* When you open webpage the script tries to restore previously saved position #0
* Positions are calculated in screen's pixels, so if you alter zoom factor, or text column width, or something else, markers may become invalid :(
* You can change script's keyboard shortcuts using GreaseMonkey menu:

User Script Commands → Y-marker hotkeys

* press Alt-Shift-0   twice to set starting Y-position for any page in the whole domain
* click on pop-up message at top-right corner to remove all markers for current webpage.

* you can control the script through bookmarklets:
   - *jump to bookmark #0:*
javascript:postMessage('Y-marker0','\*')
   - *set up  bookmark #0:*
javascript:postMessage('Y-marker0=','\*')

******

License: MIT<|endoftext|>


<commit_before>"use strict";

var cycl = require('cycl'),
    processor = require('./processor.js'),
    presets = require('./presets.js'),
    rubix = require('./rubix.js'),
    Props = require('./props.js'),
    Pointer = require('../input/pointer.js'),
    KEY = require('../opts/keys.js'),
    Data = require('../bits/data.js'),

    Action = function () {
        var self = this;

        // Create value manager
        this.values = new Values();

        // Create new property manager
        this.props = new Props();

        // Create data store
        this.data = new Data();

        // Register process wth cycl
        this.process = cycl.newProcess(function (framestamp, frameDuration) {
            processor.action(self, framestamp, frameDuration);
        });
    };

Action.prototype = {

    // [number]: Progress represented in a range of 0 - 1
    progress: 0,

    // [number]: Time elapsed in ms
    elapsed: 0,

    /*
        Play the provided actions as animations

        Syntax
            .play(playlist, [override])
                @param [string]: Playlist of presets
                @param [object]: (optional) Override object

            .play(params)
                @param [object]: Action properties

        @return [Action]
    */
    play: function (defs, override) {
        return this.start(KEY.RUBIX.TIME, presets.createBase(defs, override));
    },

    /*
        Track values to mouse, touch or custom Input

        Syntax
            .track(preset, [override], input)
                @param [string]: Name of preset
                @param [object]: (optional) Override object
                @param [event || Input]: Input or event to start tracking

            .track(params, input)
                @param [object]: Action properties
                @param [event || Input]: Input or event to start tracking

        @return [Action]
    */
    track: function (defs) {
        var hasAllArgs = (arguments[2] !== undefined),
            input = hasAllArgs ? arguments[2] : arguments[1],
            override = hasAllArgs ? arguments[1] : {},
            base = presets.createBase(defs, override);

        // If we have an input, check if it's a custom input.
        // Create a new pointer if it isn't
        if (input) {
            base.input = (input.current) ? input : new Pointer(input);
            base.inputOrigin = base.input.get();
        }

        return this.start(KEY.RUBIX.INPUT, base);
    },

    /*
        Run Action indefinitely

        Syntax
            .run(preset, [override])
                @param [string]: Name of preset
                @param [object]: (optional) Override object

            .run(params)
                @param [object]: Action properties

        @return [Action]
    */
    run: function (defs, override) {
        return this.start(KEY.RUBIX.RUN, presets.createBase(defs, override));
    },

    /*
        Start Action

        @param [string]: Name of processing type to sue
        @param [object]: Base Action properties to apply
        @return [Action]
    */
    start: function (processType, base) {
        this.resetProgress();
        this.change(processType, base);
        this.isActive(true);
        this.started = utils.currentTime() + this.props.get('delay');
        this.framestamp = this.started;
        this.firstFrame = true;

        this.process.start();

        return this;
    },

    /*
        Stop current Action process
    */
    stop: function () {
        this.isActive(false);
        this.process.stop();

        return this;
    },

    /*
        Pause current Action
    */
    pause: function () {
        this.stop();

        return this;
    },

    /*
        Resume a paused Action
    */
    resume: function () {
        this.started = utils.currentTime() - this.elapsed;
        this.framestamp = this.started;

        this.process.start();

        return this;
    },

    /*

    */
    reset: function () {
        this.resetProgress();
        this.values.reset();

        return this;
    },

    resetProgress: function () {
        this.progress = 0;
        this.elapsed = 0;
    },

    reverse: function () {
        this.progress = calc.difference(this.progress, 1);
        this.elapsed = calc.difference(this.elapsed, this.duration);
        this.values.reverse();

        return this;
    },

    setValue: function (key, value) {
        this.values.set(key, value);
    },

    getValue: function (key) {
        return this.values.get(key);
    },

    setProp: function (key, value) {
        this.props.set(key, value);
    },

    getProp: function (key) {
        return this.props.get(key);
    },

    /*
        Is Action active?

        @param [boolean] (optional): If provided, will set action to active/inactive
        @return [boolean]: Active status
    */
    isActive: function (active) {
        if (active !== active) {
            this.active = active;
        }

        return this.active;
    },

    /*
        Change Action properties

        @param [string]: Type of processing rubix to use
        @param [object]: Base properties of new input
    */
    change: function (processType, base) {
        // Assign the processing rubix
        base.rubix = rubix[processType];

        this.props.apply(base);
        this.values.apply(base.values, this.props);

        this.origin = {};
        // Create origins
        for (var key in this.values) {
            if (this.values.hasOwnProperty(key)) {
                this.origin[key] = this.values[key].current;
            }
        }
    }

};

module.exports = Action;<commit_msg>finish yoyo and loop<commit_after>"use strict";

var cycl = require('cycl'),
    processor = require('./processor.js'),
    presets = require('./presets.js'),
    rubix = require('./rubix.js'),
    Props = require('./props.js'),
    Pointer = require('../input/pointer.js'),
    KEY = require('../opts/keys.js'),
    Data = require('../bits/data.js'),

    Action = function () {
        var self = this;

        // Create value manager
        this.values = new Values();

        // Create new property manager
        this.props = new Props();

        // Create data store
        this.data = new Data();

        // Register process wth cycl
        this.process = cycl.newProcess(function (framestamp, frameDuration) {
            processor.action(self, framestamp, frameDuration);
        });
    };

Action.prototype = {

    // [number]: Progress represented in a range of 0 - 1
    progress: 0,

    // [number]: Time elapsed in ms
    elapsed: 0,

    /*
        Play the provided actions as animations

        Syntax
            .play(playlist, [override])
                @param [string]: Playlist of presets
                @param [object]: (optional) Override object

            .play(params)
                @param [object]: Action properties

        @return [Action]
    */
    play: function (defs, override) {
        return this.start(KEY.RUBIX.TIME, presets.createBase(defs, override));
    },

    /*
        Track values to mouse, touch or custom Input

        Syntax
            .track(preset, [override], input)
                @param [string]: Name of preset
                @param [object]: (optional) Override object
                @param [event || Input]: Input or event to start tracking

            .track(params, input)
                @param [object]: Action properties
                @param [event || Input]: Input or event to start tracking

        @return [Action]
    */
    track: function (defs) {
        var hasAllArgs = (arguments[2] !== undefined),
            input = hasAllArgs ? arguments[2] : arguments[1],
            override = hasAllArgs ? arguments[1] : {},
            base = presets.createBase(defs, override);

        // If we have an input, check if it's a custom input.
        // Create a new pointer if it isn't
        if (input) {
            base.input = (input.current) ? input : new Pointer(input);
            base.inputOrigin = base.input.get();
        }

        return this.start(KEY.RUBIX.INPUT, base);
    },

    /*
        Run Action indefinitely

        Syntax
            .run(preset, [override])
                @param [string]: Name of preset
                @param [object]: (optional) Override object

            .run(params)
                @param [object]: Action properties

        @return [Action]
    */
    run: function (defs, override) {
        return this.start(KEY.RUBIX.RUN, presets.createBase(defs, override));
    },

    /*
        Start Action

        @param [string]: Name of processing type to sue
        @param [object]: Base Action properties to apply
        @return [Action]
    */
    start: function (processType, base) {
        this.resetProgress();
        this.change(processType, base);
        this.isActive(true);
        this.started = utils.currentTime() + this.props.get('delay');
        this.framestamp = this.started;
        this.firstFrame = true;

        this.process.start();

        return this;
    },

    /*
        Stop current Action process
    */
    stop: function () {
        this.isActive(false);
        this.process.stop();

        return this;
    },

    /*
        Pause current Action
    */
    pause: function () {
        this.stop();

        return this;
    },

    /*
        Resume a paused Action
    */
    resume: function () {
        this.started = utils.currentTime() - this.elapsed;
        this.framestamp = this.started;

        this.process.start();

        return this;
    },

    /*

    */
    reset: function () {
        this.resetProgress();
        this.values.reset();

        return this;
    },

    resetProgress: function () {
        this.progress = 0;
        this.elapsed = 0;
    },

    reverse: function () {
        this.progress = calc.difference(this.progress, 1);
        this.elapsed = calc.difference(this.elapsed, this.duration);
        this.values.reverse();

        return this;
    },

    /*
        Check for next steps and perform, stop if not
    */
    next: function () {
        var nexts = [{
                key: 'loop',
                callback: this.reset
            }, {
                key: 'yoyo',
                callback: this.reverse
            }],
            possibles = nexts.length,
            hasNext = false;

        for (var i = 0; i < posssbiles; ++i) {
            if (this.checkNextStep(nexts[i].key, nexts[i].callback)) {
                hasFuture = true;
                break;
            }
        }

        if (!hasNext) {
            this.stop();
        }
    },

    /*
        Check next step

        @param [string]: Name of step ('yoyo' or 'loop')
        @param [callback]: Function to run if we take this step
    */
    checkNextStep: function (key, callback) {
        var stepTaken = false,
            step = this.props.get(key),
            count = this.props.get(key + 'Count'),
            forever = (yoyo === true);

        if (forever || utils.isNum(step)) {
            ++count;
            this.props.set(key + 'Count', count);

            if (forever || count <= step) {
                callback();
                stepTaken = true;
            }
        }
    },

    /*
        Next in playlist

    */
    playNext: function () {
        var stepTaken = false,
            playlist = this.props.get('playlist'),
            platlistLength = playlist.length,
            playhead = this.props.get('playhead'),
            next = {};

        // Check we have a playlist
        if (playlistLength > 1) {
            ++playhead;

            if (playhead < playlistLength) {
                next = presets.getDefined(playlist[playhead]);
                next.playhead = playhead;

                this.change(KEY.RUBIX.TIME, next);
                stepTaken = true;
            }
        }
    },

    setValue: function (key, value) {
        this.values.set(key, value);
    },

    getValue: function (key) {
        return this.values.get(key);
    },

    setProp: function (key, value) {
        this.props.set(key, value);
    },

    getProp: function (key) {
        return this.props.get(key);
    },

    /*
        Is Action active?

        @param [boolean] (optional): If provided, will set action to active/inactive
        @return [boolean]: Active status
    */
    isActive: function (active) {
        if (active !== active) {
            this.active = active;
        }

        return this.active;
    },

    /*
        Change Action properties

        @param [string]: Type of processing rubix to use
        @param [object]: Base properties of new input
    */
    change: function (processType, base) {
        // Assign the processing rubix
        base.rubix = rubix[processType];

        this.props.apply(base);
        this.values.apply(base.values, this.props);

        this.origin = {};
        // Create origins
        for (var key in this.values) {
            if (this.values.hasOwnProperty(key)) {
                this.origin[key] = this.values[key].current;
            }
        }
    }

};

module.exports = Action;<|endoftext|>


<commit_before>var through       = require('through');
var _             = require('./mixins.js');
var path          = require('path');
var Graft         = require('../graft');
var debug         = require('debug')('graft:transform:wrap');
var verboseDebug  = require('debug')('verbose:graft:transform:wrap');
var wrapper       = require('./wrap._');
var modulePathTpl = _.template("{{sysName}}.{{name}}");

function compile(filename, content, client) {
    debug('compile', filename);
    var dirname = path.basename(path.dirname(filename));
    var system  = _(Graft.systems).findWhere({ path: dirname });

    var sysName   = system && system.name || dirname;
    var name    = path.basename(filename, '.graft.js');

    var _path   = modulePathTpl({
        name    : name,
        sysName : sysName
    });

    // If this module is named the same as the system
    // name (eg: data/Data.graft.js) consider it a root module.
    var isRootModule = Graft.systems[name] &&
        (Graft.systems[name].path === dirname);

    if (isRootModule) {
        _path = name;
    }

    var opts = {
        filename     : filename,
        content      : content,
        graftPath    : client ? 'graftjs' : global.__graftPath,
        system       : system,
        module       : {
            kind     : system && system.kind || dirname,
            sysName  : sysName,
            name     : name,
            safeName : name.replace(/\./g, ''),
            path     : _path,
            fn       : _path.replace(/\./g, '') + 'Module'
        }
    };

    var kinds   = _(Graft.systems).chain()
        .where({ transform: 'wrap' })
        .pluck('name')
        .value();

    debug('kind', sysName, kinds);
    var ret = _.include(kinds, sysName) ? wrapper(opts) : content;
    verboseDebug('source :' + filename, ret);
    return ret;
}

function _through(file) {
    debug('through: ', file);
    var buffer = '';
    function data(data) {
        buffer = buffer + data;
    }
    function end() {
        this.queue(compile(file, buffer));
        this.queue(null);
    }
    return through(data, end);
}

module.exports = {
    compile: compile,
    through: _through
};
<commit_msg>less verbose debugging. [skip ci]<commit_after>var through       = require('through');
var _             = require('./mixins.js');
var path          = require('path');
var Graft         = require('../graft');
var debug         = require('debug')('graft:transform:wrap');
var verboseDebug  = require('debug')('verbose:graft:transform:wrap');
var wrapper       = require('./wrap._');
var modulePathTpl = _.template("{{sysName}}.{{name}}");

function compile(filename, content, client) {
    debug('compile', filename);
    var dirname = path.basename(path.dirname(filename));
    var system  = _(Graft.systems).findWhere({ path: dirname });

    var sysName   = system && system.name || dirname;
    var name    = path.basename(filename, '.graft.js');

    var _path   = modulePathTpl({
        name    : name,
        sysName : sysName
    });

    // If this module is named the same as the system
    // name (eg: data/Data.graft.js) consider it a root module.
    var isRootModule = Graft.systems[name] &&
        (Graft.systems[name].path === dirname);

    if (isRootModule) {
        _path = name;
    }

    var opts = {
        filename     : filename,
        content      : content,
        graftPath    : client ? 'graftjs' : global.__graftPath,
        system       : system,
        module       : {
            kind     : system && system.kind || dirname,
            sysName  : sysName,
            name     : name,
            safeName : name.replace(/\./g, ''),
            path     : _path,
            fn       : _path.replace(/\./g, '') + 'Module'
        }
    };

    var kinds   = _(Graft.systems).chain()
        .where({ transform: 'wrap' })
        .pluck('name')
        .value();

    var ret = _.include(kinds, sysName) ? wrapper(opts) : content;
    verboseDebug('source :' + filename, ret);
    return ret;
}

function _through(file) {
    debug('through: ', file);
    var buffer = '';
    function data(data) {
        buffer = buffer + data;
    }
    function end() {
        this.queue(compile(file, buffer));
        this.queue(null);
    }
    return through(data, end);
}

module.exports = {
    compile: compile,
    through: _through
};
<|endoftext|>


<commit_before>import fetch from 'isomorphic-fetch'
import { types } from '../constants/ActionTypes'

// determine where to send requests
const url = () => {
  const www = window.location.href.indexOf('www.') !== -1 ? 'www.' : ''
  return process.env.NODE_ENV === 'production'
    ? `https://${www}amodahl.no/api/public` : `http://${www}local.amodahl.no:3000`
}

const googleClientId = '778219340101-tf221dbeeho9frka8js86iv460hfuse0.apps.googleusercontent.com'

// The scopes to request for the token
const scopes = [
  'user.all',
  'user.list',
  'chess-game.all',
  'chess-game.list',
  'chess-game.create',
  'chess-move.all',
  'chess-move.list',
  'chess-move.create',
  'update.all',
  'update.list'
]

export const receiveAmodahlToken = (jwToken, user) => ({
  jwToken: jwToken,
  user: user,
  type: types.login.RECEIVE_AMODAHL_TOKEN
})

export const receiveSignereUrl = (url, accessToken, requestId) => ({
  type: types.login.RECEIVE_SIGNERE_URL,
  url: url,
  accessToken: accessToken,
  requestId: requestId
})

export const requestAmodahlTokenFailed = () => ({
  type: types.login.REQUEST_AMODAHL_TOKEN_FAILED
})

export const userLoggedOut = () => ({
  type: types.login.USER_LOGGED_OUT
})

export const updateLoginInfo = (message, displayMsg, loading, info, cancelled) => ({
  message: message,
  displayMessage: displayMsg,
  loading: loading,
  additionalInfo: info,
  cancelled: cancelled,
  type: types.login.UPDATE_LOGIN_INFO
})

/**
 * Ask the API for a token using a facebook or google token, or a
 * signere request id.
 * The API checks that the token or id is valid
 * and then responds with a JSON web token.
 *
 * @param params Login parameters
 * @param params.fbAccessToken a facebook access token (facebook)
 * @param params.googleIdToken a google id token (google)
 * @param params.signereRequestId a signere.no request id (signere)
 * @param params.signereAccessToken a signere.no OAuth2 access token (signere, optional)
 * @param params.name the users name (test)
 * @param params.email the users email (test)
 * @param params.type the type of login (test/signere/google/facebook)
 *
 * @return the API's response containing a JSON web token for amodahl.no-api
 */
export const fetchJwToken = params => (dispatch, getState) => {
  dispatch(updateLoginInfo('Logging in to amodahl.no...', true, true, params, false))
  let state = getState()

  params = {
    ...params,
    requestedScopes: JSON.stringify(scopes)
  }

  var headers = new Headers()
  headers.append('pragma', 'no-cache')
  headers.append('cache-control', 'no-cache')

  return fetch(url() + '/token', {
    method: 'POST',
    headers,
    body: JSON.stringify(params)
  })
  .then(response => { // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1

    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json()
  })
  .then(response => { // ensure query was accepted
    if (response.status !== 'ok') {
      throw new Error('Received: ' + JSON.stringify(response))
    }
    return response
  })
  .then(response => { // ensure we got what we wanted
    if (!response.user || !response.user.name || !response.user.uid || !response.token) {
      throw new Error('Received: ' + JSON.stringify(response))
    }
    if (typeof (response.user.name) !== 'string' ||
        typeof (response.user.uid) !== 'string' ||
        typeof (response.token) !== 'string') {
      throw new Error('Received: ' + JSON.stringify(response))
    }
    if (response.user.name.length === 0 ||
        response.user.uid.length === 0 ||
        response.token.length === 0) {
      throw new Error('Received: ' + JSON.stringify(response))
    }

    return response
  })
  .then(response => {
    if (!state.cancelled) {
      dispatch(receiveAmodahlToken(response.token, response.user))
      dispatch(updateLoginInfo('You logged in as ' + response.user.name,
            true, false, response, false))

        // store user info in browser (for next time app is started)
      localStorage.setItem('loginType', params.type)
      localStorage.setItem('jwToken', response.token)
      localStorage.setItem('user', JSON.stringify(response.user))
    }
  })
  .catch(error => {
    if (!state.cancelled) {
      dispatch(requestAmodahlTokenFailed())
      dispatch(updateLoginInfo('Failed to log in to amodahl.no: ' +
        error.message, true, false, error, false))
    }
  })
}

/**
 * Log in to amodahl.no-api.
 * This means authenticating with (google/facebook/signere)
 * and then requesting an amodahl.no-api token using the token/requestId
 * from (google/facebook/signere).
 *
 * In addition test users defined by the API can log
 * without further identification.
 * @param params Login parameters
 * @param params.signereRequestId a signere.no request id (signere)
 * @param params.signereAccessToken a signere.no OAuth2 access token (signere, optional)
 * @param params.name the users name (test)
 * @param params.email the users email (test)
 * @param params.type the type of login (test/signere/google/facebook)
 */
export const login = params => (dispatch, getState) => {
  if (!params) {
    // use same login type as last time
    params = {
      type: localStorage.loginType
    }
  }

  let state = getState()

  // try offline login
  dispatch(updateLoginInfo('Looking for user info in local storage...',
    false, false, params, false))
  if (localStorage.loginType && localStorage.loginType === params.type) {
    let user
    try {
      user = JSON.parse(localStorage.user)
      dispatch(receiveAmodahlToken(localStorage.jwToken, user))
      dispatch(updateLoginInfo('You logged in as ' + user.name,
        true, false, user, false))
      return
    } catch (e) {
      dispatch(updateLoginInfo(`Invalid login info found in local storage, clearing`,
        false, false, localStorage, false))
      localStorage.clear()
    }
  } else {
    dispatch(updateLoginInfo(`No ${params.type} login info found in local storage`,
      false, false, localStorage, false))
  }

  if (params.type === 'signere' && params.signereRequestId) {
    dispatch(fetchJwToken(params))
  } else if (params.type === 'signere') {
    dispatch(updateLoginInfo('Requesting url from Signere.no',
      true, true, params, false))

    // get url for iframe
    let body = {
      requested_scopes: JSON.stringify(scopes),
      type: 'signere'
    }

    fetch(url() + '/token', {
      method: 'POST',
      body: JSON.stringify(body)
    })
    .then(response => { // ensure its json
      let contentType = response.headers.get('content-type')
      let gotJson = contentType && contentType.indexOf('application/json') !== -1

      if (!gotJson) {
        throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
      }
      return response.json()
    })
    .then(result => {
      if (!state.cancelled) {
        dispatch(updateLoginInfo('Received url from Signere.no',
              true, true, result, false))
        dispatch(receiveSignereUrl(result.Url, result.AccessToken, result.RequestId))
        params.navigate('/signere-login')
      }
    })
  }

  if (params.type === 'google') {
    dispatch(updateLoginInfo('Logging in with Google...',
      true, true, params, false))

    const errorCallback = error => {
      if (!state.cancelled) {
        dispatch(updateLoginInfo('Failed to log in to google: ' +
              JSON.stringify(error), true, false, error, false))
      }
    }

    window.signinCallback = () => {
      console.log('Signing in with google')

      // TODO improve
      window.gapi.load('auth2', function () {
        // Retrieve the singleton for the GoogleAuth library and set up the client.
        window.gapi.auth2.init({
          client_id: googleClientId,
          cookiepolicy: 'single_host_origin'
        })
        .then(args => {
          window.gapi.auth2.getAuthInstance()
          .then(googleAuth => {
            googleAuth.signIn()
            .then(result => {
              let authResponse = googleAuth.currentUser.get()
                                  .getAuthResponse(true)
              if (!state.cancelled) {
                // finally we got google token and can trade it for an amodahl token
                dispatch(updateLoginInfo('Received token from Google',
                                      true, true, authResponse, false))
                dispatch(fetchJwToken({
                  googleIdToken: authResponse.id_token,
                  ...params
                }))
              }
            }, errorCallback) // asdf
          }, errorCallback) //
        }, errorCallback)
      })
    }

    if (window.document.getElementById('google-lib')) {
      window.signinCallback()
    } else {
      // init google sdk first
      console.log('Initializing google sdk')
      var jsElm = window.document.createElement('script')
      jsElm.type = 'application/javascript'
      jsElm.src = 'https://apis.google.com/js/client.js?onload=signinCallback'
      jsElm.id = 'google-lib'
      window.document.body.appendChild(jsElm)
    }
  }

  if (params.type === 'facebook') {
    dispatch(updateLoginInfo('Logging in with Facebook...',
      true, true, params, false))

    // to be called after init facebook sdk
    const fbLogin = () => {
      window.FB.login(fbResponse => {
        if (!fbResponse.authResponse) { // ensure facebook login was successful
          dispatch(updateLoginInfo('Failed to log in to facebook: ' +
            JSON.stringify(fbResponse), true, false, fbResponse, false))
          return
        }

        let fbAccessToken = fbResponse.authResponse.accessToken

        if (!state.cancelled) {
          dispatch(updateLoginInfo('Received token from Facebook',
            true, true, fbResponse.authResponse, false))
          // fetch token from amodahl.no using facebook token
          dispatch(fetchJwToken({
            ...params,
            fbAccessToken: fbAccessToken
          }))
        }
      }, {scope: 'public_profile,email'})
    }

    if (document.getElementById('facebook-jssdk')) {
      fbLogin()
    } else { // init facebook sdk first
      window.fbAsyncInit = function () {
        window.FB.init({
          appId: '772463112910269',
          xfbml: true,
          version: 'v2.8'
        })
        window.FB.AppEvents.logPageView()

        fbLogin()
      };

      (function (d, s, id) {
        let js
        let fjs = d.getElementsByTagName(s)[0]
        if (d.getElementById(id)) { return }
        js = d.createElement(s); js.id = id
        js.src = '//connect.facebook.net/en_US/sdk.js'
        fjs.parentNode.insertBefore(js, fjs)
      }(document, 'script', 'facebook-jssdk'))
    }
  }

  if (params.type === 'test') {
    dispatch(fetchJwToken(params))
  }
}

/**
 * @callback MyFetchCallback
 * @param {Object} body The response body
 * @param {Object} header The response header
 */

/**
 * Method for fetching resources from amodahl.no API.
 * If request fails because of expired token a login action is dispatched and
 * the fetch request is repeated after successfull login.
 * @param dispatch The redux dispatch function
 * @param jwToken a token for amodahl.no API
 * @param attempts Number fetch attempts for 'what'
 * @param what The url describing what to fetch
 * @param params Additional fetch parameters
 * @param callback {MyFetchCallback} called when resource has been fetched
 *
 * @return promise that is resolved with headers and body of API response
 */
export const myFetch = dispatch => jwToken => (path, params) => {
  var headers = params.headers ? params.headers : new Headers()
  headers.append('Authorization', 'Bearer ' + jwToken)
  headers.append('pragma', 'no-cache')
  headers.append('cache-control', 'no-store')

  const properties = {
    headers: headers,
    ...params
  }

  return fetch(url() + path, properties)
  .then(response => {
    // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1
    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json().then(json =>
    ({ headers: headers, body: json }))
  })
  .then(({body, headers}) => {
    // check if token has expired
    if (body.status === 'error' && body.message === 'Expired token') {
      // TODO deal with it
      throw new Error('Token expired')
    }
    return ({body, headers})
  })
  .then(({body, headers}) => {
    // ensure query was accepted
    if (body.status && body.status !== 'ok') {
      throw new Error('Received: ' + JSON.stringify(body))
    }
    return ({body, headers})
  })
}

/**
 * Clear local storage and dispatch an action to tell
 * other components that user has logged out.
 * @param {string} dispatcher From which page user clicked log out
 */
export const logout = () => dispatch => {
  dispatch(userLoggedOut())
  localStorage.clear()

  // log out of facebook
  try {
    if (window.FB) {
      window.FB.logout((response) => {
        console.log('Successfully logged out of facebook', response)
      })
    }
  } catch (err) {
    console.log('There was a problem logging out of facebook', err)
  }

  // TODO log out with google sdk?
}
<commit_msg>Deals with expired tokens<commit_after>import fetch from 'isomorphic-fetch'
import { types } from '../constants/ActionTypes'
import { push } from 'react-router-redux'

// determine where to send requests
const url = () => {
  const www = window.location.href.indexOf('www.') !== -1 ? 'www.' : ''
  return process.env.NODE_ENV === 'production'
    ? `https://${www}amodahl.no/api/public` : `http://${www}local.amodahl.no:3000`
}

const googleClientId = '778219340101-tf221dbeeho9frka8js86iv460hfuse0.apps.googleusercontent.com'

// The scopes to request for the token
const scopes = [
  'user.all',
  'user.list',
  'chess-game.all',
  'chess-game.list',
  'chess-game.create',
  'chess-move.all',
  'chess-move.list',
  'chess-move.create',
  'update.all',
  'update.list'
]

export const receiveAmodahlToken = (jwToken, user) => ({
  jwToken: jwToken,
  user: user,
  type: types.login.RECEIVE_AMODAHL_TOKEN
})

export const receiveSignereUrl = (url, accessToken, requestId) => ({
  type: types.login.RECEIVE_SIGNERE_URL,
  url: url,
  accessToken: accessToken,
  requestId: requestId
})

export const requestAmodahlTokenFailed = () => ({
  type: types.login.REQUEST_AMODAHL_TOKEN_FAILED
})

export const userLoggedOut = () => ({
  type: types.login.USER_LOGGED_OUT
})

export const updateLoginInfo = (message, displayMsg, loading, info, cancelled) => ({
  message: message,
  displayMessage: displayMsg,
  loading: loading,
  additionalInfo: info,
  cancelled: cancelled,
  type: types.login.UPDATE_LOGIN_INFO
})

/**
 * Ask the API for a token using a facebook or google token, or a
 * signere request id.
 * The API checks that the token or id is valid
 * and then responds with a JSON web token.
 *
 * @param params Login parameters
 * @param params.fbAccessToken a facebook access token (facebook)
 * @param params.googleIdToken a google id token (google)
 * @param params.signereRequestId a signere.no request id (signere)
 * @param params.signereAccessToken a signere.no OAuth2 access token (signere, optional)
 * @param params.name the users name (test)
 * @param params.email the users email (test)
 * @param params.type the type of login (test/signere/google/facebook)
 *
 * @return the API's response containing a JSON web token for amodahl.no-api
 */
export const fetchJwToken = params => (dispatch, getState) => {
  dispatch(updateLoginInfo('Logging in to amodahl.no...', true, true, params, false))
  let state = getState()

  params = {
    ...params,
    requestedScopes: JSON.stringify(scopes)
  }

  var headers = new Headers()
  headers.append('pragma', 'no-cache')
  headers.append('cache-control', 'no-cache')

  return fetch(url() + '/token', {
    method: 'POST',
    headers,
    body: JSON.stringify(params)
  })
  .then(response => { // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1

    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json()
  })
  .then(response => { // ensure query was accepted
    if (response.status !== 'ok') {
      throw new Error('Received: ' + JSON.stringify(response))
    }
    return response
  })
  .then(response => { // ensure we got what we wanted
    if (!response.user || !response.user.name || !response.user.uid || !response.token) {
      throw new Error('Received: ' + JSON.stringify(response))
    }
    if (typeof (response.user.name) !== 'string' ||
        typeof (response.user.uid) !== 'string' ||
        typeof (response.token) !== 'string') {
      throw new Error('Received: ' + JSON.stringify(response))
    }
    if (response.user.name.length === 0 ||
        response.user.uid.length === 0 ||
        response.token.length === 0) {
      throw new Error('Received: ' + JSON.stringify(response))
    }

    return response
  })
  .then(response => {
    if (!state.cancelled) {
      dispatch(receiveAmodahlToken(response.token, response.user))
      dispatch(updateLoginInfo('You logged in as ' + response.user.name,
            true, false, response, false))

        // store user info in browser (for next time app is started)
      localStorage.setItem('loginType', params.type)
      localStorage.setItem('jwToken', response.token)
      localStorage.setItem('user', JSON.stringify(response.user))
    }
  })
  .catch(error => {
    if (!state.cancelled) {
      dispatch(requestAmodahlTokenFailed())
      dispatch(updateLoginInfo('Failed to log in to amodahl.no: ' +
        error.message, true, false, error, false))
    }
  })
}

/**
 * Log in to amodahl.no-api.
 * This means authenticating with (google/facebook/signere)
 * and then requesting an amodahl.no-api token using the token/requestId
 * from (google/facebook/signere).
 *
 * In addition test users defined by the API can log
 * without further identification.
 * @param params Login parameters
 * @param params.signereRequestId a signere.no request id (signere)
 * @param params.signereAccessToken a signere.no OAuth2 access token (signere, optional)
 * @param params.name the users name (test)
 * @param params.email the users email (test)
 * @param params.type the type of login (test/signere/google/facebook)
 */
export const login = params => (dispatch, getState) => {
  if (!params) {
    // use same login type as last time
    params = {
      type: localStorage.loginType
    }
  }

  let state = getState()

  // try offline login
  dispatch(updateLoginInfo('Looking for user info in local storage...',
    false, false, params, false))
  if (localStorage.loginType && localStorage.loginType === params.type) {
    let user
    try {
      user = JSON.parse(localStorage.user)
      dispatch(receiveAmodahlToken(localStorage.jwToken, user))
      dispatch(updateLoginInfo('You logged in as ' + user.name,
        true, false, user, false))
      return
    } catch (e) {
      dispatch(updateLoginInfo(`Invalid login info found in local storage, clearing`,
        false, false, localStorage, false))
      localStorage.clear()
    }
  } else {
    dispatch(updateLoginInfo(`No ${params.type} login info found in local storage`,
      false, false, localStorage, false))
  }

  if (params.type === 'signere' && params.signereRequestId) {
    dispatch(fetchJwToken(params))
  } else if (params.type === 'signere') {
    dispatch(updateLoginInfo('Requesting url from Signere.no',
      true, true, params, false))

    // get url for iframe
    let body = {
      requested_scopes: JSON.stringify(scopes),
      type: 'signere'
    }

    fetch(url() + '/token', {
      method: 'POST',
      body: JSON.stringify(body)
    })
    .then(response => { // ensure its json
      let contentType = response.headers.get('content-type')
      let gotJson = contentType && contentType.indexOf('application/json') !== -1

      if (!gotJson) {
        throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
      }
      return response.json()
    })
    .then(result => {
      if (!state.cancelled) {
        dispatch(updateLoginInfo('Received url from Signere.no',
              true, true, result, false))
        dispatch(receiveSignereUrl(result.Url, result.AccessToken, result.RequestId))
        params.navigate('/signere-login')
      }
    })
  }

  if (params.type === 'google') {
    dispatch(updateLoginInfo('Logging in with Google...',
      true, true, params, false))

    const errorCallback = error => {
      if (!state.cancelled) {
        dispatch(updateLoginInfo('Failed to log in to google: ' +
              JSON.stringify(error), true, false, error, false))
      }
    }

    window.signinCallback = () => {
      console.log('Signing in with google')

      // TODO improve
      window.gapi.load('auth2', function () {
        // Retrieve the singleton for the GoogleAuth library and set up the client.
        window.gapi.auth2.init({
          client_id: googleClientId,
          cookiepolicy: 'single_host_origin'
        })
        .then(args => {
          window.gapi.auth2.getAuthInstance()
          .then(googleAuth => {
            googleAuth.signIn()
            .then(result => {
              let authResponse = googleAuth.currentUser.get()
                                  .getAuthResponse(true)
              if (!state.cancelled) {
                // finally we got google token and can trade it for an amodahl token
                dispatch(updateLoginInfo('Received token from Google',
                                      true, true, authResponse, false))
                dispatch(fetchJwToken({
                  googleIdToken: authResponse.id_token,
                  ...params
                }))
              }
            }, errorCallback) // asdf
          }, errorCallback) //
        }, errorCallback)
      })
    }

    if (window.document.getElementById('google-lib')) {
      window.signinCallback()
    } else {
      // init google sdk first
      console.log('Initializing google sdk')
      var jsElm = window.document.createElement('script')
      jsElm.type = 'application/javascript'
      jsElm.src = 'https://apis.google.com/js/client.js?onload=signinCallback'
      jsElm.id = 'google-lib'
      window.document.body.appendChild(jsElm)
    }
  }

  if (params.type === 'facebook') {
    dispatch(updateLoginInfo('Logging in with Facebook...',
      true, true, params, false))

    // to be called after init facebook sdk
    const fbLogin = () => {
      window.FB.login(fbResponse => {
        if (!fbResponse.authResponse) { // ensure facebook login was successful
          dispatch(updateLoginInfo('Failed to log in to facebook: ' +
            JSON.stringify(fbResponse), true, false, fbResponse, false))
          return
        }

        let fbAccessToken = fbResponse.authResponse.accessToken

        if (!state.cancelled) {
          dispatch(updateLoginInfo('Received token from Facebook',
            true, true, fbResponse.authResponse, false))
          // fetch token from amodahl.no using facebook token
          dispatch(fetchJwToken({
            ...params,
            fbAccessToken: fbAccessToken
          }))
        }
      }, {scope: 'public_profile,email'})
    }

    if (document.getElementById('facebook-jssdk')) {
      fbLogin()
    } else { // init facebook sdk first
      window.fbAsyncInit = function () {
        window.FB.init({
          appId: '772463112910269',
          xfbml: true,
          version: 'v2.8'
        })
        window.FB.AppEvents.logPageView()

        fbLogin()
      };

      (function (d, s, id) {
        let js
        let fjs = d.getElementsByTagName(s)[0]
        if (d.getElementById(id)) { return }
        js = d.createElement(s); js.id = id
        js.src = '//connect.facebook.net/en_US/sdk.js'
        fjs.parentNode.insertBefore(js, fjs)
      }(document, 'script', 'facebook-jssdk'))
    }
  }

  if (params.type === 'test') {
    dispatch(fetchJwToken(params))
  }
}

/**
 * @callback MyFetchCallback
 * @param {Object} body The response body
 * @param {Object} header The response header
 */

/**
 * Method for fetching resources from amodahl.no API.
 * If request fails because of expired token a login action is dispatched and
 * the fetch request is repeated after successfull login.
 * @param dispatch The redux dispatch function
 * @param jwToken a token for amodahl.no API
 * @param attempts Number fetch attempts for 'what'
 * @param what The url describing what to fetch
 * @param params Additional fetch parameters
 * @param callback {MyFetchCallback} called when resource has been fetched
 *
 * @return promise that is resolved with headers and body of API response
 */
export const myFetch = dispatch => jwToken => (path, params) => {
  var headers = params.headers ? params.headers : new Headers()
  headers.append('Authorization', 'Bearer ' + jwToken)
  headers.append('pragma', 'no-cache')
  headers.append('cache-control', 'no-store')

  const properties = {
    headers: headers,
    ...params
  }

  return fetch(url() + path, properties)
  .then(response => {
    // ensure its json
    let contentType = response.headers.get('content-type')
    let gotJson = contentType && contentType.indexOf('application/json') !== -1
    if (!gotJson) {
      throw new Error('Oops, we haven\'t got JSON: ' + JSON.stringify(response))
    }
    return response.json().then(json =>
    ({ headers: headers, body: json }))
  })
  .then(({body, headers}) => {
    // check if token has expired
    if (body.status === 'error' && body.message === 'Expired token') {
      // clear login info, go to login page and tell user to log in again
      dispatch(logout())
      dispatch(updateLoginInfo('Token expired, please log in again',
        true, false, {body, headers}, false))
      dispatch(push('/login'))
      throw new Error('Token expired')
    }
    return ({body, headers})
  })
  .then(({body, headers}) => {
    // ensure query was accepted
    if (body.status && body.status !== 'ok') {
      throw new Error('Received: ' + JSON.stringify(body))
    }
    return ({body, headers})
  })
}

/**
 * Clear local storage and dispatch an action to tell
 * other components that user has logged out.
 * @param {string} dispatcher From which page user clicked log out
 */
export const logout = () => dispatch => {
  dispatch(userLoggedOut())
  localStorage.clear()

  // log out of facebook
  try {
    if (window.FB) {
      window.FB.logout((response) => {
        console.log('Successfully logged out of facebook', response)
      })
    }
  } catch (err) {
    console.log('There was a problem logging out of facebook', err)
  }

  // TODO log out with google sdk?
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var inherits = require('util').inherits;
var EventEmitter = require('./lib/event_emitter');
var StateMachine = require('./state_machine');
var net = require('net');

var TChannelConnection = require('./connection');
var errors = require('./errors');
var states = require('./states');

var DEFAULT_REPORT_INTERVAL = 1000;

function TChannelPeer(channel, hostPort, options) {
    if (!(this instanceof TChannelPeer)) {
        return new TChannelPeer(channel, hostPort, options);
    }
    var self = this;
    EventEmitter.call(self);
    StateMachine.call(self);
    self.stateChangedEvent = self.defineEvent('stateChanged');
    self.allocConnectionEvent = self.defineEvent('allocConnection');

    assert(hostPort !== '0.0.0.0:0', 'Cannot create ephemeral peer');

    self.channel = channel;
    self.logger = self.channel.logger;
    self.options = options || {};
    self.hostPort = hostPort;
    self.connections = [];
    self.random = self.channel.random;

    self.stateOptions = {
        timers: self.channel.timers,
        random: self.channel.random,
        period: self.options.period,
        maxErrorRate: self.options.maxErrorRate,
        minimumRequests: self.options.minimumRequests,
        probation: self.options.probation,
        stateMachine: self,
        nextHandler: self
    };

    if (self.options.initialState) {
        self.setState(self.options.initialState);
        delete self.options.initialState;
    } else {
        self.setState(states.HealthyState);
    }

    self.reportInterval = self.options.reportInterval || DEFAULT_REPORT_INTERVAL;
    if (self.reportInterval > 0) {
        self.reportTimer = self.stateOptions.timers.setTimeout(
            onReport, self.reportInterval
        );
    }

    function onReport() {
        if (!self.hostPort) {
            return;
        }

        var count = self.countConnections('out');
        self.channel.connectionsActiveStat.update(count, {
            'host-port': self.channel.hostPort,
            'peer-host-port': self.hostPort
        });

        self.reportTimer = self.stateOptions.timers.setTimeout(
            onReport, self.reportInterval
        );
    }
}

inherits(TChannelPeer, EventEmitter);

TChannelPeer.prototype.isConnected = function isConnected(direction, identified) {
    var self = this;
    if (identified === undefined) identified = true;
    for (var i = 0; i < self.connections.length; i++) {
        var conn = self.connections[i];
        if (direction && conn.direction !== direction) {
            continue;
        } else if (conn.closing) {
            continue;
        } else if (conn.remoteName !== null || !identified) {
            return true;
        }
    }
    return false;
};

TChannelPeer.prototype.close = function close(callback) {
    var self = this;
    if (self.reportTimer) {
        self.stateOptions.timers.clearTimeout(self.reportTimer);
        self.reportTimer = null;
    }

    var counter = self.connections.length;
    if (counter) {
        self.connections.forEach(function eachConn(conn) {
            conn.close(onClose);
        });
    } else {
        self.state.close(callback);
    }
    function onClose() {
        if (--counter <= 0) {
            if (counter < 0) {
                self.logger.error('closed more peer sockets than expected', {
                    counter: counter
                });
            }
            self.state.close(callback);
        }
    }
};

TChannelPeer.prototype.setState = StateMachine.prototype.setState;

TChannelPeer.prototype.getInConnection = function getInConnection() {
    var self = this;
    for (var i = 0; i < self.connections.length; i++) {
        var conn = self.connections[i];
        if (!conn.closing) return conn;
    }
    return null;
};

TChannelPeer.prototype.getOutConnection = function getOutConnection() {
    var self = this;
    for (var i = self.connections.length - 1; i >= 0; i--) {
        var conn = self.connections[i];
        if (!conn.closing) return conn;
    }
    return null;
};

TChannelPeer.prototype.countConnections = function countConnections(direction) {
    var self = this;
    if (!direction) {
        return self.connections.length;
    }

    var count = 0;
    for (var i = 0; i < self.connections.length; i++) {
        var conn = self.connections[i];
        if (conn.direction === direction) {
            count++;
        }
    }

    return count;
};

TChannelPeer.prototype.connect = function connect(outOnly) {
    var self = this;
    var conn = self.getOutConnection();
    if (!conn || (outOnly && conn.direction !== 'out')) {
        var socket = self.makeOutSocket();
        conn = self.makeOutConnection(socket);
        self.addConnection(conn);
    }
    return conn;
};

TChannelPeer.prototype.waitForIdentified =
function waitForIdentified(callback) {
    var self = this;

    var conn = self.connect();

    if (conn.closing) {
        onConnectionClose(conn.closeError);
    } else {
        conn.closeEvent.on(onConnectionClose);

        // conn.handler is a self peer detection
        if (!self.isConnected() && conn.handler) {
            conn.identifiedEvent.on(onIdentified);
            return;
        } else {
            onIdentified();
        }
    }

    function onConnectionClose(err) {
        conn.closeEvent.removeListener(onConnectionClose);
        callback(err || errors.TChannelConnectionCloseError());
    }

    function onIdentified() {
        conn.closeEvent.removeListener(onConnectionClose);
        callback(null);
    }
};

TChannelPeer.prototype.request = function peerRequest(options) {
    var self = this;
    var req = self.connect().request(options);

    self.state.onRequest(req);

    req.errorEvent.on(onError);
    req.responseEvent.on(onResponse);

    function onError(err) {
        self.state.onRequestError(err);
    }

    function onResponse(res) {
        self.state.onRequestResponse(req);
    }

    return req;
};

TChannelPeer.prototype.addConnection = function addConnection(conn) {
    var self = this;
    // TODO: first approx alert for self.connections.length > 2
    // TODO: second approx support pruning
    if (conn.direction === 'out') {
        self.connections.push(conn);
    } else {
        self.connections.unshift(conn);
    }
    conn.errorEvent.on(onConnectionError);
    conn.closeEvent.on(onConnectionClose);
    return conn;

    function onConnectionError(err) {
        var codeName = errors.classify(err);

        var loggerInfo = {
            err: err,
            direction: conn.direction,
            remoteName: conn.remoteName,
            socketRemoteAddr: conn.socketRemoteAddr
        };

        if (codeName === 'Timeout') {
            self.logger.warn('Got a connection error', loggerInfo);
        } else {
            self.logger.error('Got an unexpected connection error', loggerInfo);
        }

        self.removeConnection(conn);
    }

    function onConnectionClose() {
        self.removeConnection(conn);
    }
};

TChannelPeer.prototype.removeConnection = function removeConnection(conn) {
    var self = this;
    var list = self.connections;
    var index = list ? list.indexOf(conn) : -1;
    if (index !== -1) {
        return list.splice(index, 1)[0];
    } else {
        return null;
    }
};

TChannelPeer.prototype.makeOutSocket = function makeOutSocket() {
    var self = this;
    var parts = self.hostPort.split(':');
    assert(parts.length === 2, 'invalid destination');
    var host = parts[0];
    var port = parts[1];
    assert(host !== '0.0.0.0', 'cannot connect to ephemeral peer');
    assert(port !== '0', 'cannot connect to dynamic port');
    var socket = net.createConnection({host: host, port: port});
    return socket;
};

TChannelPeer.prototype.makeOutConnection = function makeOutConnection(socket) {
    var self = this;
    var chan = self.channel.topChannel || self.channel;
    var conn = new TChannelConnection(chan, socket, 'out', self.hostPort);
    conn.spanEvent.on(handleSpanFromConn);
    self.allocConnectionEvent.emit(self, conn);
    return conn;

    function handleSpanFromConn(span) {
        chan.tracer.report(span);
    }
};

TChannelPeer.prototype.outPendingWeightedRandom = function outPendingWeightedRandom() {
    // A weighted random variable:
    //   random() ** (1 / weight)
    // Such that the probability distribution is uniform for weights of 0, but
    // an increasing bias with increasing weight.
    // However, although weight should start at 0 and increase probability,
    // the number of pending requests starts at 0 and should decrease
    // probability as it increases.
    // For 0 pending requests, we produce a uniform probability distribution by
    // raising a uniform random variable to the power of 1 (pending + 1).
    // As the number of pending requests increase, the magnitude of the power
    // increases and the probability distribution develops a bias toward zero.
    // TODO review weighted reservoir sampling:
    // http://arxiv.org/pdf/1012.0256.pdf
    var self = this;
    var pending = self.countOutPending();
    return Math.pow(self.random(), 1 + pending);
};

TChannelPeer.prototype.countOutPending = function countOutPending() {
    var self = this;
    var pending = 0;
    for (var index = 0; index < self.connections.length; index++) {
        var connPending = self.connections[index].ops.getPending();

        pending += connPending.out;
    }
    return pending;
};

// Consulted depending on the peer state
TChannelPeer.prototype.shouldRequest = function shouldRequest() {
    var self = this;
    // space:
    //   [0.1, 0.2)  unconnected peers
    //   [0.2, 0.3)  incoming connections
    //   [0.3, 0.4)  new outgoing connections
    //   [0.4, 1.0)  identified outgoing connections
    var inconn = self.getInConnection();
    var outconn = self.getOutConnection();
    var random = self.outPendingWeightedRandom();
    if (!inconn && !outconn) {
        return 0.1 + random * 0.1;
    } else if (!outconn || outconn.direction !== 'out') {
        self.connect();
        return 0.2 + random * 0.1;
    } else if (outconn.remoteName === null) {
        return 0.3 + random * 0.1;
    } else {
        return 0.4 + random * 0.6;
    }
};

module.exports = TChannelPeer;
<commit_msg>fix: peer should choose the identified connection first<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var inherits = require('util').inherits;
var EventEmitter = require('./lib/event_emitter');
var StateMachine = require('./state_machine');
var net = require('net');

var TChannelConnection = require('./connection');
var errors = require('./errors');
var states = require('./states');

var DEFAULT_REPORT_INTERVAL = 1000;

function TChannelPeer(channel, hostPort, options) {
    if (!(this instanceof TChannelPeer)) {
        return new TChannelPeer(channel, hostPort, options);
    }
    var self = this;
    EventEmitter.call(self);
    StateMachine.call(self);
    self.stateChangedEvent = self.defineEvent('stateChanged');
    self.allocConnectionEvent = self.defineEvent('allocConnection');

    assert(hostPort !== '0.0.0.0:0', 'Cannot create ephemeral peer');

    self.channel = channel;
    self.logger = self.channel.logger;
    self.options = options || {};
    self.hostPort = hostPort;
    self.connections = [];
    self.random = self.channel.random;

    self.stateOptions = {
        timers: self.channel.timers,
        random: self.channel.random,
        period: self.options.period,
        maxErrorRate: self.options.maxErrorRate,
        minimumRequests: self.options.minimumRequests,
        probation: self.options.probation,
        stateMachine: self,
        nextHandler: self
    };

    if (self.options.initialState) {
        self.setState(self.options.initialState);
        delete self.options.initialState;
    } else {
        self.setState(states.HealthyState);
    }

    self.reportInterval = self.options.reportInterval || DEFAULT_REPORT_INTERVAL;
    if (self.reportInterval > 0) {
        self.reportTimer = self.stateOptions.timers.setTimeout(
            onReport, self.reportInterval
        );
    }

    function onReport() {
        if (!self.hostPort) {
            return;
        }

        var count = self.countConnections('out');
        self.channel.connectionsActiveStat.update(count, {
            'host-port': self.channel.hostPort,
            'peer-host-port': self.hostPort
        });

        self.reportTimer = self.stateOptions.timers.setTimeout(
            onReport, self.reportInterval
        );
    }
}

inherits(TChannelPeer, EventEmitter);

TChannelPeer.prototype.isConnected = function isConnected(direction, identified) {
    var self = this;
    if (identified === undefined) identified = true;
    for (var i = 0; i < self.connections.length; i++) {
        var conn = self.connections[i];
        if (direction && conn.direction !== direction) {
            continue;
        } else if (conn.closing) {
            continue;
        } else if (conn.remoteName !== null || !identified) {
            return true;
        }
    }
    return false;
};

TChannelPeer.prototype.close = function close(callback) {
    var self = this;
    if (self.reportTimer) {
        self.stateOptions.timers.clearTimeout(self.reportTimer);
        self.reportTimer = null;
    }

    var counter = self.connections.length;
    if (counter) {
        self.connections.forEach(function eachConn(conn) {
            conn.close(onClose);
        });
    } else {
        self.state.close(callback);
    }
    function onClose() {
        if (--counter <= 0) {
            if (counter < 0) {
                self.logger.error('closed more peer sockets than expected', {
                    counter: counter
                });
            }
            self.state.close(callback);
        }
    }
};

TChannelPeer.prototype.setState = StateMachine.prototype.setState;

TChannelPeer.prototype.getInConnection = function getInConnection() {
    var self = this;
    for (var i = 0; i < self.connections.length; i++) {
        var conn = self.connections[i];
        if (!conn.closing) return conn;
    }
    return null;
};

TChannelPeer.prototype.getOutConnection = function getOutConnection(identified) {
    var self = this;
    var candidate = null;
    for (var i = self.connections.length - 1; i >= 0; i--) {
        var conn = self.connections[i];
        if (!conn.closing) {
            if (!identified || conn.remoteName) {
                return conn;
            } else {
                candidate = candidate || conn;
            }
        }
    }
    return candidate;
};

TChannelPeer.prototype.countConnections = function countConnections(direction) {
    var self = this;
    if (!direction) {
        return self.connections.length;
    }

    var count = 0;
    for (var i = 0; i < self.connections.length; i++) {
        var conn = self.connections[i];
        if (conn.direction === direction) {
            count++;
        }
    }

    return count;
};

TChannelPeer.prototype.connect = function connect(outOnly) {
    var self = this;
    var conn = self.getOutConnection(true);
    if (!conn || (outOnly && conn.direction !== 'out')) {
        var socket = self.makeOutSocket();
        conn = self.makeOutConnection(socket);
        self.addConnection(conn);
    }
    return conn;
};

TChannelPeer.prototype.waitForIdentified =
function waitForIdentified(callback) {
    var self = this;

    var conn = self.connect();

    if (conn.closing) {
        onConnectionClose(conn.closeError);
    } else {
        conn.closeEvent.on(onConnectionClose);

        // conn.handler is a self peer detection
        if (!self.isConnected() && conn.handler) {
            conn.identifiedEvent.on(onIdentified);
            return;
        } else {
            onIdentified();
        }
    }

    function onConnectionClose(err) {
        conn.closeEvent.removeListener(onConnectionClose);
        callback(err || errors.TChannelConnectionCloseError());
    }

    function onIdentified() {
        conn.closeEvent.removeListener(onConnectionClose);
        callback(null);
    }
};

TChannelPeer.prototype.request = function peerRequest(options) {
    var self = this;
    var req = self.connect().request(options);

    self.state.onRequest(req);

    req.errorEvent.on(onError);
    req.responseEvent.on(onResponse);

    function onError(err) {
        self.state.onRequestError(err);
    }

    function onResponse(res) {
        self.state.onRequestResponse(req);
    }

    return req;
};

TChannelPeer.prototype.addConnection = function addConnection(conn) {
    var self = this;
    // TODO: first approx alert for self.connections.length > 2
    // TODO: second approx support pruning
    if (conn.direction === 'out') {
        self.connections.push(conn);
    } else {
        self.connections.unshift(conn);
    }
    conn.errorEvent.on(onConnectionError);
    conn.closeEvent.on(onConnectionClose);
    return conn;

    function onConnectionError(err) {
        var codeName = errors.classify(err);

        var loggerInfo = {
            err: err,
            direction: conn.direction,
            remoteName: conn.remoteName,
            socketRemoteAddr: conn.socketRemoteAddr
        };

        if (codeName === 'Timeout') {
            self.logger.warn('Got a connection error', loggerInfo);
        } else {
            self.logger.error('Got an unexpected connection error', loggerInfo);
        }

        self.removeConnection(conn);
    }

    function onConnectionClose() {
        self.removeConnection(conn);
    }
};

TChannelPeer.prototype.removeConnection = function removeConnection(conn) {
    var self = this;
    var list = self.connections;
    var index = list ? list.indexOf(conn) : -1;
    if (index !== -1) {
        return list.splice(index, 1)[0];
    } else {
        return null;
    }
};

TChannelPeer.prototype.makeOutSocket = function makeOutSocket() {
    var self = this;
    var parts = self.hostPort.split(':');
    assert(parts.length === 2, 'invalid destination');
    var host = parts[0];
    var port = parts[1];
    assert(host !== '0.0.0.0', 'cannot connect to ephemeral peer');
    assert(port !== '0', 'cannot connect to dynamic port');
    var socket = net.createConnection({host: host, port: port});
    return socket;
};

TChannelPeer.prototype.makeOutConnection = function makeOutConnection(socket) {
    var self = this;
    var chan = self.channel.topChannel || self.channel;
    var conn = new TChannelConnection(chan, socket, 'out', self.hostPort);
    conn.spanEvent.on(handleSpanFromConn);
    self.allocConnectionEvent.emit(self, conn);
    return conn;

    function handleSpanFromConn(span) {
        chan.tracer.report(span);
    }
};

TChannelPeer.prototype.outPendingWeightedRandom = function outPendingWeightedRandom() {
    // A weighted random variable:
    //   random() ** (1 / weight)
    // Such that the probability distribution is uniform for weights of 0, but
    // an increasing bias with increasing weight.
    // However, although weight should start at 0 and increase probability,
    // the number of pending requests starts at 0 and should decrease
    // probability as it increases.
    // For 0 pending requests, we produce a uniform probability distribution by
    // raising a uniform random variable to the power of 1 (pending + 1).
    // As the number of pending requests increase, the magnitude of the power
    // increases and the probability distribution develops a bias toward zero.
    // TODO review weighted reservoir sampling:
    // http://arxiv.org/pdf/1012.0256.pdf
    var self = this;
    var pending = self.countOutPending();
    return Math.pow(self.random(), 1 + pending);
};

TChannelPeer.prototype.countOutPending = function countOutPending() {
    var self = this;
    var pending = 0;
    for (var index = 0; index < self.connections.length; index++) {
        var connPending = self.connections[index].ops.getPending();

        pending += connPending.out;
    }
    return pending;
};

// Consulted depending on the peer state
TChannelPeer.prototype.shouldRequest = function shouldRequest() {
    var self = this;
    // space:
    //   [0.1, 0.2)  unconnected peers
    //   [0.2, 0.3)  incoming connections
    //   [0.3, 0.4)  new outgoing connections
    //   [0.4, 1.0)  identified outgoing connections
    var inconn = self.getInConnection();
    var outconn = self.getOutConnection();
    var random = self.outPendingWeightedRandom();
    if (!inconn && !outconn) {
        return 0.1 + random * 0.1;
    } else if (!outconn || outconn.direction !== 'out') {
        self.connect();
        return 0.2 + random * 0.1;
    } else if (outconn.remoteName === null) {
        return 0.3 + random * 0.1;
    } else {
        return 0.4 + random * 0.6;
    }
};

module.exports = TChannelPeer;
<|endoftext|>


<commit_before>var ReplicationController = (function () {

    function shareIdentity() {
        var c = Context.getInstance();
        var ri = c.getReplicaIdentity();

        var payload = MessagePayload.new(
                MulticastReplicationProtocol.PayloadTypes.IDENTITY,
                1,
                ri.toString()
        );

        var msg = Message.Create(Message.Types.OUT, payload);

        log("Sharing identity as:", msg);
        msg.log();

        var identityShared_VNDW8mEr1SXu = (function () {
            debug("Identity shared: ", payload.toJSON());
        });

        ReplicationController.Replicate(msg, identityShared_VNDW8mEr1SXu);
    }

    return {

        /**
         * Replicates the given message using the replication protocol.
         * @param msg
         * @param callback
         * @constructor
         */
        Replicate: function (msg, callback) {
            debug("Replication controller, replicating:", msg);

            var n = Network.getInstance();
            n.sendMulticastMessage(MulticastReplicationProtocol.MulticastIP, MulticastReplicationProtocol.Port, msg, callback);
        },

        ReplicateCounter: function (counter) {

            debug("Replicating counter: " + counter.toJSON());

            var payload = MessagePayload.new(
                    MulticastReplicationProtocol.PayloadTypes.COUNTER,
                    counter.getId(),
                    counter.toJSON()
            );

            var msg = Message.Create(Message.Types.OUT, payload);

            var counterReplicated_r6yWxvuw84mr = (function () {
                debug("Counter replicated: ", payload.toJSON());
            });

            ReplicationController.Replicate(msg, counterReplicated_r6yWxvuw84mr);
        },

        SharePeerIdentity: function () {
            shareIdentity();
        },

        /**
         * Handles a message received via de replication protocol.
         * @param rawMsg
         * @constructor
         */
        HandleMulticastReplicationMessage: function (rawMsg) {
            debug("Replication Controller handle message: ", rawMsg);

            var repMsg = Message.CreateFromRawData(Message.Types.IN, rawMsg.data);
            debug("Replication message received:", repMsg);

            var payload = repMsg.getPayload();

            switch (payload.getType()) {
                case MulticastReplicationProtocol.PayloadTypes.COUNTER:

                    log("Received counter replication: ", payload);

                    var appController = ApplicationController.getInstance();
                    var existingCounter = appController.getOnlineUsersCounter();

                    var newCounter = CRDT.newCounterFromJSON(payload.getObjectId(), JSON.parse(payload.getContent()));

                    newCounter.merge(existingCounter);

                    appController.setOnlineUsersCounter(newCounter);

                    break;
                case MulticastReplicationProtocol.PayloadTypes.IDENTITY:

                    log("Received peer identity: " + payload.toJSON());

                    var c = Context.getInstance();
                    var content = "" + JSON.parse(payload.getContent());
                    var ri = ReplicaIdentity.newFromString(content);

                    // if we are not tracking this identity, we share ours
                    if (c.getPeer(ri.toString()) === false) {
                        log("We are not tracking this peer, so we share our identity.");
                        // share own identity
                        shareIdentity();

                        // save the identity received in the context
                        c.addPeer(PeerIdentity.new(rawMsg.remoteAddress, ri));
                    }

                    break;
                default:
            }
        },

        SendDirectReplicationRequest: function (peerIdentity) {
            log("Sending data replication request.");

            var n = Network.getInstance();

            var payload = MessagePayload.new(
                    DirectReplicationProtocol.PayloadTypes.REQUEST,
                    0,
                    "" + new Date().getTime()
            );

            var msg = Message.Create(Message.Types.OUT, payload);

            n.sendMessageThroughNewSocket(peerIdentity.getIpAddress(), DirectReplicationProtocol.Port, msg);

        },

        HandleDirectReplicationMessage: function (msg, socketId) {
            debug("Received a message through socket ["+socketId+"]");
            debug("The message contained the following information: ", msg.getPayload().toJSON());
        }
    };
})();<commit_msg>Support for direct replication protocol. Request and response code to handle sending and receiving.<commit_after>var ReplicationController = (function () {

    function shareIdentity() {
        var c = Context.getInstance();
        var ri = c.getReplicaIdentity();

        var payload = MessagePayload.new(
                MulticastReplicationProtocol.PayloadTypes.IDENTITY,
                1,
                ri.toString()
        );

        var msg = Message.Create(Message.Types.OUT, payload);

        log("Sharing identity as:", msg);
        msg.log();

        var identityShared_VNDW8mEr1SXu = (function () {
            debug("Identity shared: ", payload.toJSON());
        });

        ReplicationController.Replicate(msg, identityShared_VNDW8mEr1SXu);
    }

    /**
     * Sends a DirectReplication response through the given socketId
     * @param message
     * @param socketId
     */
    function handleDirectReplicationRequest(message, socketId) {

        log("Handling a direct replication request.");
        var c = Context.getInstance();
        var appController = ApplicationController.getInstance();
        var counter = appController.getOnlineUsersCounter();

        // create the data of the payload
        var data = {};

        // add the counter
        data.counters = [];
        data.counters.push(counter.toJSON());

        // add the registers being tracked
        data.registers = [];

        var payload = MessagePayload.new(
                DirectReplicationProtocol.PayloadTypes.RESPONSE,
                0,
                data
        );

        var msg = Message.Create(Message.Types.OUT, payload);

        n.sendMessageThroughExistingSocket(socketId, msg);
    }

    /**
     * Handles DirectReplication response and stores the data where it belongs.
     * @param msg
     * @param socketId
     */
    function handleDirectReplicationResponse(msg, socketId) {
        log("Handling a direct replication response.");

        var appController = ApplicationController.getInstance();

        var payload = msg.getPayload();

        var data = payload.getContent();

//        log("Payload: "+payload.toJSON(), payload);
//        log("Data: "+data, data);

        if (typeof data.counters !== "undefined") {
            data.counters.forEach(function (counter) {
//                log("Iterating over counters: "+counter, counter);
                var c = CRDT.newCounterFromJSON(0, counter);
                if (typeof c !== 'undefined' && c.getId() == 1) {
//                    log("Received a new user counter that should be handled. Total count: "+c.getCount());
                    // merge this counter with our online user counter
                    appController.newCounterReceived(c);
                }
            });
        }

        if (typeof data.registers !== 'undefined') {
            data.registers.forEach(function (register) {
                // do something with the registers :)
            });
        }

    }

    return {

        /**
         * Replicates the given message using the replication protocol.
         * @param msg
         * @param callback
         * @constructor
         */
        Replicate: function (msg, callback) {
            debug("Replication controller, replicating:", msg);

            var n = Network.getInstance();
            n.sendMulticastMessage(MulticastReplicationProtocol.MulticastIP, MulticastReplicationProtocol.Port, msg, callback);
        },

        ReplicateCounter: function (counter) {

            debug("Replicating counter: " + counter.toJSON());

            var payload = MessagePayload.new(
                    MulticastReplicationProtocol.PayloadTypes.COUNTER,
                    counter.getId(),
                    counter.toJSON()
            );

            var msg = Message.Create(Message.Types.OUT, payload);

            var counterReplicated_r6yWxvuw84mr = (function () {
                debug("Counter replicated: ", payload.toJSON());
            });

            ReplicationController.Replicate(msg, counterReplicated_r6yWxvuw84mr);
        },

        SharePeerIdentity: function () {
            shareIdentity();
        },

        /**
         * Handles a message received via de replication protocol.
         * @param rawMsg
         * @constructor
         */
        HandleMulticastReplicationMessage: function (rawMsg) {
            debug("Replication Controller handle message: ", rawMsg);

            var repMsg = Message.CreateFromRawData(Message.Types.IN, rawMsg.data);
            debug("Replication message received:", repMsg);

            var payload = repMsg.getPayload();

            switch (payload.getType()) {
                case MulticastReplicationProtocol.PayloadTypes.COUNTER:

                    log("Received counter replication: ", payload);

                    var appController = ApplicationController.getInstance();
                    var existingCounter = appController.getOnlineUsersCounter();

                    var newCounter = CRDT.newCounterFromJSON(payload.getObjectId(), JSON.parse(payload.getContent()));

                    newCounter.merge(existingCounter);

                    appController.setOnlineUsersCounter(newCounter);

                    break;
                case MulticastReplicationProtocol.PayloadTypes.REGISTER:
                    log("HITTING DEAD CODE: Received a register through multicast replication.");
                    break;
                case MulticastReplicationProtocol.PayloadTypes.IDENTITY:

                    log("Received peer identity: " + payload.toJSON());

                    var c = Context.getInstance();
                    var content = payload.getContent();;
                    var ri = ReplicaIdentity.newFromString(content);

                    //printing the list of peers
                    c.printPeerList();

                    // if we are not tracking this identity, we share ours
                    if (c.getPeer(ri.toString()) === false) {
                        log("We are not tracking this peer, so we share our identity.");
                        // share own identity
                        shareIdentity();

                        // save the identity received in the context
                        c.addPeer(PeerIdentity.new(rawMsg.remoteAddress, ri));
                    } else {
                        log("Not returning peer identity because we are tracking [" + ri.toString() + "]");
                    }

                    break;
                default:
            }
        },

        StartDirectReplication: function () {
            var c = Context.getInstance();
            // if we haven't done direct replication yet
            if (c.getDirectReplicationFlag() === false) {
                log("We haven't done direct replication from a peer, starting it.");
                var peers = c.getAllPeers();
                log("Sending direct replication request to peer:", peers[0]);
                // send a direct replication request to a peer
                /**
                 * TODO: should define the way of choosing the peer to query
                 * because there might be peers that where on the same partition
                 * as we were and when the two partitions merge it is hard to merge
                 */
                ReplicationController.SendDirectReplicationRequest(peers[0]);
                c.setDirectReplicationFlag(true);
            }
        },

        /**
         * Sends to the given peer a request for all the data it has in terms
         * of CRDTs.
         * @param peerIdentity
         * @constructor
         */
        SendDirectReplicationRequest: function (peerIdentity) {
            log("Sending data replication request.");

            var n = Network.getInstance();

            var payload = MessagePayload.new(
                    DirectReplicationProtocol.PayloadTypes.REQUEST,
                    0,
                    "" + new Date().getTime()
            );

            var msg = Message.Create(Message.Types.OUT, payload);

            n.sendMessageThroughNewSocket(peerIdentity.getIpAddress(), DirectReplicationProtocol.Port, msg);
        },

        /**
         * Handles the incoming messages of the DirectReplication protocol.
         * @param msg
         * @param socketId
         * @constructor
         */
        HandleDirectReplicationMessage: function (rawMsg, socketId) {
            debug("Received a message through socket [" + socketId + "]");

            var repMsg = Message.CreateFromRawData(Message.Types.IN, rawMsg);
            debug("The message contained the following information: ", repMsg.getPayload().toJSON());
            debug("Replication message received:", repMsg.toJSON());

            var payload = repMsg.getPayload();

            switch (payload.getType()) {
                case DirectReplicationProtocol.PayloadTypes.REQUEST:
                    handleDirectReplicationRequest(repMsg, socketId);
                    break;
                case DirectReplicationProtocol.PayloadTypes.RESPONSE:
                    handleDirectReplicationResponse(repMsg, socketId);
                    var n = Network.getInstance();
                    n.releaseSocket(socketId, n.TCP_TYPE);
                    break;
                default:
                    log("Received a message of a type that cannot be handled type [" + payload.getType() + "].", payload.toJSON());
            }
        }
    };
})();<|endoftext|>