<commit_before>apiTokens = new Mongo.Collection("api-tokens");

function generateUniqueApiToken() {
    let token;
    do {
        token = Random.id(64);
    } while (apiTokens.findOne({token: token}))
    return token;
}

apiTokens.allow({
    // 'update'(userId){ return Kassebaum.authorizedUser(userId); },
    'remove'(userId){ return Kassebaum.authorizedUser(userId); }
});

Meteor.methods({
    'apiToken.create'(tokenAttributes) {
        if (!this.userId || !Roles.userIsInRole(this.userId, ['admin'])) {
            throw new Meteor.Error(401, "You are not authorized to add api tokens.")
        }

        const token = generateUniqueApiToken();

        apiToken = _.extend(tokenAttributes, {
            token,
            added: new Date().getTime(),
            addedBy: this.userId
        });
        apiTokens.insert(apiToken);
    },
    'apiToken.revoke'(token) {
        if (!this.userId || !Roles.userIsInRole(this.userId, ['admin'])) {
            throw new Meteor.Error(401, "You are not authorized to revoke api tokens.")
        }
        // const apiToken = apiTokens.findOne({token});
        apiTokens.update({token}, {$set: {revoked: true}});
    },
    'apiToken.confirm'(token){
        console.log(`Validating token ${token}`)
        if (!token) {                                   // Token not present
            return false
        } else if (apiTokens.findOne({token: token, revoked: {$ne: true}})) { // Token present and valid
            return true;
        } else {                                        // Token present but not valid
            return false;
        }
    }
})
<commit_msg>removed token validation message; minor code formatting<commit_after>apiTokens = new Mongo.Collection("api-tokens");

function generateUniqueApiToken() {
    let token;
    do {
        token = Random.id(64);
    } while (apiTokens.findOne({token: token}))
    return token;
}

apiTokens.allow({
    // 'update'(userId){ return Kassebaum.authorizedUser(userId); },
    'remove'(userId){ return Kassebaum.authorizedUser(userId); }
});

Meteor.methods({
    'apiToken.create'(tokenAttributes) {
        if (!this.userId || !Roles.userIsInRole(this.userId, ['admin'])) {
            throw new Meteor.Error(401, "You are not authorized to add api tokens.")
        }

        const token = generateUniqueApiToken();

        apiToken = _.extend(tokenAttributes, {
            token,
            added: new Date().getTime(),
            addedBy: this.userId
        });
        apiTokens.insert(apiToken);
    },
    'apiToken.revoke'(token) {
        if (!this.userId || !Roles.userIsInRole(this.userId, ['admin'])) {
            throw new Meteor.Error(401, "You are not authorized to revoke api tokens.")
        }
        // const apiToken = apiTokens.findOne({token});
        apiTokens.update({token}, {$set: {revoked: true}});
    },
    'apiToken.confirm'(token){
        if (!token) {
            // Token not present
            return false
        } else if (apiTokens.findOne({token: token, revoked: {$ne: true}})) {
            // Token present and valid
            return true;
        } else {
            // Token present but not valid
            return false;
        }
    }
})
<|endoftext|>


<commit_before>var User = require('../../../models/user'),
    arrayRest = require('../../../controllers/api/v0/nestedArrayRest')(User, 'quotes'),
    checkDB = require('../../../middleware/checkDB');

module.exports = function (app) {
  app.get('/api/v0/users/:id/quotes', checkDB, arrayRest.get);
  app.post('/api/v0/users/:id/quotes', checkDB, arrayRest.post);
  app.put('/api/v0/users/:id/quotes/:value', checkDB, arrayRest.put);
  app.delete('/api/v0/users/:id/quotes/:value', checkDB, arrayRest.delete);
};<commit_msg>All data what we need to change in docs we accept only as post data<commit_after>var User = require('../../../models/user'),
    arrayRest = require('../../../controllers/api/v0/nestedArrayRest')(User, 'quotes'),
    checkDB = require('../../../middleware/checkDB');

module.exports = function (app) {
  app.get('/api/v0/users/:id/quotes', checkDB, arrayRest.get);
  app.post('/api/v0/users/:id/quotes', checkDB, arrayRest.post);
  app.put('/api/v0/users/:id/quotes', checkDB, arrayRest.put);
  app.delete('/api/v0/users/:id/quotes', checkDB, arrayRest.delete);
};<|endoftext|>


<commit_before>/**
 * @fileoverview multiple choice states handling
 *
 * states:
 *   answers:
 *     - startTime
 *     - endTime
 *     - url
 *     - value
 *     - imageSetId: <id>
 *     - repeatValue
 *     - repeatStartTime
 *     - repeatEndTime
 *   toRepeatIds:
 *     - <id>
 */

const _ = require('lodash')
const path = require('path')
const util = require('./util')

function getExperimentAttr (paths, probRepeat) {
  let attr = {
    probRepeat,
    nImage: 0,
    nQuestionMax: 0,
    nRepeatQuestionMax: 0,
  }

  let imageSetIds = []
  for (let path of paths) {
    let imageSetId = util.extractId(path)
    if (!_.includes(imageSetIds, imageSetId)) {
      imageSetIds.push(imageSetId)
    }
  }

  attr.imageSetIds = imageSetIds

  attr.nQuestionMax = imageSetIds.length
  attr.nRepeatQuestionMax = Math.ceil(attr.probRepeat * attr.nQuestionMax)
  attr.nQuestion = attr.nQuestionMax + attr.nRepeatQuestionMax

  return attr
}

function lengthOfPropList (o, key) {
  if (!(key in o)) {
    return 0
  } else {
    console.log('lengthOfProplist')
    return o[key].length
  }
}

function getIntFromStr (s) {
  try {
    return parseInt(s.replace(/^\D+/g, ''))
  } catch (e) {
    return null
  }
}

function getChoices (experiment, participant) {
  let states = participant.states

  let isRepeat = false
  let unansweredIds = _.clone(experiment.attr.imageSetIds)
  for (let answer of states.answers) {
    _.remove(unansweredIds, id => id === answer.imageSetId)
  }
  if (unansweredIds.length === 0) {
    if (lengthOfPropList(states, 'toRepeatIds')) {
      isRepeat = true
    }
  } else {
    if (lengthOfPropList(states, 'toRepeatIds')) {
      // Here is the random probability to do a repeat
      if (Math.random() <= experiment.attr.probRepeat) {
        isRepeat = true
      }
    }
  }
  console.log('> handlers.publicGetNextChoice repeat',
    isRepeat, experiment.attr.nRepeatQuestionMax, unansweredIds)

  let qualificationIds = _.filter(
    unansweredIds, i => _.startsWith(i.toLowerCase(), 'test'))
  qualificationIds = _.sortBy(qualificationIds, i => getIntFromStr(i))
  console.log('> getChoices testIds', qualificationIds)

  let imageSetId
  if (!isRepeat) {
    if (qualificationIds.length > 0) {
      imageSetId = qualificationIds[0]
    } else {
      imageSetId = unansweredIds[Math.floor(Math.random() * unansweredIds.length)]
    }
  } else {
    imageSetId = _.shuffle(states.toRepeatIds)[0]
  }

  let question
  let choices = []
  for (let url of _.map(experiment.images, 'url')) {
    if (util.extractId(url) !== imageSetId) {
      continue
    }
    if (_.includes(url, 'question')) {
      question = {
        isRepeat,
        url,
        imageSetId,
        value: _.last(path.parse(url).name.split('_'))
      }
    } else {
      choices.push({
        isRepeat,
        startTime: util.getCurrentTimeStr(),
        endTime: null,
        url,
        imageSetId,
        value: _.last(path.parse(url).name.split('_'))
      })
    }
  }
  return {question, choices}
}

function getCorrectValue(experiment, testId) {
  let image = _.find(experiment.images, image => {
    let isSameId = util.extractId(image.url) === testId
    let isQuestion = image.url.includes('question')
    return isSameId && isQuestion
  })
  return util.extractId(image.url, '_', 2)
}

function updateStatesToAttr (participant, experiment) {
  let experimentAttr = experiment.attr
  let attr = participant.attr
  let states = participant.states

  attr.nAnswer = 0
  attr.nRepeatAnswer = 0
  attr.nConsistentAnswer = 0
  attr.time = 0
  if (!_.isUndefined(states.answers)) {
    for (let answer of states.answers) {
      attr.time += util.getTimeInterval(answer)
      attr.nAnswer += 1
      if ('repeatValue' in answer) {
        attr.nAnswer += 1
        attr.nRepeatAnswer += 1
        if (answer.repeatValue === answer.value) {
          attr.nConsistentAnswer += 1
        }
      }
    }
  }
  attr.progress = attr.nAnswer / experimentAttr.nQuestion * 100

  attr.isDone = (attr.nAnswer >= experimentAttr.nQuestionMax) &&
    (attr.nRepeatAnswer >= experimentAttr.nRepeatQuestionMax)

  console.log('> handlers.updateStatesToAttr', experimentAttr)

  if (attr.isDone) {
    attr.status = 'done'
  } else if (attr.user === null) {
    attr.status = 'qualificationStart'
  } else {
    let unansweredIds = _.clone(experimentAttr.imageSetIds)
    for (let answer of states.answers) {
      _.remove(unansweredIds, id => id === answer.imageSetId)
    }
    let qualificationIds = _.filter(
      unansweredIds, i => _.startsWith(i.toLowerCase(), 'test'))
    if (qualificationIds.length > 0) {
      attr.status = 'qualifying'
    } else if (attr.user.isQualified) {
      attr.status = 'running'
    } else {
      attr.status = 'running'
      if (_.isUndefined(states.toRepeatIds)) {
        states.toRepeatIds = []
      }
      if (states.toRepeatIds.length === 0) {
        let nQualificationFail = 0
        let qualificationIds = _.filter(
          experimentAttr.imageSetIds, i => _.startsWith(i.toLowerCase(), 'test'))
        for (let testId of qualificationIds) {
          let answer = _.find(states.answers, a => a.imageSetId === testId)
          if (!(getCorrectValue(experiment, testId) === answer.value)) {
            nQualificationFail += 1
          }
        }
        if (nQualificationFail > 0) {
          attr.status = 'qualificationFailed'
        } else {
          attr.status = 'start'
        }
      }
    }
  }
}

function getNewStates (experiment) {
  return {
    answers: [],
    toRepeatIds: []
  }
}

function pushToListProp (o, key, item) {
  if (!(key in o)) {
    o[key] = []
  }
  o[key].push(item)
}

function makeChoice(states, answer) {
  answer.endTime = util.getCurrentTimeStr()
  if (!answer.isRepeat) {
    states.answers.push(answer)
    console.log(answer)
    if (!_.startsWith(answer.imageSetId.toLowerCase(), 'test')) {
      pushToListProp(states, 'toRepeatIds', answer.imageSetId)
    }
  } else {
    let originalAnswer = _.find(states.answers, a => a.imageSetId === answer.imageSetId)
    _.remove(states.toRepeatIds, id => id === answer.imageSetId)
    console.log(answer, originalAnswer, states.toRepeatIds)
    originalAnswer.repeatValue = answer.value
  }
}

module.exports = {
  getExperimentAttr,
  getChoices,
  getNewStates,
  makeChoice,
  updateStatesToAttr
}
<commit_msg>refacotred check qualification fail<commit_after>/**
 * @fileoverview multiple choice states handling
 *
 * states:
 *   answers:
 *     - startTime
 *     - endTime
 *     - url
 *     - value
 *     - imageSetId: <id>
 *     - repeatValue
 *     - repeatStartTime
 *     - repeatEndTime
 *   toRepeatIds:
 *     - <id>
 */

const _ = require('lodash')
const path = require('path')
const util = require('./util')

function getExperimentAttr (paths, probRepeat) {
  let attr = {
    probRepeat,
    nImage: 0,
    nQuestionMax: 0,
    nRepeatQuestionMax: 0,
  }

  let imageSetIds = []
  for (let path of paths) {
    let imageSetId = util.extractId(path)
    if (!_.includes(imageSetIds, imageSetId)) {
      imageSetIds.push(imageSetId)
    }
  }

  attr.imageSetIds = imageSetIds

  attr.nQuestionMax = imageSetIds.length
  attr.nRepeatQuestionMax = Math.ceil(attr.probRepeat * attr.nQuestionMax)
  attr.nQuestion = attr.nQuestionMax + attr.nRepeatQuestionMax

  return attr
}

function lengthOfPropList (o, key) {
  if (!(key in o)) {
    return 0
  } else {
    console.log('lengthOfProplist')
    return o[key].length
  }
}

function getIntFromStr (s) {
  try {
    return parseInt(s.replace(/^\D+/g, ''))
  } catch (e) {
    return null
  }
}

function getChoices (experiment, participant) {
  let states = participant.states

  let isRepeat = false
  let unansweredIds = _.clone(experiment.attr.imageSetIds)
  for (let answer of states.answers) {
    _.remove(unansweredIds, id => id === answer.imageSetId)
  }
  if (unansweredIds.length === 0) {
    if (lengthOfPropList(states, 'toRepeatIds')) {
      isRepeat = true
    }
  } else {
    if (lengthOfPropList(states, 'toRepeatIds')) {
      // Here is the random probability to do a repeat
      if (Math.random() <= experiment.attr.probRepeat) {
        isRepeat = true
      }
    }
  }
  console.log('> handlers.publicGetNextChoice repeat',
    isRepeat, experiment.attr.nRepeatQuestionMax, unansweredIds)

  let qualificationIds = _.filter(
    unansweredIds, i => _.startsWith(i.toLowerCase(), 'test'))
  qualificationIds = _.sortBy(qualificationIds, i => getIntFromStr(i))
  console.log('> getChoices testIds', qualificationIds)

  let imageSetId
  if (!isRepeat) {
    if (qualificationIds.length > 0) {
      imageSetId = qualificationIds[0]
    } else {
      imageSetId = unansweredIds[Math.floor(Math.random() * unansweredIds.length)]
    }
  } else {
    imageSetId = _.shuffle(states.toRepeatIds)[0]
  }

  let question
  let choices = []
  for (let url of _.map(experiment.images, 'url')) {
    if (util.extractId(url) !== imageSetId) {
      continue
    }
    if (_.includes(url, 'question')) {
      question = {
        isRepeat,
        url,
        imageSetId,
        value: _.last(path.parse(url).name.split('_'))
      }
    } else {
      choices.push({
        isRepeat,
        startTime: util.getCurrentTimeStr(),
        endTime: null,
        url,
        imageSetId,
        value: _.last(path.parse(url).name.split('_'))
      })
    }
  }
  return {question, choices}
}

function checkQualificationFail (experiment, states) {

  function getCorrectValue (experiment, testId) {
    let image = _.find(experiment.images, image => {
      let isSameId = util.extractId(image.url) === testId
      let isQuestion = image.url.includes('question')
      return isSameId && isQuestion
    })
    return util.extractId(image.url, '_', 2)
  }

  let nQualificationFail = 0
  let imageSetIds = experiment.attr.imageSetIds
  let qualificationIds = _.filter(imageSetIds, i => _.startsWith(i.toLowerCase(), 'test'))
  for (let testId of qualificationIds) {
    let answer = _.find(states.answers, a => a.imageSetId === testId)
    if (!(getCorrectValue(experiment, testId) === answer.value)) {
      nQualificationFail += 1
    }
  }
  return (nQualificationFail > 0)
}

function updateStatesToAttr (participant, experiment) {
  let experimentAttr = experiment.attr
  let attr = participant.attr
  let states = participant.states

  attr.nAnswer = 0
  attr.nRepeatAnswer = 0
  attr.nConsistentAnswer = 0
  attr.time = 0
  if (!_.isUndefined(states.answers)) {
    for (let answer of states.answers) {
      attr.time += util.getTimeInterval(answer)
      attr.nAnswer += 1
      if ('repeatValue' in answer) {
        attr.nAnswer += 1
        attr.nRepeatAnswer += 1
        if (answer.repeatValue === answer.value) {
          attr.nConsistentAnswer += 1
        }
      }
    }
  }
  attr.progress = attr.nAnswer / experimentAttr.nQuestion * 100

  attr.isDone = (attr.nAnswer >= experimentAttr.nQuestionMax) &&
    (attr.nRepeatAnswer >= experimentAttr.nRepeatQuestionMax)

  if (_.isUndefined(states.toRepeatIds)) {
    states.toRepeatIds = []
  }

  console.log('> handlers.updateStatesToAttr', experimentAttr)

  if (attr.isDone) {
    attr.status = 'done'
  } else if (attr.user === null) {
    attr.status = 'qualificationStart'
  } else {
    let unansweredIds = _.clone(experimentAttr.imageSetIds)
    for (let answer of states.answers) {
      _.remove(unansweredIds, id => id === answer.imageSetId)
    }
    let qualificationIds = _.filter(
      unansweredIds, i => _.startsWith(i.toLowerCase(), 'test'))
    if (qualificationIds.length > 0) {
      attr.status = 'qualifying'
    } else if (attr.user.isQualified) {
      attr.status = 'running'
    } else {
      attr.status = 'running'
      if (states.toRepeatIds.length === 0) {
        if (checkQualificationFail(experiment, states)) {
          attr.status = 'qualificationFailed'
        } else {
          attr.status = 'start'
        }
      }
    }
  }
}

function getNewStates (experiment) {
  return {
    answers: [],
    toRepeatIds: []
  }
}

function pushToListProp (o, key, item) {
  if (!(key in o)) {
    o[key] = []
  }
  o[key].push(item)
}

function makeChoice(states, answer) {
  answer.endTime = util.getCurrentTimeStr()
  if (!answer.isRepeat) {
    states.answers.push(answer)
    console.log(answer)
    if (!_.startsWith(answer.imageSetId.toLowerCase(), 'test')) {
      pushToListProp(states, 'toRepeatIds', answer.imageSetId)
    }
  } else {
    let originalAnswer = _.find(states.answers, a => a.imageSetId === answer.imageSetId)
    _.remove(states.toRepeatIds, id => id === answer.imageSetId)
    console.log(answer, originalAnswer, states.toRepeatIds)
    originalAnswer.repeatValue = answer.value
  }
}

module.exports = {
  getExperimentAttr,
  getChoices,
  getNewStates,
  makeChoice,
  updateStatesToAttr
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var express = require('express')
var autobahn = require('autobahn')
var PeerServer = require('peer').PeerServer

var app = express()
var peer_server = new PeerServer({port: 9000})

app.use('/', express.static(__dirname + '/../client_pubsub'));
app.listen(9001);

var connection = new autobahn.Connection({
    url: "ws://localhost:8080/ws",
    realm: "peerinstruction"
})

var students = Array()
var instructors = Array()
var rooms = {}

var BROADCAST_MODE = 2
var GROUP_MODE = 3
var mode = BROADCAST_MODE

connection.onopen = function(session) {
    console.log("Autobahn connection opened.")

    // now instead of writing `com.peerinstruction.method` simply use
    // `api:method`
    session.prefix("api", "com.peerinstruction")

    // when a new student arrives, add them to the array and redraw DOM list
    session.subscribe("api:new_student", function(args, kwargs, details) {
        console.log("Event: new_student")
        students.push(kwargs["user_id"])
    })

    // when student leaves, remove them from the array and redraw DOM list
    session.subscribe("api:student_gone", function(args, kwargs, details) {
        console.log("Event: student_gone")

        // remove 1 element starting at index of the leaving user
        idx = students.indexOf(kwargs["user_id"])
        if (idx != -1) students.splice(idx, 1)
    })

    // when a new instructor arrives, add them to the array and redraw DOM list
    session.subscribe("api:new_instructor", function(args, kwargs, details) {
        console.log("Event: new_instructor")
        instructors.push(kwargs["user_id"])
    })

    // when instructor leaves, remove them from the array and redraw DOM list
    session.subscribe("api:instructor_gone", function(args, kwargs, details) {
        console.log("Event: instructor_gone")

        // remove 1 element starting at index of the leaving user
        idx = instructors.indexOf(kwargs["user_id"])
        if (idx != -1) instructors.splice(idx, 1)
    })

    // two simple RPCs for newcomers
    session.register("api:get_students_list", function(args, kwargs, details) {
        console.log("Event: receive students list via RPC")
        return students
    })
    session.register("api:get_instructors_list", function(args, kwargs, details) {
        console.log("Event: receive instructors list via RPC")
        return instructors
    })
    // and one RPC to get the current mode of the service
    session.register("api:get_working_mode", function(args, kwargs, details) {
        console.log("Event: receive currently operating mode")
        return mode
    })

    // first step to split students into smaller groups is to initialize
    // split-mode - via this RPC command
    session.register("api:init_split_mode", function(args, kwargs, details) {
        console.log("Event: some instructor initialized split mode with the size of",
                    kwargs["size"])
        if (mode == GROUP_MODE)
            throw new autobahn.Error("api:mode_change_error")

        mode = GROUP_MODE

        // in worst case (odd number of students) there's one student without
        // peers
        var students_per_room = kwargs["size"] || 2

        // put students into rooms
        rooms = {}
        students_count = students.length
        var j = 0
        for (var i = 0; i < students_count; i += students_per_room) {
            rooms["room" + j] = students.slice(i, i + students_per_room)
            j++
        }

        // if in the last room there's only one student, move them to the
        // precedent room
        if (rooms["room" + (j - 1)].length == 1) {
            lone_student = rooms["room" + (j - 1)].pop()
            rooms["room" + (j - 2)].push(lone_student)
        }

        // announce split mode to every peer (including instructors)
        session.publish("api:split_mode_enabled")
        session.publish("api:mode_changed", [mode], {mode: mode})

        return mode  // mode is 2 or 3, if we get 0 then it means errors
    })

    // any instructor is allowed to deactivate split-mode by simply invoking
    // this RPC command
    session.register("api:end_split_mode", function(args, kwargs, details) {
        console.log("Event: some instructor ended split mode")
        // TODO: check if the mode hasn't been disabled before

        mode = BROADCAST_MODE
        rooms = {}

        // announce end of split mode to every peer (including instructors)
        session.publish("api:split_mode_disabled")
        session.publish("api:mode_changed", [mode], {mode: mode})

        return mode  // mode is 2 or 3, if we get 0 then it means errors
    })

    session.register("api:get_room_information", function(args, kwargs, details) {
        console.log("Event: some student wants to know their room")

        user_id = kwargs["user_id"]
        room_names = Object.keys(rooms)

        for (var i = 0; i < room_names.length; i++) {
            room = room_names[i]
            if (rooms[room].indexOf(user_id) != -1) {
                return rooms[room]
            }
        }
        return false
    })
}

console.log("Server listening on http://localhost:9001/")
connection.open()
<commit_msg>Faster room information for students after split<commit_after>#!/usr/bin/env node

var express = require('express')
var autobahn = require('autobahn')
var PeerServer = require('peer').PeerServer

var app = express()
var peer_server = new PeerServer({port: 9000})

app.use('/', express.static(__dirname + '/../client_pubsub'));
app.listen(9001);

var connection = new autobahn.Connection({
    url: "ws://localhost:8080/ws",
    realm: "peerinstruction"
})

var students = Array()
var instructors = Array()
var rooms = {}  // rooms contain arrays of students
var students_rooms = {}  // student->room relation
// for example, when rooms["room1"] = Array("student1", "student2")
// then students_rooms["student1"] = "room1" and
// students_rooms["student2"] = "room1"

var BROADCAST_MODE = 2
var GROUP_MODE = 3
var mode = BROADCAST_MODE

connection.onopen = function(session) {
    console.log("Autobahn connection opened.")

    // now instead of writing `com.peerinstruction.method` simply use
    // `api:method`
    session.prefix("api", "com.peerinstruction")

    // when a new student arrives, add them to the array and redraw DOM list
    session.subscribe("api:new_student", function(args, kwargs, details) {
        console.log("Event: new_student")
        students.push(kwargs["user_id"])
    })

    // when student leaves, remove them from the array and redraw DOM list
    session.subscribe("api:student_gone", function(args, kwargs, details) {
        console.log("Event: student_gone")

        // remove 1 element starting at index of the leaving user
        idx = students.indexOf(kwargs["user_id"])
        if (idx != -1) students.splice(idx, 1)
    })

    // when a new instructor arrives, add them to the array and redraw DOM list
    session.subscribe("api:new_instructor", function(args, kwargs, details) {
        console.log("Event: new_instructor")
        instructors.push(kwargs["user_id"])
    })

    // when instructor leaves, remove them from the array and redraw DOM list
    session.subscribe("api:instructor_gone", function(args, kwargs, details) {
        console.log("Event: instructor_gone")

        // remove 1 element starting at index of the leaving user
        idx = instructors.indexOf(kwargs["user_id"])
        if (idx != -1) instructors.splice(idx, 1)
    })

    // two simple RPCs for newcomers
    session.register("api:get_students_list", function(args, kwargs, details) {
        console.log("Event: receive students list via RPC")
        return students
    })
    session.register("api:get_instructors_list", function(args, kwargs, details) {
        console.log("Event: receive instructors list via RPC")
        return instructors
    })
    // and one RPC to get the current mode of the service
    session.register("api:get_working_mode", function(args, kwargs, details) {
        console.log("Event: receive currently operating mode")
        return mode
    })

    // first step to split students into smaller groups is to initialize
    // split-mode - via this RPC command
    session.register("api:init_split_mode", function(args, kwargs, details) {
        console.log("Event: some instructor initialized split mode with the size of",
                    kwargs["size"])
        if (mode == GROUP_MODE)
            throw new autobahn.Error("api:mode_change_error")

        mode = GROUP_MODE

        // in worst case (odd number of students) there's one student without
        // peers
        var students_per_room = kwargs["size"] || 2

        // put students into rooms
        rooms = {}
        students_count = students.length
        var j = 0
        for (var i = 0; i < students_count; i += students_per_room) {
            rooms["room" + j] = students.slice(i, i + students_per_room)

            // save student and corresponding room in students_rooms
            for (var k = i; k < i + students_per_room; k++) {
                student = students[k]
                students_rooms[student] = "room" + j
            }

            j++
        }

        // if in the last room there's only one student, move them to the
        // precedent room
        if (rooms["room" + (j - 1)].length == 1) {
            lone_student = rooms["room" + (j - 1)].pop()
            rooms["room" + (j - 2)].push(lone_student)
            students_rooms[lone_student] = "room" + (j - 2)
        }

        // announce split mode to every peer (including instructors)
        session.publish("api:split_mode_enabled")
        session.publish("api:mode_changed", [mode], {mode: mode})

        return mode  // mode is 2 or 3, if we get 0 then it means errors
    })

    // any instructor is allowed to deactivate split-mode by simply invoking
    // this RPC command
    session.register("api:end_split_mode", function(args, kwargs, details) {
        console.log("Event: some instructor ended split mode")
        // TODO: check if the mode hasn't been disabled before

        mode = BROADCAST_MODE
        rooms = {}

        // announce end of split mode to every peer (including instructors)
        session.publish("api:split_mode_disabled")
        session.publish("api:mode_changed", [mode], {mode: mode})

        return mode  // mode is 2 or 3, if we get 0 then it means errors
    })

    session.register("api:get_room_information", function(args, kwargs, details) {
        console.log("Event: some student wants to know their room")

        user_id = kwargs["user_id"]
        result = students_rooms[user_id]
        if (result) return result
        else return false;

        // room_names = Object.keys(rooms)
        // for (var i = 0; i < room_names.length; i++) {
        //     room = room_names[i]
        //     if (rooms[room].indexOf(user_id) != -1) {
        //         return rooms[room]
        //     }
        // }
        // return false
    })
}

console.log("Server listening on http://localhost:9001/")
connection.open()
<|endoftext|>


<commit_before>import FlatButton from 'material-ui/FlatButton';
import Paper from 'material-ui/Paper';
import RaisedButton from 'material-ui/RaisedButton';
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import PropTypes from 'prop-types';
import React from 'react';
import { createRefetchContainer, graphql } from 'react-relay';

import theme from '../theme';

import EventItem from './EventItem';

const ITEMS_PER_PAGE = 20;

class Events extends React.Component {
    static propTypes = {
        organization: PropTypes.object,
        relay: PropTypes.object,
    }

    static childContextTypes = {
        muiTheme: PropTypes.object.isRequired,
    }

    constructor(props) {
        super(props);
        this.muiTheme = getMuiTheme(theme);
    }

    getChildContext() {
        return { muiTheme: this.muiTheme };
    }

    loadMoreEvents = () => {
        this.props.relay.refetch((variables) => {
            return {
                showItems: variables.showItems + ITEMS_PER_PAGE,
            };
        });
    }

    render() {
        const { organization } = this.props;
        const { events } = organization;
        return (
            <Paper className="row">

Aktiviteter

                    <Toolbar style={{ backgroundColor: theme.palette.fullWhite }}>
                        <ToolbarGroup lastChild>
                            <FlatButton
                                label="Kalenderfil"
                                href={`webcal://${organization.webdomain}/events/public.ics`}
                            />
                        </ToolbarGroup>
                    </Toolbar>

                    {events.edges.map((edge) => {
                        return (
                            <EventItem
                                key={edge.node.id}
                                event={edge.node}
                            />
                        );
                    })}

                {events.pageInfo.hasNextPage
                    ? <RaisedButton
                        primary
                        onClick={this.loadMoreEvents}
                        label="Mer"
                    />
                    : null
                }
            </Paper>
        );
    }
}

export default createRefetchContainer(
    Events,
    {
        organization: graphql`
        fragment Events_organization on Organization
        @argumentDefinitions(
            showItems: {type: "Int", defaultValue: 20}
        )
        {
            id
            memberGroup {
                id
            }
            webdomain
            events(first: $showItems) {
                edges {
                    node {
                        id
                        ...EventItem_event
                    }
                }
                pageInfo {
                    hasNextPage
                }
            }
        }`,
    },
    graphql`
    query EventsRefetchQuery($showItems: Int) {
        organization {
            ...Events_organization @arguments(showItems: $showItems)
        }
    }`,
);
<commit_msg>Link to help page (only existing in production)<commit_after>import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import Paper from 'material-ui/Paper';
import RaisedButton from 'material-ui/RaisedButton';
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import ActionHelp from 'material-ui/svg-icons/action/help';
import PropTypes from 'prop-types';
import React from 'react';
import { createRefetchContainer, graphql } from 'react-relay';

import theme from '../theme';

import EventItem from './EventItem';

const ITEMS_PER_PAGE = 20;

class Events extends React.Component {
    static propTypes = {
        organization: PropTypes.object,
        relay: PropTypes.object,
    }

    static childContextTypes = {
        muiTheme: PropTypes.object.isRequired,
    }

    constructor(props) {
        super(props);
        this.muiTheme = getMuiTheme(theme);
    }

    getChildContext() {
        return { muiTheme: this.muiTheme };
    }

    loadMoreEvents = () => {
        this.props.relay.refetch((variables) => {
            return {
                showItems: variables.showItems + ITEMS_PER_PAGE,
            };
        });
    }

    render() {
        const { organization } = this.props;
        const { events } = organization;
        return (
            <Paper className="row">

Aktiviteter

                    <Toolbar style={{ backgroundColor: theme.palette.fullWhite }}>
                        <ToolbarGroup lastChild>
                            <FlatButton
                                label="Abbonner på kalender"
                                href={`webcal://${organization.webdomain}/events/public.ics`}
                            />
                            <IconButton
                                href="/hjelp-om-aktiviteter"
                            >
                                <ActionHelp />
                            </IconButton>
                        </ToolbarGroup>
                    </Toolbar>

                    {events.edges.map((edge) => {
                        return (
                            <EventItem
                                key={edge.node.id}
                                event={edge.node}
                            />
                        );
                    })}

                {events.pageInfo.hasNextPage
                    ? <RaisedButton
                        primary
                        onClick={this.loadMoreEvents}
                        label="Mer"
                    />
                    : null
                }
            </Paper>
        );
    }
}

export default createRefetchContainer(
    Events,
    {
        organization: graphql`
        fragment Events_organization on Organization
        @argumentDefinitions(
            showItems: {type: "Int", defaultValue: 20}
        )
        {
            id
            memberGroup {
                id
            }
            webdomain
            events(first: $showItems) {
                edges {
                    node {
                        id
                        ...EventItem_event
                    }
                }
                pageInfo {
                    hasNextPage
                }
            }
        }`,
    },
    graphql`
    query EventsRefetchQuery($showItems: Int) {
        organization {
            ...Events_organization @arguments(showItems: $showItems)
        }
    }`,
);
<|endoftext|>


<commit_before>/* Main background script that communicates with the client and enables the extension */

//tracks the state of the connection to the content script {tabId : true | 'pending'} per tab if the tab's content script connected
//otherwise the id of the tab is not defined in the object
//allows to track which tabs need to be reloaded to make the extension work otherwise there is a port disconnection error
//this happens when chrome doesn't manage to inject a content script in a tab, it happens
var TabsConnected = {};

/* * * * storage daemons * * * */
initStorage();
syncStorage();

/* * * * chrome events * * * */

//called when the extension is first installed or the runtime is reloaded
chrome.runtime.onInstalled.addListener(function(info){
	refreshContentScripts();
	updateIcon();
});

//called when the extension is enabled
chrome.management.onEnabled.addListener(function(info){
	//enabling/disabling the extension doesn't reload the runtime which causes port disconnections
	//so we force runtime reload when enabling the extension it triggers the onInstalled hook
	chrome.runtime.reload();
});

//called when the client connects to the extension
chrome.runtime.onConnect.addListener(function(port){
	//as soons as a content script connects we trace the connection status here
	if(port.sender.tab){
		TabsConnected[port.sender.tab.id] = true;
		updateIcon();
	}

	port.onMessage.addListener(function(post) {

		if(typeof port.sender.tab == 'undefined'){
			return;
		}

		if(typeof post.data != 'undefined'){
			console.log(post.data);
		}

		var urlKey = port.sender.tab.url.replace(/([^#]*)#.*/, '$1');

		if(post.method == 'updateSettings'){

			updateSettings(urlKey, post.data);

		}

	});
});

//called when tab is reloaded
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){

	if(changeInfo.status == 'complete'){
		//if tab just opened we set the connection status to pending until client script connects
		TabsConnected[tab.id] = 'pending';

		var url = tab.url.replace(/([^#]*)#.*/, '$1');

		getSettings(url, function(settings){
			var port = chrome.tabs.connect(tab.id);
			port.postMessage({method : 'install', settings : settings, from : 'tab updated'});
			updateIcon();
		});
	}

});

//called when tab is activated
chrome.tabs.onActivated.addListener(function(activeInfo) {

	chrome.tabs.get(activeInfo.tabId, function(tab){
		var url = tab.url.replace(/([^#]*)#.*/, '$1');
		var port = chrome.tabs.connect(tab.id);

		//if tab just opened we set the connection status to pending until client script connects
		TabsConnected[tab.id] = 'pending';

		getSettings(url, function(settings){
			port.postMessage({method: "updateStatus", settings : settings, from : 'tab activated'});
		});

		updateIcon();

	});
});

//called when the icon is clicked
chrome.browserAction.onClicked.addListener(function(tab) {

 var urlKey = tab.url.replace(/([^#]*)#.*/, '$1');
 var port = chrome.tabs.connect(tab.id);
 var isHttp = urlKey.match(/^https?/);
 var isBlocked = urlKey.match(/chrome\.google\.com\/webstore/);

 //if it is still pending it means it never connected
 if(TabsConnected[tab.id] == 'pending'){
 	delete TabsConnected[tab.id];
 }

 if(!isHttp || isBlocked){
 	return;
 }

 getSettings(urlKey, init);

 function init(settings){

	 if(settings.enabled === true){
	 	settings.enabled = false;
	 }else{
	 	settings.enabled = true;
	 }

	updateSettings(urlKey, settings, function(){

		port.postMessage({method: "updateStatus", settings : settings, from : 'click on icon'});

		updateIcon();
	});
 }

});

//called when the storage is being changed
chrome.storage.onChanged.addListener(function(changes, areaName){

	//we put the changes in a queue that will later be ingested by the syncStorage daemon
	//we do this to avoid writing more than the allowed limit in the sync storage
	if(areaName == 'local'){
		storageSynced = false;
		storageChangesQueue.unshift(changes);
	}

});

/* * * * libraries * * * */

//gets the settings for a tab depending on its url
function getSettings(url, callback){

	getStorage(url, function(settings){
		//it gets a date once it is stored only
		//nothing is stored until the icon is clicked
		//so if the record is empty it has the following disabled settings
		if(isEmpty(settings)){
			settings = {
				enabled : false,
				m1Top : null,
				m2Top : null,
				url : url
			};
		}

		if(typeof callback == 'function'){
			callback(settings);
		}
	});
}

//writes the changed settings in the storage
//called when icon is clicked and when client asks to update the settings
function updateSettings(urlKey, changes, callback){
	getStorage(urlKey, function(settings){
		var save = {};

		for(var key in changes){
			if(changes.hasOwnProperty(key)){
				settings[key] = changes[key];
			}
		}

		settings['time'] = Date.now();
		settings['date'] = new Date().toString();

		save[urlKey] = settings;

		setStorage(save, function(){
			if(typeof callback == 'function'){
				callback();
			}
		});
	});
}

//gets the record from the storage
function getStorage(key, callback){

	//ideally the record should always come from the sync storage
	//but if it is not synced yet we get record from local storage
	if(storageSynced === true){
		chrome.storage.sync.get(key, onResult);
	}else{
		chrome.storage.local.get(key, onResult);
	}

	function onResult(res){
		try{
			if(chrome.runtime.lastError){
		 		console.log('error in setStorage getStorage');
				console.error(chrome.runtime.lastError);
			}
			//if nothing res = {}
			var result = res;

			if(typeof(res[key]) != 'undefined'){
				result = res[key];
			}

			if(typeof(callback) == 'function'){
				callback(result);
			}
		}
		catch(e){
			onStorageError(e);
		}
	}

}

//writes the settings in the local storage to avoid sync storage limitation
//local storage is not limited in any way
function setStorage(save, callback){
	chrome.storage.local.set(save, function(){
		try{
			if(chrome.runtime.lastError){
				console.log('error in setStorage');
		 		console.error(chrome.runtime.lastError);
		 		return;
		 	}

			if(typeof callback == 'function'){
				callback();
			}
		}
		catch(e){
			onStorageError(e);
		}
	});
}

function onStorageError(e){
	//if the content script is not reloaded when the extension is updated/reloaded
	//storage callbacks provoke a disconnected port error
	//this can happen when chrome doesn't manage to inject the content script it happens
	console.error(e);

	var msg = e.message;
	//should never happen if the content scripts are refreshed every enable/install
	//but sometimes it happens
	if(msg == 'Attempting to use a disconnected port object'){
		updateIcon({error : 'disconnected'});
	}
}

/* * * * icon updates * * * */

//called at every change of tab reloaded or activated
//it just updates the icon based on the current tab returned by the tab query
//the icon updates are independant from the client script
function updateIcon(params){

	chrome.tabs.query(
	  {currentWindow: true, active : true, windowType : 'normal'},
	  function(tabArray){
		var currentTab = tabArray[0];
		//don't change anything if there is no result => the current tab must be a devtools tab
		if(typeof currentTab == 'undefined'){
			return;
		}

		//otherwise update the icon
		var id = currentTab.id;
		var urlKey = currentTab.url.replace(/([^#]*)#.*/, '$1');
		var isHttp = urlKey.match(/^https?/);
		var isBlocked = urlKey.match(/chrome\.google\.com\/webstore/);

		//content scripts are blocked on the webstore
		if(!isHttp || isBlocked){
			setError('blocked');
			return;
		}

		//case when port disconnected from storage fcts
		if(params && params.error){
			delete TabsConnected[id];

			if(params.error == 'disconnected'){
				setError('reload');
			}

			return;
		}

		//case when tab exists but never connected
		if(typeof TabsConnected[id] == 'undefined'){
			setError('reload');
			return;
		}

		if(TabsConnected[id] == 'pending'){
			setDisabled();
		}

		getSettings(urlKey, function(settings){
			if(settings.enabled === true){
				setEnabled();
			}else{
				setDisabled();
			}
		});

	  }
	);
}

//sets the icon in error if some error happens
function setError(msg){
	var title = "";

	chrome.browserAction.setBadgeText({text : 'ERR'});
	chrome.browserAction.setBadgeBackgroundColor({color : '#B01100'});

	switch(msg){
		case 'reload':
			title = 'Please reload this tab to use Infometer';
			break;
		case 'blocked':
			chrome.browserAction.setBadgeText({text : 'OFF'});
			title = "Infometer doesn't work on this type of page";
			break;
		default :
			title = 'An error occured';
			break;
	}

	chrome.browserAction.setTitle({title : title});
}

//switches the icon on
function setEnabled(tab){
	 chrome.browserAction.setBadgeText({text : 'ON'});
	 chrome.browserAction.setBadgeBackgroundColor({color : '#fec603'});
	 chrome.browserAction.setTitle({title : 'Infometer is active'});
}

//switches the icon off
function setDisabled(){
	 chrome.browserAction.setBadgeText({text : 'OFF'});
	 chrome.browserAction.setBadgeBackgroundColor({color : [140,140,140,255]});
	 chrome.browserAction.setTitle({title : 'Infometer is inactive'});
}

/* * * * utils * * * */

//forces all tabs in all windows to rerun their content scripts
//used when extension is enabled or installed
function refreshContentScripts(){
	// Get all windows
	chrome.windows.getAll({
	    populate: true
	}, function (windows) {
	    var w = windows.length, currentWindow;
	    for( var i = 0; i < w; i++ ) {
	        currentWindow = windows[i];
	        var t = currentWindow.tabs.length, currentTab;
	        for( var j = 0; j < t; j++ ) {
	            currentTab = currentWindow.tabs[j];
	            // Skip chrome://
	            if( /^https?:/.test(currentTab.url) ) {
	                injectJsIntoTab(currentTab);
	            }
	        }
	    }
	});
}

//inject all content scripts in the manifest in a given tab
function injectJsIntoTab(tab) {
	chrome.manifest = chrome.app.getDetails();
    var scripts = chrome.manifest.content_scripts[0].js;
    var s = scripts.length;

    for( var i = 0; i < s; i++ ) {
        chrome.tabs.executeScript(tab.id, {
            file: scripts[i]
        }, function(){
        	if(i == s){
        		var port = chrome.tabs.connect(tab.id);
        		port.postMessage({method : 'install', from : 'injector'});
        	}
        });
    }
}

function isEmpty(obj){
	 for (var key in obj) {
	    if (hasOwnProperty.call(obj, key)) return false;
	 }

	 return true;
}
<commit_msg>fixes<commit_after>/* Main background script that communicates with the client and enables the extension */

//tracks the state of the connection to the content script {tabId : true | 'pending'} per tab if the tab's content script connected
//otherwise the id of the tab is not defined in the object
//allows to track which tabs need to be reloaded to make the extension work otherwise there is a port disconnection error
//this happens when chrome doesn't manage to inject a content script in a tab, it happens
var TabsConnected = {};

/* * * * storage daemons * * * */
initStorage();
syncStorage();

/* * * * chrome events * * * */

//called when the extension is first installed or the runtime is reloaded
chrome.runtime.onInstalled.addListener(function(info){
	refreshContentScripts();
	updateIcon();
});

//called when the extension is enabled
chrome.management.onEnabled.addListener(function(info){
	//enabling/disabling the extension doesn't reload the runtime which causes port disconnections
	//so we force runtime reload when enabling the extension it triggers the onInstalled hook
	chrome.runtime.reload();
});

//called when the client connects to the extension
chrome.runtime.onConnect.addListener(function(port){
	//as soons as a content script connects we trace the connection status here
	if(port.sender.tab){
		TabsConnected[port.sender.tab.id] = true;
		updateIcon();
	}

	port.onMessage.addListener(function(post) {

		if(typeof port.sender.tab == 'undefined'){
			return;
		}

		if(typeof post.data != 'undefined'){
			console.log(post.data);
		}

		var urlKey = port.sender.tab.url.replace(/([^#]*)#.*/, '$1');

		if(post.method == 'updateSettings'){

			updateSettings(urlKey, post.data);

		}

	});
});

//called when tab is reloaded
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){

	if(changeInfo.status == 'complete'){
		//if tab just opened we set the connection status to pending until client script connects
		TabsConnected[tab.id] = TabsConnected[tab.id] || 'pending';

		var url = tab.url.replace(/([^#]*)#.*/, '$1');

		getSettings(url, function(settings){
			var port = chrome.tabs.connect(tab.id);
			port.postMessage({method : 'install', settings : settings, from : 'tab updated'});
			updateIcon();
		});
	}

});

//called when tab is activated
chrome.tabs.onActivated.addListener(function(activeInfo) {

	chrome.tabs.get(activeInfo.tabId, function(tab){
		var url = tab.url.replace(/([^#]*)#.*/, '$1');
		var port = chrome.tabs.connect(tab.id);

		//if tab just opened we set the connection status to pending until client script connects
		TabsConnected[tab.id] = TabsConnected[tab.id] || 'pending';

		getSettings(url, function(settings){
			port.postMessage({method: "updateStatus", settings : settings, from : 'tab activated'});
		});

		updateIcon();

	});
});

//called when the icon is clicked
chrome.browserAction.onClicked.addListener(function(tab) {

 var urlKey = tab.url.replace(/([^#]*)#.*/, '$1');
 var port = chrome.tabs.connect(tab.id);
 var isHttp = urlKey.match(/^https?/);
 var isBlocked = urlKey.match(/chrome\.google\.com\/webstore/);

 //if it is still pending it means it never connected
 if(TabsConnected[tab.id] == 'pending'){
 	delete TabsConnected[tab.id];
 }

 if(!isHttp || isBlocked){
 	return;
 }

 getSettings(urlKey, init);

 function init(settings){

	 if(settings.enabled === true){
	 	settings.enabled = false;
	 }else{
	 	settings.enabled = true;
	 }

	updateSettings(urlKey, settings, function(){

		port.postMessage({method: "updateStatus", settings : settings, from : 'click on icon'});

		updateIcon();
	});
 }

});

//called when the storage is being changed
chrome.storage.onChanged.addListener(function(changes, areaName){

	//we put the changes in a queue that will later be ingested by the syncStorage daemon
	//we do this to avoid writing more than the allowed limit in the sync storage
	if(areaName == 'local'){
		storageSynced = false;
		storageChangesQueue.unshift(changes);
	}

});

/* * * * libraries * * * */

//gets the settings for a tab depending on its url
function getSettings(url, callback){

	getStorage(url, function(settings){
		//it gets a date once it is stored only
		//nothing is stored until the icon is clicked
		//so if the record is empty it has the following disabled settings
		if(isEmpty(settings)){
			settings = {
				enabled : false,
				m1Top : null,
				m2Top : null,
				url : url
			};
		}

		if(typeof callback == 'function'){
			callback(settings);
		}
	});
}

//writes the changed settings in the storage
//called when icon is clicked and when client asks to update the settings
function updateSettings(urlKey, changes, callback){
	getStorage(urlKey, function(settings){
		var save = {};

		for(var key in changes){
			if(changes.hasOwnProperty(key)){
				settings[key] = changes[key];
			}
		}

		settings['time'] = Date.now();
		settings['date'] = new Date().toString();

		save[urlKey] = settings;

		setStorage(save, function(){
			if(typeof callback == 'function'){
				callback();
			}
		});
	});
}

//gets the record from the storage
function getStorage(key, callback){

	//ideally the record should always come from the sync storage
	//but if it is not synced yet we get record from local storage
	if(storageSynced === true){
		chrome.storage.sync.get(key, onResult);
	}else{
		chrome.storage.local.get(key, onResult);
	}

	function onResult(res){
		try{
			if(chrome.runtime.lastError){
		 		console.log('error in setStorage getStorage');
				console.error(chrome.runtime.lastError);
			}
			//if nothing res = {}
			var result = res;

			if(typeof(res[key]) != 'undefined'){
				result = res[key];
			}

			if(typeof(callback) == 'function'){
				callback(result);
			}
		}
		catch(e){
			onStorageError(e);
		}
	}

}

//writes the settings in the local storage to avoid sync storage limitation
//local storage is not limited in any way
function setStorage(save, callback){
	chrome.storage.local.set(save, function(){
		try{
			if(chrome.runtime.lastError){
				console.log('error in setStorage');
		 		console.error(chrome.runtime.lastError);
		 		return;
		 	}

			if(typeof callback == 'function'){
				callback();
			}
		}
		catch(e){
			onStorageError(e);
		}
	});
}

function onStorageError(e){
	//if the content script is not reloaded when the extension is updated/reloaded
	//storage callbacks provoke a disconnected port error
	//this can happen when chrome doesn't manage to inject the content script it happens
	console.error(e);

	var msg = e.message;
	//should never happen if the content scripts are refreshed every enable/install
	//but sometimes it happens
	if(msg == 'Attempting to use a disconnected port object'){
		updateIcon({error : 'disconnected'});
	}
}

/* * * * icon updates * * * */

//called at every change of tab reloaded or activated
//it just updates the icon based on the current tab returned by the tab query
//the icon updates are independant from the client script
function updateIcon(params){

	chrome.tabs.query(
	  {currentWindow: true, active : true, windowType : 'normal'},
	  function(tabArray){
		var currentTab = tabArray[0];
		//don't change anything if there is no result => the current tab must be a devtools tab
		if(typeof currentTab == 'undefined'){
			return;
		}

		//otherwise update the icon
		var id = currentTab.id;
		var urlKey = currentTab.url.replace(/([^#]*)#.*/, '$1');
		var isHttp = urlKey.match(/^https?/);
		var isBlocked = urlKey.match(/chrome\.google\.com\/webstore/);

		//content scripts are blocked on the webstore
		if(!isHttp || isBlocked){
			setError('blocked');
			return;
		}

		//case when port disconnected from storage fcts
		if(params && params.error){
			delete TabsConnected[id];

			if(params.error == 'disconnected'){
				setError('reload');
			}

			return;
		}

		//case when tab exists but never connected
		if(typeof TabsConnected[id] == 'undefined'){
			setError('reload');
			return;
		}

		if(TabsConnected[id] == 'pending'){
			setDisabled();
		}

		getSettings(urlKey, function(settings){
			if(settings.enabled === true){
				setEnabled();
			}else{
				setDisabled();
			}
		});

	  }
	);
}

//sets the icon in error if some error happens
function setError(msg){
	var title = "";

	chrome.browserAction.setBadgeText({text : 'ERR'});
	chrome.browserAction.setBadgeBackgroundColor({color : '#B01100'});

	switch(msg){
		case 'reload':
			title = 'Please reload this tab to use Infometer';
			break;
		case 'blocked':
			chrome.browserAction.setBadgeText({text : 'OFF'});
			title = "Infometer doesn't work on this type of page";
			break;
		default :
			title = 'An error occured';
			break;
	}

	chrome.browserAction.setTitle({title : title});
}

//switches the icon on
function setEnabled(tab){
	 chrome.browserAction.setBadgeText({text : 'ON'});
	 chrome.browserAction.setBadgeBackgroundColor({color : '#fec603'});
	 chrome.browserAction.setTitle({title : 'Infometer is active'});
}

//switches the icon off
function setDisabled(){
	 chrome.browserAction.setBadgeText({text : 'OFF'});
	 chrome.browserAction.setBadgeBackgroundColor({color : [140,140,140,255]});
	 chrome.browserAction.setTitle({title : 'Infometer is inactive'});
}

/* * * * utils * * * */

//forces all tabs in all windows to rerun their content scripts
//used when extension is enabled or installed
function refreshContentScripts(){
	// Get all windows
	chrome.windows.getAll({
	    populate: true
	}, function (windows) {
	    var w = windows.length, currentWindow;
	    for( var i = 0; i < w; i++ ) {
	        currentWindow = windows[i];
	        var t = currentWindow.tabs.length, currentTab;
	        for( var j = 0; j < t; j++ ) {
	            currentTab = currentWindow.tabs[j];
	            // Skip chrome://
	            if( /^https?:/.test(currentTab.url) ) {
	                injectJsIntoTab(currentTab);
	            }
	        }
	    }
	});
}

//inject all content scripts in the manifest in a given tab
function injectJsIntoTab(tab) {
	chrome.manifest = chrome.app.getDetails();
    var scripts = chrome.manifest.content_scripts[0].js;
    var s = scripts.length;

    for( var i = 0; i < s; i++ ) {
        chrome.tabs.executeScript(tab.id, {
            file: scripts[i]
        }, function(){
        	if(i == s){
        		var port = chrome.tabs.connect(tab.id);
        		port.postMessage({method : 'install', from : 'injector'});
        	}
        });
    }
}

function isEmpty(obj){
	 for (var key in obj) {
	    if (hasOwnProperty.call(obj, key)) return false;
	 }

	 return true;
}
<|endoftext|>


<commit_before>window.$ = (function (window, document, fn, nsRegAndEvents, id, s_EventListener, s_MatchesSelector, i, j, k, l, $) {
  'use strict';
  $ = function (s, context) {
    return new $.i(s, context);
  };
  $.i = function (s, context) {
    fn.push.apply(this, !s ? fn : s.nodeType || s == window ? [s] : '' + s === s ? /</.test(s) ?
      ((i = document.createElement(context || 'q')).innerHTML = s, i.children) : (context && $(context)[0] || document).querySelectorAll(s) : /f/.test(typeof s) ? /c/.test(document.readyState) ? s() : $(document).on('DOMContentLoaded', s) : s);
  };
  $.i[l = 'prototype'] = ($.extend = function (obj) {
    k = arguments;
    for (i = 1; i < k.length; i++) {
      if (l = k[i]) {
        for (j in l) {
          if (Object.prototype.hasOwnProperty.call(l, j)) obj[j] = l[j];
        }
      }
    }
    return obj;
  }).call($, $.fn = $[l] = fn, {
    on: function (n, f) {
      // n = [eventName, nameSpace]
      n = n.split(nsRegAndEvents);
      this.map(function (item) {
        // item.b$ is balalaika_id for an element
        // i is eventName + id ("click75")
        // nsRegAndEvents[i] is array of events (eg all click events for element#75) ([[namespace, handler], [namespace, handler]])
        (nsRegAndEvents[i = n[0] + (item.b$ = item.b$ || ++id)] = nsRegAndEvents[i] || []).push([f, n[ 1 ]]);
        item['add' + s_EventListener](n[0], f);
      });
      return this;
    },
    off: function (n, f) {
      // n = [eventName, nameSpace]
      n = n.split(nsRegAndEvents);
      l = 'remove' + s_EventListener;
      this.map(function (item) {
        // k - array of events
        // item.b$ - balalaika_id for an element
        // n[0] + item.b$ - eventName + id ("click75")
        k = nsRegAndEvents[n[0] + item.b$];
        // if array of events exist then i = length of array of events
        if (i = k && k.length) {
          // while j = one of array of events
          while (j = k[--i]) {
            // if( no f and no namespace || f but no namespace || no f but namespace || f and namespace)
            if((!f || f == j[0]) && (!n[1] || n[1] == j[1])) {
              // item.removeEventListener(eventName, handler);
              item[l](n[0], j[0]);
              // remove event from array of events
              k.splice(i, 1);
            }
          }
        } else if (!n[1]) item[l](n[0], f); // if event added before using addEventListener, just remove it using item.removeEventListener(eventName, f)
      });
      return this;
    },
    is: function (s) {
      i = this[0];
      return (i.matches ||
        i['webkit' + s_MatchesSelector] ||
        i['moz' + s_MatchesSelector] ||
        i['ms' + s_MatchesSelector]).call(i, s);
    }
  });
  return $;
})(window, document, Array.prototype, /\.(.+)/, 0, 'EventListener', 'MatchesSelector');
<commit_msg>Actually, $ should extend *an instance of* Array.<commit_after>window.$ = (function (window, document, fn, nsRegAndEvents, id, s_EventListener, s_MatchesSelector, i, j, k, l, $) {
  'use strict';
  $ = function (s, context) {
    return new $.i(s, context);
  };
  $.i = function (s, context) {
    fn.push.apply(this, !s ? fn : s.nodeType || s == window ? [s] : '' + s === s ? /</.test(s) ?
      ((i = document.createElement(context || 'q')).innerHTML = s, i.children) : (context && $(context)[0] || document).querySelectorAll(s) : /f/.test(typeof s) ? /c/.test(document.readyState) ? s() : $(document).on('DOMContentLoaded', s) : s);
  };
  $.i[l = 'prototype'] = ($.extend = function (obj) {
    k = arguments;
    for (i = 1; i < k.length; i++) {
      if (l = k[i]) {
        for (j in l) {
          obj[j] = l[j];
        }
      }
    }
    return obj;
  }).call($, $.fn = $[l] = fn, {
    on: function (n, f) {
      // n = [eventName, nameSpace]
      n = n.split(nsRegAndEvents);
      this.map(function (item) {
        // item.b$ is balalaika_id for an element
        // i is eventName + id ("click75")
        // nsRegAndEvents[i] is array of events (eg all click events for element#75) ([[namespace, handler], [namespace, handler]])
        (nsRegAndEvents[i = n[0] + (item.b$ = item.b$ || ++id)] = nsRegAndEvents[i] || []).push([f, n[ 1 ]]);
        item['add' + s_EventListener](n[0], f);
      });
      return this;
    },
    off: function (n, f) {
      // n = [eventName, nameSpace]
      n = n.split(nsRegAndEvents);
      l = 'remove' + s_EventListener;
      this.map(function (item) {
        // k - array of events
        // item.b$ - balalaika_id for an element
        // n[0] + item.b$ - eventName + id ("click75")
        k = nsRegAndEvents[n[0] + item.b$];
        // if array of events exist then i = length of array of events
        if (i = k && k.length) {
          // while j = one of array of events
          while (j = k[--i]) {
            // if( no f and no namespace || f but no namespace || no f but namespace || f and namespace)
            if((!f || f == j[0]) && (!n[1] || n[1] == j[1])) {
              // item.removeEventListener(eventName, handler);
              item[l](n[0], j[0]);
              // remove event from array of events
              k.splice(i, 1);
            }
          }
        } else if (!n[1]) item[l](n[0], f); // if event added before using addEventListener, just remove it using item.removeEventListener(eventName, f)
      });
      return this;
    },
    is: function (s) {
      i = this[0];
      return (i.matches ||
        i['webkit' + s_MatchesSelector] ||
        i['moz' + s_MatchesSelector] ||
        i['ms' + s_MatchesSelector]).call(i, s);
    }
  });
  return $;
})(window, document, [], /\.(.+)/, 0, 'EventListener', 'MatchesSelector');
<|endoftext|>


<commit_before>'use strict'

const _isArray = require('lodash/isArray')
const _isFinite = require('lodash/isFinite')
const runExample = require('../util/run_example')

module.exports = runExample({
  name: 'ws2-sequencing',
  ws: { env: true, connect: true, auth: true }
}, async ({ ws, debug }) => {
  // Enables internal sequence tracking; an error will be emitted if there is a
  // seq # mis-match
  await ws.enableSequencing({ audit: true })

  if (!ws.isFlagEnabled(65536)) {
    throw new Error('seq enable succeeded, but flag not updated')
  }

  debug('sequencing enabled')

  await ws.subscribeTrades('tBTCUSD')

  ws.on('message', (msg) => {
    if (!_isArray(msg)) return // only array messages have sequence #s

    // auth seq number, available as the last element on chan 0 packets
    const authSeq = msg[0] === 0 && msg[1] !== 'hb'
      ? msg[msg.length - 1]
      : NaN

    // public seq number, last or 2nd to last element on all packets
    const seq = msg[0] === 0 && msg[1] !== 'hb'
      ? msg[msg.length - 2]
      : msg[msg.length - 1]

    if (!_isFinite(authSeq)) {
      debug('recv public seq # %d', seq)
    } else {
      debug('recv public seq # %d, auth seq # %d', seq, authSeq)
    }
  })
})
<commit_msg>uodate 'sequencing' example<commit_after>'use strict'

const _isArray = require('lodash/isArray')
const _isFinite = require('lodash/isFinite')
const { args: { apiKey, apiSecret }, debug } = require('../util/setup')
const WSv2 = require('../../lib/transports/ws2')

async function execute () {
  const ws = new WSv2({
    apiKey,
    apiSecret,
    transform: true
  })
  ws.on('error', e => debug('WSv2 error: %s', e.message | e))
  await ws.open()
  await ws.auth()

  // Enables internal sequence tracking; an error will be emitted if there is a
  // seq # mis-match
  await ws.enableSequencing({ audit: true })

  if (!ws.isFlagEnabled(65536)) {
    throw new Error('seq enable succeeded, but flag not updated')
  }

  debug('sequencing enabled')

  await ws.subscribeTrades('tBTCUSD')

  ws.on('message', (msg) => {
    if (!_isArray(msg)) return // only array messages have sequence #s

    // auth seq number, available as the last element on chan 0 packets
    const authSeq = msg[0] === 0 && msg[1] !== 'hb'
      ? msg[msg.length - 1]
      : NaN

    // public seq number, last or 2nd to last element on all packets
    const seq = msg[0] === 0 && msg[1] !== 'hb'
      ? msg[msg.length - 2]
      : msg[msg.length - 1]

    if (!_isFinite(authSeq)) {
      debug('recv public seq # %d', seq)
    } else {
      debug('recv public seq # %d, auth seq # %d', seq, authSeq)
    }
  })
  await ws.close()
}

execute()
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {Panel, FormGroup, Button, InputGroup, FormControl } from 'react-bootstrap';
import Nameday from './Nameday';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';

class AddConnection extends Component {
  constructor(){
    super();
    this.state = {
      newConnection: {
        id: '',
        name: '',
        phone: '',
        birthday: '',
        nameday: {
          nameday_id: '10',
          date: null
        },
      },
      date: '',
      nameday_date: null, // variable that updates through the 'Nameday' component
      newNameday_id: '10',
      controlId: null,
      validationState: null,
      nameChange: '' // variable passed to the 'Nameday' component that updates every time the connection name changes
    }
    this.handleNameChange = this.handleNameChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.onNamedayChange = this.onNamedayChange.bind(this);
  }

  // handler to control date change of DatePicker
  handleChange(selected) {
    this.setState({
      date: selected,
    })
   }

   // handler to update the nameChange variable every time the connection name changes
  handleNameChange(value) {
    this.setState({nameChange: this.name.value})
  }

  // handler to update the nameday_date variable each time a new date is selected in the 'Nameday' component
  onNamedayChange(date, id){
    this.setState({
      nameday_date: date,
      newNameday_id: id
      });
  }

  handleSubmit(e, id){
    // name, phone validation
    // FIXME: refactor code to be DRY
    const pattern_name = /^\s+$/;
    const pattern_phone = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;

    // sets the birthday date format to a uniform type of DD/MM/YYYY
    let newBirthday
    if(typeof this.state.date === 'object') {
      newBirthday = this.state.date.format().toString().slice(8,10)+'-'+this.state.date.format().toString().slice(5,7)+'-'+this.state.date.format().toString().slice(0,4)
    } else {
      newBirthday = this.state.date
    }

    if(pattern_name.test(this.name.value) || this.name.value === ''){
      alert('Name is required');
      if (!pattern_phone.test(this.phone.value) && this.phone.value!==""){
        this.setState({controlId: "formValidationError1", validationState: "error"})
      } else {
        this.setState({controlId: null, validationState: null})
      }
    } else if (!pattern_phone.test(this.phone.value) && this.phone.value!==""){
      this.setState({controlId: "formValidationError1", validationState: "error"})
    } else {

        this.setState({
          newConnection: {
            id: this.props.parent.connections.length === 0 ? 1 : this.props.parent.connections[Object.keys(this.props.parent.connections).length - 1].id+1,
            name: this.name.value,
            phone: this.phone.value,
            birthday: newBirthday,
            nameday: {
              nameday_id: this.state.newNameday_id,
              date: this.state.nameday_date,
            },
          },
        }, function(){
          let connections = [ ...this.props.parent.connections, this.state.newConnection];
          this.props.callbackParent(this.props.id, connections);
        });
        this.name.value = '';
        this.phone.value = '';

        this.setState({
          controlId: null,
          validationState: null,
          nameChange: ''
        });

    }

    e.preventDefault();
  }

  render() {
    return (

          Add {this.props.parent.firstName}'s Connection

        <Panel>
          <form onSubmit={this.handleSubmit}>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                <FormControl
                  type="text"
                  placeholder="Add Connection's Name"
                  inputRef={(ref) => {this.name = ref}}
                  onBlur={this.handleNameChange}
                />
              </InputGroup>
            </FormGroup>
            <FormGroup controlId={this.state.controlId} validationState={this.state.validationState}>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                  <FormControl
                    type="text"
                    placeholder="Add Connection's Phone Number"
                    inputRef={(ref) => {this.phone = ref}}
                  />
              </InputGroup>
            </FormGroup>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                <DatePicker
                  selected={this.state.date}
                  onChange={this.handleChange}
                  dateFormat="DD/MM/YYYY"
                  className="form-control"
                  placeholderText="Add Connection's Birthday"
                />
              </InputGroup>
            </FormGroup>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                  <Nameday name={this.state.nameChange} callbackNameday={this.onNamedayChange} onList={false} />
              </InputGroup>
            </FormGroup>

            <Button className="custom-submit" type="submit">Submit</Button>
          </form>
        </Panel>

    );
  }
}

export default AddConnection;
<commit_msg>switch to arrow functions on AddConnection<commit_after>import React, { Component } from 'react';
import {Panel, FormGroup, Button, InputGroup, FormControl } from 'react-bootstrap';
import Nameday from './Nameday';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';

class AddConnection extends Component {
  constructor(){
    super();
    this.state = {
      newConnection: {
        id: '',
        name: '',
        phone: '',
        birthday: '',
        nameday: {
          nameday_id: '10',
          date: null
        },
      },
      date: '',
      nameday_date: null, // variable that updates through the 'Nameday' component
      newNameday_id: '10',
      controlId: null,
      validationState: null,
      nameChange: '' // variable passed to the 'Nameday' component that updates every time the connection name changes
    }
  }

  // handler to control date change of DatePicker
  handleChange = selected => this.setState({date: selected,})

   // handler to update the nameChange variable every time the connection name changes
  handleNameChange = value => this.setState({nameChange: this.name.value})

  // handler to update the nameday_date variable each time a new date is selected in the 'Nameday' component
  onNamedayChange = (date, id) => this.setState({ nameday_date: date, newNameday_id: id })

  handleSubmit = (e, id) => {
    // name, phone validation
    // FIXME: refactor code to be DRY
    const pattern_name = /^\s+$/;
    const pattern_phone = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;

    // sets the birthday date format to a uniform type of DD/MM/YYYY
    let newBirthday
    if(typeof this.state.date === 'object') {
      newBirthday = this.state.date.format().toString().slice(8,10)+'-'+this.state.date.format().toString().slice(5,7)+'-'+this.state.date.format().toString().slice(0,4)
    } else {
      newBirthday = this.state.date
    }

    if(pattern_name.test(this.name.value) || this.name.value === ''){
      alert('Name is required');
      if (!pattern_phone.test(this.phone.value) && this.phone.value!==""){
        this.setState({controlId: "formValidationError1", validationState: "error"})
      } else {
        this.setState({controlId: null, validationState: null})
      }
    } else if (!pattern_phone.test(this.phone.value) && this.phone.value!==""){
      this.setState({controlId: "formValidationError1", validationState: "error"})
    } else {

        this.setState({
          newConnection: {
            id: this.props.parent.connections.length === 0 ? 1 : this.props.parent.connections[Object.keys(this.props.parent.connections).length - 1].id+1,
            name: this.name.value,
            phone: this.phone.value,
            birthday: newBirthday,
            nameday: {
              nameday_id: this.state.newNameday_id,
              date: this.state.nameday_date,
            },
          },
        }, function(){
          let connections = [ ...this.props.parent.connections, this.state.newConnection];
          this.props.callbackParent(this.props.id, connections);
        });
        this.name.value = '';
        this.phone.value = '';

        this.setState({
          controlId: null,
          validationState: null,
          nameChange: ''
        });

    }

    e.preventDefault();
  }

  render() {
    return (

          Add {this.props.parent.firstName}'s Connection

        <Panel>
          <form onSubmit={this.handleSubmit}>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                <FormControl
                  type="text"
                  placeholder="Add Connection's Name"
                  inputRef={(ref) => {this.name = ref}}
                  onBlur={this.handleNameChange}
                />
              </InputGroup>
            </FormGroup>
            <FormGroup controlId={this.state.controlId} validationState={this.state.validationState}>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                  <FormControl
                    type="text"
                    placeholder="Add Connection's Phone Number"
                    inputRef={(ref) => {this.phone = ref}}
                  />
              </InputGroup>
            </FormGroup>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                <DatePicker
                  selected={this.state.date}
                  onChange={this.handleChange}
                  dateFormat="DD/MM/YYYY"
                  className="form-control"
                  placeholderText="Add Connection's Birthday"
                />
              </InputGroup>
            </FormGroup>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon className='glyph-input'></InputGroup.Addon>
                  <Nameday name={this.state.nameChange} callbackNameday={this.onNamedayChange} onList={false} />
              </InputGroup>
            </FormGroup>

            <Button className="custom-submit" type="submit">Submit</Button>
          </form>
        </Panel>

    );
  }
}

export default AddConnection;
<|endoftext|>


<commit_before>import React from 'react';
import { FlatList, Text, TouchableOpacity, StyleSheet, View } from 'react-native';
import I18n from '../../i18n/i18n';
import AlertsApi from '../../api/AlertsApi';
import { renderHeader, renderFooter, handleLoadMore } from '../common/listUtils';
import { NoResults } from '../common/NoResults';
import COMMON_STYLES from '../../styles/common';

export default class Alerts extends React.Component {
  state = {
    data: [],
    total: 0,
    page: 1,
    lastPage: false,
    initialLoading: true,
    loadingMore: false,
  };

  componentDidMount() {
    this.alertsApi = new AlertsApi();
    this.loadData();
  }

  loadData() {
    if (this.state.page > 1) {
      this.setState({ loadingMore: true });
    }

    this.alertsApi.getAlerts(this.state.page).then((response) => {
      this.setData(response);
    });
  }

  setData(response) {
    this.setState({
      data: [...this.state.data, ...response.data],
      total: response.total,
      initialLoading: false,
      loadingMore: false,
      lastPage: !response.next_page_url,
    });
  }

  goToProfile(item) {
    this.props.navigation.navigate('Profile', { id: item.user_id });
  }

  render() {
    return (
      <View style={styles.container}>
        {this.state.total === 0 &&
          !this.state.initialLoading && <NoResults text={I18n.t('validators.no_alerts_found')} />}

        {(this.state.total > 0 || this.state.initialLoading) && (
          <FlatList
            data={this.state.data}
            ListHeaderComponent={renderHeader.bind(this)}
            ListFooterComponent={renderFooter.bind(this)}
            onEndReached={handleLoadMore.bind(this)}
            onEndReachedThreshold={0.5}
            keyExtractor={item => item.id}
            renderItem={({ item }) => (
              <TouchableOpacity
                activeOpacity={0.9}
                style={styles.itemBox}
                onPress={() => this.goToProfile(item)}
              >
                <Text style={styles.title}>{item.student}</Text>

                <Text>
                  {I18n.t('reg-profile.country')}: {item.country}
                </Text>

                <Text>
                  {I18n.t('validators.study_level')}: {item.study_level}
                </Text>

                <Text>
                  {I18n.t('validators.when_it_was_sent')}: {item.when_alert_relative}
                </Text>
              </TouchableOpacity>
            )}
          />
        )}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: COMMON_STYLES.LIGHT_GRAY,
  },
  itemBox: {
    margin: 5,
    padding: 15,
    backgroundColor: COMMON_STYLES.WHITE,
    borderColor: COMMON_STYLES.GRAY,
    borderWidth: StyleSheet.hairlineWidth,
    shadowRadius: 2,
    elevation: 1,
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
  },
});
<commit_msg>Add 'goBack' button when enter in a profile from Alerts<commit_after>import React from 'react';
import { FlatList, Text, TouchableOpacity, StyleSheet, View } from 'react-native';
import I18n from '../../i18n/i18n';
import AlertsApi from '../../api/AlertsApi';
import { renderHeader, renderFooter, handleLoadMore } from '../common/listUtils';
import { NoResults } from '../common/NoResults';
import COMMON_STYLES from '../../styles/common';

export default class Alerts extends React.Component {
  state = {
    data: [],
    total: 0,
    page: 1,
    lastPage: false,
    initialLoading: true,
    loadingMore: false,
  };

  componentDidMount() {
    this.alertsApi = new AlertsApi();
    this.loadData();
  }

  loadData() {
    if (this.state.page > 1) {
      this.setState({ loadingMore: true });
    }

    this.alertsApi.getAlerts(this.state.page).then((response) => {
      this.setData(response);
    });
  }

  setData(response) {
    this.setState({
      data: [...this.state.data, ...response.data],
      total: response.total,
      initialLoading: false,
      loadingMore: false,
      lastPage: !response.next_page_url,
    });
  }

  goToProfile(item) {
    this.props.navigation.navigate('Profile', { id: item.user_id, goBack: true });
  }

  render() {
    return (
      <View style={styles.container}>
        {this.state.total === 0 &&
          !this.state.initialLoading && <NoResults text={I18n.t('validators.no_alerts_found')} />}

        {(this.state.total > 0 || this.state.initialLoading) && (
          <FlatList
            data={this.state.data}
            ListHeaderComponent={renderHeader.bind(this)}
            ListFooterComponent={renderFooter.bind(this)}
            onEndReached={handleLoadMore.bind(this)}
            onEndReachedThreshold={0.5}
            keyExtractor={item => item.id}
            renderItem={({ item }) => (
              <TouchableOpacity
                activeOpacity={0.9}
                style={styles.itemBox}
                onPress={() => this.goToProfile(item)}
              >
                <Text style={styles.title}>{item.student}</Text>

                <Text>
                  {I18n.t('reg-profile.country')}: {item.country}
                </Text>

                <Text>
                  {I18n.t('validators.study_level')}: {item.study_level}
                </Text>

                <Text>
                  {I18n.t('validators.when_it_was_sent')}: {item.when_alert_relative}
                </Text>
              </TouchableOpacity>
            )}
          />
        )}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: COMMON_STYLES.LIGHT_GRAY,
  },
  itemBox: {
    margin: 5,
    padding: 15,
    backgroundColor: COMMON_STYLES.WHITE,
    borderColor: COMMON_STYLES.GRAY,
    borderWidth: StyleSheet.hairlineWidth,
    shadowRadius: 2,
    elevation: 1,
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
  },
});
<|endoftext|>


<commit_before>import React from 'react'
import { IndexLink, Link } from 'react-router'
import { Navbar,Nav,NavItem,NavDropdown,MenuItem,ButtonToolbar,ButtonGroup,Button } from 'react-bootstrap';
import './Header.scss'

export const Header = () => (

  //

  //   <Link to='/pattern' activeClassName='route--active'>
  //     Pattern
  //   </Link>
  //   {' · '}
  //   <Link to='/pianoroll' activeClassName='route--active'>
  //     Pianoroll
  //   </Link>
  //   {' · '}
  //   <Link to='/instrument' activeClassName='route--active'>
  //     Instrument
  //   </Link>
  //   {' · '}
  //   <Link to='/mixer' activeClassName='route--active'>
  //     Mixer
  //   </Link>
  //

  <Navbar default>
    <Navbar.Brand>
      BentoBeats
    </Navbar.Brand>

      <NavDropdown eventKey={3} title="File" id="basic-nav-dropdown">
        <MenuItem eventKey={3.1}>Action</MenuItem>
        <MenuItem eventKey={3.2}>Another action</MenuItem>
        <MenuItem eventKey={3.3}>Something else here</MenuItem>
        <MenuItem divider />
        <MenuItem eventKey={3.3}>Separated link</MenuItem>
      </NavDropdown>

      <ButtonToolbar className="navbar-btn">
        <ButtonGroup>
          <Button>Pattern</Button>
          <Button>Pianoroll</Button>
          <Button>Instrument</Button>
          <Button>Mixer</Button>
        </ButtonGroup>
        <ButtonGroup>
          <Button>

          </Button>
        </ButtonGroup>
      </ButtonToolbar>

  </Navbar>
)

export default Header
<commit_msg>Unrounded navbar.<commit_after>import React from 'react'
import { IndexLink, Link } from 'react-router'
import { Navbar,Nav,NavItem,NavDropdown,MenuItem,ButtonToolbar,ButtonGroup,Button } from 'react-bootstrap';
import './Header.scss'

export const Header = () => (

  //

  //   <Link to='/pattern' activeClassName='route--active'>
  //     Pattern
  //   </Link>
  //   {' · '}
  //   <Link to='/pianoroll' activeClassName='route--active'>
  //     Pianoroll
  //   </Link>
  //   {' · '}
  //   <Link to='/instrument' activeClassName='route--active'>
  //     Instrument
  //   </Link>
  //   {' · '}
  //   <Link to='/mixer' activeClassName='route--active'>
  //     Mixer
  //   </Link>
  //

  <Navbar className="navbar navbar-default navbar-fixed-top">
    <Navbar.Brand>
      BentoBeats
    </Navbar.Brand>

      <NavDropdown eventKey={3} title="File" id="basic-nav-dropdown">
        <MenuItem eventKey={3.1}>Action</MenuItem>
        <MenuItem eventKey={3.2}>Another action</MenuItem>
        <MenuItem eventKey={3.3}>Something else here</MenuItem>
        <MenuItem divider />
        <MenuItem eventKey={3.3}>Separated link</MenuItem>
      </NavDropdown>

      <ButtonToolbar className="navbar-btn">
        <ButtonGroup>
          <Button>Pattern</Button>
          <Button>Pianoroll</Button>
          <Button>Instrument</Button>
          <Button>Mixer</Button>
        </ButtonGroup>
        <ButtonGroup>
          <Button>

          </Button>
        </ButtonGroup>
      </ButtonToolbar>

  </Navbar>
)

export default Header
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import './style.css'
import classnames from 'classnames';

export default class Players extends Component {
    //Props: SetAlert gamename players SetActivePlayer activehole ReloadPlayers
    constructor(props){
        super(props)
        this.state = {
            activeplayer: null,
        }
        this.SetActive       = this.SetActive.bind(this);
        this.UpdateScoreUp   = this.UpdateScoreUp.bind(this);
        this.UpdateScoreDown = this.UpdateScoreDown.bind(this);
    }
    SetActive(e){
        e.preventDefault();
        var n = e.target.getAttribute('data-value')
        console.log("Person: active:" + n);
        this.setState({activeplayer: n});
        this.props.SetActivePlayer(n);
    }
    UpdateScoreUp(e){
        e.preventDefault();
        var name = e.target.getAttribute('data-value')
        this.UpdateScore(this.props.gamename,name,'up')
    }
    UpdateScoreDown(e){
        e.preventDefault();
        var name = e.target.getAttribute('data-value')
        this.UpdateScore(this.props.gamename,name,'down')
    }
    UpdateScore(game, player, dir){
        var that = this;
        fetch(process.env.REACT_APP_BASE_URL+
            "/api/score/"+that.props.gamename+"/"+player+"/"+dir,
                {method: 'put'})
                .then(function(res){
                    return res.json();
                })
                .then(function(data){
                    console.log(JSON.stringify(data))
                    that.props.ReloadPlayers()
                })
        console.log("Players: updating " + game + " - " + player + "hole: " + this.props.activehole + " - " + dir)
    };
    render(){
        const { className, ...props } = this.props;
        //console.log("Players length, gamename " + JSON.stringify(this.props.players) + " --- " + this.props.gamename)
        var players = [];
        var divcolor, player = null;
        var that = this
        this.props.players.map(function(p){
            if (p.name == that.state.activeplayer){
                divcolor = { 'background-color': 'lightgray' }
            } else {
                divcolor = { 'background-color': 'white' }
            }
            if (that.props.activehole == null) {
                var score = 0
                if (p.course != [] && p.course != undefined){
                    p.course.reduce(function(acc, val){
                        acc += val
                    }, score)
                }
                player = (

{ score }

                )
            } else {
                var tmp = p.course[that.props.activehole]
                if (!tmp){
                    tmp = 0;
                }
                player = (

                        {tmp}

                )
            }
            players.push(
                //TODO: This seems like a large push for the component to render
                //especially when the player does not need to be selected in order to increase the score
                // should probably make this a spearate component ...

                        {p.name}

                    {player}

)
        })
        return(

                {players}

        )
    }
}
<commit_msg>Rest of the front end<commit_after>import React, { PropTypes, Component } from 'react';
import './style.css'
import classnames from 'classnames';

export default class Players extends Component {
    //Props: SetAlert gamename players SetActivePlayer activehole ReloadPlayers
    constructor(props){
        super(props)
        this.state = {
            activeplayer: null,
        }
        this.SetActive       = this.SetActive.bind(this);
        this.UpdateScoreUp   = this.UpdateScoreUp.bind(this);
        this.UpdateScoreDown = this.UpdateScoreDown.bind(this);
    }
    SetActive(e){
        e.preventDefault();
        var n = e.target.getAttribute('data-value')
        console.log("Person: active:" + n);
        this.setState({activeplayer: n});
        this.props.SetActivePlayer(n);
    }
    UpdateScoreUp(e){
        e.preventDefault();
        var name = e.target.getAttribute('data-value')
        this.UpdateScore(this.props.gamename,name,'up')
    }
    UpdateScoreDown(e){
        e.preventDefault();
        var name = e.target.getAttribute('data-value')
        this.UpdateScore(this.props.gamename,name,'down')
    }
    UpdateScore(game, player, dir){
        var that = this;
        fetch(process.env.REACT_APP_BASE_URL+
            "/api/score/"+that.props.gamename+"/"+player+"/"+dir,
                {method: 'put'})
                .then(function(res){
                    return res.json();
                })
                .then(function(data){
                    console.log(JSON.stringify(data))
                    that.props.ReloadPlayers()
                })
        console.log("Players: updating " + game + " - " + player + "hole: " + this.props.activehole + " - " + dir)
    };
    render(){
        const { className, ...props } = this.props;
        //console.log("Players length, gamename " + JSON.stringify(this.props.players) + " --- " + this.props.gamename)
        var players = [];
        var divcolor, activescore, player = null;
        var that = this
        this.props.players.map(function(p){
            if (p.name == that.state.activeplayer){
                divcolor = { 'background-color': 'lightgray' }
            } else {
                divcolor = { 'background-color': 'white' }
            }
            if (that.props.activehole == null) {
                var score = 0
                //TODO: good time to go and reload players' scores if necessary
                if (p.course != [] && p.course != undefined){
                    score = p.course.reduce(function(acc, val){
                        return acc += val
                    }, 0)
                }
                player = (

{ score }

                )
            } else {
                var score = p.course[that.props.activehole]
                if (!score){
                    score = 0;
                }
                player = (

                        {score}

                )
            }
            players.push(
                //TODO: This seems like a large push for the component to render
                //especially when the player does not need to be selected in order to increase the score
                // should probably make this a spearate component ...

                        {p.name}

                    {player}

)
        })
        return(

                {players}

        )
    }
}
<|endoftext|>


<commit_before>import React from 'react'
import Logo from './logo'
import styled from 'styled-components'
import Link from 'gatsby-link'
import {MobileHide} from '../MobileHide'

const Sidebar = ({ className, color, bgColor }) => (

    <Link to="/">
      <Logo fill={color || "#000"} hoverfill={color ? '#CCC' : "#0000FF"} />
    </Link>
    <MobileHide>

contact me via

        info@halfof8.com

        fb{' '}
        instagram

    </MobileHide>

)

const SidebarStyled = styled(Sidebar)`
  min-width: 250px;
  width: 15.5%;
  height: 100%;
  position: fixed;
  padding-top: 26px;
  flex-shrink: 0;
  flex-grow: 0;
  text-align: center;
  font-size: 13px;
  background-color: ${props => props.bgColor ? props.bgColor : '#fff'};
  line-height: 1.7;
  max-width: 320px;
  p {
    color: ${props => props.color ? props.color : '#001d60'};
  }
  ${props => props.color ?
  `a{
    color ${props.color}
  }`
  : ''}
  @media(max-width: 560px){
    position: relative;
  }
`

export default SidebarStyled
<commit_msg>no contacts at the sidebar<commit_after>import React from 'react'
import Logo from './logo'
import styled from 'styled-components'
import Link from 'gatsby-link'
import {MobileHide} from '../MobileHide'

const Sidebar = ({ className, color, bgColor }) => (

    <Link to="/">
      <Logo fill={color || "#000"} hoverfill={color ? '#CCC' : "#0000FF"} />
    </Link>
    <MobileHide>

    </MobileHide>

)

const SidebarStyled = styled(Sidebar)`
  min-width: 250px;
  width: 15.5%;
  height: 100%;
  position: fixed;
  padding-top: 26px;
  flex-shrink: 0;
  flex-grow: 0;
  text-align: center;
  font-size: 13px;
  background-color: ${props => props.bgColor ? props.bgColor : '#fff'};
  line-height: 1.7;
  max-width: 320px;
  p {
    color: ${props => props.color ? props.color : '#001d60'};
  }
  ${props => props.color ?
  `a{
    color ${props.color}
  }`
  : ''}
  @media(max-width: 560px){
    position: relative;
  }
`

export default SidebarStyled
<|endoftext|>


<commit_before>import React from 'react'
import Logo from './logo'
import styled from 'styled-components'
import Link from 'gatsby-link'
import {MobileHide} from '../MobileHide'
import { TypingText } from "../components/Intro";

const Sidebar = ({ className, color, bgColor }) => (

    <Link to="/">
      <Logo fill={color || "#000"} hoverfill={color ? '#CCC' : "#0000FF"} />
    </Link>
    <MobileHide>

Half of Eight = <TypingText />

    </MobileHide>

)

const SidebarStyled = styled(Sidebar)`
  min-width: 250px;
  width: 15.5%;
  height: 100%;
  position: fixed;
  padding-top: 26px;
  flex-shrink: 0;
  flex-grow: 0;
  text-align: center;
  font-size: 13px;
  background-color: ${props => props.bgColor ? props.bgColor : '#fff'};
  line-height: 1.7;
  max-width: 320px;
  p {
    color: ${props => props.color ? props.color : '#001d60'};
  }
  ${props => props.color ?
  `a{
    color ${props.color}
  }`
  : ''}
  @media(max-width: 560px){
    position: relative;
    width: 100%;
    margin: 0 auto;
  }
`

export default SidebarStyled
<commit_msg>typing text in sidebar<commit_after>import React from 'react'
import Logo from './logo'
import styled from 'styled-components'
import Link from 'gatsby-link'
import {MobileHide} from '../MobileHide'
import { TypingText } from "../Intro";

const Sidebar = ({ className, color, bgColor }) => (

    <Link to="/">
      <Logo fill={color || "#000"} hoverfill={color ? '#CCC' : "#0000FF"} />
    </Link>
    <MobileHide>

Half of Eight = <TypingText />

    </MobileHide>

)

const SidebarStyled = styled(Sidebar)`
  min-width: 250px;
  width: 15.5%;
  height: 100%;
  position: fixed;
  padding-top: 26px;
  flex-shrink: 0;
  flex-grow: 0;
  text-align: center;
  font-size: 13px;
  background-color: ${props => props.bgColor ? props.bgColor : '#fff'};
  line-height: 1.7;
  max-width: 320px;
  p {
    color: ${props => props.color ? props.color : '#001d60'};
  }
  ${props => props.color ?
  `a{
    color ${props.color}
  }`
  : ''}
  @media(max-width: 560px){
    position: relative;
    width: 100%;
    margin: 0 auto;
  }
`

export default SidebarStyled
<|endoftext|>