<commit_before>function $MakeArgs($fname,$args,$required,$defaults,$other_args,$other_kw){
    // builds a namespace from the arguments provided in $args
    // in a function call like foo(x,y,z=1,*args,**kw) the parameters are
    // $required : ['x','y']
    // $defaults : {'z':int(1)}
    // $other_args = 'args'
    // $other_kw = 'kw'
    var i=null,$PyVars = {},$def_names = [],$ns = {}
    for(var k in $defaults){$def_names.push(k);$ns[k]=$defaults[k]}
    if($other_args != null){$ns[$other_args]=[]}
    if($other_kw != null){$dict_keys=[];$dict_values=[]}
    // create new list of arguments in case some are packed
    var upargs = []
    for(var i=0;i<$args.length;i++){
        if($args[i]===null){upargs.push(null)}
        else if(isinstance($args[i],$ptuple)){
            for(var j=0;j<$args[i].arg.length;j++){
                upargs.push($args[i].arg[j])
            }
        }else if(isinstance($args[i],$pdict)){
            for(var j=0;j<$args[i].arg.$keys.length;j++){
                upargs.push($Kw($args[i].arg.$keys[j],$args[i].arg.$values[j]))
            }
        }else{
            upargs.push($args[i])
        }
    }
    for(var $i=0;$i<upargs.length;$i++){
        $arg=upargs[$i]
        $PyVar=$JS2Py($arg)
        if(isinstance($arg,$Kw)){ // keyword argument
            $PyVar = $arg.value
            if($arg.name in $PyVars){
                throw new TypeError($fname+"() got multiple values for argument '"+$arg.name+"'")
            } else if($required.indexOf($arg.name)>-1){
                var ix = $required.indexOf($arg.name)
                eval('var '+$required[ix]+"=$PyVar")
                $ns[$required[ix]]=$PyVar
            } else if($arg.name in $defaults){
                $ns[$arg.name]=$PyVar
            } else if($other_kw!=null){
                $dict_keys.push($arg.name)
                $dict_values.push($PyVar)
            } else {
                throw new TypeError($fname+"() got an unexpected keyword argument '"+$arg.name+"'")
            }
            if($arg.name in $defaults){delete $defaults[$arg.name]}
        }else{ // positional arguments
            if($i<$required.length){
                eval('var '+$required[$i]+"=$PyVar")
                $ns[$required[$i]]=$PyVar
            } else if($i<$required.length+$def_names.length) {
                $ns[$def_names[$i-$required.length]]=$PyVar
            } else if($other_args!=null){
                eval('$ns["'+$other_args+'"].push($PyVar)')
            } else {
                msg = $fname+"() takes "+$required.length+' positional arguments '
                msg += 'but more were given'
                throw TypeError(msg)
            }
        }
    }
    if($other_kw!=null){$ns[$other_kw]=new $DictClass($dict_keys,$dict_values)}
    return $ns
}

function $list_comp(){
    var $env = arguments[0]
    for(var $arg in $env){
        eval("var "+$arg+'=$env["'+$arg+'"]')
    }
    var $res = 'res'+Math.random().toString(36).substr(2,8)
    var $py = $res+"=[]\n"
    var indent=0
    for(var i=2;i<arguments.length;i++){
        for(var j=0;j<indent;j++){$py += ' '}
        $py += arguments[i]+':\n'
        indent += 4
    }
    for(var j=0;j<indent;j++){$py += ' '}
    $py += $res+'.append('+arguments[1]+')'
    var $js = $py2js($py).to_js()
    eval($js)
    return eval($res)
}

function $ternary(expr1,cond,expr2){
    var res = 'var $res=expr1\n'
    res += 'if(!cond){$res=expr2}\n'
    eval(res)
    return $res
}

function $lambda(args,body){
    var $res = 'res'+Math.random().toString(36).substr(2,8)
    var $py = 'def '+$res+'('+args+'):\n'
    $py += '    return '+body
    var $js = $py2js($py).to_js()
    eval($js)
    return eval($res)
}

// transform native JS types into Brython types
function $JS2Py(src){
    if(src===null){return None}
    if(typeof src==='number'){
        if(src%1===0){return src}
        else{return float(src)}
    }
    if(src.__class__!==undefined){return src}
    if(typeof src=="object"){
        if(src.constructor===Array){return src}
        else if($isNode(src)){return $DOMNode(src)}
        else if($isEvent(src)){return $DOMEvent(src)}
    }
    return JSObject(src)
}

// generic class for modules
function $module(){}
$module.__class__ = $type
$module.__str__ = function(){return "<class 'module'>"}

// generic attribute getter
function $getattr(obj,attr){
    if(obj[attr]!==undefined){
        var res = obj[attr]
        if(typeof res==="function"){
            res = $bind(res, obj) // see below
        }
        return $JS2Py(res)
    }
}

// this trick is necessary to set "this" to the instance inside functions
// found at http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
function $bind(func, thisValue) {
    return function() {return func.apply(thisValue, arguments)}
}

// exceptions
function $raise(){
    // used for "raise" without specifying an exception
    // if there is an exception in the stack, use it, else throw a simple Exception
    if(document.$exc_stack.length>0){throw $last(document.$exc_stack)}
    else{throw Error('Exception')}
}

function $src_error(name,module,msg,pos) {
    // map position to line number
    var pos2line = {}
    var lnum=1
    var src = document.$py_src[module]
    var line_pos = {1:0}
    for(i=0;i<src.length;i++){
        pos2line[i]=lnum
        if(src.charAt(i)=='\n'){lnum+=1;line_pos[lnum]=i}
    }
    var line_num = pos2line[pos]
    var lines = src.split('\n')
    info = "\nmodule '"+module+"' line "+line_num
    info += '\n'+lines[line_num-1]+'\n'
    var lpos = pos-line_pos[line_num]
    for(var i=0;i<lpos;i++){info+=' '}
    info += '^'
    err = new Error()
    err.name = name
    err.__name__ = name
    err.message = msg
    err.info = info
    err.py_error = true
    if(document.$stderr!==null){document.$stderr_buff = err.message}
    throw err
}

function $SyntaxError(module,msg,pos) {
    $src_error('SyntaxError',module,msg,pos)
}

function $IndentationError(module,msg,pos) {
    $src_error('IndentationError',module,msg,pos)
}

// generic code for class constructor
function $class_constructor(class_name,factory){
    var f = function(){
        var obj = new Object()
        for(var attr in factory){
            if(typeof factory[attr]==="function"){
                var func = factory[attr]
                obj[attr] = (function(func){
                    return function(){
                        var args = [obj]
                        for(var i=0;i<arguments.length;i++){args.push(arguments[i])}
                        return func.apply(obj,args)
                    }
                })(func)
            }else{obj[attr] = factory[attr]}
        }
        obj.__class__ = f
        obj.__getattr__ = function(attr){
            if(obj[attr]!==undefined){return obj[attr]}
            else{throw AttributeError(obj+" has no attribute '"+attr+"'")}
        }
        obj.__setattr__ = function(attr,value){obj[attr]=value}
        obj.__str__ = function(){return "<object '"+class_name+"'>"}
        obj.toString = obj.__str__
        if(obj.__init__ !== undefined){
            var args = [obj]
            for(var i=0;i<arguments.length;i++){args.push(arguments[i])}
            factory.__init__.apply(obj,args)
        }
        return obj
        }
    f.__str__ = function(){return "<class '"+class_name+"'>"}
    for(var attr in factory){
        f[attr]=factory[attr]
        f[attr].__str__ = (function(x){
            return function(){return "<function "+class_name+'.'+x+'>'}
            })(attr)
    }
    f.__getattr__ = function(attr){
        if(f[attr]!==undefined){return f[attr]}
        return factory[attr]
    }
    return f
}

// escaping double quotes
var $dq_regexp = new RegExp('"',"g") // to escape double quotes in arguments
function $escape_dq(arg){return arg.replace($dq_regexp,'\\"')}

// default standard output and error
// can be reset by sys.stdout or sys.stderr
document.$stderr = {'write':function(data){void(0)}}
document.$stderr_buff = '' // buffer for standard output

document.$stdout = {
    write: function(data){console.log(data)}
}

// used for class of classes
function $type(){}
$type.__class__ = $type
$type.toString = function(){return "<class 'type'>"}

function $UnsupportedOpType(op,class1,class2){
    $raise('TypeError',
        "unsupported operand type(s) for "+op+": '"+class1+"' and '"+class2+"'")
}

// classes used for passing parameters to functions
// keyword arguments : foo(x=1)
function $KwClass(name,value){
    this.__class__ = $Kw
    this.name = name
    this.value = value
}
$KwClass.prototype.toString = function(){
    return '<kw '+this.name+' : '+this.value.toString()+'>'
}
function $Kw(name,value){
    return new $KwClass(name,value)
}

// packed tuple : foo(*args)
function $ptuple_class(arg){
    this.__class__ = $ptuple
    this.arg=arg
}
function $ptuple(arg){return new $ptuple_class(arg)}

// packed dict : foo(**kw)
function $pdict_class(arg){
    this.__class__ = $pdict
    this.arg=arg
}
function $pdict(arg){return new $pdict_class(arg)}

function $test_item(expr){
    // used to evaluate expressions with "and" or "or"
    // returns a Javascript boolean (true or false) and stores
    // the evaluation in a global variable $test_result
    document.$test_result = expr
    return bool(expr)
}

function $test_expr(){
    // returns the last evaluated item
    return document.$test_result
}

// define a function __eq__ for functions to allow test on Python classes
// such as object.__class__ == SomeClass
Function.prototype.__eq__ = function(other){
    if(typeof other !== 'function'){return False}
    return other+''===this+''
}
Function.prototype.__class__ = Function
Function.prototype.get_name = function(){
    var src = this.toString() // coerce to string
    pattern = new RegExp("function (.*?)\\(")
    var res = pattern.exec(src)
    value = '<function '+res[1]+'>'
}

Array.prototype.match = function(other){
    // return true if array and other have the same first items
    var $i = 0
    while($i<this.length && $i<other.length){
        if(this[$i]!==other[$i]){return false}
        $i++
    }
    return true
}

// IE doesn't implement indexOf on Arrays
if(!Array.indexOf){
Array.prototype.indexOf = function(obj){
    for(var i=0;i<this.length;i++){
        if(this[i]==obj){
            return i;
        }
    }
    return -1;
 }
}

// in case console is not defined
try{console}
catch(err){
    console = {'log':function(data){void(0)}}
}

function $List2Dict(){
    var res = {}
    var i=0
    if(arguments.length==1 && arguments[0].constructor==Array){
        // arguments passed as a list
        for(i=0;i<arguments[0].length;i++){
            res[arguments[0][i]]=0
        }
    } else {
        for(i=0;i<arguments.length;i++){
            res[arguments[i]]=0
        }
    }
    return res
}

function $last(item){
    if(typeof item=="string"){return item.charAt(item.length-1)}
    else if(typeof item=="object"){return item[item.length-1]}
}
<commit_msg>py_utils.js : minor bug fix (__str__ method of user-defined classes)<commit_after>function $MakeArgs($fname,$args,$required,$defaults,$other_args,$other_kw){
    // builds a namespace from the arguments provided in $args
    // in a function call like foo(x,y,z=1,*args,**kw) the parameters are
    // $required : ['x','y']
    // $defaults : {'z':int(1)}
    // $other_args = 'args'
    // $other_kw = 'kw'
    var i=null,$PyVars = {},$def_names = [],$ns = {}
    for(var k in $defaults){$def_names.push(k);$ns[k]=$defaults[k]}
    if($other_args != null){$ns[$other_args]=[]}
    if($other_kw != null){$dict_keys=[];$dict_values=[]}
    // create new list of arguments in case some are packed
    var upargs = []
    for(var i=0;i<$args.length;i++){
        if($args[i]===null){upargs.push(null)}
        else if(isinstance($args[i],$ptuple)){
            for(var j=0;j<$args[i].arg.length;j++){
                upargs.push($args[i].arg[j])
            }
        }else if(isinstance($args[i],$pdict)){
            for(var j=0;j<$args[i].arg.$keys.length;j++){
                upargs.push($Kw($args[i].arg.$keys[j],$args[i].arg.$values[j]))
            }
        }else{
            upargs.push($args[i])
        }
    }
    for(var $i=0;$i<upargs.length;$i++){
        $arg=upargs[$i]
        $PyVar=$JS2Py($arg)
        if(isinstance($arg,$Kw)){ // keyword argument
            $PyVar = $arg.value
            if($arg.name in $PyVars){
                throw new TypeError($fname+"() got multiple values for argument '"+$arg.name+"'")
            } else if($required.indexOf($arg.name)>-1){
                var ix = $required.indexOf($arg.name)
                eval('var '+$required[ix]+"=$PyVar")
                $ns[$required[ix]]=$PyVar
            } else if($arg.name in $defaults){
                $ns[$arg.name]=$PyVar
            } else if($other_kw!=null){
                $dict_keys.push($arg.name)
                $dict_values.push($PyVar)
            } else {
                throw new TypeError($fname+"() got an unexpected keyword argument '"+$arg.name+"'")
            }
            if($arg.name in $defaults){delete $defaults[$arg.name]}
        }else{ // positional arguments
            if($i<$required.length){
                eval('var '+$required[$i]+"=$PyVar")
                $ns[$required[$i]]=$PyVar
            } else if($i<$required.length+$def_names.length) {
                $ns[$def_names[$i-$required.length]]=$PyVar
            } else if($other_args!=null){
                eval('$ns["'+$other_args+'"].push($PyVar)')
            } else {
                msg = $fname+"() takes "+$required.length+' positional arguments '
                msg += 'but more were given'
                throw TypeError(msg)
            }
        }
    }
    if($other_kw!=null){$ns[$other_kw]=new $DictClass($dict_keys,$dict_values)}
    return $ns
}

function $list_comp(){
    var $env = arguments[0]
    for(var $arg in $env){
        eval("var "+$arg+'=$env["'+$arg+'"]')
    }
    var $res = 'res'+Math.random().toString(36).substr(2,8)
    var $py = $res+"=[]\n"
    var indent=0
    for(var i=2;i<arguments.length;i++){
        for(var j=0;j<indent;j++){$py += ' '}
        $py += arguments[i]+':\n'
        indent += 4
    }
    for(var j=0;j<indent;j++){$py += ' '}
    $py += $res+'.append('+arguments[1]+')'
    var $js = $py2js($py).to_js()
    eval($js)
    return eval($res)
}

function $ternary(expr1,cond,expr2){
    var res = 'var $res=expr1\n'
    res += 'if(!cond){$res=expr2}\n'
    eval(res)
    return $res
}

function $lambda(args,body){
    var $res = 'res'+Math.random().toString(36).substr(2,8)
    var $py = 'def '+$res+'('+args+'):\n'
    $py += '    return '+body
    var $js = $py2js($py).to_js()
    eval($js)
    return eval($res)
}

// transform native JS types into Brython types
function $JS2Py(src){
    if(src===null){return None}
    if(typeof src==='number'){
        if(src%1===0){return src}
        else{return float(src)}
    }
    if(src.__class__!==undefined){return src}
    if(typeof src=="object"){
        if(src.constructor===Array){return src}
        else if($isNode(src)){return $DOMNode(src)}
        else if($isEvent(src)){return $DOMEvent(src)}
    }
    return JSObject(src)
}

// generic class for modules
function $module(){}
$module.__class__ = $type
$module.__str__ = function(){return "<class 'module'>"}

// generic attribute getter
function $getattr(obj,attr){
    if(obj[attr]!==undefined){
        var res = obj[attr]
        if(typeof res==="function"){
            res = $bind(res, obj) // see below
        }
        return $JS2Py(res)
    }
}

// this trick is necessary to set "this" to the instance inside functions
// found at http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
function $bind(func, thisValue) {
    return function() {return func.apply(thisValue, arguments)}
}

// exceptions
function $raise(){
    // used for "raise" without specifying an exception
    // if there is an exception in the stack, use it, else throw a simple Exception
    if(document.$exc_stack.length>0){throw $last(document.$exc_stack)}
    else{throw Error('Exception')}
}

function $src_error(name,module,msg,pos) {
    // map position to line number
    var pos2line = {}
    var lnum=1
    var src = document.$py_src[module]
    var line_pos = {1:0}
    for(i=0;i<src.length;i++){
        pos2line[i]=lnum
        if(src.charAt(i)=='\n'){lnum+=1;line_pos[lnum]=i}
    }
    var line_num = pos2line[pos]
    var lines = src.split('\n')
    info = "\nmodule '"+module+"' line "+line_num
    info += '\n'+lines[line_num-1]+'\n'
    var lpos = pos-line_pos[line_num]
    for(var i=0;i<lpos;i++){info+=' '}
    info += '^'
    err = new Error()
    err.name = name
    err.__name__ = name
    err.message = msg
    err.info = info
    err.py_error = true
    if(document.$stderr!==null){document.$stderr_buff = err.message}
    throw err
}

function $SyntaxError(module,msg,pos) {
    $src_error('SyntaxError',module,msg,pos)
}

function $IndentationError(module,msg,pos) {
    $src_error('IndentationError',module,msg,pos)
}

// generic code for class constructor
function $class_constructor(class_name,factory){
    var f = function(){
        var obj = new Object()
        for(var attr in factory){
            if(typeof factory[attr]==="function"){
                var func = factory[attr]
                obj[attr] = (function(func){
                    return function(){
                        var args = [obj]
                        for(var i=0;i<arguments.length;i++){args.push(arguments[i])}
                        return func.apply(obj,args)
                    }
                })(func)
            }else{obj[attr] = factory[attr]}
        }
        obj.__class__ = f
        obj.__getattr__ = function(attr){
            if(obj[attr]!==undefined){return obj[attr]}
            else{throw AttributeError(obj+" has no attribute '"+attr+"'")}
        }
        obj.__setattr__ = function(attr,value){obj[attr]=value}
        if(obj.__str__===undefined){
            obj.__str__ = function(){
                return "<object '"+class_name+"'>"
            }
        }
        obj.toString = obj.__str__
        if(obj.__init__ !== undefined){
            var args = [obj]
            for(var i=0;i<arguments.length;i++){args.push(arguments[i])}
            factory.__init__.apply(obj,args)
        }
        return obj
        }
    f.__str__ = function(){return "<class '"+class_name+"'>"}
    for(var attr in factory){
        f[attr]=factory[attr]
        f[attr].__str__ = (function(x){
            return function(){return "<function "+class_name+'.'+x+'>'}
            })(attr)
    }
    f.__getattr__ = function(attr){
        if(f[attr]!==undefined){return f[attr]}
        return factory[attr]
    }
    return f
}

// escaping double quotes
var $dq_regexp = new RegExp('"',"g") // to escape double quotes in arguments
function $escape_dq(arg){return arg.replace($dq_regexp,'\\"')}

// default standard output and error
// can be reset by sys.stdout or sys.stderr
document.$stderr = {'write':function(data){void(0)}}
document.$stderr_buff = '' // buffer for standard output

document.$stdout = {
    write: function(data){console.log(data)}
}

// used for class of classes
function $type(){}
$type.__class__ = $type
$type.toString = function(){return "<class 'type'>"}

function $UnsupportedOpType(op,class1,class2){
    $raise('TypeError',
        "unsupported operand type(s) for "+op+": '"+class1+"' and '"+class2+"'")
}

// classes used for passing parameters to functions
// keyword arguments : foo(x=1)
function $KwClass(name,value){
    this.__class__ = $Kw
    this.name = name
    this.value = value
}
$KwClass.prototype.toString = function(){
    return '<kw '+this.name+' : '+this.value.toString()+'>'
}
function $Kw(name,value){
    return new $KwClass(name,value)
}

// packed tuple : foo(*args)
function $ptuple_class(arg){
    this.__class__ = $ptuple
    this.arg=arg
}
function $ptuple(arg){return new $ptuple_class(arg)}

// packed dict : foo(**kw)
function $pdict_class(arg){
    this.__class__ = $pdict
    this.arg=arg
}
function $pdict(arg){return new $pdict_class(arg)}

function $test_item(expr){
    // used to evaluate expressions with "and" or "or"
    // returns a Javascript boolean (true or false) and stores
    // the evaluation in a global variable $test_result
    document.$test_result = expr
    return bool(expr)
}

function $test_expr(){
    // returns the last evaluated item
    return document.$test_result
}

// define a function __eq__ for functions to allow test on Python classes
// such as object.__class__ == SomeClass
Function.prototype.__eq__ = function(other){
    if(typeof other !== 'function'){return False}
    return other+''===this+''
}
Function.prototype.__class__ = Function
Function.prototype.get_name = function(){
    var src = this.toString() // coerce to string
    pattern = new RegExp("function (.*?)\\(")
    var res = pattern.exec(src)
    value = '<function '+res[1]+'>'
}

Array.prototype.match = function(other){
    // return true if array and other have the same first items
    var $i = 0
    while($i<this.length && $i<other.length){
        if(this[$i]!==other[$i]){return false}
        $i++
    }
    return true
}

// IE doesn't implement indexOf on Arrays
if(!Array.indexOf){
Array.prototype.indexOf = function(obj){
    for(var i=0;i<this.length;i++){
        if(this[i]==obj){
            return i;
        }
    }
    return -1;
 }
}

// in case console is not defined
try{console}
catch(err){
    console = {'log':function(data){void(0)}}
}

function $List2Dict(){
    var res = {}
    var i=0
    if(arguments.length==1 && arguments[0].constructor==Array){
        // arguments passed as a list
        for(i=0;i<arguments[0].length;i++){
            res[arguments[0][i]]=0
        }
    } else {
        for(i=0;i<arguments.length;i++){
            res[arguments[i]]=0
        }
    }
    return res
}

function $last(item){
    if(typeof item=="string"){return item.charAt(item.length-1)}
    else if(typeof item=="object"){return item[item.length-1]}
}
<|endoftext|>


<commit_before>var url = require('url')
import { isEmpty } from 'lodash'
import emitter from 'socket.io-emitter'
import decode from 'ent/decode'
import parseRedisUrl from 'parse-redis-url'

import { userRoom } from '../services/Websockets'

const TYPE = {
  Mention: 'mention', // you are mentioned in a post or comment
  TagFollow: 'TagFollow',
  NewPost: 'newPost',
  Comment: 'comment', // someone makes a comment on a post you follow
  Contribution: 'contribution', // you are added as a contributor
  FollowAdd: 'followAdd', // you are added as a follower
  Follow: 'follow', // someone follows your post
  Unfollow: 'unfollow', // someone leaves your post
  Welcome: 'welcome', // a welcome post
  JoinRequest: 'joinRequest',
  ApprovedJoinRequest: 'approvedJoinRequest',
  Message: 'message'
}

const MEDIUM = {
  InApp: 0,
  Push: 1,
  Email: 2
}

module.exports = bookshelf.Model.extend({
  tableName: 'notifications',

  activity: function () {
    return this.belongsTo(Activity)
  },

  post: function () {
    return this.relations.activity.relations.post
  },

  comment: function () {
    return this.relations.activity.relations.comment
  },

  reader: function () {
    return this.relations.activity.relations.reader
  },

  actor: function () {
    return this.relations.activity.relations.actor
  },

  send: function () {
    var action
    switch (this.get('medium')) {
      case MEDIUM.Push:
        action = this.sendPush()
        break
      case MEDIUM.Email:
        action = this.sendEmail()
        break
      case MEDIUM.InApp:
        const userId = this.reader().id
        action = User.incNewNotificationCount(userId)
          .then(() => this.updateUserSocketRoom(userId))
        break
    }
    if (action) {
      return action
      .then(() => this.save({'sent_at': (new Date()).toISOString()}))
    } else {
      return Promise.resolve()
    }
  },

  sendPush: function () {
    switch (Notification.priorityReason(this.relations.activity.get('meta').reasons)) {
      case 'mention':
        return this.sendPostPush('mention')
      case 'commentMention':
        return this.sendCommentPush('mention')
      case 'newComment':
        return this.sendCommentPush()
      case 'newContribution':
        return this.sendContributionPush()
      case 'newPost':
        return this.sendPostPush()
      case 'joinRequest':
        return this.sendJoinRequestPush()
      case 'approvedJoinRequest':
        return this.sendApprovedJoinRequestPush()
      default:
        return Promise.resolve()
    }
  },

  sendPostPush: function (version) {
    var post = this.post()
    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => {
      var path = url.parse(Frontend.Route.post(post, community)).path
      var alertText = PushNotification.textForPost(post, community, this.relations.activity.get('reader_id'), version)
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendContributionPush: function (version) {
    return this.load(['contribution', 'contribution.post'])
    .then(() => {
      const { contribution } = this.relations.activity.relations
      var path = url.parse(Frontend.Route.post(contribution.relations.post)).path
      var alertText = PushNotification.textForContribution(contribution, version)
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendCommentPush: function (version) {
    var comment = this.comment()
    var path = url.parse(Frontend.Route.post(comment.relations.post)).path
    var alertText = PushNotification.textForComment(comment, version)
    if (!this.reader().enabledNotification(TYPE.Comment, MEDIUM.Push)) {
      return Promise.resolve()
    }
    return this.reader().sendPushNotification(alertText, path)
  },

  sendJoinRequestPush: function () {
    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => {
      var path = url.parse(Frontend.Route.communityJoinRequests(community)).path
      var alertText = PushNotification.textForJoinRequest(community, this.actor())
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendApprovedJoinRequestPush: function () {
    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => {
      var path = url.parse(Frontend.Route.community(community)).path
      var alertText = PushNotification.textForApprovedJoinRequest(community, this.actor())
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendEmail: function () {
    switch (Notification.priorityReason(this.relations.activity.get('meta').reasons)) {
      case 'mention':
        return this.sendPostMentionEmail()
      case 'joinRequest':
        return this.sendJoinRequestEmail()
      case 'approvedJoinRequest':
        return this.sendApprovedJoinRequestEmail()
      default:
        return Promise.resolve()
    }
  },

  sendPostMentionEmail: function () {
    var post = this.post()
    var reader = this.reader()
    var user = post.relations.user
    var description = RichText.qualifyLinks(post.get('description'))
    var replyTo = Email.postReplyAddress(post.id, reader.id)

    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendPostMentionNotification({
        email: reader.get('email'),
        sender: {
          address: replyTo,
          reply_to: replyTo,
          name: `${user.get('name')} (via Hylo)`
        },
        data: {
          community_name: community.get('name'),
          post_user_name: user.get('name'),
          post_user_avatar_url: Frontend.Route.tokenLogin(reader, token,
            user.get('avatar_url') + '?ctt=post_mention_email&cti=' + reader.id),
          post_user_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(user) + '?ctt=post_mention_email&cti=' + reader.id),
          post_description: description,
          post_title: decode(post.get('name')),
          post_type: 'conversation',
          post_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.post(post) + '?ctt=post_mention_email&cti=' + reader.id),
          unfollow_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.unfollow(post, community) + '?ctt=post_mention_email&cti=' + reader.id),
          tracking_pixel_url: Analytics.pixelUrl('Mention in Post', {userId: reader.id})
        }
      })))
  },

  // version corresponds to names of versions in SendWithUs
  sendCommentNotificationEmail: function (version) {
    const comment = this.comment()
    const reader = this.reader()
    if (!comment) return

    const post = comment.relations.post
    const commenter = comment.relations.user
    const text = RichText.qualifyLinks(comment.get('text'))
    const replyTo = Email.postReplyAddress(post.id, reader.id)
    const title = decode(post.get('name'))

    var postLabel = `"${title}"`
    if (post.get('type') === 'welcome') {
      var relatedUser = post.relations.relatedUsers.first()
      if (relatedUser.id === reader.id) {
        postLabel = 'your welcoming post'
      } else {
        postLabel = `${relatedUser.get('name')}'s welcoming post`
      }
    }

    const communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendNewCommentNotification({
        version: version,
        email: reader.get('email'),
        sender: {
          address: replyTo,
          reply_to: replyTo,
          name: `${commenter.get('name')} (via Hylo)`
        },
        data: {
          community_name: community.get('name'),
          commenter_name: commenter.get('name'),
          commenter_avatar_url: commenter.get('avatar_url'),
          commenter_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(commenter) + '?ctt=comment_email&cti=' + reader.id),
          comment_text: text,
          post_label: postLabel,
          post_title: title,
          comment_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.post(post) + '?ctt=comment_email&cti=' + reader.id + `#comment-${comment.id}`),
          unfollow_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.unfollow(post, community)),
          tracking_pixel_url: Analytics.pixelUrl('Comment', {userId: reader.id})
        }
      })))
  },

  sendJoinRequestEmail: function () {
    const actor = this.actor()
    const reader = this.reader()
    const communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendJoinRequestNotification({
        email: reader.get('email'),
        sender: {name: community.get('name')},
        data: {
          community_name: community.get('name'),
          requester_name: actor.get('name'),
          requester_avatar_url: actor.get('avatar_url'),
          requester_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(actor) +
            `?ctt=comment_email&cti=${reader.id}&check-join-requests=1`),
          settings_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.communityJoinRequests(community))
        }
      })))
  },

  sendApprovedJoinRequestEmail: function () {
    const actor = this.actor()
    const reader = this.reader()
    const communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendApprovedJoinRequestNotification({
        email: reader.get('email'),
        sender: {name: community.get('name')},
        data: {
          community_name: community.get('name'),
          community_avatar_url: community.get('avatar_url'),
          approver_name: actor.get('name'),
          approver_avatar_url: actor.get('avatar_url'),
          approver_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(actor) + '?ctt=comment_email&cti=' + reader.id),
          community_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.community(community))
        }
      })))
  },

  updateUserSocketRoom: function (userId) {
    const actor = Object.assign({},
      this.actor().pick([ 'id', 'name' ]),
      { avatarUrl: this.actor().get('avatar_url') }
    )
    const comment = this.comment().pick([ 'id', 'text' ])
    const community = this.relations.activity.relations.community.pick([ 'id', 'name', 'slug' ])
    const post = { id: this.post().id, title: this.post().get('name') }
    const payload = {
      id: '' + this.id,
      createdAt: this.get('created_at'),
      activity: Object.assign({},
        this.relations.activity.pick([ 'action', 'id', 'meta', 'unread' ]),
        { actor, comment, community, post })
    }

    const redisInfo = parseRedisUrl().parse(process.env.REDIS_URL)
    const io = emitter(redisInfo)
    io.on('error', err => console.error(`Socket error on newNotification: ${err}`))
    io.in(userRoom(userId)).emit('newNotification', payload)
  }
}, {
  MEDIUM,
  TYPE,

  find: function (id, options) {
    if (!id) return Promise.resolve(null)
    return Notification.where({id: id}).fetch(options)
  },

  findUnsent: function (options = {}) {
    const { raw } = bookshelf.knex
    return Notification.query(q => {
      q.where({sent_at: null})
      if (!options.includeOld) {
        q.where('created_at', '>', raw("now() - interval '6 hour'"))
      }
      q.where(function () {
        this.where('failed_at', null)
        .orWhere('failed_at', '<', raw("now() - interval '1 hour'"))
      })
      q.limit(200)
    })
    .fetchAll(options)
  },

  sendUnsent: function () {
    // FIXME empty out this withRelated list and just load things on demand when
    // creating push notifications / emails
    return Notification.findUnsent({withRelated: [
      'activity',
      'activity.post',
      'activity.post.communities',
      'activity.post.user',
      'activity.comment',
      'activity.comment.media',
      'activity.comment.user',
      'activity.comment.post',
      'activity.comment.post.user',
      'activity.comment.post.relatedUsers',
      'activity.comment.post.communities',
      'activity.community',
      'activity.reader',
      'activity.actor'
    ]})
    .then(ns => ns.length > 0 &&
      Promise.each(ns.models,
        n => n.send().catch(() =>
          n.save({failed_at: new Date()}, {patch: true})))
      .then(() => Notification.sendUnsent()))
  },

  priorityReason: function (reasons) {
    const orderedLabels = [
      'mention', 'commentMention', 'newComment', 'newContribution', 'tag', 'newPost', 'follow',
      'followAdd', 'unfollow', 'joinRequest', 'approvedJoinRequest'
    ]

    const match = label => reasons.some(r => r.match(new RegExp('^' + label)))
    return orderedLabels.find(match) || ''
  },

  removeOldNotifications: function () {
    return Notification.query()
    .whereRaw("created_at < now() - interval '1 month'")
    .del()
  }
})
<commit_msg>Provide more debugging context on Redis errors (#322)<commit_after>var url = require('url')
import { isEmpty } from 'lodash'
import emitter from 'socket.io-emitter'
import decode from 'ent/decode'
import parseRedisUrl from 'parse-redis-url'

import { userRoom } from '../services/Websockets'

const TYPE = {
  Mention: 'mention', // you are mentioned in a post or comment
  TagFollow: 'TagFollow',
  NewPost: 'newPost',
  Comment: 'comment', // someone makes a comment on a post you follow
  Contribution: 'contribution', // you are added as a contributor
  FollowAdd: 'followAdd', // you are added as a follower
  Follow: 'follow', // someone follows your post
  Unfollow: 'unfollow', // someone leaves your post
  Welcome: 'welcome', // a welcome post
  JoinRequest: 'joinRequest',
  ApprovedJoinRequest: 'approvedJoinRequest',
  Message: 'message'
}

const MEDIUM = {
  InApp: 0,
  Push: 1,
  Email: 2
}

module.exports = bookshelf.Model.extend({
  tableName: 'notifications',

  activity: function () {
    return this.belongsTo(Activity)
  },

  post: function () {
    return this.relations.activity.relations.post
  },

  comment: function () {
    return this.relations.activity.relations.comment
  },

  reader: function () {
    return this.relations.activity.relations.reader
  },

  actor: function () {
    return this.relations.activity.relations.actor
  },

  send: function () {
    var action
    switch (this.get('medium')) {
      case MEDIUM.Push:
        action = this.sendPush()
        break
      case MEDIUM.Email:
        action = this.sendEmail()
        break
      case MEDIUM.InApp:
        const userId = this.reader().id
        action = User.incNewNotificationCount(userId)
          .then(() => this.updateUserSocketRoom(userId))
        break
    }
    if (action) {
      return action
      .then(() => this.save({'sent_at': (new Date()).toISOString()}))
    } else {
      return Promise.resolve()
    }
  },

  sendPush: function () {
    switch (Notification.priorityReason(this.relations.activity.get('meta').reasons)) {
      case 'mention':
        return this.sendPostPush('mention')
      case 'commentMention':
        return this.sendCommentPush('mention')
      case 'newComment':
        return this.sendCommentPush()
      case 'newContribution':
        return this.sendContributionPush()
      case 'newPost':
        return this.sendPostPush()
      case 'joinRequest':
        return this.sendJoinRequestPush()
      case 'approvedJoinRequest':
        return this.sendApprovedJoinRequestPush()
      default:
        return Promise.resolve()
    }
  },

  sendPostPush: function (version) {
    var post = this.post()
    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => {
      var path = url.parse(Frontend.Route.post(post, community)).path
      var alertText = PushNotification.textForPost(post, community, this.relations.activity.get('reader_id'), version)
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendContributionPush: function (version) {
    return this.load(['contribution', 'contribution.post'])
    .then(() => {
      const { contribution } = this.relations.activity.relations
      var path = url.parse(Frontend.Route.post(contribution.relations.post)).path
      var alertText = PushNotification.textForContribution(contribution, version)
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendCommentPush: function (version) {
    var comment = this.comment()
    var path = url.parse(Frontend.Route.post(comment.relations.post)).path
    var alertText = PushNotification.textForComment(comment, version)
    if (!this.reader().enabledNotification(TYPE.Comment, MEDIUM.Push)) {
      return Promise.resolve()
    }
    return this.reader().sendPushNotification(alertText, path)
  },

  sendJoinRequestPush: function () {
    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => {
      var path = url.parse(Frontend.Route.communityJoinRequests(community)).path
      var alertText = PushNotification.textForJoinRequest(community, this.actor())
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendApprovedJoinRequestPush: function () {
    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => {
      var path = url.parse(Frontend.Route.community(community)).path
      var alertText = PushNotification.textForApprovedJoinRequest(community, this.actor())
      return this.reader().sendPushNotification(alertText, path)
    })
  },

  sendEmail: function () {
    switch (Notification.priorityReason(this.relations.activity.get('meta').reasons)) {
      case 'mention':
        return this.sendPostMentionEmail()
      case 'joinRequest':
        return this.sendJoinRequestEmail()
      case 'approvedJoinRequest':
        return this.sendApprovedJoinRequestEmail()
      default:
        return Promise.resolve()
    }
  },

  sendPostMentionEmail: function () {
    var post = this.post()
    var reader = this.reader()
    var user = post.relations.user
    var description = RichText.qualifyLinks(post.get('description'))
    var replyTo = Email.postReplyAddress(post.id, reader.id)

    var communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendPostMentionNotification({
        email: reader.get('email'),
        sender: {
          address: replyTo,
          reply_to: replyTo,
          name: `${user.get('name')} (via Hylo)`
        },
        data: {
          community_name: community.get('name'),
          post_user_name: user.get('name'),
          post_user_avatar_url: Frontend.Route.tokenLogin(reader, token,
            user.get('avatar_url') + '?ctt=post_mention_email&cti=' + reader.id),
          post_user_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(user) + '?ctt=post_mention_email&cti=' + reader.id),
          post_description: description,
          post_title: decode(post.get('name')),
          post_type: 'conversation',
          post_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.post(post) + '?ctt=post_mention_email&cti=' + reader.id),
          unfollow_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.unfollow(post, community) + '?ctt=post_mention_email&cti=' + reader.id),
          tracking_pixel_url: Analytics.pixelUrl('Mention in Post', {userId: reader.id})
        }
      })))
  },

  // version corresponds to names of versions in SendWithUs
  sendCommentNotificationEmail: function (version) {
    const comment = this.comment()
    const reader = this.reader()
    if (!comment) return

    const post = comment.relations.post
    const commenter = comment.relations.user
    const text = RichText.qualifyLinks(comment.get('text'))
    const replyTo = Email.postReplyAddress(post.id, reader.id)
    const title = decode(post.get('name'))

    var postLabel = `"${title}"`
    if (post.get('type') === 'welcome') {
      var relatedUser = post.relations.relatedUsers.first()
      if (relatedUser.id === reader.id) {
        postLabel = 'your welcoming post'
      } else {
        postLabel = `${relatedUser.get('name')}'s welcoming post`
      }
    }

    const communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendNewCommentNotification({
        version: version,
        email: reader.get('email'),
        sender: {
          address: replyTo,
          reply_to: replyTo,
          name: `${commenter.get('name')} (via Hylo)`
        },
        data: {
          community_name: community.get('name'),
          commenter_name: commenter.get('name'),
          commenter_avatar_url: commenter.get('avatar_url'),
          commenter_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(commenter) + '?ctt=comment_email&cti=' + reader.id),
          comment_text: text,
          post_label: postLabel,
          post_title: title,
          comment_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.post(post) + '?ctt=comment_email&cti=' + reader.id + `#comment-${comment.id}`),
          unfollow_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.unfollow(post, community)),
          tracking_pixel_url: Analytics.pixelUrl('Comment', {userId: reader.id})
        }
      })))
  },

  sendJoinRequestEmail: function () {
    const actor = this.actor()
    const reader = this.reader()
    const communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendJoinRequestNotification({
        email: reader.get('email'),
        sender: {name: community.get('name')},
        data: {
          community_name: community.get('name'),
          requester_name: actor.get('name'),
          requester_avatar_url: actor.get('avatar_url'),
          requester_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(actor) +
            `?ctt=comment_email&cti=${reader.id}&check-join-requests=1`),
          settings_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.communityJoinRequests(community))
        }
      })))
  },

  sendApprovedJoinRequestEmail: function () {
    const actor = this.actor()
    const reader = this.reader()
    const communityIds = Activity.communityIds(this.relations.activity)
    if (isEmpty(communityIds)) throw new Error('no community ids in activity')
    return Community.find(communityIds[0])
    .then(community => reader.generateToken()
      .then(token => Email.sendApprovedJoinRequestNotification({
        email: reader.get('email'),
        sender: {name: community.get('name')},
        data: {
          community_name: community.get('name'),
          community_avatar_url: community.get('avatar_url'),
          approver_name: actor.get('name'),
          approver_avatar_url: actor.get('avatar_url'),
          approver_profile_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.profile(actor) + '?ctt=comment_email&cti=' + reader.id),
          community_url: Frontend.Route.tokenLogin(reader, token,
            Frontend.Route.community(community))
        }
      })))
  },

  updateUserSocketRoom: function (userId) {
    const actor = Object.assign({},
      this.actor().pick([ 'id', 'name' ]),
      { avatarUrl: this.actor().get('avatar_url') }
    )
    const comment = this.comment().pick([ 'id', 'text' ])
    const community = this.relations.activity.relations.community.pick([ 'id', 'name', 'slug' ])
    const post = { id: this.post().id, title: this.post().get('name') }
    const payload = {
      id: '' + this.id,
      createdAt: this.get('created_at'),
      activity: Object.assign({},
        this.relations.activity.pick([ 'action', 'id', 'meta', 'unread' ]),
        { actor, comment, community, post })
    }

    const redisInfo = parseRedisUrl().parse(process.env.REDIS_URL)
    const io = emitter(redisInfo)
    io.on('error', err => console.error(`Socket error on newNotification: ${err}`))
    io.redis.on('error', err => console.error(`
      Redis error: ${JSON.stringify(err)}
      while attempting to send notification via socket from actor: ${JSON.stringify(actor)}
      in community ${JSON.stringify(community)}
    `))
    io.in(userRoom(userId)).emit('newNotification', payload)
  }
}, {
  MEDIUM,
  TYPE,

  find: function (id, options) {
    if (!id) return Promise.resolve(null)
    return Notification.where({id: id}).fetch(options)
  },

  findUnsent: function (options = {}) {
    const { raw } = bookshelf.knex
    return Notification.query(q => {
      q.where({sent_at: null})
      if (!options.includeOld) {
        q.where('created_at', '>', raw("now() - interval '6 hour'"))
      }
      q.where(function () {
        this.where('failed_at', null)
        .orWhere('failed_at', '<', raw("now() - interval '1 hour'"))
      })
      q.limit(200)
    })
    .fetchAll(options)
  },

  sendUnsent: function () {
    // FIXME empty out this withRelated list and just load things on demand when
    // creating push notifications / emails
    return Notification.findUnsent({withRelated: [
      'activity',
      'activity.post',
      'activity.post.communities',
      'activity.post.user',
      'activity.comment',
      'activity.comment.media',
      'activity.comment.user',
      'activity.comment.post',
      'activity.comment.post.user',
      'activity.comment.post.relatedUsers',
      'activity.comment.post.communities',
      'activity.community',
      'activity.reader',
      'activity.actor'
    ]})
    .then(ns => ns.length > 0 &&
      Promise.each(ns.models,
        n => n.send().catch(() =>
          n.save({failed_at: new Date()}, {patch: true})))
      .then(() => Notification.sendUnsent()))
  },

  priorityReason: function (reasons) {
    const orderedLabels = [
      'mention', 'commentMention', 'newComment', 'newContribution', 'tag', 'newPost', 'follow',
      'followAdd', 'unfollow', 'joinRequest', 'approvedJoinRequest'
    ]

    const match = label => reasons.some(r => r.match(new RegExp('^' + label)))
    return orderedLabels.find(match) || ''
  },

  removeOldNotifications: function () {
    return Notification.query()
    .whereRaw("created_at < now() - interval '1 month'")
    .del()
  }
})
<|endoftext|>


<commit_before>'use strict';

describe('Controller: SignupCtrl', function () {

  // load the controller's module
  beforeEach(module('badgerApp'));

  var SignupCtrl, scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    SignupCtrl = $controller('SignupCtrl', {
      $scope: scope
    });
  }));

  it('should default to account type client', function ($scope) {
    console.log($scope);
    expect($scope.user.accountType).toEqual('Client');
  });

  it('should display an error if email is left blank', function ($scope) {
    $scope.register();
    expect($('.help-block')).toEqual(true);
  });

});
<commit_msg>(#58) Update for signup tests.<commit_after>'use strict';

describe('Controller: SignupCtrl', function () {
  var SignupCtrl, createController, $scope, $rootScope, $controller, $form;

  // Load the controller's module
  beforeEach(module('badgerApp'));

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($injector) {
    $rootScope = $injector.get('$rootScope');
    $controller = $injector.get('$controller');

    $scope = $rootScope.$new();

    createController = function() {
      SignupCtrl = $controller('SignupCtrl', {
        $scope: $scope
      });
    };
  }));

  it('should default to account type client', function () {
    createController();
    expect($scope.user.accountType).toEqual('Client');
  });

  it('should display an error if email is left blank', function () {
    createController();
    $form = $("<form />");
    $form.trigger("submit");
    expect(!!$('.help-block')).toEqual(true);
  });

});
<|endoftext|>


<commit_before>'use strict';

/**
 * UserPassword
 *
 * @description :: Accepts and validates incoming POST request for user password.
 * @help        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

module.exports = {
  // Allow only whitelisted attributes.
  schema: true,

  // Turn off migrations.
  migrate: 'safe',

  // Turn off auto fields.
  autoCreatedAt: false,
  autoUpdatedAt: false,
  autoPK: false,

  // Validation rules.
  attributes: {
    user_id: {
      type: 'string',
      required: true,
      hexadecimal: true,
      defaultsTo: ""
    },
    email: {
      type: 'string',
      required: true,
      email: true,
      defaultsTo: ""
    },
    mobile: {
      type: 'string',
      required: true,
      defaultsTo: ""
    },
    application_id: {
      type: 'string'
    },
    email_template: {
      type: 'string'
    },
    toMessage() {
      return NorthstarService.getUserFor(this).then((user) => {
        return {
          activity: 'user_password',
          email: user.email,
          uid: user.drupalID,
          merge_vars: {
            MEMBER_COUNT: null,
            FNAME: null,
            RESET_LINK: null,
          },
          user_country: user.country,
          user_language: user.language,
          email_template: '',
          email_tags: ['drupal_user_password'],
          activity_timestamp: null,
          application_id: 'quicksilver-api',
        };
      });
    },
  },

  /**
   * Validate the presences of at least one of the fields.
   */
  beforeValidate(values, cb) {
    // TODO: Replace this with custom attribute validation method:
    // https://github.com/balderdashy/waterline-docs/blob/master/models/validations.md#custom-validations

    // The list of possible fields.
    const fields = ['user_id', 'email', 'mobile'];

    // Check if at least one field is present and truthy.
    const found = fields.some(field => values[field]);

    // Turn off presence validations when at least one fields is present,
    // turn on when all fields are absent.
    fields.forEach((field) => {
      if (found) {
        // It should be possible to just set required to false,
        // but for some reason it turns off the rest of field rules.
        delete this._validator.validations[field].required;
      } else {
        // Reenable presence validation if no fields found.
        this._validator.validations[field].required = true;
      }
      return true;
    });

    // Continue.
    cb();
  }
};
<commit_msg>Fix per #75 review.<commit_after>'use strict';

/**
 * UserPassword
 *
 * @description :: Accepts and validates incoming POST request for user password.
 * @help        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

module.exports = {
  // Allow only whitelisted attributes.
  schema: true,

  // Turn off migrations.
  migrate: 'safe',

  // Turn off auto fields.
  autoCreatedAt: false,
  autoUpdatedAt: false,
  autoPK: false,

  // Validation rules.
  attributes: {
    user_id: {
      type: 'string',
      required: true,
      hexadecimal: true,
      defaultsTo: ""
    },
    email: {
      type: 'string',
      required: true,
      email: true,
      defaultsTo: ""
    },
    mobile: {
      type: 'string',
      required: true,
      defaultsTo: ""
    },
    application_id: {
      type: 'string'
    },
    email_template: {
      type: 'string'
    },
    toMessage() {
      return NorthstarService.getUserFor(this).then((user) => {
        return {
          activity: 'user_password',
          email: user.email,
          uid: user.drupalID,
          merge_vars: {
            MEMBER_COUNT: null,
            FNAME: null,
            RESET_LINK: null,
          },
          user_country: user.country,
          user_language: user.language,
          email_template: '',
          email_tags: ['drupal_user_password'],
          activity_timestamp: null,
          application_id: null,
        };
      });
    },
  },

  /**
   * Validate the presences of at least one of the fields.
   */
  beforeValidate(values, cb) {
    // TODO: Replace this with custom attribute validation method:
    // https://github.com/balderdashy/waterline-docs/blob/master/models/validations.md#custom-validations

    // The list of possible fields.
    const fields = ['user_id', 'email', 'mobile'];

    // Check if at least one field is present and truthy.
    const found = fields.some(field => values[field]);

    // Turn off presence validations when at least one fields is present,
    // turn on when all fields are absent.
    fields.forEach((field) => {
      if (found) {
        // It should be possible to just set required to false,
        // but for some reason it turns off the rest of field rules.
        delete this._validator.validations[field].required;
      } else {
        // Reenable presence validation if no fields found.
        this._validator.validations[field].required = true;
      }
      return true;
    });

    // Continue.
    cb();
  }
};
<|endoftext|>


<commit_before>// module EntryForm
//
// Features
// - use same form for entry creation and edit.
// - handles the API calls internally
// - is able to upload attachments
// - temporary store of content for interrupted creation or edit
//
var template = require('./template.ejs');
var AttachmentsView = require('./Attachments');
var ui = require('tresdb-ui');
var emitter = require('component-emitter');
var account = tresdb.stores.account;

// Megabyte
var MB = 1024 * 1024;

module.exports = function (location, entry) {
  // Entry form View.
  //
  // Parameters:
  //   location
  //     location object
  //   entry
  //     optional entry object. If not given, a blank entry form is shown.
  //
  if (!entry) {
    entry = {
      user: account.getName(),
      markdown: '',
      attachments: [],
    };
  }

  var self = this;
  emitter(self);

  var bound = {};
  var children = {};

  self.bind = function ($mount) {

    var isNew = !('_id' in entry);
    var isAuthor = account.isMe(entry.user);
    var isAdmin = account.isAdmin();
    var isAuthorOrAdmin = (isAuthor || isAdmin);

    if (!isAuthorOrAdmin) {
      // No form needed non-authors and non-admins
      return;
    }

    $mount.html(template({
      entry: entry,
      isAuthor: isAuthor,
      isNew: isNew,
      markdownSyntax: ui.markdownSyntax(),
      limit: Math.round(tresdb.config.uploadSizeLimit / MB),
    }));

    bound.syntaxOpen = $mount.find('.entry-syntax-open');
    bound.syntaxOpen.click(function (ev) {
      ev.preventDefault();
      ui.toggleHidden($mount.find('.entry-syntax'));
    });

    children.attachments = new AttachmentsView(entry, entry.attachments);
    children.attachments.bind($mount.find('.form-attachments-container'));

    bound.cancelBtn = $mount.find('.entry-form-cancel');
    bound.cancelBtn.click(function () {
      self.emit('exit');
    });
  };

  self.unbind = function () {
    ui.offAll(bound);
    ui.unbindAll(children);
  };
};
<commit_msg>Sketch entry submission<commit_after>// module EntryForm
//
// Features
// - use same form for entry creation and edit.
// - handles the API calls internally
// - is able to upload attachments
// - temporary store of content for interrupted creation or edit
//
var template = require('./template.ejs');
var AttachmentsView = require('./Attachments');
var ui = require('tresdb-ui');
var emitter = require('component-emitter');
var account = tresdb.stores.account;
var entries = tresdb.stores.entries;

// Megabyte
var MB = 1024 * 1024;

module.exports = function (location, entry) {
  // Entry form View.
  //
  // Parameters:
  //   location
  //     location object
  //   entry
  //     optional entry object. If not given, a blank entry form is shown.
  //
  if (!entry) {
    entry = {
      user: account.getName(),
      markdown: '',
      attachments: [],
    };
  }

  var self = this;
  emitter(self);

  var bound = {};
  var children = {};

  self.bind = function ($mount) {

    var isNew = !('_id' in entry);
    var isAuthor = account.isMe(entry.user);
    var isAdmin = account.isAdmin();
    var isAuthorOrAdmin = (isAuthor || isAdmin);

    if (!isAuthorOrAdmin) {
      // No form needed non-authors and non-admins
      return;
    }

    $mount.html(template({
      entry: entry,
      isAuthor: isAuthor,
      isNew: isNew,
      markdownSyntax: ui.markdownSyntax(),
      limit: Math.round(tresdb.config.uploadSizeLimit / MB),
    }));

    bound.syntaxOpen = $mount.find('.entry-syntax-open');
    bound.syntaxOpen.click(function (ev) {
      ev.preventDefault();
      ui.toggleHidden($mount.find('.entry-syntax'));
    });

    children.attachments = new AttachmentsView(entry, entry.attachments);
    children.attachments.bind($mount.find('.form-attachments-container'));

    bound.saveBtn = $mount.find('.entry-form-save');
    bound.saveBtn.click(function () {
      // Create new entry or update the existing

      var entryData = {
        markdown: '', // TODO children.markdown.getMarkdown(),
        attachments: children.attachments.getAttachments(),
        flags: [], // TODO children.flags.getFlags(),
      };

      if (isNew) {
        entries.create(location._id, entryData, function (err) {
          if (err) {
            // TODO show submit error
            return console.log(err);
          }
          self.emit('success');
        });
      } else {
        entries.change(location._id, entry._id, entryData, function (err) {
          if (err) {
            // TODO
            return console.log(err);
          }
          self.emit('success');
        });
      }
    });

    bound.cancelBtn = $mount.find('.entry-form-cancel');
    bound.cancelBtn.click(function () {
      self.emit('exit');
    });
  };

  self.unbind = function () {
    ui.offAll(bound);
    ui.unbindAll(children);
  };
};
<|endoftext|>


<commit_before>const fs = require('fs')
const tape = require('tape')
const Kernel = require('ewasm-kernel')
const KernelInterface = require('ewasm-kernel/interface.js')
const KernelEnvironment = require('ewasm-kernel/environment.js')
const ethUtil = require('ethereumjs-util')
const compiler = require('../index.js')
const argv = require('minimist')(process.argv.slice(2))
const dir = `${__dirname}/opcode`

// Transcompiled contracts have their EVM1 memory start at this WASM memory location
const EVM_MEMORY_OFFSET = 33832

let testFiles = fs.readdirSync(dir).filter((name) => name.endsWith('.json'))

// run a single file
if (argv.file) {
  testFiles = [argv.file]
}

tape('testing EVM1 Ops', (t) => {
  testFiles.forEach((path) => {
    let opTest = require(`${dir}/${path}`)
    opTest.forEach((test) => {
      const testEnvironment = new KernelEnvironment()
      const testInterface = new KernelInterface(testEnvironment)
      const testInstance = buildTest(test.op, testInterface)

      // FIXME: have separate `t.test()` for better grouping
      t.comment(`testing ${test.op} ${test.description}`)

      // populate the environment
      if (test.environment) {
        Object.keys(test.environment).forEach((key) => {
          let value = test.environment[key]
          if (key === 'caller' || key === 'address')
            value = hexToUint8Array(value, 20)
          else if (key === 'callData')
            // NOTE: no padding needed
            value = hexToUint8Array(value, (value.length - 2) / 2)
          else
            throw new Error('Unsupported environment variable')
          testEnvironment[key] = value
        })
      }

      // populate the stack with predefined values
      test.in.stack.forEach((item, index) => {
        item = hexToUint8Array(item)
        setMemory(testInstance, item, index * 32)
      })

      // populate the memory
      if (test.in.memory) {
        Object.keys(test.in.memory).forEach((offset) => {
            let item = test.in.memory[offset]
            offset |= 0
            offset += EVM_MEMORY_OFFSET
            item = hexToUint8Array(item)
            setMemory(testInstance, item, offset)
        })
      }

      // Runs the opcode. An empty stack must start with the stack pointer at -8.
      // also we have to add 8 to the resulting sp to accommodate for the fact
      // that the sp is pointing to memory segment holding the last stack item
      let sp = (test.in.stack.length - 1) * 32

      try {
        sp = testInstance.exports[test.op](...(test.params || []), sp) + 32
      } catch(e) {
        t.fail('WASM exception: ' + e)
      }

      if (isNaN(sp)) {
        t.fail('methods should return the stack pointer')
      }

      if (sp % 32) {
        t.fail('stack must be a multiple of 32 bytes')
      }

      t.equal(sp / 32, test.out.stack.length, 'should have correct number of items on the stack')
      sp = 0

      // compare the output stack against the predefined values
      test.out.stack.forEach((item, index) => {
        const expectedItem = hexToUint8Array(item)
        const result = getMemory(testInstance, sp, sp = sp + 32)
        t.equals(result.toString(), expectedItem.toString(), 'should have correct item on stack')
      })

      // check the memory
      if (test.out.memory) {
        Object.keys(test.out.memory).forEach((offset) => {
            offset |= 0
            const item = test.out.memory[offset]
            const expectedItem = hexToUint8Array(item)
            offset += EVM_MEMORY_OFFSET
            const result = getMemory(testInstance, offset, offset + expectedItem.length)
            t.equals(result.toString(), expectedItem.toString(), `should have the correct memory slot at ${offset}`)
        })
      }

      // check for EVM return value
      if (test.out.return) {
        const expectedItem = hexToUint8Array(test.out.return)
        const result = testEnvironment.returnValue
        t.equals(result.toString(), expectedItem.toString(), 'should have correct return value')
      }

      if (test.out.gasUsed) {
        t.equals(-testEnvironment.gasLimit, test.out.gasUsed, 'should have used the correct amount of gas')
      }
    })
  })
  t.end()
})

function buildTest (op, ethInterface) {
  const funcs = compiler.resolveFunctions(new Set([op]))
  const linked = compiler.buildModule(funcs, [], [op])
  const wasm = compiler.compileWAST(linked)
  return Kernel.codeHandler(wasm, ethInterface)
}

function hexToUint8Array(item, length) {
  return new Uint8Array(ethUtil.setLength(new Buffer(item.slice(2), 'hex'), length || 32)).reverse()
}

function setMemory(instance, value, start) {
  new Uint8Array(instance.exports.memory).set(value, start)
}

function getMemory(instance, start, end) {
  return new Uint8Array(instance.exports.memory).slice(start, end)
}
<commit_msg>updated opcodeRunner to check for gas correctly<commit_after>const fs = require('fs')
const tape = require('tape')
const Kernel = require('ewasm-kernel')
const KernelInterface = require('ewasm-kernel/interface.js')
const KernelEnvironment = require('ewasm-kernel/environment.js')
const ethUtil = require('ethereumjs-util')
const compiler = require('../index.js')
const argv = require('minimist')(process.argv.slice(2))
const dir = `${__dirname}/opcode`

// Transcompiled contracts have their EVM1 memory start at this WASM memory location
const EVM_MEMORY_OFFSET = 33832

let testFiles = fs.readdirSync(dir).filter((name) => name.endsWith('.json'))

// run a single file
if (argv.file) {
  testFiles = [argv.file]
}

tape('testing EVM1 Ops', (t) => {
  testFiles.forEach((path) => {
    let opTest = require(`${dir}/${path}`)
    opTest.forEach((test) => {
      const testEnvironment = new KernelEnvironment()
      const testInterface = new KernelInterface(testEnvironment)
      const testInstance = buildTest(test.op, testInterface)

      // FIXME: have separate `t.test()` for better grouping
      t.comment(`testing ${test.op} ${test.description}`)

      // populate the environment
      if (test.environment) {
        Object.keys(test.environment).forEach((key) => {
          let value = test.environment[key]
          if (key === 'caller' || key === 'address')
            value = hexToUint8Array(value, 20)
          else if (key === 'callData')
            // NOTE: no padding needed
            value = hexToUint8Array(value, (value.length - 2) / 2)
          else
            throw new Error('Unsupported environment variable')
          testEnvironment[key] = value
        })
      }

      // populate the stack with predefined values
      test.in.stack.forEach((item, index) => {
        item = hexToUint8Array(item)
        setMemory(testInstance, item, index * 32)
      })

      // populate the memory
      if (test.in.memory) {
        Object.keys(test.in.memory).forEach((offset) => {
            let item = test.in.memory[offset]
            offset |= 0
            offset += EVM_MEMORY_OFFSET
            item = hexToUint8Array(item)
            setMemory(testInstance, item, offset)
        })
      }

      // Runs the opcode. An empty stack must start with the stack pointer at -8.
      // also we have to add 8 to the resulting sp to accommodate for the fact
      // that the sp is pointing to memory segment holding the last stack item
      let sp = (test.in.stack.length - 1) * 32

      try {
        sp = testInstance.exports[test.op](...(test.params || []), sp) + 32
      } catch(e) {
        t.fail('WASM exception: ' + e)
      }

      if (isNaN(sp)) {
        t.fail('methods should return the stack pointer')
      }

      if (sp % 32) {
        t.fail('stack must be a multiple of 32 bytes')
      }

      t.equal(sp / 32, test.out.stack.length, 'should have correct number of items on the stack')
      sp = 0

      // compare the output stack against the predefined values
      test.out.stack.forEach((item, index) => {
        const expectedItem = hexToUint8Array(item)
        const result = getMemory(testInstance, sp, sp = sp + 32)
        t.equals(result.toString(), expectedItem.toString(), 'should have correct item on stack')
      })

      // check the memory
      if (test.out.memory) {
        Object.keys(test.out.memory).forEach((offset) => {
          offset |= 0
          const item = test.out.memory[offset]
          const expectedItem = hexToUint8Array(item)
          offset += EVM_MEMORY_OFFSET
          const result = getMemory(testInstance, offset, offset + expectedItem.length)
          t.equals(result.toString(), expectedItem.toString(), `should have the correct memory slot at ${offset}`)
        })
      }

      // check for EVM return value
      if (test.out.return) {
        const expectedItem = hexToUint8Array(test.out.return)
        const result = testEnvironment.returnValue
        t.equals(result.toString(), expectedItem.toString(), 'should have correct return value')
      }

      if (test.out.gasUsed) {
        t.equals(1000000 - testEnvironment.gasLimit, test.out.gasUsed, 'should have used the correct amount of gas')
      }
    })
  })
  t.end()
})

function buildTest (op, ethInterface) {
  const funcs = compiler.resolveFunctions(new Set([op]))
  const linked = compiler.buildModule(funcs, [], [op])
  const wasm = compiler.compileWAST(linked)
  return Kernel.codeHandler(wasm, ethInterface)
}

function hexToUint8Array(item, length) {
  return new Uint8Array(ethUtil.setLength(new Buffer(item.slice(2), 'hex'), length || 32)).reverse()
}

function setMemory(instance, value, start) {
  new Uint8Array(instance.exports.memory).set(value, start)
}

function getMemory(instance, start, end) {
  return new Uint8Array(instance.exports.memory).slice(start, end)
}
<|endoftext|>


<commit_before>const fs = require('fs')
const tape = require('tape')
const Kernel = require('ewasm-kernel')
const KernelInterface = require('ewasm-kernel/interface.js')
const KernelEnvironment = require('ewasm-kernel/environment.js')
const ethUtil = require('ethereumjs-util')
const compiler = require('../index.js')
const dir = `${__dirname}/opcode`

let testFiles = fs.readdirSync(dir).filter((name) => name.endsWith('.json'))

tape('testing EVM1 Ops', (t) => {
  testFiles.forEach((path) => {
    let opTest = require(`${dir}/${path}`)
    opTest.forEach((test) => {
      const testEnvironment = new KernelEnvironment()
      const testInterface = new KernelInterface(testEnvironment)
      const testInstance = buildTest(test.op, testInterface)

      // FIXME: have separate `t.test()` for better grouping
      t.comment(`testing ${test.op} ${test.description}`)

      // populate the stack with predefined values
      test.stack.in.reverse().forEach((item, index) => {
        item = hexToUint8Array(item)
        setMemory(testInstance, item, index * 32)
      })

      // populate the memory
      if (test.memory) {
        Object.keys(test.memory.in).forEach((offset) => {
          test.memory.in[offset].forEach((item, index) => {
            offset |= 0
            item = hexToUint8Array(item)
            setMemory(testInstance, item, offset + index * 32)
          })
        })
      }

      // Runs the opcode. An empty stack must start with the stack pointer at -8.
      // also we have to add 8 to the resulting sp to accommodate for the fact
      // that the sp is pointing to memory segment holding the last stack item
      let sp = test.stack.in.length * 32 - 8
      sp = testInstance.exports[test.op](...(test.params || []), sp) + 8
      t.equal(sp / 32, test.stack.out.length, 'should have corrent number of items on the stack')
      sp = 0

      // compare the output stack against the predefined values
      test.stack.out.forEach((item, index) => {
        const expectedItem = hexToUint8Array(item)
        const result = getMemory(testInstance, sp, sp = sp + 32)
        t.equals(result.toString(), expectedItem.toString(), 'should have correct item on stack')
      })

      // check the memory
      if (test.memory) {
        Object.keys(test.memory.out).forEach((offset) => {
          test.memory.out[offset].forEach((item, index) => {
            offset |= 0
            const expectedItem = hexToUint8Array(item)
            const result = getMemory(testInstance, offset + index * 32, offset + index * 32 + expectedItem.length)
            t.equals(result.toString(), expectedItem.toString(), `should have the correct memory slot at ${offset}:${index}`)
          })
        })
      }

      // check for EVM return value
      if (test.return) {
        const expectedItem = hexToUint8Array(test.return)
        //const result = testEnvironment.returnValue
        const result = testInterface.env.returnValue
        t.equals(result.toString(), expectedItem.toString(), 'should have correct return value')
      }
    })
  })
  t.end()
})

function buildTest (op, interface) {
  const funcs = compiler.resolveFunctions(new Set([op]))
  const linked = compiler.buildModule(funcs, [], [op])
  const wasm = compiler.compileWAST(linked)
  return Kernel.codeHandler(wasm, interface)
}

function hexToUint8Array(item, length) {
  return new Uint8Array(ethUtil.setLength(new Buffer(item.slice(2), 'hex'), 32)).reverse()
}

function setMemory(instance, value, start) {
  new Uint8Array(instance.exports.memory).set(value, start)
}

function getMemory(instance, start, end) {
  return new Uint8Array(instance.exports.memory).slice(start, end)
}
<commit_msg>Tests: ewasm-kernel was fixed, rely on testEnvironment directly<commit_after>const fs = require('fs')
const tape = require('tape')
const Kernel = require('ewasm-kernel')
const KernelInterface = require('ewasm-kernel/interface.js')
const KernelEnvironment = require('ewasm-kernel/environment.js')
const ethUtil = require('ethereumjs-util')
const compiler = require('../index.js')
const dir = `${__dirname}/opcode`

let testFiles = fs.readdirSync(dir).filter((name) => name.endsWith('.json'))

tape('testing EVM1 Ops', (t) => {
  testFiles.forEach((path) => {
    let opTest = require(`${dir}/${path}`)
    opTest.forEach((test) => {
      const testEnvironment = new KernelEnvironment()
      const testInterface = new KernelInterface(testEnvironment)
      const testInstance = buildTest(test.op, testInterface)

      // FIXME: have separate `t.test()` for better grouping
      t.comment(`testing ${test.op} ${test.description}`)

      // populate the stack with predefined values
      test.stack.in.reverse().forEach((item, index) => {
        item = hexToUint8Array(item)
        setMemory(testInstance, item, index * 32)
      })

      // populate the memory
      if (test.memory) {
        Object.keys(test.memory.in).forEach((offset) => {
          test.memory.in[offset].forEach((item, index) => {
            offset |= 0
            item = hexToUint8Array(item)
            setMemory(testInstance, item, offset + index * 32)
          })
        })
      }

      // Runs the opcode. An empty stack must start with the stack pointer at -8.
      // also we have to add 8 to the resulting sp to accommodate for the fact
      // that the sp is pointing to memory segment holding the last stack item
      let sp = test.stack.in.length * 32 - 8
      sp = testInstance.exports[test.op](...(test.params || []), sp) + 8
      t.equal(sp / 32, test.stack.out.length, 'should have corrent number of items on the stack')
      sp = 0

      // compare the output stack against the predefined values
      test.stack.out.forEach((item, index) => {
        const expectedItem = hexToUint8Array(item)
        const result = getMemory(testInstance, sp, sp = sp + 32)
        t.equals(result.toString(), expectedItem.toString(), 'should have correct item on stack')
      })

      // check the memory
      if (test.memory) {
        Object.keys(test.memory.out).forEach((offset) => {
          test.memory.out[offset].forEach((item, index) => {
            offset |= 0
            const expectedItem = hexToUint8Array(item)
            const result = getMemory(testInstance, offset + index * 32, offset + index * 32 + expectedItem.length)
            t.equals(result.toString(), expectedItem.toString(), `should have the correct memory slot at ${offset}:${index}`)
          })
        })
      }

      // check for EVM return value
      if (test.return) {
        const expectedItem = hexToUint8Array(test.return)
        const result = testEnvironment.returnValue
        t.equals(result.toString(), expectedItem.toString(), 'should have correct return value')
      }
    })
  })
  t.end()
})

function buildTest (op, interface) {
  const funcs = compiler.resolveFunctions(new Set([op]))
  const linked = compiler.buildModule(funcs, [], [op])
  const wasm = compiler.compileWAST(linked)
  return Kernel.codeHandler(wasm, interface)
}

function hexToUint8Array(item, length) {
  return new Uint8Array(ethUtil.setLength(new Buffer(item.slice(2), 'hex'), 32)).reverse()
}

function setMemory(instance, value, start) {
  new Uint8Array(instance.exports.memory).set(value, start)
}

function getMemory(instance, start, end) {
  return new Uint8Array(instance.exports.memory).slice(start, end)
}
<|endoftext|>


<commit_before>//import //

var _elm_lang$core$Native_Platform = function() {

// EFFECT MANAGERS

var globalManagerInfo = {};

function makeManager(info, mainProcess)
{
	var router = {
		main: mainProcess,
		self: undefined
	};

	var tag = info.tag;
	var onEffects = info.onEffects;
	var onSelfMsg = info.onSelfMsg;

	function onMessage(msg, state)
	{
		if (msg.ctor === 'self')
		{
			return A3(onSelfMsg, router, msg._0, state);
		}

		var fx = msg._0;
		switch (tag)
		{
			case 'cmd':
				return A3(onEffects, router, fx.cmds, state);

			case 'sub':
				return A3(onEffects, router, fx.subs, state);

			case 'fx':
				return A4(onEffects, router, fx.cmds, fx.subs, state);
		}
	}

	var process = spawnLoop(info.init, onMessage);
	router.self = process;
	return process;
}

function sendToApp(router, msg)
{
	return A2(_elm_lang$core$Native_Scheduler.send, router.main, msg);
}

function sendToSelf(router, msg)
{
	return A2(_elm_lang$core$Native_Scheduler.send, router.self, {
		ctor: 'self',
		_0: msg
	});
}

// HELPER for STATEFUL LOOPS

function spawnLoop(init, onMessage)
{
	var andThen = _elm_lang$core$Native_Scheduler.andThen;

	function loop(state)
	{
		var handleMsg = _elm_lang$core$Native_Scheduler.receive(function(msg) {
			return onMessage(msg, state);
		});
		return A2(andThen, handleMsg, loop);
	}

	var task = A2(andThen, init, loop);

	return _elm_lang$core$Native_Scheduler.rawSpawn(task);
}

// PROGRAMS

function addPublicModule(object, name, main)
{
	object['fullscreen'] = main ? fullscreenFor(name, main) : mainIsUndefined(name);
}

function mainIsUndefined(name)
{
	return function() {
		throw new Error(
			'Cannot initialize module ' + name +
			' because it has no `main` value! What would I run?'
		);
	};
}

function fullscreenFor(moduleName, main)
{
	// main is a union type with one of the following tags: vdom, no-flags, flags
	// the follow section figures out what to do in each case.

	if (main.ctor === 'vdom')
	{
		return function fullscreen()
		{
			_elm_lang$virtual_dom$Native_VirtualDom.staticProgram(document.body, main._0);
		}
	}

	// Define: init, update, subscriptions, view, and makeRenderer

	var program, init;

	if (main.ctor === 'no-flags')
	{
		program = main._0;
		init = program.init;
	}
	else
	{
		program = main._1;

		var flagDecoder = main._0;
		var rawInit = program.init;

		init = function(flags)
		{
			var result = A2(_elm_lang$core$Native_Json.run, flagDecoder, flags);
			if (result.ctor === 'Err')
			{
				throw new Error('You trying to initialize module `' + moduleName + '` with unexpected flags.\n'
					+ 'When trying to convert the flags to a usable Elm value, I run into this problem:\n\n'
					+ result._0
				);
			}
			return rawInit(result._0);
		}
	}

	var update = program.update;
	var subscriptions = program.subscriptions;
	var view = program.view;
	var makeRenderer = program.renderer;

	// Actually initialize program

	return function fullscreen(flags)
	{
		// ambient state
		var managers = {};
		var renderer;

		// init and update state in main process
		var initApp = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
			var results = init(flags);
			var model = results._0;
			renderer = makeRenderer(document.body, enqueue, view(model));
			var cmds = results._1;
			var subs = subscriptions(model);
			dispatchEffects(managers, cmds, subs);
			callback(_elm_lang$core$Native_Scheduler.succeed(model));
		});

		function onMessage(msg, model)
		{
			return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
				var results = A2(update, msg, model);
				model = results._0;
				renderer.update(view(model));
				var cmds = results._1;
				var subs = subscriptions(model);
				dispatchEffects(managers, cmds, subs);
				callback(_elm_lang$core$Native_Scheduler.succeed(model));
			});
		}

		var mainProcess = spawnLoop(initApp, onMessage);

		function enqueue(msg)
		{
			_elm_lang$core$Native_Scheduler.rawSend(mainProcess, msg);
		}

		// setup all necessary effect managers
		for (var key in globalManagerInfo)
		{
			managers[key] = makeManager(globalManagerInfo[key], mainProcess);
		}

		// setup all foreign effects
		var foreign = {};

		for (var key in globalForeignCmds)
		{
			setupCmd(key, managers, foreign);
		}

		for (var key in globalForeignSubs)
		{
			setupSub(key, managers, foreign, mainProcess);
		}

		return { foreign: foreign };
	};
}

// BAGS

function leaf(home)
{
	return function(value)
	{
		return {
			type: 'leaf',
			home: home,
			value: value
		};
	};
}

function batch(list)
{
	return {
		type: 'node',
		branches: list
	};
}

function map(tagger, bag)
{
	return {
		type: 'map',
		tagger: tagger,
		tree: bag
	}
}

// PIPE BAGS INTO EFFECT MANAGERS

function dispatchEffects(managers, cmdBag, subBag)
{
	var effectsDict = {};
	gatherEffects(true, cmdBag, effectsDict, null);
	gatherEffects(false, subBag, effectsDict, null);

	for (var home in effectsDict)
	{
		_elm_lang$core$Native_Scheduler.rawSend(managers[home], {
			ctor: 'fx',
			_0: effectsDict[home]
		});
	}
}

function gatherEffects(isCmd, bag, effectsDict, taggers)
{
	switch (bag.type)
	{
		case 'leaf':
			var home = bag.home;
			var effect = toEffect(isCmd, home, taggers, bag.value);
			effectsDict[home] = insert(isCmd, effect, effectsDict[home]);
			return;

		case 'node':
			var list = bag.branches;
			while (list.ctor !== '[]')
			{
				gatherEffects(isCmd, list._0, effectsDict, taggers);
				list = list._1;
			}
			return;

		case 'map':
			gatherEffects(isCmd, bag.tree, effectsDict, {
				tagger: bag.tagger,
				rest: taggers
			});
			return;
	}
}

function toEffect(isCmd, home, taggers, value)
{
	function applyTaggers(x)
	{
		while (taggers)
		{
			x = taggers.tagger(x);
			taggers = taggers.rest;
		}
		return x;
	}

	var map = isCmd
		? globalManagerInfo[home].cmdMap
		: globalManagerInfo[home].subMap;

	return A2(map, applyTaggers, value)
}

function insert(isCmd, newEffect, effects)
{
	effects = effects || {
		cmds: _elm_lang$core$Native_List.Nil,
		subs: _elm_lang$core$Native_List.Nil
	};
	if (isCmd)
	{
		effects.cmds = _elm_lang$core$Native_List.Cons(newEffect, effects.cmds);
		return effects;
	}
	effects.subs = _elm_lang$core$Native_List.Cons(newEffect, effects.subs);
	return effects;
}

// FOREIGN EFFECT LEAFS

var globalForeignCmds = {};
var globalForeignSubs = {};

function foreignCmd(name, converter)
{
	checkForeignName(name);
	globalForeignCmds[name] = converter;
	return leaf(name);
}

function foreignSub(name, converter)
{
	checkForeignName(name);
	globalForeignSubs[name] = converter;
	return leaf(name);
}

function checkForeignName(name)
{
	if (name in globalForeignCmds || name in globalForeignSubs)
	{
		throw new Error('There can only be one foreign effect named `'
			+ name + '`, but your program has multiple.');
	}
}

// SET UP FOREIGN EFFECTS

function setupCmd(name, managers, foreign)
{
	var converter = globalForeignCmds[name];
	var subs = [];

	// internal dispatching

	var init = _elm_lang$core$Native_Scheduler.succeed(null);

	function onMessage(msg, state)
	{
		var cmdList = msg._0.cmds;

		while (cmdList.ctor !== '[]')
		{
			var value = converter(cmdList._0);

			for (var i = 0; i < subs.length; i++)
			{
				subs[i](value);
			}

			cmdList = cmdList._1;
		}

		return init;
	}

	managers[name] = spawnLoop(init, onMessage);
	globalManagerInfo[name] = { cmdMap: F2(foreignCmdMap) };

	// external subscriptions

	function subscribe(callback)
	{
		subs.push(callback);
	}

	function unsubscribe(callback)
	{
		var index = subs.indexOf(callback);
		if (index >= 0)
		{
			subs.splice(index, 1);
		}
	}

	foreign[name] = {
		subscribe: subscribe,
		unsubscribe: unsubscribe
	};
}

// setup foreign subscriptions
function setupSub(name, managers, foreign, mainProcess)
{
	var converter = globalForeignSubs[name];
	var subs = _elm_lang$core$Native_List.Nil;

	// internal

	var init = _elm_lang$core$Native_Scheduler.succeed(null);

	function onMessage(msg, state)
	{
		subs = msg._0.subs;
		return init;
	}

	managers[name] = spawnLoop(init, onMessage);
	globalManagerInfo[name] = { subMap: F2(foreignSubMap) };

	// external

	function send(value)
	{
		var result = A2(_elm_lang$core$Json_Decode$decodeValue, converter, value);
		if (result.ctor === 'Err')
		{
			throw new Error('Trying to send an unexpected type of value through `' + name + '`:\n' + result._0);
		}

		var value = result._0;
		var temp = subs;
		while (temp.ctor !== '[]')
		{
			_elm_lang$core$Native_Scheduler.rawSend(mainProcess, temp._0(value));
			temp = temp._1;
		}
	}

	foreign[name] = { send: send };
}

// FOREIGN MAPS

function foreignCmdMap(tagger, value)
{
	return value;
}

function foreignSubMap(tagger, finalTagger)
{
	return function(value)
	{
		return tagger(finalTagger(value));
	};
}

return {
	// routers
	sendToApp: F2(sendToApp),
	sendToSelf: F2(sendToSelf),

	// global setup
	globalManagerInfo: globalManagerInfo,
	addPublicModule: addPublicModule,

	// effect bags
	leaf: leaf,
	batch: batch,
	map: F2(map),
	foreignCmd: foreignCmd,
	foreignSub: foreignSub
};

}();<commit_msg>Add support for embedding programs in particular nodes<commit_after>//import //

var _elm_lang$core$Native_Platform = function() {

// EFFECT MANAGERS

var globalManagerInfo = {};

function makeManager(info, mainProcess)
{
	var router = {
		main: mainProcess,
		self: undefined
	};

	var tag = info.tag;
	var onEffects = info.onEffects;
	var onSelfMsg = info.onSelfMsg;

	function onMessage(msg, state)
	{
		if (msg.ctor === 'self')
		{
			return A3(onSelfMsg, router, msg._0, state);
		}

		var fx = msg._0;
		switch (tag)
		{
			case 'cmd':
				return A3(onEffects, router, fx.cmds, state);

			case 'sub':
				return A3(onEffects, router, fx.subs, state);

			case 'fx':
				return A4(onEffects, router, fx.cmds, fx.subs, state);
		}
	}

	var process = spawnLoop(info.init, onMessage);
	router.self = process;
	return process;
}

function sendToApp(router, msg)
{
	return A2(_elm_lang$core$Native_Scheduler.send, router.main, msg);
}

function sendToSelf(router, msg)
{
	return A2(_elm_lang$core$Native_Scheduler.send, router.self, {
		ctor: 'self',
		_0: msg
	});
}

// HELPER for STATEFUL LOOPS

function spawnLoop(init, onMessage)
{
	var andThen = _elm_lang$core$Native_Scheduler.andThen;

	function loop(state)
	{
		var handleMsg = _elm_lang$core$Native_Scheduler.receive(function(msg) {
			return onMessage(msg, state);
		});
		return A2(andThen, handleMsg, loop);
	}

	var task = A2(andThen, init, loop);

	return _elm_lang$core$Native_Scheduler.rawSpawn(task);
}

// PROGRAMS

function addPublicModule(object, name, main)
{
	if (!main)
	{
		var badTimes = mainIsUndefined(name);
		object['embed'] = badTimes;
		object['fullscreen'] = badTimes;
		return;
	}

	var embed = makeEmbed(name, main);
	object['embed'] = embed;
	object['fullscreen'] = function fullscreen(flags)
	{
		return embed(document.body, flags);
	};
}

function mainIsUndefined(name)
{
	return function()
	{
		throw new Error(
			'Cannot initialize module ' + name +
			' because it has no `main` value! What should I show on screen?'
		);
	};
}

function makeEmbed(moduleName, main)
{
	return function embed(rootDomNode, flags)
	{
		return makeEmbedHelp(moduleName, main, rootDomNode, flags);
	};
}

function makeEmbedHelp(moduleName, main, rootDomNode, flags)
{
	// main is a union type with one of the following tags: vdom, no-flags, flags
	// the follow section figures out what to do in each case.
	if (main.ctor === 'vdom')
	{
		_elm_lang$virtual_dom$Native_VirtualDom.staticProgram(rootDomNode, main._0);
		return {};
	}

	// Define: init, update, subscriptions, view, and makeRenderer
	var program = main._0;
	var init = makeInit(main);
	var update = program.update;
	var subscriptions = program.subscriptions;
	var view = program.view;
	var makeRenderer = program.renderer;

	// ambient state
	var managers = {};
	var renderer;

	// init and update state in main process
	var initApp = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
		var results = init(flags);
		var model = results._0;
		renderer = makeRenderer(rootDomNode, enqueue, view(model));
		var cmds = results._1;
		var subs = subscriptions(model);
		dispatchEffects(managers, cmds, subs);
		callback(_elm_lang$core$Native_Scheduler.succeed(model));
	});

	function onMessage(msg, model)
	{
		return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
			var results = A2(update, msg, model);
			model = results._0;
			renderer.update(view(model));
			var cmds = results._1;
			var subs = subscriptions(model);
			dispatchEffects(managers, cmds, subs);
			callback(_elm_lang$core$Native_Scheduler.succeed(model));
		});
	}

	var mainProcess = spawnLoop(initApp, onMessage);

	function enqueue(msg)
	{
		_elm_lang$core$Native_Scheduler.rawSend(mainProcess, msg);
	}

	// setup all necessary effect managers
	for (var key in globalManagerInfo)
	{
		managers[key] = makeManager(globalManagerInfo[key], mainProcess);
	}

	// setup all foreign effects
	var foreign = {};
	var hasForeigns = false;

	for (var key in globalForeignCmds)
	{
		hasForeigns = true;
		setupCmd(key, managers, foreign);
	}

	for (var key in globalForeignSubs)
	{
		hasForeigns = true;
		setupSub(key, managers, foreign, mainProcess);
	}

	return hasForeigns ? { foreign: foreign } : {};
}

function makeInit(main)
{
	var rawInit = main._0.init;

	if (main.ctor === 'no-flags')
	{
		return rawInit;
	}

	var flagDecoder = main._1;

	return function init(flags)
	{
		var result = A2(_elm_lang$core$Native_Json.run, flagDecoder, flags);
		if (result.ctor === 'Err')
		{
			throw new Error('You trying to initialize module `' + moduleName + '` with unexpected flags.\n'
				+ 'When trying to convert the flags to a usable Elm value, I run into this problem:\n\n'
				+ result._0
			);
		}
		return rawInit(result._0);
	}
}

// BAGS

function leaf(home)
{
	return function(value)
	{
		return {
			type: 'leaf',
			home: home,
			value: value
		};
	};
}

function batch(list)
{
	return {
		type: 'node',
		branches: list
	};
}

function map(tagger, bag)
{
	return {
		type: 'map',
		tagger: tagger,
		tree: bag
	}
}

// PIPE BAGS INTO EFFECT MANAGERS

function dispatchEffects(managers, cmdBag, subBag)
{
	var effectsDict = {};
	gatherEffects(true, cmdBag, effectsDict, null);
	gatherEffects(false, subBag, effectsDict, null);

	for (var home in effectsDict)
	{
		_elm_lang$core$Native_Scheduler.rawSend(managers[home], {
			ctor: 'fx',
			_0: effectsDict[home]
		});
	}
}

function gatherEffects(isCmd, bag, effectsDict, taggers)
{
	switch (bag.type)
	{
		case 'leaf':
			var home = bag.home;
			var effect = toEffect(isCmd, home, taggers, bag.value);
			effectsDict[home] = insert(isCmd, effect, effectsDict[home]);
			return;

		case 'node':
			var list = bag.branches;
			while (list.ctor !== '[]')
			{
				gatherEffects(isCmd, list._0, effectsDict, taggers);
				list = list._1;
			}
			return;

		case 'map':
			gatherEffects(isCmd, bag.tree, effectsDict, {
				tagger: bag.tagger,
				rest: taggers
			});
			return;
	}
}

function toEffect(isCmd, home, taggers, value)
{
	function applyTaggers(x)
	{
		while (taggers)
		{
			x = taggers.tagger(x);
			taggers = taggers.rest;
		}
		return x;
	}

	var map = isCmd
		? globalManagerInfo[home].cmdMap
		: globalManagerInfo[home].subMap;

	return A2(map, applyTaggers, value)
}

function insert(isCmd, newEffect, effects)
{
	effects = effects || {
		cmds: _elm_lang$core$Native_List.Nil,
		subs: _elm_lang$core$Native_List.Nil
	};
	if (isCmd)
	{
		effects.cmds = _elm_lang$core$Native_List.Cons(newEffect, effects.cmds);
		return effects;
	}
	effects.subs = _elm_lang$core$Native_List.Cons(newEffect, effects.subs);
	return effects;
}

// FOREIGN EFFECT LEAFS

var globalForeignCmds = {};
var globalForeignSubs = {};

function foreignCmd(name, converter)
{
	checkForeignName(name);
	globalForeignCmds[name] = converter;
	return leaf(name);
}

function foreignSub(name, converter)
{
	checkForeignName(name);
	globalForeignSubs[name] = converter;
	return leaf(name);
}

function checkForeignName(name)
{
	if (name in globalForeignCmds || name in globalForeignSubs)
	{
		throw new Error('There can only be one foreign effect named `'
			+ name + '`, but your program has multiple.');
	}
}

// SET UP FOREIGN EFFECTS

function setupCmd(name, managers, foreign)
{
	var converter = globalForeignCmds[name];
	var subs = [];

	// internal dispatching

	var init = _elm_lang$core$Native_Scheduler.succeed(null);

	function onMessage(msg, state)
	{
		var cmdList = msg._0.cmds;

		while (cmdList.ctor !== '[]')
		{
			var value = converter(cmdList._0);

			for (var i = 0; i < subs.length; i++)
			{
				subs[i](value);
			}

			cmdList = cmdList._1;
		}

		return init;
	}

	managers[name] = spawnLoop(init, onMessage);
	globalManagerInfo[name] = { cmdMap: F2(foreignCmdMap) };

	// external subscriptions

	function subscribe(callback)
	{
		subs.push(callback);
	}

	function unsubscribe(callback)
	{
		var index = subs.indexOf(callback);
		if (index >= 0)
		{
			subs.splice(index, 1);
		}
	}

	foreign[name] = {
		subscribe: subscribe,
		unsubscribe: unsubscribe
	};
}

// setup foreign subscriptions
function setupSub(name, managers, foreign, mainProcess)
{
	var converter = globalForeignSubs[name];
	var subs = _elm_lang$core$Native_List.Nil;

	// internal

	var init = _elm_lang$core$Native_Scheduler.succeed(null);

	function onMessage(msg, state)
	{
		subs = msg._0.subs;
		return init;
	}

	managers[name] = spawnLoop(init, onMessage);
	globalManagerInfo[name] = { subMap: F2(foreignSubMap) };

	// external

	function send(value)
	{
		var result = A2(_elm_lang$core$Json_Decode$decodeValue, converter, value);
		if (result.ctor === 'Err')
		{
			throw new Error('Trying to send an unexpected type of value through `' + name + '`:\n' + result._0);
		}

		var value = result._0;
		var temp = subs;
		while (temp.ctor !== '[]')
		{
			_elm_lang$core$Native_Scheduler.rawSend(mainProcess, temp._0(value));
			temp = temp._1;
		}
	}

	foreign[name] = { send: send };
}

// FOREIGN MAPS

function foreignCmdMap(tagger, value)
{
	return value;
}

function foreignSubMap(tagger, finalTagger)
{
	return function(value)
	{
		return tagger(finalTagger(value));
	};
}

return {
	// routers
	sendToApp: F2(sendToApp),
	sendToSelf: F2(sendToSelf),

	// global setup
	globalManagerInfo: globalManagerInfo,
	addPublicModule: addPublicModule,

	// effect bags
	leaf: leaf,
	batch: batch,
	map: F2(map),
	foreignCmd: foreignCmd,
	foreignSub: foreignSub
};

}();<|endoftext|>


<commit_before><commit_msg>removed misspelledWords trim formatter<commit_after><|endoftext|>